On Mon, Dec 12, 2022 at 10:35:40AM +0000, Kevin Brodsky wrote:
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 | 132 ++++++++++++++++++++++++++++++++++++++++++ lib/Makefile | 2 + lib/cheri.c | 72 +++++++++++++++++++++++ 4 files changed, 209 insertions(+) create mode 100644 include/linux/cheri.h create mode 100644 lib/cheri.c
diff --git a/arch/Kconfig b/arch/Kconfig index afde8a4eeee6..6c3c2e19b77e 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -1376,6 +1376,9 @@ config DYNAMIC_SIGFRAME config HAVE_ARCH_NODE_DEV_GROUP bool +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..5f95f1ac8f01 --- /dev/null +++ b/include/linux/cheri.h @@ -0,0 +1,132 @@ +/* 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:
- @addr is a valid userspace address.
- The (@addr, @len) tuple can be represented as capability bounds.
- @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);
The returned capability might be invalid as of having tag cleared. I assume in most of the cases it will be just fine, but wondering if it would make sense to return either capability with null capability , or one that could be validated with PTR_ERR somewhat ? Just thinking at loud here, how one would verify the result is valid (currently it would need check tag but maybe we could utilize more generic way of validating pointers ... On the other hand those new helpers are available only when __CHERI__ is defined so I assume there are only to be used in CHERI-related contexts ?
+/**
- 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:
- @addr is a valid userspace address.
- @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(void * __capability cap, size_t len, cheri_perms_t perms);
Minor but maybe void * __capability with const qualifier ?
+/*
- 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_all_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_all_cap; /* Userspace root (all permissions) */
xxx_root_all_cap somehwat reads to me as 'all capabilities' instead of 'capability with all permissions' even the context guards the interpretation, but maybe xxx_root_fill_cap instead ?
+#endif /* __CHERI__ */
+#endif /* _LINUX_CHERI_H */ diff --git a/lib/Makefile b/lib/Makefile index 6b9ffc1bd1ee..8dd8c00fee31 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -263,6 +263,8 @@ obj-$(CONFIG_MEMREGION) += memregion.o obj-$(CONFIG_STMP_DEVICE) += stmp_device.o obj-$(CONFIG_IRQ_POLL) += irq_poll.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..d895e6594c21 --- /dev/null +++ b/lib/cheri.c @@ -0,0 +1,72 @@ +/* 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_all_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(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__ */
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