From: Amit Daniel Kachhap amitdaniel.kachhap@arm.com
Helper functions check_user_ptr_owning(), make_user_ptr_owning() and user_ptr_owning_perms_from_prot() are added to manage owning capabilities as per the PCuABI specification. These helpers will be mostly used by memory management syscalls to apply the different capability constraints.
* check_user_ptr_owning() checks if the capability owns the input range after page aligning them and has the CHERI_PERM_SW_VMEM permission bit set.
* make_user_ptr_owning() creates the relevant owning capability from the input reservation.
Both of these functions are implemented on top of the cheri_* helpers in linux/cheri.h.
* user_ptr_owning_perms_from_prot() converts memory mapping protection flags and vm_flags to capability permissions.
Note: These helper functions currently check only capability bounds and not capability permission constraints, support for which will be added in a subsequent patch.
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 | 15 ++++++++ include/linux/user_ptr.h | 60 +++++++++++++++++++++++++++++ lib/user_ptr.c | 33 ++++++++++++++++ 3 files changed, 108 insertions(+)
diff --git a/Documentation/core-api/user_ptr.rst b/Documentation/core-api/user_ptr.rst index d314bc215c65..0632bc9f4e8b 100644 --- a/Documentation/core-api/user_ptr.rst +++ b/Documentation/core-api/user_ptr.rst @@ -348,3 +348,18 @@ accidentally providing capabilities to userspace in PCuABI. | routines suffixed with ``with_captags``. See ``<linux/uaccess.h>`` | | for details. | +-----------------------------------------------------------------------+ + +Managing user pointers by mm subsystem +====================================== + +The user pointers created by the Linux mm subsystem are referred to as +owning capabilities in PCuABI and have the owning permission bit +CHERI_PERM_SW_VMEM set. CHERI bounds representability is also considered for +user pointer bounds. The APIs below consider those requirements while +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)`` + +See ``<linux/user_ptr.h>`` for details on how to use them. diff --git a/include/linux/user_ptr.h b/include/linux/user_ptr.h index 3b0bc117fcfb..a48a294b434e 100644 --- a/include/linux/user_ptr.h +++ b/include/linux/user_ptr.h @@ -6,6 +6,8 @@ #include <linux/limits.h> #include <linux/typecheck.h>
+struct reserv_struct; + /** * as_user_ptr() - Convert an arbitrary integer value to a user pointer. * @x: The integer value to convert. @@ -110,6 +112,46 @@ bool check_user_ptr_read(const void __user *ptr, size_t len); bool check_user_ptr_write(void __user *ptr, size_t len); bool check_user_ptr_rw(void __user *ptr, size_t len);
+/** + * check_user_ptr_owning() - Check whether a user pointer owns a memory region. + * @user_ptr: The pointer to check. + * @len: The size of the region to check. + * + * Checks whether @ptr owns the memory region starting at the address of @ptr + * and of size @len. + * + * Return: true if @ptr passes the check. + */ +bool check_user_ptr_owning(user_uintptr_t user_ptr, size_t len); + +/** + * make_user_ptr_owning() - Create a user pointer owning the specified + * reservation. + * + * @reserv: Reservation information. + * @addr: Address to set the user pointer to. + * + * Return: The constructed user pointer. + * + * The bounds of the returned user pointer are set (exactly) to the bounds of + * @reserv, and so are its permissions. + */ +user_uintptr_t make_user_ptr_owning(const struct reserv_struct *reserv, + ptraddr_t addr); + +/** + * user_ptr_owning_perms_from_prot() - Calculate capability permissions from + * prot flags and vm_flags. + * @prot: Memory protection flags. + * @vm_flags: vm_flags of the underlying VMA. + * + * Return: Calculated capability permission flags. + * + * Note: unsigned long is used instead of vm_flags_t as linux/mm_types.h cannot + * be included here. + */ +user_ptr_perms_t user_ptr_owning_perms_from_prot(int prot, unsigned long vm_flags); + #else /* CONFIG_CHERI_PURECAP_UABI */
typedef int user_ptr_perms_t; @@ -150,6 +192,24 @@ static inline bool check_user_ptr_rw(void __user *ptr, size_t len) return true; }
+static inline bool check_user_ptr_owning(user_uintptr_t user_ptr, size_t len) + +{ + return true; +} + +static inline user_uintptr_t make_user_ptr_owning(const struct reserv_struct *reserv, + ptraddr_t addr) +{ + return addr; +} + +static inline user_ptr_perms_t user_ptr_owning_perms_from_prot(int prot, + unsigned long vm_flags) +{ + return 0; +} + #endif /* CONFIG_CHERI_PURECAP_UABI */
/** diff --git a/lib/user_ptr.c b/lib/user_ptr.c index 115efc9fe678..6f7fc9111b1d 100644 --- a/lib/user_ptr.c +++ b/lib/user_ptr.c @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0-only */ #include <linux/bug.h> #include <linux/cheri.h> +#include <linux/mm_types.h> #include <linux/user_ptr.h>
void __user *uaddr_to_user_ptr(ptraddr_t addr) @@ -70,3 +71,35 @@ bool check_user_ptr_rw(void __user *ptr, size_t len) { return cheri_check_cap(ptr, len, CHERI_PERM_LOAD | CHERI_PERM_STORE); } + +bool check_user_ptr_owning(user_uintptr_t user_ptr, size_t len) +{ + ptraddr_t addr; + + addr = round_down((ptraddr_t)user_ptr, PAGE_SIZE); + len = round_up(len, PAGE_SIZE); + user_ptr = cheri_address_set(user_ptr, addr); + + return cheri_check_cap((void * __capability)user_ptr, len, + CHERI_PERMS_ROOTCAP); +} + +user_uintptr_t make_user_ptr_owning(const struct reserv_struct *reserv, + ptraddr_t addr) +{ + user_uintptr_t user_ptr; + + user_ptr = (user_uintptr_t)cheri_build_user_cap(reserv->start, + reserv->len, + reserv->perms); + user_ptr = cheri_address_set(user_ptr, addr); + + return user_ptr; +} + +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); +}