Introduce new helpers to operate on capability metadata in PCuABI: * make_privileged_user_ptr() creates a user capability with the requested bounds and permissions. * check_user_ptr() checks that the capability is valid and its bounds and permissions are appropriate for the requested access.
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 provided length and permissions are ignored).
make_privileged_user_ptr() supersedes uaddr_to_user_ptr_safe() as it creates safer capabilities for the kernel to manipulate.
Note that check_user_ptr() is only meant to be used when explicit checking is actually required. In particular, it is not needed when a user pointer is passed to uaccess functions and otherwise treated as an opaque value, which is the vast majority of cases.
Signed-off-by: Kevin Brodsky kevin.brodsky@arm.com --- include/linux/user_ptr.h | 64 ++++++++++++++++++++++++++++++++++++++++ lib/user_ptr.c | 33 +++++++++++++++++++++ 2 files changed, 97 insertions(+)
diff --git a/include/linux/user_ptr.h b/include/linux/user_ptr.h index 21cc0f6e1832..2a830a7f7449 100644 --- a/include/linux/user_ptr.h +++ b/include/linux/user_ptr.h @@ -4,6 +4,11 @@
#include <linux/typecheck.h>
+typedef int user_ptr_perms_t; + +#define USER_PTR_CAN_READ 0x1 +#define USER_PTR_CAN_WRITE 0x2 + /** * as_user_ptr() - Convert an arbitrary integer value to a user pointer. * @x: The integer value to convert. @@ -51,9 +56,56 @@ void __user *uaddr_to_user_ptr(ptraddr_t 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). + * + * Deprecated in favour of make_privileged_user_ptr(), which provides more + * precise information for the pure-capability uABI. */ void __user *uaddr_to_user_ptr_safe(ptraddr_t addr);
+/** + * make_privileged_user_ptr() - Create a user pointer from kernel-generated + * parameters, for in-kernel use only. + * @addr: The address to set the pointer to. + * @len: The minimum size of the region the pointer should allow to access. + * @perms: The type of operation the pointer should allow; bitwise combination + * of USER_PTR_CAN_*. + * + * Returns 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. Additionally, the returned user pointer should only + * be used by the kernel itself. In other words, the parameters should not be + * provided by 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 according to @perms. + */ +void __user *make_privileged_user_ptr(ptraddr_t addr, size_t len, + user_ptr_perms_t perms); + +/** + * check_user_ptr() - 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_*. + * + * Returns 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 @perms. + * + * This function only checks 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 + * the function always returns true. + */ +bool check_user_ptr(void __user *ptr, size_t len, user_ptr_perms_t perms); + #else /* CONFIG_CHERI_PURECAP_UABI */
static inline void __user *uaddr_to_user_ptr(ptraddr_t addr) @@ -66,6 +118,18 @@ static inline void __user *uaddr_to_user_ptr_safe(ptraddr_t addr) return as_user_ptr(addr); }
+static inline void __user *make_privileged_user_ptr(ptraddr_t addr, size_t len, + user_ptr_perms_t perms) +{ + return as_user_ptr(addr); +} + +static inline bool check_user_ptr(void __user *ptr, size_t len, + user_ptr_perms_t perms) +{ + return true; +} + #endif /* CONFIG_CHERI_PURECAP_UABI */
/** diff --git a/lib/user_ptr.c b/lib/user_ptr.c index 503a8369d019..826996cd45bd 100644 --- a/lib/user_ptr.c +++ b/lib/user_ptr.c @@ -27,3 +27,36 @@ void __user *uaddr_to_user_ptr_safe(ptraddr_t addr) return ret; }
+void __user *make_privileged_user_ptr(ptraddr_t addr, size_t len, + user_ptr_perms_t perms) +{ + cheri_perms_t cap_perms = CHERI_PERM_GLOBAL; + /* + * Grant all permissions in each category, e.g. loading/storing + * capabilities in addition to standard data. + */ + if (perms & USER_PTR_CAN_READ) + cap_perms |= CHERI_PERMS_READ; + if (perms & USER_PTR_CAN_WRITE) + cap_perms |= CHERI_PERMS_WRITE; + + return cheri_build_user_cap_inexact_bounds(addr, len, cap_perms); +} + +bool check_user_ptr(void __user *ptr, size_t len, user_ptr_perms_t perms) +{ + cheri_perms_t cap_perms = 0; + /* + * Only check whether the capability has the minimal data permissions + * (Load / Store). The underlying assumption is that this function is + * 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. + */ + if (perms & USER_PTR_CAN_READ) + cap_perms |= CHERI_PERM_LOAD; + if (perms & USER_PTR_CAN_WRITE) + cap_perms |= CHERI_PERM_STORE; + + return cheri_check_cap_data_access(ptr, len, cap_perms); +}