Hi,
This is a fairly small update to this series, see the cover letter of v2 for more information. v3 addresses comments from Beata and Amit, thanks for the reviews!
v2..v3: * "pps: Add missing #include" (patch 1 in v2) merged in next separately. * Renamed cheri_user_root_all_cap to cheri_user_root_allperms_cap. * Added reference to upstream commit removing CONFIG_SET_FS (patch 1). * Made cheri_check_cap() take a const capability (patch 4). * Rewrote the initialisation of root capabilities, using a new build_cap() helper (patch 5). * Added CAP_OTYPE_FIELD_BITS to calculate the number of object types (patch 5). * Rebased on 6.1 (some conflicts but all trivial).
Review branch:
https://git.morello-project.org/kbrodsky-arm/linux/-/commits/morello/cheri_p...
Thanks, Kevin
Kevin Brodsky (14): linux/user_ptr.h: Remove kaddr_to_user_ptr() linux/user_ptr.h: Improve comment formatting arm64: uapi: Add asm/cheri.h linux/cheri.h: Introduce CHERI helpers arm64: morello: Implement cheri.h fs/binfmt_elf: Use appropriate caps for AT_CHERI_{SEAL,CID}_CAP arm64: compat: Use appropriate root cap in compat_ptr() in PCuABI linux/user_ptr.h: Generic PCuABI impl for uaddr_to_user_ptr* arm64: Remove asm/user_ptr.h arm64: morello: Initialise user capabilities from cheri_user_root_* arm64: morello: Initialise user DDC from cheri_user_root_* arm64: morello: Build arbitrary user caps using appropriate root arm64: morello: Remove morello_root_cap arm64: morello: Update root capability in documentation
Documentation/arm64/morello.rst | 23 +++-- Documentation/core-api/user_ptr.rst | 8 -- arch/Kconfig | 2 +- arch/arm64/Kconfig | 2 +- arch/arm64/include/asm/cheri.h | 11 ++ arch/arm64/include/asm/compat.h | 9 +- arch/arm64/include/asm/morello.h | 12 ++- arch/arm64/include/asm/user_ptr.h | 43 -------- arch/arm64/include/uapi/asm/cheri.h | 11 ++ arch/arm64/kernel/morello.c | 152 +++++++++++++++++----------- arch/arm64/kernel/process.c | 2 +- arch/arm64/kernel/ptrace.c | 2 +- arch/arm64/lib/morello.S | 17 ++-- fs/binfmt_elf.c | 10 +- include/linux/cheri.h | 134 ++++++++++++++++++++++++ include/linux/user_ptr.h | 69 ++++++------- lib/Makefile | 3 + lib/cheri.c | 73 +++++++++++++ lib/user_ptr.c | 26 +++++ 19 files changed, 423 insertions(+), 186 deletions(-) create mode 100644 arch/arm64/include/asm/cheri.h delete mode 100644 arch/arm64/include/asm/user_ptr.h create mode 100644 arch/arm64/include/uapi/asm/cheri.h create mode 100644 include/linux/cheri.h create mode 100644 lib/cheri.c create mode 100644 lib/user_ptr.c
The removal of set_fs() is now firmly established, see commit 967747bbc084 ("uaccess: remove CONFIG_SET_FS"). There should therefore be no need to create user pointers from kernel addresses any more. This means we can get rid of kaddr_to_user_ptr(), which is unused.
Signed-off-by: Kevin Brodsky kevin.brodsky@arm.com --- Documentation/core-api/user_ptr.rst | 8 -------- arch/arm64/include/asm/user_ptr.h | 8 -------- include/linux/user_ptr.h | 16 ---------------- 3 files changed, 32 deletions(-)
diff --git a/Documentation/core-api/user_ptr.rst b/Documentation/core-api/user_ptr.rst index 32228199f68b..c461f91b9068 100644 --- a/Documentation/core-api/user_ptr.rst +++ b/Documentation/core-api/user_ptr.rst @@ -98,7 +98,6 @@ Each function covers a particular category of input integer:
- User-provided user address: ``uaddr_to_user_ptr()`` - Kernel-controlled user address: ``uaddr_to_user_ptr_safe()`` - - Kernel address: ``kaddr_to_user_ptr()``
* **Compat pointer**: ``compat_ptr()``
@@ -138,13 +137,6 @@ derived from in the PCuABI case. | | user address | mappings during | | kernel needs to access user memory using a bare | | | | process initialisation | | virtual address that is not provided by userspace. | +------------------------------+--------------------+------------------------+-----------------------------------+------------------------------------------------------+ -| ``kaddr_to_user_ptr()`` | Kernel address | [None currently] | Kernel root capability | There used to be a number of situations where kernel | -| | | | | memory was accessed through uaccess, requiring user | -| | | | | pointers to be created out of kernel addresses. | -| | | | | This should no longer be the case and this function | -| | | | | will be removed once it is confirmed that there is | -| | | | | no use-case left. | -+------------------------------+--------------------+------------------------+-----------------------------------+------------------------------------------------------+ | ``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 | | | diff --git a/arch/arm64/include/asm/user_ptr.h b/arch/arm64/include/asm/user_ptr.h index 745e7d34f25b..323ad0301cbd 100644 --- a/arch/arm64/include/asm/user_ptr.h +++ b/arch/arm64/include/asm/user_ptr.h @@ -30,14 +30,6 @@ static inline void __user *uaddr_to_user_ptr_safe(ptraddr_t addr) } #define uaddr_to_user_ptr_safe(addr) uaddr_to_user_ptr_safe(addr)
-static inline void __user *kaddr_to_user_ptr(ptraddr_t addr) -{ - uintcap_t root_cap = morello_get_root_cap(); - - return (void __user *)__builtin_cheri_address_set(root_cap, addr); -} -#define kaddr_to_user_ptr(addr) kaddr_to_user_ptr(addr) - #endif /* CONFIG_CHERI_PURECAP_UABI */
#endif /* __ASM_USER_PTR_H */ diff --git a/include/linux/user_ptr.h b/include/linux/user_ptr.h index 516024f3fead..33b332acab03 100644 --- a/include/linux/user_ptr.h +++ b/include/linux/user_ptr.h @@ -65,22 +65,6 @@ static inline void __user *uaddr_to_user_ptr_safe(ptraddr_t addr) } #endif
-#ifndef kaddr_to_user_ptr -/** - * kaddr_to_user_ptr - convert a kernel address to a user pointer - * @addr: the address to set the pointer to - * - * Returns a user pointer with its address set to @addr. - * - * This function should be used when kernel memory needs to be accessed via a - * user pointer. There should be no use for it after the removal of set_fs(). - */ -static inline void __user *kaddr_to_user_ptr(ptraddr_t addr) -{ - return as_user_ptr(addr); -} -#endif - /** * user_ptr_addr - extract the address of a user pointer * @ptr: the user pointer to extract the address from
Format kernel-doc comments as per the recommendations in Documentation/doc-guide/kernel-doc.rst and the output of kernel-doc -v.
Signed-off-by: Kevin Brodsky kevin.brodsky@arm.com --- include/linux/user_ptr.h | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-)
diff --git a/include/linux/user_ptr.h b/include/linux/user_ptr.h index 33b332acab03..a432cfa95580 100644 --- a/include/linux/user_ptr.h +++ b/include/linux/user_ptr.h @@ -9,11 +9,11 @@ #endif
/** - * as_user_ptr - convert an arbitrary integer value to a user pointer - * @x: the integer value to convert + * as_user_ptr() - Convert an arbitrary integer value to a user pointer. + * @x: The integer value to convert. * - * Returns up to 64 bits of @x represented as a user pointer. The result is - * not a valid pointer and shall not be dereferenced. + * Return: Up to 64 bits of @x represented as a user pointer. The result is + * not a valid pointer and shall not be dereferenced. */ #define as_user_ptr(x) ((void __user *)(user_uintptr_t)(u64)(x))
@@ -30,10 +30,10 @@
#ifndef uaddr_to_user_ptr /** - * uaddr_to_user_ptr - convert a user-provided address to a user pointer - * @addr: the address to set the pointer to + * uaddr_to_user_ptr() - Convert a user-provided address to a user pointer. + * @addr: The address to set the pointer to. * - * Returns a user pointer with its address set to @addr. + * Return: A user pointer with its address set to @addr. * * This function should be used when a user pointer is required because userspace * provided a raw address (e.g. via a __u64 member of a struct), and the memory @@ -50,10 +50,11 @@ static inline void __user *uaddr_to_user_ptr(ptraddr_t addr)
#ifndef uaddr_to_user_ptr_safe /** - * uaddr_to_user_ptr_safe - convert a kernel-generated user address to a user pointer - * @addr: the address to set the pointer to + * uaddr_to_user_ptr_safe() - Convert a kernel-generated user address to a + * user pointer. + * @addr: The address to set the pointer to. * - * Returns a user pointer with its address set to @addr. + * 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 @@ -66,10 +67,10 @@ static inline void __user *uaddr_to_user_ptr_safe(ptraddr_t addr) #endif
/** - * user_ptr_addr - extract the address of a user pointer - * @ptr: the user pointer to extract the address from + * user_ptr_addr() - Extract the address of a user pointer. + * @ptr: The user pointer to extract the address from. * - * Returns the address @ptr points to. + * Return: The address @ptr points to. */ static inline ptraddr_t user_ptr_addr(const void __user *ptr) { @@ -77,9 +78,11 @@ static inline ptraddr_t user_ptr_addr(const void __user *ptr) }
/** - * user_ptr_is_same - checks where two user pointers are exactly the same + * user_ptr_is_same() - Checks where two user pointers are exactly the same. + * @p1: The first user pointer to check. + * @p2: The second user pointer to check. * - * Returns true if @p1 and @p2 are exactly the same user pointers. + * Return: true if @p1 and @p2 are exactly the same user pointers. * * Only use this function if you need to know that two user pointers are * interchangeable, not to check that their address is the same (use the ==
Add a new uapi header to define generic CHERI concepts with arm64-specific values.
To start with, provide a macro representing the VMem software permission, as defined in the PCuABI specification. The macro name matches CheriBSD, facilitating its use in a (somewhat) OS-agnostic way.
Signed-off-by: Kevin Brodsky kevin.brodsky@arm.com --- arch/arm64/include/uapi/asm/cheri.h | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 arch/arm64/include/uapi/asm/cheri.h
diff --git a/arch/arm64/include/uapi/asm/cheri.h b/arch/arm64/include/uapi/asm/cheri.h new file mode 100644 index 000000000000..f08444167e64 --- /dev/null +++ b/arch/arm64/include/uapi/asm/cheri.h @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _UAPI__ASM_CHERI_H +#define _UAPI__ASM_CHERI_H + +/* + * VMem software-defined capability permission, assigned to the User[0] + * permission on Morello (bit 2). + */ +#define CHERI_PERM_SW_VMEM (1 << 2) + +#endif /* _UAPI__ASM_CHERI_H */
For architectures implementing CHERI capabilities, introduce a new cheri.h header that defines a few helpers and macros, in addition to what is available in the compiler-provided <cheriintrin.h>.
This header is only meant for code manipulating CHERI capabilities explicitly and expands to nothing if __CHERI__ is not defined.
CONFIG_HAVE_ARCH_CHERI_H is also added to allow architectures to override some of the definitions by providing their own asm/cheri.h.
Signed-off-by: Kevin Brodsky kevin.brodsky@arm.com --- arch/Kconfig | 3 + include/linux/cheri.h | 134 ++++++++++++++++++++++++++++++++++++++++++ lib/Makefile | 2 + lib/cheri.c | 73 +++++++++++++++++++++++ 4 files changed, 212 insertions(+) create mode 100644 include/linux/cheri.h create mode 100644 lib/cheri.c
diff --git a/arch/Kconfig b/arch/Kconfig index b872b13778db..12698fec3838 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -1424,6 +1424,9 @@ config ARCH_HAS_NONLEAF_PMD_YOUNG address translations. Page table walkers that clear the accessed bit may use this capability to reduce their search space.
+config HAVE_ARCH_CHERI_H + bool + config ARCH_HAS_USER_PTR_H bool
diff --git a/include/linux/cheri.h b/include/linux/cheri.h new file mode 100644 index 000000000000..e5f588b056ad --- /dev/null +++ b/include/linux/cheri.h @@ -0,0 +1,134 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef _LINUX_CHERI_H +#define _LINUX_CHERI_H + +#ifdef __CHERI__ + +#include <cheriintrin.h> + +#include <linux/types.h> + +#include <uapi/asm/cheri.h> +#ifdef CONFIG_HAVE_ARCH_CHERI_H +#include <asm/cheri.h> +#endif + +/* + * Standard permission sets for new capabilities. Can be overridden by + * architectures to add arch-specific permissions. + */ +#ifndef CHERI_PERMS_READ +#define CHERI_PERMS_READ \ + (CHERI_PERM_LOAD | CHERI_PERM_LOAD_CAP) +#endif + +#ifndef CHERI_PERMS_WRITE +#define CHERI_PERMS_WRITE \ + (CHERI_PERM_STORE | CHERI_PERM_STORE_CAP | CHERI_PERM_STORE_LOCAL_CAP) +#endif + +#ifndef CHERI_PERMS_EXEC +#define CHERI_PERMS_EXEC \ + (CHERI_PERM_EXECUTE | CHERI_PERM_SYSTEM_REGS) +#endif + +#ifndef CHERI_PERMS_ROOTCAP +#define CHERI_PERMS_ROOTCAP \ + (CHERI_PERM_GLOBAL | CHERI_PERM_SW_VMEM) +#endif + +/** + * cheri_build_user_cap() - Create a userspace capability. + * @addr: Requested capability address. + * @len: Requested capability length. + * @perms: Requested capability permissions. + * + * Return: A new capability derived from cheri_user_root_cap. Its address and + * permissions are set according to @addr and @perms respectively. Its + * bounds are set exactly with @addr as base address and @len as + * length. + * + * The caller is responsible to ensure that: + * 1. @addr is a valid userspace address. + * 2. The (@addr, @len) tuple can be represented as capability bounds. + * 3. @perms are valid permissions for a regular userspace capability. + * + * If either 1. or 2. does not hold, the resulting capability will be invalid. + * If 3. does not hold, the returned capability will not have any of the invalid + * permissions. + */ +void * __capability +cheri_build_user_cap(ptraddr_t addr, size_t len, cheri_perms_t perms); + +/** + * cheri_build_user_cap_inexact_bounds() - Create a userspace capability, + * allowing bounds to be enlarged. + * @addr: Requested capability address. + * @len: Requested capability length. + * @perms: Requested capability permissions. + * + * Return: A new capability derived from cheri_user_root_cap. Its address and + * permissions are set according to @addr and @perms respectively. Its + * bounds are set to the smallest representable range that includes the + * range [@addr, @addr + @len[. + * + * This variant of cheri_build_user_cap() should only be used when it is safe to + * enlarge the bounds of the capability. In particular, it should never be used + * when creating a capability that is to be provided to userspace, because the + * potentially enlarged bounds might give access to unrelated objects. + * + * The caller is responsible to ensure that: + * 1. @addr is a valid userspace address. + * 2. @perms are valid permissions for a regular userspace capability. + * + * If 1. does not hold, the resulting capability will be invalid. + * If 2. does not hold, the returned capability will not have any of the invalid + * permissions. + */ +void * __capability +cheri_build_user_cap_inexact_bounds(ptraddr_t addr, size_t len, + cheri_perms_t perms); + + +/** + * cheri_check_cap() - Check whether a capability gives access to a range of + * addresses. + * @cap: Capability to check. + * @len: Length of the access. + * @perms: Required permissions. + * + * Checks whether @cap gives access to a given range of addresses and has the + * requested permissions. This means that: + * * @cap is valid and unsealed. + * * The range [@cap.address, @cap.address + @len[ is within the bounds + * of @cap. + * * The permissions of @cap include at least @perms. + * + * Return: true if @cap passes the checks. + */ +bool cheri_check_cap(const void * __capability cap, size_t len, + cheri_perms_t perms); + + +/* + * Root capabilities. Should be set in arch code during the early init phase, + * read-only after that. + * + * cheri_user_root_cap is the standard root capability to derive new regular + * (data/code) capabilities from. It does not include the special permissions + * Seal/Unseal and CompartmentID; those are available separately via + * cheri_user_root_{seal,cid}_cap. Finally cheri_user_root_allperms_cap + * includes all permissions accessible to userspace and is ultimately the root + * of all user capabilities; it should only be used in very specific + * situations. + * + * The helpers above should be used instead where possible. + */ +extern uintcap_t cheri_user_root_cap; /* Userspace (data/code) root */ +extern uintcap_t cheri_user_root_seal_cap; /* Userspace sealing root */ +extern uintcap_t cheri_user_root_cid_cap; /* Userspace compartment ID root */ +extern uintcap_t cheri_user_root_allperms_cap; /* Userspace root (all permissions) */ + +#endif /* __CHERI__ */ + +#endif /* _LINUX_CHERI_H */ diff --git a/lib/Makefile b/lib/Makefile index 59bd7c2f793a..ae1fced594c0 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -265,6 +265,8 @@ obj-$(CONFIG_IRQ_POLL) += irq_poll.o
obj-$(CONFIG_POLYNOMIAL) += polynomial.o
+obj-y += cheri.o + # stackdepot.c should not be instrumented or call instrumented functions. # Prevent the compiler from calling builtins like memcmp() or bcmp() from this # file. diff --git a/lib/cheri.c b/lib/cheri.c new file mode 100644 index 000000000000..3438718aa5db --- /dev/null +++ b/lib/cheri.c @@ -0,0 +1,73 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifdef __CHERI__ + +#include <linux/bug.h> +#include <linux/cheri.h> +#include <linux/mm.h> + +uintcap_t cheri_user_root_cap __ro_after_init; +uintcap_t cheri_user_root_seal_cap __ro_after_init; +uintcap_t cheri_user_root_cid_cap __ro_after_init; +uintcap_t cheri_user_root_allperms_cap __ro_after_init; + +static void * __capability +build_user_cap(ptraddr_t addr, size_t len, cheri_perms_t perms, bool exact_bounds) +{ + void * __capability ret = (void * __capability)cheri_user_root_cap; + cheri_perms_t root_perms = cheri_perms_get(ret); + + ret = cheri_perms_and(ret, perms); + ret = cheri_address_set(ret, addr); + + if (exact_bounds) + ret = cheri_bounds_set_exact(ret, len); + else + ret = cheri_bounds_set(ret, len); + + WARN(perms & ~root_perms, + "Permission mask %#lx discarded while creating user capability %#lp\n", + perms & ~root_perms, ret); + WARN(cheri_is_invalid(ret), + "Invalid user capability created: %#lp (%s bounds requested)\n", + ret, (exact_bounds ? "exact" : "inexact")); + + return ret; +} + +void * __capability +cheri_build_user_cap(ptraddr_t addr, size_t len, cheri_perms_t perms) +{ + return build_user_cap(addr, len, perms, true); +} + +void * __capability +cheri_build_user_cap_inexact_bounds(ptraddr_t addr, size_t len, + cheri_perms_t perms) +{ + return build_user_cap(addr, len, perms, false); +} + +bool cheri_check_cap(const void * __capability cap, size_t len, + cheri_perms_t perms) +{ + ptraddr_t addr = untagged_addr(cheri_address_get(cap)); + /* + * The base address (as returned by cheri_base_get()) is never tagged, + * that is its top byte is always canonical, so no need for + * untagged_addr(). + */ + ptraddr_t base = cheri_base_get(cap); + + if (cheri_is_invalid(cap) || cheri_is_sealed(cap)) + return false; + + if (addr < base || addr > base + cheri_length_get(cap) - len) + return false; + + if (perms & ~cheri_perms_get(cap)) + return false; + + return true; +} + +#endif /* __CHERI__ */
Implement the Morello-specific aspects of linux/cheri.h, that is:
* Override standard permission sets to include the appropriate Morello-specific permissions.
* Initialise the global root capabilities appropriately.
Signed-off-by: Kevin Brodsky kevin.brodsky@arm.com --- arch/arm64/Kconfig | 1 + arch/arm64/include/asm/cheri.h | 11 ++++++ arch/arm64/kernel/morello.c | 71 ++++++++++++++++++++++++++-------- 3 files changed, 66 insertions(+), 17 deletions(-) create mode 100644 arch/arm64/include/asm/cheri.h
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index e676991a97e7..b64f37575979 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -29,6 +29,7 @@ config ARM64 select ARCH_HAS_GCOV_PROFILE_ALL select ARCH_HAS_GIGANTIC_PAGE select ARCH_HAS_KCOV + select HAVE_ARCH_CHERI_H select ARCH_HAS_USER_PTR_H select ARCH_HAS_KEEPINITRD select ARCH_HAS_MEMBARRIER_SYNC_CORE diff --git a/arch/arm64/include/asm/cheri.h b/arch/arm64/include/asm/cheri.h new file mode 100644 index 000000000000..eaae6ed88db5 --- /dev/null +++ b/arch/arm64/include/asm/cheri.h @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef __ASM_CHERI_H +#define __ASM_CHERI_H + +#define CHERI_PERMS_READ \ + (CHERI_PERM_LOAD | CHERI_PERM_LOAD_CAP | ARM_CAP_PERMISSION_MUTABLE_LOAD) + +#define CHERI_PERMS_EXEC \ + (CHERI_PERM_EXECUTE | CHERI_PERM_SYSTEM_REGS | ARM_CAP_PERMISSION_EXECUTIVE) + +#endif /* __ASM_CHERI_H */ diff --git a/arch/arm64/kernel/morello.c b/arch/arm64/kernel/morello.c index 2a57104c2bd7..c9db2153d6de 100644 --- a/arch/arm64/kernel/morello.c +++ b/arch/arm64/kernel/morello.c @@ -5,10 +5,9 @@
#define pr_fmt(fmt) "morello: " fmt
-#include <cheriintrin.h> - #include <linux/cache.h> #include <linux/capability.h> +#include <linux/cheri.h> #include <linux/compat.h> #include <linux/mm.h> #include <linux/printk.h> @@ -21,10 +20,6 @@ #include <asm/morello.h> #include <asm/ptrace.h>
-#ifdef CONFIG_CHERI_PURECAP_UABI -#include <cheriintrin.h> -#endif - /* Private functions implemented in morello.S */ void __morello_cap_lo_hi_tag(uintcap_t cap, u64 *lo_val, u64 *hi_val, u8 *tag); @@ -41,6 +36,8 @@ static uintcap_t morello_sentry_unsealcap __ro_after_init; #define DDC_RESET_VAL_LOW_64 0x0 #define DDC_RESET_VAL_HIGH_64 0xffffc00000010005ULL
+#define CAP_OTYPE_FIELD_BITS 15 + uintcap_t morello_get_root_cap(void) { return morello_root_cap; @@ -294,8 +291,8 @@ static void __init check_root_cap(uintcap_t cap) __morello_cap_lo_hi_tag(cap, &lo_val, &hi_val, &tag);
/* - * Check that DDC has the reset value, otherwise morello_root_cap and - * all capabilities derived from it (especially those exposed to + * Check that DDC has the reset value, otherwise root capabilities and + * all capabilities derived from them (notably those exposed to * userspace) may not be reliable. */ if (!(tag == 1 && @@ -304,22 +301,62 @@ static void __init check_root_cap(uintcap_t cap) pr_warn("DDC does not have its reset value, this may be a firmware bug\n"); }
+#define __build_cap(root, perms, length, ...) \ +({ \ + uintcap_t c = (root); \ + size_t len = (length); \ + \ + c = cheri_perms_and(c, (perms)); \ + if (len) \ + c = cheri_bounds_set(c, len); \ + \ + c; \ +}) +#define build_cap(root, perms, ...) __build_cap((root), (perms), ##__VA_ARGS__, 0) + static int __init morello_cap_init(void) { -#ifdef CONFIG_CHERI_PURECAP_UABI - uintcap_t ctemp; -#endif + uintcap_t root_cap; + cheri_perms_t perms; + + root_cap = (uintcap_t)cheri_ddc_get(); + check_root_cap(root_cap); + + /* Initialise standard CHERI root capabilities. */ + + perms = CHERI_PERMS_ROOTCAP | + CHERI_PERMS_READ | CHERI_PERMS_WRITE | CHERI_PERMS_EXEC | + ARM_CAP_PERMISSION_BRANCH_SEALED_PAIR | + CHERI_PERM_SEAL | CHERI_PERM_UNSEAL | + ARM_CAP_PERMISSION_COMPARTMENT_ID; + /* Same upper limit as for access_ok() and __uaccess_mask_ptr() */ + cheri_user_root_allperms_cap = build_cap(root_cap, perms, TASK_SIZE_MAX); + + perms = CHERI_PERMS_ROOTCAP | + CHERI_PERMS_READ | CHERI_PERMS_WRITE | CHERI_PERMS_EXEC | + ARM_CAP_PERMISSION_BRANCH_SEALED_PAIR; + cheri_user_root_cap = build_cap(cheri_user_root_allperms_cap, perms); + + perms = CHERI_PERM_GLOBAL | CHERI_PERM_SEAL | CHERI_PERM_UNSEAL; + /* + * Includes all object types, not a final decision - some of them may + * be later reserved to the kernel. + */ + cheri_user_root_seal_cap = build_cap(cheri_user_root_allperms_cap, + perms, 1u << CAP_OTYPE_FIELD_BITS);
- morello_root_cap = (uintcap_t)cheri_ddc_get(); + perms = CHERI_PERM_GLOBAL | ARM_CAP_PERMISSION_COMPARTMENT_ID; + /* Maximum userspace bounds for the time being. */ + cheri_user_root_cid_cap = build_cap(cheri_user_root_allperms_cap, perms);
- check_root_cap(morello_root_cap); + /* Initialise Morello-specific root capabilities. */ + morello_root_cap = root_cap;
#ifdef CONFIG_CHERI_PURECAP_UABI /* Initialize a capability able to unseal sentry capabilities. */ - ctemp = cheri_address_set(morello_root_cap, CHERI_OTYPE_SENTRY); - ctemp = cheri_bounds_set(ctemp, 1); - ctemp = cheri_perms_and(ctemp, CHERI_PERM_GLOBAL | CHERI_PERM_UNSEAL); - morello_sentry_unsealcap = ctemp; + perms = CHERI_PERM_GLOBAL | CHERI_PERM_UNSEAL; + morello_sentry_unsealcap = cheri_address_set(root_cap, CHERI_OTYPE_SENTRY); + morello_sentry_unsealcap = build_cap(morello_sentry_unsealcap, perms, 1); #endif
return 0;
uaddr_to_user_ptr_safe() (called by elf_uaddr_to_user_ptr()) will soon return capabilities without the special permissions Seal/Unseal/CompartmentID. We should therefore initialise AT_CHERI_{SEAL,CID}_CAP with the corresponding root capabilities that linux/cheri.h now provides.
Signed-off-by: Kevin Brodsky kevin.brodsky@arm.com --- fs/binfmt_elf.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-)
diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c index e7c8f6b73309..1d82465cb9e9 100644 --- a/fs/binfmt_elf.c +++ b/fs/binfmt_elf.c @@ -46,13 +46,10 @@ #include <linux/cred.h> #include <linux/dax.h> #include <linux/uaccess.h> +#include <linux/cheri.h> #include <asm/param.h> #include <asm/page.h>
-#ifdef CONFIG_CHERI_PURECAP_UABI -#include <cheriintrin.h> -#endif - #ifndef ELF_COMPAT #define ELF_COMPAT 0 #endif @@ -328,9 +325,8 @@ create_elf_tables(struct linux_binprm *bprm, const struct elfhdr *exec, elf_uaddr_to_user_ptr(interp_load_info->start_elf_rx) : NULL)); NEW_AUX_ENT(AT_CHERI_STACK_CAP, elf_uaddr_to_user_ptr(0)); - NEW_AUX_ENT(AT_CHERI_SEAL_CAP, - cheri_bounds_set_exact(elf_uaddr_to_user_ptr(0), 1 << 15)); - NEW_AUX_ENT(AT_CHERI_CID_CAP, elf_uaddr_to_user_ptr(0)); + NEW_AUX_ENT(AT_CHERI_SEAL_CAP, cheri_user_root_seal_cap); + NEW_AUX_ENT(AT_CHERI_CID_CAP, cheri_user_root_cid_cap);
/* * Since the auxv entries are inserted into the mm struct before the
All capabilities in hybrid (here compat64) should now be derived from cheri_user_root_allperms_cap. There is no helper to derive capabilities from this root, so do it manually. This is specific to PCuABI, but the generic implementation of compat_ptr() is appropriate in !PCuABI, so we can simply #ifdef the whole definition.
Also update the comment as this does not pertain to PCuABI as such.
Signed-off-by: Kevin Brodsky kevin.brodsky@arm.com --- arch/arm64/include/asm/compat.h | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/arch/arm64/include/asm/compat.h b/arch/arm64/include/asm/compat.h index fe88e40f8960..40cd9f48eb53 100644 --- a/arch/arm64/include/asm/compat.h +++ b/arch/arm64/include/asm/compat.h @@ -29,6 +29,7 @@ typedef u16 compat_ipc_pid_t; #include <linux/types.h> #include <linux/sched.h> #include <linux/sched/task_stack.h> +#include <linux/cheri.h>
#ifdef __AARCH64EB__ #define COMPAT_UTS_MACHINE "armv8b\0\0" @@ -85,15 +86,17 @@ struct compat_statfs { compat_long_t f_spare[4]; };
+#ifdef CONFIG_CHERI_PURECAP_UABI static inline void __user *compat_ptr(compat_uptr_t uptr) { /* - * TODO [PCuABI] - this should be done using the current user DDC, not - * the root kernel one. + * TODO [Morello] - this should be done using the current user DDC, not + * the root user capability. */ - return uaddr_to_user_ptr_safe(uptr); + return (void __user *)cheri_address_set(cheri_user_root_allperms_cap, uptr); } #define compat_ptr(uptr) compat_ptr(uptr) +#endif
#define compat_user_stack_pointer() (user_stack_pointer(task_pt_regs(current))) #ifdef CONFIG_COMPAT32
Now that linux/cheri.h provides CHERI-generic functionalities, it is possible to implement uaddr_to_user_ptr* for PCuABI in an arch-agnostic way.
With this patch, the PCuABI version is implemented out of line in lib/user_ptr.c. This is notably to avoid pulling in linux/cheri.h in linux/user_ptr.h, itself included from linux/kernel.h.
This approach supersedes asm/user_ptr.h, which is no longer used and will be subsequently removed.
Signed-off-by: Kevin Brodsky kevin.brodsky@arm.com --- include/linux/user_ptr.h | 26 ++++++++++++++------------ lib/Makefile | 1 + lib/user_ptr.c | 26 ++++++++++++++++++++++++++ 3 files changed, 41 insertions(+), 12 deletions(-) create mode 100644 lib/user_ptr.c
diff --git a/include/linux/user_ptr.h b/include/linux/user_ptr.h index a432cfa95580..21070de7c17c 100644 --- a/include/linux/user_ptr.h +++ b/include/linux/user_ptr.h @@ -4,10 +4,6 @@
#include <linux/typecheck.h>
-#ifdef CONFIG_ARCH_HAS_USER_PTR_H -#include <asm/user_ptr.h> -#endif - /** * as_user_ptr() - Convert an arbitrary integer value to a user pointer. * @x: The integer value to convert. @@ -28,7 +24,8 @@ /* Legacy user pointer conversion macro, new code should use as_user_ptr() */ #define u64_to_user_ptr(x) as_user_ptr_strict(u64, (x))
-#ifndef uaddr_to_user_ptr +#ifdef CONFIG_CHERI_PURECAP_UABI + /** * uaddr_to_user_ptr() - Convert a user-provided address to a user pointer. * @addr: The address to set the pointer to. @@ -42,13 +39,8 @@ * When the pure-capability uABI is targeted, uses of this function bypass the * capability model and should be minimised. */ -static inline void __user *uaddr_to_user_ptr(ptraddr_t addr) -{ - return as_user_ptr(addr); -} -#endif +void __user *uaddr_to_user_ptr(ptraddr_t addr);
-#ifndef uaddr_to_user_ptr_safe /** * uaddr_to_user_ptr_safe() - Convert a kernel-generated user address to a * user pointer. @@ -60,11 +52,21 @@ static inline void __user *uaddr_to_user_ptr(ptraddr_t addr) * 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). */ +void __user *uaddr_to_user_ptr_safe(ptraddr_t addr); + +#else /* CONFIG_CHERI_PURECAP_UABI */ + +static inline void __user *uaddr_to_user_ptr(ptraddr_t addr) +{ + return as_user_ptr(addr); +} + static inline void __user *uaddr_to_user_ptr_safe(ptraddr_t addr) { return as_user_ptr(addr); } -#endif + +#endif /* CONFIG_CHERI_PURECAP_UABI */
/** * user_ptr_addr() - Extract the address of a user pointer. diff --git a/lib/Makefile b/lib/Makefile index ae1fced594c0..d9fe329564c5 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -266,6 +266,7 @@ obj-$(CONFIG_IRQ_POLL) += irq_poll.o obj-$(CONFIG_POLYNOMIAL) += polynomial.o
obj-y += cheri.o +obj-$(CONFIG_CHERI_PURECAP_UABI) += user_ptr.o
# stackdepot.c should not be instrumented or call instrumented functions. # Prevent the compiler from calling builtins like memcmp() or bcmp() from this diff --git a/lib/user_ptr.c b/lib/user_ptr.c new file mode 100644 index 000000000000..7fd67f793122 --- /dev/null +++ b/lib/user_ptr.c @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#include <linux/bug.h> +#include <linux/cheri.h> +#include <linux/user_ptr.h> + +void __user *uaddr_to_user_ptr(ptraddr_t addr) +{ + /* + * No warning if the result is invalid as the input address is not + * controlled by the kernel. + */ + return (void __user *)cheri_address_set(cheri_user_root_cap, addr); +} + +void __user *uaddr_to_user_ptr_safe(ptraddr_t addr) +{ + void __user *ret; + + ret = (void __user *)cheri_address_set(cheri_user_root_cap, addr); + + WARN(!cheri_tag_get(ret), + "Invalid user capability created: %#lp\n", ret); + + return ret; +} +
linux/user_ptr.h now uses generic PCuABI implementations of uaddr_to_user_ptr* and no longer includes asm/user_ptr.h. Time to remove it along with the corresponding config option.
Signed-off-by: Kevin Brodsky kevin.brodsky@arm.com --- arch/Kconfig | 3 --- arch/arm64/Kconfig | 1 - arch/arm64/include/asm/user_ptr.h | 35 ------------------------------- 3 files changed, 39 deletions(-) delete mode 100644 arch/arm64/include/asm/user_ptr.h
diff --git a/arch/Kconfig b/arch/Kconfig index 12698fec3838..143825c4d3af 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -1427,9 +1427,6 @@ config ARCH_HAS_NONLEAF_PMD_YOUNG config HAVE_ARCH_CHERI_H bool
-config ARCH_HAS_USER_PTR_H - bool - config ARCH_HAS_CHERI_CAPABILITIES bool
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index b64f37575979..d5f022c82c68 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -30,7 +30,6 @@ config ARM64 select ARCH_HAS_GIGANTIC_PAGE select ARCH_HAS_KCOV select HAVE_ARCH_CHERI_H - select ARCH_HAS_USER_PTR_H select ARCH_HAS_KEEPINITRD select ARCH_HAS_MEMBARRIER_SYNC_CORE select ARCH_HAS_NON_OVERLAPPING_ADDRESS_SPACE diff --git a/arch/arm64/include/asm/user_ptr.h b/arch/arm64/include/asm/user_ptr.h deleted file mode 100644 index 323ad0301cbd..000000000000 --- a/arch/arm64/include/asm/user_ptr.h +++ /dev/null @@ -1,35 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -#ifndef __ASM_USER_PTR_H -#define __ASM_USER_PTR_H - -#include <asm/morello.h> - -#ifdef CONFIG_CHERI_PURECAP_UABI - -static inline void __user *uaddr_to_user_ptr(ptraddr_t addr) -{ - /* - * TODO [PCuABI] - the user root capability should be used, not the - * kernel one. - */ - uintcap_t root_cap = morello_get_root_cap(); - - return (void __user *)__builtin_cheri_address_set(root_cap, addr); -} -#define uaddr_to_user_ptr(addr) uaddr_to_user_ptr(addr) - -static inline void __user *uaddr_to_user_ptr_safe(ptraddr_t addr) -{ - /* - * TODO [PCuABI] - the user root capability should be used, not the - * kernel one. - */ - uintcap_t root_cap = morello_get_root_cap(); - - return (void __user *)__builtin_cheri_address_set(root_cap, addr); -} -#define uaddr_to_user_ptr_safe(addr) uaddr_to_user_ptr_safe(addr) - -#endif /* CONFIG_CHERI_PURECAP_UABI */ - -#endif /* __ASM_USER_PTR_H */
Now that we have introduced the generic cheri_user_root_* userspace root capabilities, we can make use of them to derive the initial user capabilities (process startup and signal delivery). Note that DDC will be taken care of separately, as it is not saved in struct pt_regs and is not initialised in morello_thread_start() either.
For purecap (PCuABI) processes, we derive the capabilities from the "regular" root capability; their bounds and permissions will later be reduced further as per the PCuABI specification.
For hybrid processes (i.e. plain AArch64 ABI + capability support), the capabilities are derived from the special root capability with all permissions, cheri_user_root_allperms_cap. That's because we are not attempting to constrain the capabilities provided to hybrid processes in any way, and there is no mechanism to provide special permissions (Seal/Unseal/CompartmentID) separately in hybrid.
Since the purecap/hybrid initialisation is now clearly separated, we also use that opportunity to leave CSP zero-initialised in hybrid; a valid CSP should only be provided in purecap.
To avoid some ungraceful #ifdef'ing in morello_setup_signal_return(), morello_sentry_unsealcap is now always defined, as the overhead is insignificant (a 16-byte global and a few instructions on startup).
Signed-off-by: Kevin Brodsky kevin.brodsky@arm.com --- arch/arm64/kernel/morello.c | 71 ++++++++++++++++++------------------- 1 file changed, 34 insertions(+), 37 deletions(-)
diff --git a/arch/arm64/kernel/morello.c b/arch/arm64/kernel/morello.c index c9db2153d6de..e14812916907 100644 --- a/arch/arm64/kernel/morello.c +++ b/arch/arm64/kernel/morello.c @@ -28,9 +28,7 @@ bool __morello_cap_has_executive(uintcap_t cap);
/* Not defined as static because morello.S refers to it */ uintcap_t morello_root_cap __ro_after_init; -#ifdef CONFIG_CHERI_PURECAP_UABI static uintcap_t morello_sentry_unsealcap __ro_after_init; -#endif
/* DDC_ELx reset value (low/high 64 bits), as defined in the Morello spec */ #define DDC_RESET_VAL_LOW_64 0x0 @@ -43,14 +41,9 @@ uintcap_t morello_get_root_cap(void) return morello_root_cap; }
-static void init_pcc(struct pt_regs *regs) +static bool is_pure_task(void) { - /* - * Set PCC to the root capability. There is no need to set its value to - * pc, this will be taken care of when PC is merged into PCC during - * ret_to_user. - */ - regs->pcc = morello_root_cap; + return IS_ENABLED(CONFIG_CHERI_PURECAP_UABI) && !is_compat_task(); }
static void update_regs_c64(struct pt_regs *regs, unsigned long pc) @@ -66,21 +59,24 @@ static void update_regs_c64(struct pt_regs *regs, unsigned long pc) } }
-static void init_csp(struct pt_regs *regs) -{ - /* - * TODO [PCuABI] - Adjust the bounds/permissions properly - * This should also be somehow hooked up for stack limit changes - */ - /* The actual value for CSP will be set during ret_to_user */ - regs->csp = morello_root_cap; -} - void morello_thread_start(struct pt_regs *regs, unsigned long pc) { - init_pcc(regs); update_regs_c64(regs, pc); - init_csp(regs); + + /* + * Note: there is no need to explicitly set the address of PCC/CSP as + * PC/SP are already set to the appropriate values in regs, and X/C + * register merging automatically happens during ret_to_user. + */ + if (is_pure_task()) { + /* TODO [PCuABI] - Adjust the bounds/permissions properly */ + regs->pcc = cheri_user_root_cap; + + regs->csp = cheri_user_root_cap; + } else /* Hybrid */ { + regs->pcc = cheri_user_root_allperms_cap; + /* CSP is null-derived in hybrid */ + } }
#ifdef CONFIG_CHERI_PURECAP_UABI @@ -99,22 +95,25 @@ void morello_setup_signal_return(struct pt_regs *regs) * point (this means in particular that the signal handler is invoked in * Executive). */ -#ifdef CONFIG_CHERI_PURECAP_UABI - if (is_compat_task()) - init_pcc(regs); - /* Unseal if the pcc has sentry object type */ - else if (cheri_is_sentry(regs->pcc)) - regs->pcc = cheri_unseal(regs->pcc, morello_sentry_unsealcap); -#else - init_pcc(regs); -#endif update_regs_c64(regs, regs->pc);
- /* - * Also set CLR to a valid capability, to allow a C64 handler to return - * to the trampoline using `ret clr`. - */ - regs->cregs[30] = morello_root_cap; + if (is_pure_task()) { + /* Unseal if the pcc has sentry object type */ + if (cheri_is_sentry(regs->pcc)) + regs->pcc = cheri_unseal(regs->pcc, + morello_sentry_unsealcap); + + /* TODO [PCuABI] - Adjust the bounds/permissions properly */ + regs->cregs[30] = cheri_user_root_cap; + } else /* Hybrid */ { + regs->pcc = cheri_user_root_allperms_cap; + + /* + * Also set CLR to a valid capability, to allow a C64 handler + * to return to the trampoline using `ret clr`. + */ + regs->cregs[30] = cheri_user_root_allperms_cap; + } }
static char *format_cap(char *buf, size_t size, uintcap_t cap) @@ -352,12 +351,10 @@ static int __init morello_cap_init(void) /* Initialise Morello-specific root capabilities. */ morello_root_cap = root_cap;
-#ifdef CONFIG_CHERI_PURECAP_UABI /* Initialize a capability able to unseal sentry capabilities. */ perms = CHERI_PERM_GLOBAL | CHERI_PERM_UNSEAL; morello_sentry_unsealcap = cheri_address_set(root_cap, CHERI_OTYPE_SENTRY); morello_sentry_unsealcap = build_cap(morello_sentry_unsealcap, perms, 1); -#endif
return 0; }
Just like PCC/CSP, we can now derive the initial user DDC from the appropriate cheri_user_root_* userspace root capability. We still provide it in purecap to be consistent with the transitional PCuABI specification, but it will eventually be set to the null capability.
Checking whether we are in compat or not in assembly would be tedious, so a C wrapper is used to do the purecap/hybrid selection. morello_thread_init_user() always targets current, so to keep things simple and use the argumentless is_pure_task() helper, we remove its task_struct * argument. This is in line with other helpers called from arch_setup_new_exec().
Signed-off-by: Kevin Brodsky kevin.brodsky@arm.com --- arch/arm64/include/asm/morello.h | 2 +- arch/arm64/kernel/morello.c | 10 ++++++++++ arch/arm64/kernel/process.c | 2 +- arch/arm64/lib/morello.S | 8 +++----- 4 files changed, 15 insertions(+), 7 deletions(-)
diff --git a/arch/arm64/include/asm/morello.h b/arch/arm64/include/asm/morello.h index 7ba5a527891b..e60ad5727fca 100644 --- a/arch/arm64/include/asm/morello.h +++ b/arch/arm64/include/asm/morello.h @@ -63,7 +63,7 @@ void morello_thread_set_csp(struct pt_regs *regs, user_uintptr_t sp); void *morello_capcpy(void *dst, const void *src, size_t len);
void morello_thread_start(struct pt_regs *regs, unsigned long pc); -void morello_thread_init_user(struct task_struct *tsk); +void morello_thread_init_user(void); void morello_thread_save_user_state(struct task_struct *tsk); void morello_thread_restore_user_state(struct task_struct *tsk); void morello_task_save_user_tls(struct task_struct *tsk, user_uintptr_t *tp_ptr); diff --git a/arch/arm64/kernel/morello.c b/arch/arm64/kernel/morello.c index e14812916907..945718ed4541 100644 --- a/arch/arm64/kernel/morello.c +++ b/arch/arm64/kernel/morello.c @@ -25,6 +25,7 @@ void __morello_cap_lo_hi_tag(uintcap_t cap, u64 *lo_val, u64 *hi_val, u8 *tag); void __morello_merge_c_x(uintcap_t *creg, u64 xreg); bool __morello_cap_has_executive(uintcap_t cap); +void __morello_thread_init_user(struct task_struct *tsk, uintcap_t ddc);
/* Not defined as static because morello.S refers to it */ uintcap_t morello_root_cap __ro_after_init; @@ -79,6 +80,15 @@ void morello_thread_start(struct pt_regs *regs, unsigned long pc) } }
+void morello_thread_init_user(void) +{ + /* TODO [PCuABI] - Set DDC to the null capability */ + uintcap_t ddc = is_pure_task() ? cheri_user_root_cap + : cheri_user_root_allperms_cap; + + __morello_thread_init_user(current, ddc); +} + #ifdef CONFIG_CHERI_PURECAP_UABI void morello_thread_set_csp(struct pt_regs *regs, user_uintptr_t sp) { diff --git a/arch/arm64/kernel/process.c b/arch/arm64/kernel/process.c index 1f8093bf87f9..f88529ae0631 100644 --- a/arch/arm64/kernel/process.c +++ b/arch/arm64/kernel/process.c @@ -707,7 +707,7 @@ void arch_setup_new_exec(void) }
if (system_supports_morello()) - morello_thread_init_user(current); + morello_thread_init_user(); }
#ifdef CONFIG_ARM64_TAGGED_ADDR_ABI diff --git a/arch/arm64/lib/morello.S b/arch/arm64/lib/morello.S index e250ff3dfc49..e1cd14355721 100644 --- a/arch/arm64/lib/morello.S +++ b/arch/arm64/lib/morello.S @@ -67,11 +67,9 @@ SYM_FUNC_START(morello_capcpy) ret SYM_FUNC_END(morello_capcpy)
-SYM_FUNC_START(morello_thread_init_user) +SYM_FUNC_START(__morello_thread_init_user) mov x9, #THREAD_MORELLO_USER_STATE add x0, x0, x9 // x0 = tsk->thread.morello_user_state - adr_l x1, morello_root_cap - ldr c1, [x1]
/* * CTPIDR doesn't need to be initialised explicitly: @@ -86,7 +84,7 @@ SYM_FUNC_START(morello_thread_init_user) */ msr rctpidr_el0, czr
- /* DDC: initialised to the root capability (like PCC) */ + /* DDC: initialised to the specified value */ msr ddc_el0, c1 /* RDDC: null capability (processes are always started in Executive) */ msr rddc_el0, czr @@ -99,7 +97,7 @@ SYM_FUNC_START(morello_thread_init_user) str xzr, [x0, #MORELLO_STATE_CCTLR]
ret -SYM_FUNC_END(morello_thread_init_user) +SYM_FUNC_END(__morello_thread_init_user)
SYM_FUNC_START(morello_thread_save_user_state) mov x9, #THREAD_MORELLO_USER_STATE
As a privileged operation (disabled by default), arbitrary capabilities may be created in a process via ptrace. Now that we have defined generic user root capabilities, make use of cheri_user_root_allperms_cap (the "root of roots") to build these capabilities from, instead of the kernel root capability. Change the name of the helper accordingly and clarify that it should not be used for unprivileged operations.
Using cheri_user_root_allperms_cap is a clear improvement, however this still allows creating capabilities that could not otherwise be obtained in PCuABI (e.g. with Seal combined with other permissions). Considering the additional complexity that restricting this further would entail, and the operation being only involved in privileged debugging, this situation is considered acceptable.
Signed-off-by: Kevin Brodsky kevin.brodsky@arm.com --- arch/arm64/include/asm/morello.h | 10 +++++++++- arch/arm64/kernel/morello.c | 4 ++-- arch/arm64/kernel/ptrace.c | 2 +- arch/arm64/lib/morello.S | 9 +++------ 4 files changed, 15 insertions(+), 10 deletions(-)
diff --git a/arch/arm64/include/asm/morello.h b/arch/arm64/include/asm/morello.h index e60ad5727fca..56052a028c0e 100644 --- a/arch/arm64/include/asm/morello.h +++ b/arch/arm64/include/asm/morello.h @@ -28,7 +28,15 @@ struct morello_state { };
void morello_cap_get_val_tag(uintcap_t cap, __uint128_t *val, u8 *tag); -uintcap_t morello_build_cap_from_root_cap(const __uint128_t *val, u8 tag); + +/* + * Builds a user capability from a 128-bit pattern + tag. The capability will + * be derived from cheri_user_root_allperms_cap and the object type will be + * preserved. + * + * This function should only be used for privileged operations (e.g. debug). + */ +uintcap_t morello_build_any_user_cap(const __uint128_t *val, u8 tag);
uintcap_t morello_get_root_cap(void);
diff --git a/arch/arm64/kernel/morello.c b/arch/arm64/kernel/morello.c index 945718ed4541..0b79618ae6a9 100644 --- a/arch/arm64/kernel/morello.c +++ b/arch/arm64/kernel/morello.c @@ -204,8 +204,8 @@ static int access_remote_cap(struct task_struct *tsk, struct mm_struct *mm, goto out_put; }
- *kaddr = morello_build_cap_from_root_cap(&user_cap->val, - user_cap->tag); + *kaddr = morello_build_any_user_cap(&user_cap->val, + user_cap->tag); flush_ptrace_access(vma, (unsigned long)kaddr, (unsigned long)kaddr + sizeof(uintcap_t)); set_page_dirty_lock(page); diff --git a/arch/arm64/kernel/ptrace.c b/arch/arm64/kernel/ptrace.c index bcc6adc4819e..655e771c1a49 100644 --- a/arch/arm64/kernel/ptrace.c +++ b/arch/arm64/kernel/ptrace.c @@ -1423,7 +1423,7 @@ static int morello_get(struct task_struct *target,
#define MORELLO_STATE_BUILD_CAP(state, reg, cap) do { \ u8 tag = (state.tag_map >> MORELLO_PT_TAG_MAP_REG_BIT(reg)) & 0x1; \ - cap = morello_build_cap_from_root_cap(&state.reg, tag); \ + cap = morello_build_any_user_cap(&state.reg, tag); \ } while (0)
static int morello_set(struct task_struct *target, diff --git a/arch/arm64/lib/morello.S b/arch/arm64/lib/morello.S index e1cd14355721..5bc1540c829f 100644 --- a/arch/arm64/lib/morello.S +++ b/arch/arm64/lib/morello.S @@ -22,7 +22,7 @@ SYM_FUNC_START(morello_cap_get_val_tag) ret SYM_FUNC_END(morello_cap_get_val_tag)
-SYM_FUNC_START(morello_build_cap_from_root_cap) +SYM_FUNC_START(morello_build_any_user_cap) ldr c0, [x0] cbz w1, 1f /* @@ -31,11 +31,8 @@ SYM_FUNC_START(morello_build_cap_from_root_cap) * In case c0 is sealed (non-zero object type), we need to extract the * object type first to be able to reseal it after BUILD. The CSEAL * instruction is used to cover the case where c0 was not sealed. - * - * TODO [PCuABI] - the user root capability should be used, not the - * kernel one. */ - adr_l x3, morello_root_cap + adr_l x3, cheri_user_root_allperms_cap ldr c3, [x3]
cpytype c4, c3, c0 @@ -47,7 +44,7 @@ SYM_FUNC_START(morello_build_cap_from_root_cap) clrtag c0, c0 2: ret -SYM_FUNC_END(morello_build_cap_from_root_cap) +SYM_FUNC_END(morello_build_any_user_cap)
SYM_FUNC_START(morello_capcpy) mov x3, x0
All users of morello_root_cap have now been moved to the appropriate cheri_user_root*_cap, and it is time to retire it.
Signed-off-by: Kevin Brodsky kevin.brodsky@arm.com --- arch/arm64/include/asm/morello.h | 2 -- arch/arm64/kernel/morello.c | 10 ---------- 2 files changed, 12 deletions(-)
diff --git a/arch/arm64/include/asm/morello.h b/arch/arm64/include/asm/morello.h index 56052a028c0e..9f35d710cbfe 100644 --- a/arch/arm64/include/asm/morello.h +++ b/arch/arm64/include/asm/morello.h @@ -38,8 +38,6 @@ void morello_cap_get_val_tag(uintcap_t cap, __uint128_t *val, u8 *tag); */ uintcap_t morello_build_any_user_cap(const __uint128_t *val, u8 tag);
-uintcap_t morello_get_root_cap(void); - /* * Reads or writes a capability from/to tsk's address space (depending on * gup_flags & FOLL_WRITE). diff --git a/arch/arm64/kernel/morello.c b/arch/arm64/kernel/morello.c index 0b79618ae6a9..d999506509be 100644 --- a/arch/arm64/kernel/morello.c +++ b/arch/arm64/kernel/morello.c @@ -27,8 +27,6 @@ void __morello_merge_c_x(uintcap_t *creg, u64 xreg); bool __morello_cap_has_executive(uintcap_t cap); void __morello_thread_init_user(struct task_struct *tsk, uintcap_t ddc);
-/* Not defined as static because morello.S refers to it */ -uintcap_t morello_root_cap __ro_after_init; static uintcap_t morello_sentry_unsealcap __ro_after_init;
/* DDC_ELx reset value (low/high 64 bits), as defined in the Morello spec */ @@ -37,11 +35,6 @@ static uintcap_t morello_sentry_unsealcap __ro_after_init;
#define CAP_OTYPE_FIELD_BITS 15
-uintcap_t morello_get_root_cap(void) -{ - return morello_root_cap; -} - static bool is_pure_task(void) { return IS_ENABLED(CONFIG_CHERI_PURECAP_UABI) && !is_compat_task(); @@ -358,9 +351,6 @@ static int __init morello_cap_init(void) /* Maximum userspace bounds for the time being. */ cheri_user_root_cid_cap = build_cap(cheri_user_root_allperms_cap, perms);
- /* Initialise Morello-specific root capabilities. */ - morello_root_cap = root_cap; - /* Initialize a capability able to unseal sentry capabilities. */ perms = CHERI_PERM_GLOBAL | CHERI_PERM_UNSEAL; morello_sentry_unsealcap = cheri_address_set(root_cap, CHERI_OTYPE_SENTRY);
Update the documentation in line with the move to cheri_user_root_allperms_cap to derive all user capabilities from in hybrid, instead of the reset DDC.
Signed-off-by: Kevin Brodsky kevin.brodsky@arm.com --- Documentation/arm64/morello.rst | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-)
diff --git a/Documentation/arm64/morello.rst b/Documentation/arm64/morello.rst index bc0d98596762..0686245c8381 100644 --- a/Documentation/arm64/morello.rst +++ b/Documentation/arm64/morello.rst @@ -5,7 +5,7 @@ Morello in AArch64 Linux Author: Kevin Brodsky kevin.brodsky@arm.com
| Original date: 2020-09-07 -| Last updated: 2022-04-22 +| Last updated: 2022-12-07 |
This document describes the provision of Morello functionalities to @@ -293,8 +293,7 @@ are initialized as follows:
* For capability registers, the upper 64 bits and tag are set to:
- - CMAX for PCC and DDC_EL0, as defined in the architecture (tag set, - maximum bounds, maximum permissions, object type set to 0). + - CROOT for PCC and DDC_EL0, as defined below. - All zeroes for all other registers.
* For capability registers, the lower 64 bits are set to: @@ -307,10 +306,18 @@ are initialized as follows:
* CCTLR_EL0 is set to 0.
+CROOT corresponds to the following capability attributes: + +* Tag set. +* Object type set to 0. +* Bounds including the entire user address space (whose size depends on + ``CONFIG_ARM64_VA_BITS``). +* All hardware-defined permissions and the User[0] permission. + Note - PCC has all permissions set after ``execve()``, which means that a - process is always started in Executive. All Restricted registers are - zeroed. + This means in particular that PCC is initialized with the Executive + permission set; as a result a process is always started in Executive. All + Restricted registers are zeroed.
Register merging principle ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -484,14 +491,14 @@ Signal handling
When a signal handler is invoked:
-* PCC is reset to CMAX (see Generalities_ in the Register handling +* PCC is reset to CROOT (see Generalities_ in the Register handling section), and its address is set as usual to the signal handler's. This means in particular that **signal handlers are always run in Executive**. Accordingly, the signal frame is stored on the Executive stack (i.e. through CSP_EL0), if the alternate signal stack is not used.
-* CLR (C30) is also reset to CMAX, and its address set as usual (to the +* CLR (C30) is also reset to CROOT, and its address set as usual (to the signal trampoline). This allows a signal handler to return to the trampoline using the ``ret clr`` instruction, in addition to the usual ``ret [lr]`` instruction.
Hi Kevin,
It all looks goot to me.
--- BR B. On Thu, Feb 16, 2023 at 08:38:21AM +0000, Kevin Brodsky wrote:
Hi,
This is a fairly small update to this series, see the cover letter of v2 for more information. v3 addresses comments from Beata and Amit, thanks for the reviews!
v2..v3:
- "pps: Add missing #include" (patch 1 in v2) merged in next separately.
- Renamed cheri_user_root_all_cap to cheri_user_root_allperms_cap.
- Added reference to upstream commit removing CONFIG_SET_FS (patch 1).
- Made cheri_check_cap() take a const capability (patch 4).
- Rewrote the initialisation of root capabilities, using a new build_cap() helper (patch 5).
- Added CAP_OTYPE_FIELD_BITS to calculate the number of object types (patch 5).
- Rebased on 6.1 (some conflicts but all trivial).
Review branch:
https://git.morello-project.org/kbrodsky-arm/linux/-/commits/morello/cheri_p...
Thanks, Kevin
Kevin Brodsky (14): linux/user_ptr.h: Remove kaddr_to_user_ptr() linux/user_ptr.h: Improve comment formatting arm64: uapi: Add asm/cheri.h linux/cheri.h: Introduce CHERI helpers arm64: morello: Implement cheri.h fs/binfmt_elf: Use appropriate caps for AT_CHERI_{SEAL,CID}_CAP arm64: compat: Use appropriate root cap in compat_ptr() in PCuABI linux/user_ptr.h: Generic PCuABI impl for uaddr_to_user_ptr* arm64: Remove asm/user_ptr.h arm64: morello: Initialise user capabilities from cheri_user_root_* arm64: morello: Initialise user DDC from cheri_user_root_* arm64: morello: Build arbitrary user caps using appropriate root arm64: morello: Remove morello_root_cap arm64: morello: Update root capability in documentation
Documentation/arm64/morello.rst | 23 +++-- Documentation/core-api/user_ptr.rst | 8 -- arch/Kconfig | 2 +- arch/arm64/Kconfig | 2 +- arch/arm64/include/asm/cheri.h | 11 ++ arch/arm64/include/asm/compat.h | 9 +- arch/arm64/include/asm/morello.h | 12 ++- arch/arm64/include/asm/user_ptr.h | 43 -------- arch/arm64/include/uapi/asm/cheri.h | 11 ++ arch/arm64/kernel/morello.c | 152 +++++++++++++++++----------- arch/arm64/kernel/process.c | 2 +- arch/arm64/kernel/ptrace.c | 2 +- arch/arm64/lib/morello.S | 17 ++-- fs/binfmt_elf.c | 10 +- include/linux/cheri.h | 134 ++++++++++++++++++++++++ include/linux/user_ptr.h | 69 ++++++------- lib/Makefile | 3 + lib/cheri.c | 73 +++++++++++++ lib/user_ptr.c | 26 +++++ 19 files changed, 423 insertions(+), 186 deletions(-) create mode 100644 arch/arm64/include/asm/cheri.h delete mode 100644 arch/arm64/include/asm/user_ptr.h create mode 100644 arch/arm64/include/uapi/asm/cheri.h create mode 100644 include/linux/cheri.h create mode 100644 lib/cheri.c create mode 100644 lib/user_ptr.c
-- 2.38.1
linux-morello mailing list -- linux-morello@op-lists.linaro.org To unsubscribe send an email to linux-morello-leave@op-lists.linaro.org
On 23/02/2023 11:42, Beata Michalska wrote:
Hi Kevin,
It all looks goot to me.
Thanks for the final review! Now in next.
Kevin
BR B. On Thu, Feb 16, 2023 at 08:38:21AM +0000, Kevin Brodsky wrote:
Hi,
This is a fairly small update to this series, see the cover letter of v2 for more information. v3 addresses comments from Beata and Amit, thanks for the reviews!
v2..v3:
- "pps: Add missing #include" (patch 1 in v2) merged in next separately.
- Renamed cheri_user_root_all_cap to cheri_user_root_allperms_cap.
- Added reference to upstream commit removing CONFIG_SET_FS (patch 1).
- Made cheri_check_cap() take a const capability (patch 4).
- Rewrote the initialisation of root capabilities, using a new build_cap() helper (patch 5).
- Added CAP_OTYPE_FIELD_BITS to calculate the number of object types (patch 5).
- Rebased on 6.1 (some conflicts but all trivial).
Review branch:
https://git.morello-project.org/kbrodsky-arm/linux/-/commits/morello/cheri_p...
Thanks, Kevin
Kevin Brodsky (14): linux/user_ptr.h: Remove kaddr_to_user_ptr() linux/user_ptr.h: Improve comment formatting arm64: uapi: Add asm/cheri.h linux/cheri.h: Introduce CHERI helpers arm64: morello: Implement cheri.h fs/binfmt_elf: Use appropriate caps for AT_CHERI_{SEAL,CID}_CAP arm64: compat: Use appropriate root cap in compat_ptr() in PCuABI linux/user_ptr.h: Generic PCuABI impl for uaddr_to_user_ptr* arm64: Remove asm/user_ptr.h arm64: morello: Initialise user capabilities from cheri_user_root_* arm64: morello: Initialise user DDC from cheri_user_root_* arm64: morello: Build arbitrary user caps using appropriate root arm64: morello: Remove morello_root_cap arm64: morello: Update root capability in documentation
Documentation/arm64/morello.rst | 23 +++-- Documentation/core-api/user_ptr.rst | 8 -- arch/Kconfig | 2 +- arch/arm64/Kconfig | 2 +- arch/arm64/include/asm/cheri.h | 11 ++ arch/arm64/include/asm/compat.h | 9 +- arch/arm64/include/asm/morello.h | 12 ++- arch/arm64/include/asm/user_ptr.h | 43 -------- arch/arm64/include/uapi/asm/cheri.h | 11 ++ arch/arm64/kernel/morello.c | 152 +++++++++++++++++----------- arch/arm64/kernel/process.c | 2 +- arch/arm64/kernel/ptrace.c | 2 +- arch/arm64/lib/morello.S | 17 ++-- fs/binfmt_elf.c | 10 +- include/linux/cheri.h | 134 ++++++++++++++++++++++++ include/linux/user_ptr.h | 69 ++++++------- lib/Makefile | 3 + lib/cheri.c | 73 +++++++++++++ lib/user_ptr.c | 26 +++++ 19 files changed, 423 insertions(+), 186 deletions(-) create mode 100644 arch/arm64/include/asm/cheri.h delete mode 100644 arch/arm64/include/asm/user_ptr.h create mode 100644 arch/arm64/include/uapi/asm/cheri.h create mode 100644 include/linux/cheri.h create mode 100644 lib/cheri.c create mode 100644 lib/user_ptr.c
-- 2.38.1
linux-morello mailing list -- linux-morello@op-lists.linaro.org To unsubscribe send an email to linux-morello-leave@op-lists.linaro.org
linux-morello@op-lists.linaro.org