Helper functions user_ptr_may_set_prot() and user_ptr_perms_from_prot() are added/modified to manage capability permissions in memory management syscalls as per PCuABI specifications.
Also, use arch-specific hook arch_user_ptr_perms_from_prot() to convert arch-specific mapping protection to capability permissions.
Signed-off-by: Amit Daniel Kachhap amitdaniel.kachhap@arm.com --- Documentation/core-api/user_ptr.rst | 1 + arch/Kconfig | 3 ++ include/linux/user_ptr.h | 16 +++++++ lib/user_ptr.c | 66 ++++++++++++++++++++++++++--- 4 files changed, 81 insertions(+), 5 deletions(-)
diff --git a/Documentation/core-api/user_ptr.rst b/Documentation/core-api/user_ptr.rst index 627bcea2a07e..a85112a5ba7b 100644 --- a/Documentation/core-api/user_ptr.rst +++ b/Documentation/core-api/user_ptr.rst @@ -358,5 +358,6 @@ implementation, such as compat64 mode. * ``check_user_ptr_owning(ptr, addr, n)`` * ``make_user_ptr_owning(addr, n, perm)`` * ``user_ptr_perms_from_prot(prot, tag_perm)`` +* ``user_ptr_may_set_prot(ptr, prot)``
See ``<linux/user_ptr.h>`` for details on how to use them. diff --git a/arch/Kconfig b/arch/Kconfig index 19f7bbb20a41..161f7002b0ab 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -1502,6 +1502,9 @@ config CHERI_PURECAP_UABI availability of CHERI capabilities at compile-time; the resulting kernel image will not boot on incompatible hardware.
+config HAVE_ARCH_USER_PTR_H + bool + source "kernel/gcov/Kconfig"
source "scripts/gcc-plugins/Kconfig" diff --git a/include/linux/user_ptr.h b/include/linux/user_ptr.h index 1eb59442b06e..993635f2eda1 100644 --- a/include/linux/user_ptr.h +++ b/include/linux/user_ptr.h @@ -142,6 +142,17 @@ user_uintptr_t make_user_ptr_owning(ptraddr_t addr, size_t len, user_ptr_perms_t */ user_ptr_perms_t user_ptr_perms_from_prot(int prot, bool has_tag_access);
+/** + * user_ptr_may_set_prot() - Verify if the mapping protection flags confirms + * with the capability permission flags. + * @user_ptr: User pointer. + * @prot: Memory protection flag. + * + * Return: True if the capability permissions includes the protection flags + * or false otherwise. + */ +bool user_ptr_may_set_prot(user_uintptr_t user_ptr, int prot); + #else /* CONFIG_CHERI_PURECAP_UABI */
#define user_ptr_perms_t int @@ -197,6 +208,11 @@ static inline user_ptr_perms_t user_ptr_perms_from_prot(int prot, bool has_tag_a return 0; }
+static inline bool user_ptr_may_set_prot(user_uintptr_t user_ptr, int prot) +{ + return true; +} + #endif /* CONFIG_CHERI_PURECAP_UABI */
/** diff --git a/lib/user_ptr.c b/lib/user_ptr.c index f597f73191bb..6cab8f8864d8 100644 --- a/lib/user_ptr.c +++ b/lib/user_ptr.c @@ -2,9 +2,14 @@ #include <linux/bug.h> #include <linux/cap_addr_mgmt.h> #include <linux/cheri.h> +#include <linux/mman.h> #include <linux/sched.h> #include <linux/user_ptr.h>
+#ifdef CONFIG_HAVE_ARCH_USER_PTR_H +#include <asm/user_ptr.h> +#endif + void __user *uaddr_to_user_ptr(ptraddr_t addr) { /* @@ -100,10 +105,61 @@ user_uintptr_t make_user_ptr_owning(ptraddr_t addr, size_t len, user_ptr_perms_t return cheri_address_set(user_ptr, addr); }
-user_ptr_perms_t user_ptr_perms_from_prot(int prot __maybe_unused, - bool has_tag_access __maybe_unused) +static 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); +} + +#ifndef arch_user_ptr_perms_from_prot +static __always_inline user_ptr_perms_t arch_user_ptr_perms_from_prot(int prot, bool has_tag_access) { - /* TODO [PCuABI] - capability permission conversion from memory permission */ - return (CHERI_PERMS_READ | CHERI_PERMS_WRITE | - CHERI_PERMS_EXEC | CHERI_PERMS_ROOTCAP); + return 0; +} +#define arch_user_ptr_perms_from_prot arch_user_ptr_perms_from_prot +#endif /* arch_user_ptr_perms_from_prot */ + +user_ptr_perms_t user_ptr_perms_from_prot(int prot, bool has_tag_access) +{ + user_ptr_perms_t perms = 0; + + if (!reserv_is_supported(current->mm)) + return perms; + 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; + + /* Fetch any extra architecture specific permissions */ + perms |= arch_user_ptr_perms_from_prot(PROT_MAX_EXTRACT(prot) ? + PROT_MAX_EXTRACT(prot) : prot, has_tag_access); + perms |= CHERI_PERMS_ROOTCAP; + + return perms; +} + +bool user_ptr_may_set_prot(user_uintptr_t user_ptr, int prot) +{ + user_ptr_perms_t perms = cheri_perms_get(user_ptr); + + if (!reserv_is_supported(current->mm)) + 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; }