Helper functions check_user_ptr_owning(), make_user_ptr_owning() and user_ptr_perms_from_prot() are added to manage owning capability constraints as per PCuABI specifications. 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. The input range is page aligned, and the operation is ignored for non PCuABI case.
* make_user_ptr_owning() creates the relevant capability from the input range and permissions. The input range is first page aligned and then CHERI representable aligned.
Both of these functions are implemented on top of the cheri_* helpers in linux/cheri.h.
* user_ptr_perms_from_prot() converts memory mapping protections to capability permissions.
Note: These helper functions currently check only capability bounds and not capability permission constraints and full support will be incrementally added in subsequent commits.
Signed-off-by: Amit Daniel Kachhap amitdaniel.kachhap@arm.com --- Documentation/core-api/user_ptr.rst | 15 +++++++++ include/linux/user_ptr.h | 47 +++++++++++++++++++++++++++++ lib/user_ptr.c | 37 +++++++++++++++++++++++ mm/cap_addr_mgmt.c | 2 +- 4 files changed, 100 insertions(+), 1 deletion(-)
diff --git a/Documentation/core-api/user_ptr.rst b/Documentation/core-api/user_ptr.rst index 1427c4701af8..627bcea2a07e 100644 --- a/Documentation/core-api/user_ptr.rst +++ b/Documentation/core-api/user_ptr.rst @@ -345,3 +345,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 +-------------------------------------- + +User pointers and memory length managed in Linux mm subsystem are usually +page aligned or sometimes CHERI representable aligned. Below, APIs consider +those requirements while creating and checking user pointers. They also +check the mm flag MMF_PCUABI_RESERV to skip the operation for non-PCuABI +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)`` + +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 8cf69280bfcc..1eb59442b06e 100644 --- a/include/linux/user_ptr.h +++ b/include/linux/user_ptr.h @@ -110,6 +110,38 @@ 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 if the address range is within the valid + * user pointer capability bound. + * @user_ptr: User pointer. + * @addr: Address start value. + * @len: Address length. + * + * Return: True if address within the capability bound or false otherwise. + */ +bool check_user_ptr_owning(user_uintptr_t user_ptr, ptraddr_t addr, size_t len); + +/** + * make_user_ptr_owning() - Creates a userspace capability from the + * requested base address, length and memory permission flags. + * @addr: Requested capability address. + * @len: Requested capability length. + * @perm: Requested capability permission flags. + * + * Return: A new capability derived from cheri_user_root_cap. + */ +user_uintptr_t make_user_ptr_owning(ptraddr_t addr, size_t len, user_ptr_perms_t perm); + +/** + * user_ptr_perms_from_prot() - Converts memory mapping protection flags to + * capability permission flags. + * @prot: Memory protection flags. + * @has_tag_access: Capability permissions to have tag check flags. + * + * Return: Capability permission flags + */ +user_ptr_perms_t user_ptr_perms_from_prot(int prot, bool has_tag_access); + #else /* CONFIG_CHERI_PURECAP_UABI */
#define user_ptr_perms_t int @@ -150,6 +182,21 @@ 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, ptraddr_t addr, size_t len) +{ + return true; +} + +static inline user_uintptr_t make_user_ptr_owning(ptraddr_t addr, size_t len, user_ptr_perms_t perm) +{ + return addr; +} + +static inline user_ptr_perms_t user_ptr_perms_from_prot(int prot, bool has_tag_access) +{ + return 0; +} + #endif /* CONFIG_CHERI_PURECAP_UABI */
/** diff --git a/lib/user_ptr.c b/lib/user_ptr.c index 115efc9fe678..f597f73191bb 100644 --- a/lib/user_ptr.c +++ b/lib/user_ptr.c @@ -1,6 +1,8 @@ /* SPDX-License-Identifier: GPL-2.0-only */ #include <linux/bug.h> +#include <linux/cap_addr_mgmt.h> #include <linux/cheri.h> +#include <linux/sched.h> #include <linux/user_ptr.h>
void __user *uaddr_to_user_ptr(ptraddr_t addr) @@ -70,3 +72,38 @@ 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, ptraddr_t addr, size_t len) +{ + if (!reserv_is_supported(current->mm)) + return true; + + addr = round_down(addr, PAGE_SIZE); + len = round_up(len, PAGE_SIZE); + + return cheri_check_cap((const void * __capability)cheri_address_set(user_ptr, addr), + len, CHERI_PERM_GLOBAL | CHERI_PERM_SW_VMEM); +} + +user_uintptr_t make_user_ptr_owning(ptraddr_t addr, size_t len, user_ptr_perms_t perm) +{ + ptraddr_t align_addr; + user_uintptr_t user_ptr; + + if (!reserv_is_supported(current->mm)) + return (user_uintptr_t)addr; + + align_addr = reserv_representable_base(round_down(addr, PAGE_SIZE), len); + len = cheri_representable_length(round_up(len, PAGE_SIZE)); + user_ptr = (user_uintptr_t)cheri_build_user_cap(align_addr, len, perm); + + 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) +{ + /* TODO [PCuABI] - capability permission conversion from memory permission */ + return (CHERI_PERMS_READ | CHERI_PERMS_WRITE | + CHERI_PERMS_EXEC | CHERI_PERMS_ROOTCAP); +} diff --git a/mm/cap_addr_mgmt.c b/mm/cap_addr_mgmt.c index 5586fde34d0a..890101eec187 100644 --- a/mm/cap_addr_mgmt.c +++ b/mm/cap_addr_mgmt.c @@ -58,7 +58,7 @@ user_uintptr_t reserv_range_set_reserv(ptraddr_t start, size_t len, user_ptr_per } if (!locked) mmap_write_unlock(current->mm); - ret = (user_uintptr_t)uaddr_to_user_ptr_safe(start); + ret = make_user_ptr_owning(start, len, perm);
return ret; }