Helper functions capability_may_set_prot(), mapping_to_capability_perm() and build_owning_capability() are added/modified to manage capability permissions in address space management syscalls as per PCuABI specifications.
Also a arch specific hook arch_map_to_cap_perm() is added to manage arch specific capability permissions.
Signed-off-by: Amit Daniel Kachhap amitdaniel.kachhap@arm.com --- arch/arm64/include/asm/cap_addr_mgmt.h | 22 +++++++++++ include/linux/cap_addr_mgmt.h | 20 ++++++++++ mm/cap_addr_mgmt.c | 54 +++++++++++++++++++++++--- 3 files changed, 91 insertions(+), 5 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 6a42e714ecd5..81125cfe74a8 100644 --- a/include/linux/cap_addr_mgmt.h +++ b/include/linux/cap_addr_mgmt.h @@ -9,6 +9,7 @@ #include <linux/mm_types.h> #include <linux/sched/coredump.h> #include <linux/types.h> +#include <asm/cap_addr_mgmt.h>
#ifdef CONFIG_CHERI_PURECAP_UABI #define CHERI_REPRESENTABLE_ALIGNMENT(len) \ @@ -165,6 +166,17 @@ bool capability_owns_range(user_uintptr_t cap, ptraddr_t addr, size_t len); */ user_uintptr_t build_owning_capability(ptraddr_t addr, size_t len, cheri_perms_t perm);
+/** + * capability_may_set_prot() - Verify if the mapping protection flags confirms + * with the capability permission flags. + * @cap: Capability value. + * @prot: Memory protection flags. + * + * Return: True if the capability permissions includes the protection flags + * or false otherwise. + */ +bool capability_may_set_prot(user_uintptr_t cap, int prot); + /** * mapping_to_capability_perm() - Converts memory mapping protection flags to * capability permission flags. @@ -245,4 +257,12 @@ static inline cheri_perms_t mapping_to_capability_perm(int prot, bool has_tag_ac
#endif /* CONFIG_CHERI_PURECAP_UABI */
+#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 /* _LINUX_CAP_ADDR_MGMT_H */ diff --git a/mm/cap_addr_mgmt.c b/mm/cap_addr_mgmt.c index 0112b2136755..5ff4fdf26d28 100644 --- a/mm/cap_addr_mgmt.c +++ b/mm/cap_addr_mgmt.c @@ -199,12 +199,56 @@ user_uintptr_t build_owning_capability(ptraddr_t addr, size_t len, cheri_perms_t return cheri_address_set(ret, addr); }
-cheri_perms_t mapping_to_capability_perm(int prot __maybe_unused, - bool has_tag_access __maybe_unused) +static bool mapping_may_have_prot_flag(int prot, int map_val) { - /* TODO [PCuABI] - capability permission conversion from memory permission */ - return (CHERI_PERMS_READ | CHERI_PERMS_WRITE | - CHERI_PERMS_EXEC | CHERI_PERMS_ROOTCAP); + 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(user_uintptr_t cap, int prot) +{ + cheri_perms_t perms = cheri_perms_get(cap); + + if (!test_bit(MMF_PCUABI_RESERVE, ¤t->mm->flags)) + return true; + + 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; +} + +cheri_perms_t mapping_to_capability_perm(int prot, bool has_tag_access) +{ + 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; + } + /* 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 perms; }
#endif /* CONFIG_CHERI_PURECAP_UABI */