Helper functions such as mapping_may_have_prot_flag(), capability_may_set_prot() and build_owning_capability() are added/modified to manage capability permissions in address space management syscalls as per PCuABI specifications.
Signed-off-by: Amit Daniel Kachhap amit.kachhap@arm.com --- arch/arm64/include/asm/cap_addr_mgmt.h | 22 ++++++++++++ include/linux/cap_addr_mgmt.h | 35 +++++++++++++++++- lib/cap_addr_mgmt.c | 49 +++++++++++++++++++++++--- mm/mmap.c | 3 +- mm/mremap.c | 6 ++-- 5 files changed, 107 insertions(+), 8 deletions(-) create mode 100644 arch/arm64/include/asm/cap_addr_mgmt.h
diff --git a/arch/arm64/include/asm/cap_addr_mgmt.h b/arch/arm64/include/asm/cap_addr_mgmt.h new file mode 100644 index 000000000000..aadb4768d2fd --- /dev/null +++ b/arch/arm64/include/asm/cap_addr_mgmt.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef __ASM_CAP_ADDR_MGMT_H +#define __ASM_CAP_ADDR_MGMT_H + +#include <linux/cheri.h> +#include <linux/mman.h> + +static __always_inline cheri_perms_t arch_map_to_cap_perm(int prot, bool has_tag_access) +{ + cheri_perms_t perms = 0; + + if ((prot & PROT_READ) && has_tag_access) + perms |= ARM_CAP_PERMISSION_MUTABLE_LOAD; + + if ((prot & PROT_EXEC) && + (cheri_perms_get(cheri_pcc_get()) & ARM_CAP_PERMISSION_EXECUTIVE)) + perms |= ARM_CAP_PERMISSION_EXECUTIVE; + + return perms; +} +#define arch_map_to_cap_perm arch_map_to_cap_perm +#endif /* __ASM_CAP_ADDR_MGMT_H */ diff --git a/include/linux/cap_addr_mgmt.h b/include/linux/cap_addr_mgmt.h index c8040871316b..3e2865bb95f7 100644 --- a/include/linux/cap_addr_mgmt.h +++ b/include/linux/cap_addr_mgmt.h @@ -8,6 +8,7 @@ #include <linux/types.h>
#ifdef CONFIG_CHERI_PURECAP_UABI +#include <asm/cap_addr_mgmt.h>
struct reserv_mt_entry { unsigned long reserv_start; @@ -124,10 +125,42 @@ bool capability_owns_range(uintcap_t cap, unsigned long addr, unsigned long len) * @addr: Requested capability address. * @len: Requested capability length. * @prot: Requested protection flags. + * @has_tag_access: capability needs tag related permissions. * * Return: A new capability derived from cheri_user_root_cap. */ -uintcap_t build_owning_capability(unsigned long addr, unsigned long len, int prot); +uintcap_t build_owning_capability(unsigned long addr, unsigned long len, int prot, + bool has_tag_access); + +/** + * mapping_may_have_prot_flag() - Verify if the mapping matches with the maximum + * protection flags if present otherwise with the normal protection flags. + * @prot: Complete(normal + maximum) protection flags. + * @map_val: Mapping flags to verify. + * + * Return: True if mapping flag matches with the protection flags or false + * otherwise. + */ +bool mapping_may_have_prot_flag(int prot, int map_val); + +/** + * capability_may_set_prot() - Verify if the mapping protection flags confirms + * with the capability permission flags. + * @cap: Capability value. + * @prot: Normal protection flags. + * + * Return: True if the capability permissions includes the protection flags + * or false otherwise. + */ +bool capability_may_set_prot(uintcap_t cap, int prot); + +#ifndef arch_map_to_cap_perm +static __always_inline cheri_perms_t arch_map_to_cap_perm(int prot, + bool has_tag_access) +{ + return 0; +} +#endif /* arch_map_to_cap_perm */
#endif /* CONFIG_CHERI_PURECAP_UABI */
diff --git a/lib/cap_addr_mgmt.c b/lib/cap_addr_mgmt.c index f6007a4e9c4e..f2e290696e73 100644 --- a/lib/cap_addr_mgmt.c +++ b/lib/cap_addr_mgmt.c @@ -189,14 +189,55 @@ bool capability_owns_range(uintcap_t cap, unsigned long addr, unsigned long len) align_len, CHERI_PERM_GLOBAL | CHERI_PERM_SW_VMEM); }
-uintcap_t build_owning_capability(unsigned long start, unsigned long len, int prot __maybe_unused) +bool mapping_may_have_prot_flag(int prot, int map_val) +{ + int prot_max = PROT_MAX_EXTRACT(prot); + + if (prot_max) + return !!(prot_max & map_val); + else + return !!(prot & map_val); +} + +bool capability_may_set_prot(uintcap_t cap, int prot) +{ + cheri_perms_t perms = cheri_perms_get(cap); + + if (((prot & PROT_READ) && !(perms & CHERI_PERM_LOAD)) || + ((prot & PROT_WRITE) && !(perms & CHERI_PERM_STORE)) || + ((prot & PROT_EXEC) && !(perms & CHERI_PERM_EXECUTE))) + return false; + + return true; +} + +uintcap_t build_owning_capability(unsigned long start, unsigned long len, int prot, + bool has_tag_access) { unsigned long align_start = round_down(start, PAGE_SIZE); unsigned long align_len = cheri_representable_length(round_up(len, PAGE_SIZE)); + cheri_perms_t perms = 0; + + if (mapping_may_have_prot_flag(prot, PROT_READ)) { + perms |= CHERI_PERM_LOAD; + if (has_tag_access) + perms |= CHERI_PERM_LOAD_CAP; + } + if (mapping_may_have_prot_flag(prot, PROT_WRITE)) { + perms |= CHERI_PERM_STORE; + if (has_tag_access) + perms |= (CHERI_PERM_STORE_CAP | CHERI_PERM_STORE_LOCAL_CAP); + } + if (mapping_may_have_prot_flag(prot, PROT_EXEC)) { + perms |= CHERI_PERM_EXECUTE; + if (cheri_perms_get(cheri_pcc_get()) & CHERI_PERM_SYSTEM_REGS) + perms |= CHERI_PERM_SYSTEM_REGS; + }
- /* TODO [PCuABI] - capability permission conversion from memory permission */ - cheri_perms_t perms = CHERI_PERMS_READ | CHERI_PERMS_WRITE | - CHERI_PERMS_EXEC | CHERI_PERMS_ROOTCAP; + /* Fetch any extra architecture specific permissions */ + perms |= arch_map_to_cap_perm(PROT_MAX_EXTRACT(prot) ? PROT_MAX_EXTRACT(prot) : prot, + has_tag_access); + perms |= CHERI_PERMS_ROOTCAP;
return (uintcap_t)cheri_build_user_cap(align_start, align_len, perms); } diff --git a/mm/mmap.c b/mm/mmap.c index 34880a7c3c30..771d99f965da 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -1442,7 +1442,8 @@ user_uintptr_t do_mmap(struct file *file, user_uintptr_t user_addr, ret = reserv_mt_insert_entry(&mm->reserv_mt, addr, len, prot); if (ret) return ret; - user_addr = build_owning_capability(addr, len, prot); + user_addr = build_owning_capability(addr, len, prot, + (flags & MAP_PRIVATE) ? true : false); } else { user_addr = cheri_address_set(user_addr, addr); } diff --git a/mm/mremap.c b/mm/mremap.c index 2ed627c4c25d..57a21cf833a2 100644 --- a/mm/mremap.c +++ b/mm/mremap.c @@ -890,7 +890,8 @@ static user_uintptr_t mremap_to(user_uintptr_t user_addr, unsigned long old_len, if (IS_ERR_VALUE(ret)) reserv_mt_move_entry(&mm->reserv_mt, new_addr, new_len, addr, old_len, &old_perm); else - ret = build_owning_capability(new_addr, new_len, old_perm); + ret = build_owning_capability(new_addr, new_len, old_perm, + (map_flags & MAP_SHARED) ? false : true); #endif out: return ret; @@ -1162,7 +1163,8 @@ SYSCALL_DEFINE5(__retptr__(mremap), user_uintptr_t, user_addr, unsigned long, ol reserv_mt_move_entry(&mm->reserv_mt, new_addr, new_len, addr, old_len, &old_perm); else - ret = build_owning_capability(new_addr, new_len, old_perm); + ret = build_owning_capability(new_addr, new_len, old_perm, + (map_flags & MAP_SHARED) ? false : true); #endif } out: