Introduce new helpers that help interact with user pointers for the purpose of accessing user memory, in a PCuABI-friendly way:
* make_user_ptr_for_<perms>_uaccess() creates a user capability with the requested bounds and permissions, strictly to perform uaccess.
* check_user_ptr_<perms>() checks that the capability is valid and its bounds and permissions are appropriate for the requested access. This is needed if (and only if) user memory is accessed via kernel mappings, and capability metadata therefore needs to be checked upfront in PCuABI. When normal uaccess is performed, capability metadata is checked directly by the hardware; these helpers can be seen as the counterparts of such checks for "indirect uaccess" via kernel mappings.
In the PCuABI case, these functions are implemented on top of the new cheri_* helpers in linux/cheri.h. The implementation is trivial in the !PCuABI case (the specified length and permissions are ignored).
make_user_ptr_for_<perms>_uaccess() replaces uaddr_to_user_ptr_safe() to create user pointers to perform uaccess. The latter is still used (for the time being) to provide new user pointers to userspace.
The comments and documentation are updated accordingly.
Signed-off-by: Kevin Brodsky kevin.brodsky@arm.com --- Documentation/core-api/user_ptr.rst | 100 ++++++++++++++++++---------- include/linux/user_ptr.h | 86 ++++++++++++++++++++++-- lib/user_ptr.c | 46 +++++++++++++ 3 files changed, 194 insertions(+), 38 deletions(-)
diff --git a/Documentation/core-api/user_ptr.rst b/Documentation/core-api/user_ptr.rst index 849433096b65..78aef3fbb753 100644 --- a/Documentation/core-api/user_ptr.rst +++ b/Documentation/core-api/user_ptr.rst @@ -78,7 +78,8 @@ Each function covers a particular category of input integer: * **Address**
- User-provided user address: ``uaddr_to_user_ptr()`` - - Kernel-controlled user address: ``uaddr_to_user_ptr_safe()`` + - Kernel-controlled user address: ``make_user_ptr_for_*_uaccess()``, + ``uaddr_to_user_ptr_safe()`` (see notes in the table below)
* **Compat pointer**: ``compat_ptr()``
@@ -103,39 +104,57 @@ PCuABI. The table below provides additional information about these functions, as well as the base capability that the user pointer is derived from in the PCuABI case.
-+------------------------------+--------------------+------------------------+-----------------------------------+------------------------------------------------------+ -| Name | Suitable input | Example of input | Capability derived from | Notes | -+==============================+====================+========================+===================================+======================================================+ -| ``uaddr_to_user_ptr()`` | User-provided | Address stored in | By default, user root capability. | Using this function weakens the enforcement of the | -| | address | __u64 field of a | This could be modified for | capability model, as it allows a process to trigger | -| | | user-provided struct | testing purposes (e.g. null | accesses to its own memory without an appropriate | -| | | | capability to prevent such | capability. | -| | | | capabilities from being created | It is therefore only a stopgap while waiting for a | -| | | | at runtime). | uapi change allowing userspace to provide an actual | -| | | | | pointer instead of an address. | -+------------------------------+--------------------+------------------------+-----------------------------------+------------------------------------------------------+ -| ``uaddr_to_user_ptr_safe()`` | Kernel-controlled | Address of new user | User root capability | This function should only be used in cases where the | -| | user address | mappings during | | kernel needs to access user memory using a bare | -| | | process initialisation | | virtual address that is not provided by userspace. | -+------------------------------+--------------------+------------------------+-----------------------------------+------------------------------------------------------+ -| ``compat_ptr()`` | Compat pointer | Pointer in a | Current user DDC | Must be used whenever converting a compat user | -| | | user-provided | | pointer to a native user pointer. | -| | | ``compat_*`` struct | | | -+------------------------------+--------------------+------------------------+-----------------------------------+------------------------------------------------------+ -| ``as_user_ptr()`` | Arbitrary integer | Error code | Null capability | This is a pure representation change, as suggested | -| | | | | by the ``as_`` prefix. Returns up to 64 bits of an | -| | | | | arbitrary integer represented as a user pointer. The | -| | | | | result is not a valid pointer and cannot be | -| | | | | dereferenced. | -+------------------------------+--------------------+------------------------+-----------------------------------+------------------------------------------------------+ -| ``u64_to_user_ptr()`` | ``u64`` integer | [Deprecated] | Null capability | Legacy function, new callers should not be added. | -| | | | | Existing callers should move to either | -| | | | | ``as_user_ptr()`` if the user pointer is not used to | -| | | | | access memory, or ``uaddr_to_user_ptr()`` if the | -| | | | | input is an address and the user pointer is | -| | | | | dereferenced (or ideally removed if the uapi can be | -| | | | | changed appropriately). | -+------------------------------+--------------------+------------------------+-----------------------------------+------------------------------------------------------+ ++-----------------------------------+--------------------+---------------------------+-----------------------------------+------------------------------------------------------+ +| Name | Suitable input | Example of input | Capability derived from | Notes | ++===================================+====================+===========================+===================================+======================================================+ +| ``uaddr_to_user_ptr()`` | User-provided | Address stored in | By default, user root capability. | Using this function weakens the enforcement of the | +| | address | ``__u64`` field of a | This could be modified for | capability model, as it allows a process to trigger | +| | | user-provided struct | testing purposes (e.g. null | accesses to its own memory without an appropriate | +| | | | capability to prevent such | capability. | +| | | | capabilities from being created | | +| | | | at runtime). | It is therefore only a stopgap while waiting for a | +| | | | | uapi change allowing userspace to provide an actual | +| | | | | pointer instead of an address. | ++-----------------------------------+--------------------+---------------------------+-----------------------------------+------------------------------------------------------+ +| ``uaddr_to_user_ptr_safe()`` | Kernel-controlled | Address of a user mapping | User root capability | This function should only be used in the few cases | +| | user address | | | where the kernel needs to **provide** a new pointer | +| | | | | to userspace, starting from an address. A typical | +| | | | | example is returning a pointer to a newly created | +| | | | | mapping in ``mmap()``. It should not be used to | +| | | | | perform uaccess, ``make_user_ptr_for_*_uaccess()`` | +| | | | | should be used for that purpose. | +| | | | | | +| | | | | Like ``uaddr_to_user_ptr()``, this function is a | +| | | | | stopgap. In PCuABI, capability pointers provided to | +| | | | | userspace have well-defined bounds and permissions, | +| | | | | and these will eventually be set by dedicated code, | +| | | | | removing the need for this function. | ++-----------------------------------+--------------------+---------------------------+-----------------------------------+------------------------------------------------------+ +| ``make_user_ptr_for_*_uaccess()`` | Kernel-controlled | Address of a user mapping | User root capability | This function should be used when the kernel needs | +| | user address | | | to access user memory at a certain address. That | +| | | | | address must be determined by the kernel itself, | +| | | | | typically from mm information (start address of a | +| | | | | mapping, address of program arguments, etc.). | ++-----------------------------------+--------------------+---------------------------+-----------------------------------+------------------------------------------------------+ +| ``compat_ptr()`` | Compat pointer | Pointer in a | Current user DDC | Must be used whenever converting a compat user | +| | | user-provided | | pointer to a native user pointer. | +| | | ``compat_*`` struct | | | ++-----------------------------------+--------------------+---------------------------+-----------------------------------+------------------------------------------------------+ +| ``as_user_ptr()`` | Arbitrary integer | Error code | Null capability | This is a pure representation change, as suggested | +| | | | | by the ``as_`` prefix. Returns up to 64 bits of an | +| | | | | arbitrary integer represented as a user pointer. The | +| | | | | result is not a valid pointer and cannot be | +| | | | | dereferenced. | ++-----------------------------------+--------------------+---------------------------+-----------------------------------+------------------------------------------------------+ +| ``u64_to_user_ptr()`` | ``u64`` integer | [Deprecated] | Null capability | Legacy function, new callers should not be added. | +| | | | | | +| | | | | Existing callers should move to either | +| | | | | ``as_user_ptr()`` if the user pointer is not used to | +| | | | | access memory, or ``uaddr_to_user_ptr()`` if the | +| | | | | input is an address and the user pointer is | +| | | | | dereferenced (or ideally removed if the uapi can be | +| | | | | changed appropriately). | ++-----------------------------------+--------------------+---------------------------+-----------------------------------+------------------------------------------------------+
+-----------------------------------------------------------------------+ @@ -218,6 +237,19 @@ preserving their metadata in PCuABI).
* ``USER_PTR_PAGE_ALIGN(p)``
+Explicit checking +----------------- + +In a few situations, typically when user memory is accessed via a kernel +mapping, it may be necessary to explicitly check that a user pointer allows +a given type of access to a given range, without dereferencing it. + +This can be done using the ``check_user_ptr_*()`` functions, see +``<linux/user_ptr.h>`` for details on how to use them. + +Note that these functions are no-ops (always succeed) when PCuABI is not +selected, as there is no user pointer metadata to check in that case. + Other functions handling user pointers --------------------------------------
diff --git a/include/linux/user_ptr.h b/include/linux/user_ptr.h index 21070de7c17c..82558777984c 100644 --- a/include/linux/user_ptr.h +++ b/include/linux/user_ptr.h @@ -43,17 +43,69 @@ void __user *uaddr_to_user_ptr(ptraddr_t addr);
/** * uaddr_to_user_ptr_safe() - Convert a kernel-generated user address to a - * user pointer. + * user pointer. * @addr: The address to set the pointer to. * * Return: A user pointer with its address set to @addr. * - * This function should be used when a user pointer is required because user - * memory at a certain address needs to be accessed, and that address originates - * from the kernel itself (i.e. it is not provided by userspace). + * This function should be used when a new user pointer needs to be provided to + * userspace. @addr should be controlled by the kernel (i.e. not an arbitrary + * user-provided value). + * + * When a user pointer is needed to access user memory (in-kernel use), + * make_user_ptr_for_*_uaccess() should be used instead. + * + * All uses of this function should eventually be replaced by dedicated code + * ensuring that the bounds and permissions of the user capability are minimised + * in the pure-capability ABI. */ void __user *uaddr_to_user_ptr_safe(ptraddr_t addr);
+/** + * make_user_ptr_for_<perms>_uaccess() - Create a user pointer from kernel-generated + * parameters, to access user memory. + * @addr: The address to set the pointer to. + * @len: The minimum size of the region the pointer should allow to access. + * + * Return: A user pointer with its address set to @addr. + * + * These functions should be used when a user pointer is required because user + * memory at a certain address needs to be accessed. The parameters should not + * originate from userspace, and the returned pointer should not be provided to + * userspace in any way. + * + * When the pure-capability uABI is targeted, the returned capability pointer + * will have its length set to at least @len (the base and length may be + * expanded because of representability constraints), and its permissions will + * be set appropriately for each function (read/write/RW). + */ +void __user *make_user_ptr_for_read_uaccess(ptraddr_t addr, size_t len); +void __user *make_user_ptr_for_write_uaccess(ptraddr_t addr, size_t len); +void __user *make_user_ptr_for_rw_uaccess(ptraddr_t addr, size_t len); + +/** + * check_user_ptr_<perms>() - Check whether a user pointer grants access to a + * memory region. + * @ptr: The pointer to check. + * @len: The size of the region that needs to be accessible. + * @perms: The type of operation the pointer needs to allow; bitwise combination + * of USER_PTR_CAN_*. + * + * Checks whether @ptr allows accessing the memory region starting at the + * address of @ptr and of size @len. The type of access that @ptr should allow + * is specified by calling the appropriate function (read/write/RW). + * + * These functions only check whether the **pointer itself** allows a given + * access; no other check is performed. Such checks are only performed when user + * pointers have appropriate metadata, as in the pure-capability uABI, otherwise + * true is always returned. + * + * Return: true if @ptr passes the checks. + */ +bool check_user_ptr_read(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); + #else /* CONFIG_CHERI_PURECAP_UABI */
static inline void __user *uaddr_to_user_ptr(ptraddr_t addr) @@ -66,6 +118,32 @@ static inline void __user *uaddr_to_user_ptr_safe(ptraddr_t addr) return as_user_ptr(addr); }
+static inline void __user *make_user_ptr_for_read_uaccess(ptraddr_t addr, size_t len) +{ + return as_user_ptr(addr); +} +static inline void __user *make_user_ptr_for_write_uaccess(ptraddr_t addr, size_t len) +{ + return as_user_ptr(addr); +} +static inline void __user *make_user_ptr_for_rw_uaccess(ptraddr_t addr, size_t len) +{ + return as_user_ptr(addr); +} + +static inline bool check_user_ptr_read(void __user *ptr, size_t len) +{ + return true; +} +static inline bool check_user_ptr_write(void __user *ptr, size_t len) +{ + return true; +} +static inline bool check_user_ptr_rw(void __user *ptr, size_t len) +{ + return true; +} + #endif /* CONFIG_CHERI_PURECAP_UABI */
/** diff --git a/lib/user_ptr.c b/lib/user_ptr.c index 7fd67f793122..ae4936c0999a 100644 --- a/lib/user_ptr.c +++ b/lib/user_ptr.c @@ -24,3 +24,49 @@ void __user *uaddr_to_user_ptr_safe(ptraddr_t addr) return ret; }
+/* + * Grant all permissions in each category, e.g. loading/storing capabilities in + * addition to standard data. + */ +void __user *make_user_ptr_for_read_uaccess(ptraddr_t addr, size_t len) +{ + cheri_perms_t cap_perms = CHERI_PERM_GLOBAL | CHERI_PERMS_READ; + + return cheri_build_user_cap_inexact_bounds(addr, len, cap_perms); +} + +void __user *make_user_ptr_for_write_uaccess(ptraddr_t addr, size_t len) +{ + cheri_perms_t cap_perms = CHERI_PERM_GLOBAL | CHERI_PERMS_WRITE; + + return cheri_build_user_cap_inexact_bounds(addr, len, cap_perms); +} + +void __user *make_user_ptr_for_rw_uaccess(ptraddr_t addr, size_t len) +{ + cheri_perms_t cap_perms = CHERI_PERM_GLOBAL | CHERI_PERMS_READ + | CHERI_PERMS_WRITE; + + return cheri_build_user_cap_inexact_bounds(addr, len, cap_perms); +} + +/* + * Only check whether the capability has the minimal data permissions (Load / + * Store). The underlying assumption is that these functions are only used + * before user data pages are grabbed via GUP and the data is then copied + * through a kernel mapping, and does not contain capabilities. + */ +bool check_user_ptr_read(void __user *ptr, size_t len) +{ + return cheri_check_cap(ptr, len, CHERI_PERM_LOAD); +} + +bool check_user_ptr_write(void __user *ptr, size_t len) +{ + return cheri_check_cap(ptr, len, CHERI_PERM_STORE); +} + +bool check_user_ptr_rw(void __user *ptr, size_t len) +{ + return cheri_check_cap(ptr, len, CHERI_PERM_LOAD | CHERI_PERM_STORE); +}