From: Amit Daniel Kachhap amitdaniel.kachhap@arm.com
user_ptr_owning_perms_from_prot() is modified to calculate capability permissions depending on prot flags and whether tag access is enabled (VM_{READ,WRITE}_CAPS), as per the PCuABI specification.
A new helper function user_ptr_may_set_prot() is introduced to check whether prot flags may be set depending on the capability permissions.
Also introduce an arch hook arch_user_ptr_owning_perms_from_prot() to convert flags to arch-specific capability permissions.
Signed-off-by: Amit Daniel Kachhap amitdaniel.kachhap@arm.com Co-developed-by: Kevin Brodsky kevin.brodsky@arm.com Signed-off-by: Kevin Brodsky kevin.brodsky@arm.com --- Documentation/core-api/user_ptr.rst | 1 + arch/Kconfig | 3 ++ include/linux/user_ptr.h | 16 ++++++++++ lib/user_ptr.c | 46 +++++++++++++++++++++++++++-- 4 files changed, 63 insertions(+), 3 deletions(-)
diff --git a/Documentation/core-api/user_ptr.rst b/Documentation/core-api/user_ptr.rst index 0632bc9f4e8b..902686c27895 100644 --- a/Documentation/core-api/user_ptr.rst +++ b/Documentation/core-api/user_ptr.rst @@ -361,5 +361,6 @@ creating and checking user pointers. * ``check_user_ptr_owning(ptr, len)`` * ``make_user_ptr_owning(reserv, len)`` * ``user_ptr_owning_perms_from_prot(prot, vm_flags)`` +* ``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 a48a294b434e..72b07c6e410a 100644 --- a/include/linux/user_ptr.h +++ b/include/linux/user_ptr.h @@ -152,6 +152,17 @@ user_uintptr_t make_user_ptr_owning(const struct reserv_struct *reserv, */ user_ptr_perms_t user_ptr_owning_perms_from_prot(int prot, unsigned long vm_flags);
+/** + * user_ptr_may_set_prot() - Check if the user pointer allows setting the given + * memory protection flags. + * @user_ptr: User pointer. + * @prot: Memory protection flags. + * + * Return: True if the capability permissions allow setting the protection flags + * or false otherwise. + */ +bool user_ptr_may_set_prot(user_uintptr_t user_ptr, int prot); + #else /* CONFIG_CHERI_PURECAP_UABI */
typedef int user_ptr_perms_t; @@ -210,6 +221,11 @@ static inline user_ptr_perms_t user_ptr_owning_perms_from_prot(int prot, 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 6f7fc9111b1d..c2c53a3bc698 100644 --- a/lib/user_ptr.c +++ b/lib/user_ptr.c @@ -1,9 +1,23 @@ /* SPDX-License-Identifier: GPL-2.0-only */ #include <linux/bug.h> #include <linux/cheri.h> +#include <linux/mman.h> #include <linux/mm_types.h> #include <linux/user_ptr.h>
+#ifdef CONFIG_HAVE_ARCH_USER_PTR_H +#include <asm/user_ptr.h> +#endif + +#ifndef arch_user_ptr_owning_perms_from_prot +static inline +user_ptr_perms_t arch_user_ptr_owning_perms_from_prot(int prot, unsigned long vm_flags) +{ + return 0; +} +#endif /* arch_user_ptr_owning_perms_from_prot */ + + void __user *uaddr_to_user_ptr(ptraddr_t addr) { /* @@ -99,7 +113,33 @@ user_uintptr_t make_user_ptr_owning(const struct reserv_struct *reserv,
user_ptr_perms_t user_ptr_owning_perms_from_prot(int prot, unsigned long vm_flags) { - /* TODO [PCuABI] - capability permission conversion from memory permission */ - return (CHERI_PERMS_READ | CHERI_PERMS_WRITE | - CHERI_PERMS_EXEC | CHERI_PERMS_ROOTCAP); + user_ptr_perms_t perms = CHERI_PERMS_ROOTCAP; + int used_prot = PROT_MAX_EXTRACT(prot) ? PROT_MAX_EXTRACT(prot) : prot; + + if (used_prot & PROT_READ) { + perms |= CHERI_PERM_LOAD; + if (vm_flags & VM_READ_CAPS) + perms |= CHERI_PERM_LOAD_CAP; + } + if (used_prot & PROT_WRITE) { + perms |= CHERI_PERM_STORE; + if (vm_flags & VM_WRITE_CAPS) + perms |= (CHERI_PERM_STORE_CAP | CHERI_PERM_STORE_LOCAL_CAP); + } + if (used_prot & PROT_EXEC) + perms |= CHERI_PERM_EXECUTE; + + /* Fetch any extra architecture specific permissions */ + perms |= arch_user_ptr_owning_perms_from_prot(used_prot, vm_flags); + + 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); + + return !(((prot & PROT_READ) && !(perms & CHERI_PERM_LOAD)) || + ((prot & PROT_WRITE) && !(perms & CHERI_PERM_STORE)) || + ((prot & PROT_EXEC) && !(perms & CHERI_PERM_EXECUTE))); }