Helper functions check_user_ptr_owning(), make_user_ptr_owning() and user_ptr_owning_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 after page aligning them and has the CHERI_PERM_SW_VMEM owning bit set.
* make_user_ptr_owning() creates the relevant owning 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_owning_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 | 30 ++++++++++++++++++ mm/cap_addr_mgmt.c | 2 +- 4 files changed, 93 insertions(+), 1 deletion(-)
diff --git a/Documentation/core-api/user_ptr.rst b/Documentation/core-api/user_ptr.rst index 1427c4701af8..0ad6e14e14c4 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 +====================================== + +The user pointers created by the Linux mm subsystem are referred to as the +owning capability in PCuABI and have the owning bit CHERI_PERM_SW_VMEM set +as the permission. Also, the user pointers and memory length managed in mm +subsystem are page aligned or sometimes CHERI representable aligned. Below, +APIs consider those requirements while creating and checking user pointers. + +* ``check_user_ptr_owning(ptr, addr, n)`` +* ``make_user_ptr_owning(addr, n, perm)`` +* ``user_ptr_owning_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 85137f0fc23e..41ab156653c7 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_owning_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_owning_perms_from_prot(int prot, bool has_tag_access); + #else /* CONFIG_CHERI_PURECAP_UABI */
typedef int user_ptr_perms_t; @@ -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_owning_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..2ef58193fdad 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,31 @@ 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) +{ + 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; + + 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_owning_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); +} diff --git a/mm/cap_addr_mgmt.c b/mm/cap_addr_mgmt.c index a8d41c7a5fbb..845e4a99556e 100644 --- a/mm/cap_addr_mgmt.c +++ b/mm/cap_addr_mgmt.c @@ -56,7 +56,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, perms);
return ret; }