Add generic helpers to obtain the bounds (base and limit) of a user pointer. These are useful when an algorithm wants to know whether it is possible to access memory beyond the bounds of a given object. There should be no need for them for checking in-bound accesses, as those checks are already carried out during uaccess, or explicitly via check_user_ptr_*() (which perform a full validity check, not just a bounds check).
Signed-off-by: Kevin Brodsky kevin.brodsky@arm.com --- Documentation/core-api/user_ptr.rst | 13 +++++++++ include/linux/user_ptr.h | 44 +++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+)
diff --git a/Documentation/core-api/user_ptr.rst b/Documentation/core-api/user_ptr.rst index 8ce09bf306b0..9db5e9271578 100644 --- a/Documentation/core-api/user_ptr.rst +++ b/Documentation/core-api/user_ptr.rst @@ -250,6 +250,19 @@ This can be done using the ``check_user_ptr_*()`` functions, see 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.
+Bounds +------ + +In very specific cases, it may be useful to query the lower and upper +bounds (base and limit) of a given user pointer. This is done using +``user_ptr_base(p)`` and ``user_ptr_limit(p)`` (``<linux/user_ptr.h>``). +When user pointers do not carry such information, the start and end of +the address space are returned, respectively. + +Note that the address of a user pointer should not be directly compared +with these bounds, as it may be tagged. ``untagged_addr(p)`` should be +used instead of ``user_ptr_addr(p)`` to obtain a comparable value. + Other functions handling user pointers --------------------------------------
diff --git a/include/linux/user_ptr.h b/include/linux/user_ptr.h index 2c2180f0f0c3..685586bc0d89 100644 --- a/include/linux/user_ptr.h +++ b/include/linux/user_ptr.h @@ -2,6 +2,7 @@ #ifndef _LINUX_USER_PTR_H #define _LINUX_USER_PTR_H
+#include <linux/limits.h> #include <linux/typecheck.h>
/** @@ -157,6 +158,49 @@ static inline ptraddr_t user_ptr_addr(const void __user *ptr) return (ptraddr_t)(user_uintptr_t)ptr; }
+/** + * user_ptr_base() - Extract the lower bound (base) of a user pointer. + * @ptr: The user pointer to extract the base from. + * + * The base of @ptr represents the lowest address than can be accessed + * through @ptr. If @ptr does not carry any bound information, the start of the + * address space is returned. + * + * Return: The base of @ptr. + */ +static inline ptraddr_t user_ptr_base(const void __user *ptr) +{ +#ifdef CONFIG_CHERI_PURECAP_UABI + return __builtin_cheri_base_get(ptr); +#else + return 0; +#endif +} + +/** + * user_ptr_limit() - Extract the upper bound (limit) of a user pointer. + * @ptr: The user pointer to extract the limit from. + * + * The limit of @ptr represents the end of the region than can be accessed + * through @ptr (that is one byte past the highest accessible address). If @ptr + * does not carry any bound information, the end of the address space is + * returned. + * + * Return: The limit of @ptr. + */ +static inline ptraddr_t user_ptr_limit(const void __user *ptr) +{ +#ifdef CONFIG_CHERI_PURECAP_UABI + return __builtin_cheri_base_get(ptr) + __builtin_cheri_length_get(ptr); +#else + /* + * Ideally TASK_SIZE_MAX, unfortunately we cannot safely include + * <linux/uaccess.h> in this header. + */ + return ULONG_MAX; +#endif +} + /** * user_ptr_is_same() - Checks where two user pointers are exactly the same. * @p1: The first user pointer to check.