Hi,
The morello/next branch has been rebased from v6.4 to v6.7. Make sure to
reset/rebase any local branch tracking next. The final 6.4-based commit has been
tagged morello-last-6.4.
Note that morello.rst has moved under Documentation/arch/arm64, following the
documentation reorganisation merged upstream in 6.5.
There should be no other user-visible change following this rebase, aside from
upstream changes between 6.4 and 6.7. The rest of this email provides a detailed
changelog for developers.
Cheers,
Kevin
--------------
Noteworthy changes:
- "fs/binfmt_elf: Add additional Aux vector entries" has been slightly adapted
following changes to load_elf_interp() [1]. This revealed an issue with the
original patch, where the file size (p_filesz) of segments was considered
instead of the memory size (p_memsz). Because we are concerned with the
entirety of memory mapped for the executable/interpreter, we should rather
consider p_memsz, which may be greater than p_filesz (the leftover is
zero-padded / zero-mapped). The calculation of both exec_load_info and
interp_load_info has been modified accordingly.
- io_uring:
* New patch "io_uring: Use full pointers for IORING_SETUP_NO_MMAP" [2]
following the introduction of that flag in [3].
* Adaptations following the addition of getsockopt/setsockopt [4] (patch
9-10): new struct io_uring_sqe::optval member. A new patch "io_uring/cmd:
Convert compat64 pointers for OP_{G,S}ETSOCKOPT" [5] takes care of the
compat64 conversion, as it cannot realistically be done upfront.
* Straightforward adaptations (replacing u64_to_user_ptr() with a cast and
using io_user_data_is_same() instead of direct comparison) to the following
additions:
- waitid operation [6]
- futex operations [7]
* "io_uring: Implement compat versions of uAPI structs and handle them"
adapted following [8] (patch 3-4), and additions to uapi structs reflected
in linux/io_uring_compat.h.
- BPF:
* "bpf: compat64: add handler and convert bpf_attr in", "bpf: compat64: handle
bpf_{btf,prog,link}_info" and "bpf: use user pointer types in uAPI structs"
extended to cover uapi additions since 6.4. The handling of those is
straightforward, albeit with a trick to handle BPF_LINK_TYPE_PERF_EVENT in
convert_compat_link_info_in(), following [9].
- New commit "fs/proc: Update pagemap ioctl handler for PCuABI" to adapt the new
PAGEMAP_SCAN ioctl handler [10]. These are minimal changes; the struct passed
to the ioctl should be adapted too. Created issue #64 to track this [11].
- Due to the refactoring and additional macros introduced by [12], the
permission split between private and shared mappings introduced in "arm64:
morello: Enable access to capabilities in memory" became more difficult to
implement fully. Since this permission split is introduced in a more proper
and less invasive manner in "arm64: morello: Explicitly add VM_*_CAPS to
private user mappings", the split was removed from the original commit, to
simplify further rebasing. It should remain functionally correct, beside
granting tag access to shared mappings (unlikely to be an issue when
bisecting). This simplification also made the following commit unnecessary,
and it has now been dropped: "arm64: Refactor PAGE_* definitions"
Minor changes:
- Added missing declarations in "arm64: add syscall wrapper for compat tasks in
purecap", aligning with [13].
- Switched "arm64: morello: ptrace: Add request for reading a capability in
memory" to using get_user_page_vma_remote(), following [14].
- "net: Modify ioctl to use user_uintptr_t" is now smaller (several hunks
disappeared) as struct proto's ioctl callback no longer manipulates user
pointers [15].
- "arm64: vdso: Build a pure-capability vDSO" adapted for the new vdso_install
approach [16].
- "futex: Implement PCuABI for futex_waitv syscall" now amends the new
futex_requeue syscall introduced in [17] too (simply dropping some
u64_to_user_ptr()).
- Added an explicit definition of struct sched_param to "kselftests/arm64:
morello: Clean-up header files", as [18] removed it from the uapi header.
- Added a new commit "tools/include: Add __kernel_uintptr_t to types.h" as arm64
kselftests include tools/include since [19], shadowing the uapi linux/types.h.
Notes:
- "arm64: morello: Handle capability faults": SEGV_CAPTAGERR conflicts with the
new x86-specific (CET) SEGV_CPERR. Its value is left unchanged (10) to
avoid a uapi change, which doesn't seem justified as it is currently trivial
to disambiguate the two codes (one is x86-only, the other Morello-only).
Dropped:
- The following patches were posted [20] and merged upstream:
* "fcntl: Cast commands with int args explicitly"
* "fs: Pass argument to fcntl_setlease as int"
* "pipe: Pass argument of pipe_fcntl as int"
* "dnotify: Pass argument of fcntl_dirnotify as int"
* "NFSv4: Fix nfs4_proc_setlease definition"
[1] https://lore.kernel.org/all/20230929031716.it.155-kees@kernel.org/
[2]
https://op-lists.linaro.org/archives/list/linux-morello@op-lists.linaro.org…
[3] https://lore.kernel.org/all/20230513141643.1037620-1-axboe@kernel.dk/
[4] https://lore.kernel.org/all/20231016134750.1381153-1-leitao@debian.org/
[5]
https://op-lists.linaro.org/archives/list/linux-morello@op-lists.linaro.org…
[6] https://lore.kernel.org/lkml/20230909151124.1229695-1-axboe@kernel.dk/
[7] https://lore.kernel.org/lkml/20230928172517.961093-1-axboe@kernel.dk/
[8]
https://lore.kernel.org/all/447aa1b2968978c99e655ba88db536e903df0fe9.169291…
[9] https://lore.kernel.org/all/20230709025630.3735-9-laoar.shao@gmail.com/
[10] https://lore.kernel.org/all/20230821141518.870589-1-usama.anjum@collabora.c…
[11] https://git.morello-project.org/morello/kernel/linux/-/issues/64
[12] https://lore.kernel.org/all/20230606145859.697944-1-joey.gouly@arm.com/
[13] https://lore.kernel.org/all/20230516160642.523862-4-arnd@kernel.org/
[14] https://lore.kernel.org/all/cover.1684350871.git.lstoakes@gmail.com/
[15] https://lore.kernel.org/all/20230609152800.830401-1-leitao@debian.org/
[16] https://lore.kernel.org/lkml/20231014105436.2119702-1-masahiroy@kernel.org/
[17]
https://lore.kernel.org/all/20230921105248.511860556@noisy.programming.kick…
[18] https://lore.kernel.org/all/20230808030357.1213829-1-kolyshkin@gmail.com/
[19]
https://lore.kernel.org/all/20230728-arm64-signal-memcpy-fix-v4-4-0c1290db5…
[20]
https://lore.kernel.org/linux-fsdevel/20230414152459.816046-1-Luca.Vizzarro…
Hi All,
I am glad to inform you on the availability of a new version of our SDK and
base rootfs images for Morello (1.7.0). After months of hard work we are happy
to share with you what we put together.
Honoring our motto "Let Linux developers focus on the porting of their own
application", we feel that this is another steps in the right direction.
[Morello SDK]
In less than 10 minutes you should be able to setup a docker container with
everything you need to build an application for Morello.
- Documentation: https://sdk.morello-project.org/
- Code repository: https://git.morello-project.org/morello/morello-sdk
New in 1.7.0:
- GDB support with Capabilities.
- Support for vDSO in musl.
- Initial support for compartments (limited demo of basic cases) [1].
- New versions for compilers and C libraries.
[1] https://git.morello-project.org/morello/morello-examples
If you want to try a demo of the SDK that runs on a Morello FVP (for more
information on what is an FVP: www.morello-project.org) please have a look below:
[Morello Linux]
In less than 10 minutes you should be able to setup a docker container with
everything you need to build and boot into a Morello Debian environment.
- Documentation: https://linux.morello-project.org/
- Code repository: https://git.morello-project.org/morello/morello-linux
Note: The documentation covers the instructions for Linux but if you know what
you are doing and are familiar with docker no one stops you from running our
solution on Windows or Mac.
New in 1.7.0:
- Updated Kernel ABI with initial security support.
- Support for vDSO in kernel and C library.
- New versions for compilers and C libraries.
- Basic Compartment Demos.
- Bug fixing and polishing of existing features.
Note: This release does not include a new version of the Android environment.
Further Android releases are now deprecated. Ongoing releases will focus on
the Morello Linux Environment.
Are we done with it?
No, by any mean. This is just the beginning and we need your help and
collaboration to make sure that we improve our solution to meet developers
needs: your needs!
So why don't you try it and let us know your thoughts?
Thanks and Regards,
Vincenzo
Add tests for capability enforcement via the standard copy routines
(copy_{to, from}_user, {get, put}_user), futex operations and the
explicit uaccess checks used in iov_iter via readv and writev.
Signed-off-by: Akram Ahmad <Akram.Ahmad(a)arm.com>
---
Hi everyone,
This is v4 of the uaccess kselftest patch and introduces the following:
- Simplification of the futex test to only use one futex
- Move FUTEX_WAKE_OP description to futex test
- Various minor corrections
V3 introduced the following changes:
- Corrections to function prototypes to remove void * in favour of the
actual struct pointer types and other minor corrections.
- Correction to the futex test, which now tests for a correctly enforced
uaccess routine when used in an atomic operation.
- Reintroduction of the out-of-bounds checking in tests.
V2 introduced the following changes:
- More concise formatting changes
- Corrections to various mistakes in tests
- Addition of a strlen_user() test
- Fixed futex test
The review branch can be found at:
https://git.morello-project.org/arkamnite/linux/-/commits/morello/uaccess_v4
The related issue can also be found at:
https://git.morello-project.org/morello/kernel/linux/-/issues/58
Many thanks,
Akram
---
.../testing/selftests/arm64/morello/Makefile | 2 +-
.../testing/selftests/arm64/morello/uaccess.c | 262 ++++++++++++++++++
2 files changed, 263 insertions(+), 1 deletion(-)
create mode 100644 tools/testing/selftests/arm64/morello/uaccess.c
diff --git a/tools/testing/selftests/arm64/morello/Makefile b/tools/testing/selftests/arm64/morello/Makefile
index ecfa6fc5e989..7b794d3e436c 100644
--- a/tools/testing/selftests/arm64/morello/Makefile
+++ b/tools/testing/selftests/arm64/morello/Makefile
@@ -17,7 +17,7 @@ CFLAGS += $(CLANG_FLAGS) $(CFLAGS_PURECAP) $(CFLAGS_COMMON)
LDFLAGS += $(CLANG_LDFLAGS) $(CLANG_FLAGS) -nostdlib -static
SRCS := $(wildcard *.c) $(wildcard *.S)
-PROGS := bootstrap clone exit mmap read_write sched signal
+PROGS := bootstrap clone exit mmap read_write sched signal uaccess
DEPS := $(wildcard *.h)
# these are the final executables
diff --git a/tools/testing/selftests/arm64/morello/uaccess.c b/tools/testing/selftests/arm64/morello/uaccess.c
new file mode 100644
index 000000000000..ee2847472bc2
--- /dev/null
+++ b/tools/testing/selftests/arm64/morello/uaccess.c
@@ -0,0 +1,262 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) 2023 Arm Limited
+
+#include <stdint.h>
+#include <stddef.h>
+#include <stdbool.h>
+
+#include <asm/fcntl.h>
+#include <asm-generic/errno-base.h>
+#include <cheriintrin.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/futex.h>
+#include <linux/mman.h>
+#include <linux/signal.h>
+#include <linux/sysinfo.h>
+#include <linux/socket.h>
+#include <linux/time.h>
+#include <linux/uio.h>
+
+#include "freestanding.h"
+
+#define MSG_LEN 16
+
+static const char file[] = "/my_file.txt";
+
+static inline int fsopen(const char *string)
+{
+ return syscall(__NR_fsopen, string);
+}
+
+
+static inline int futex_wake_op(uint32_t *uaddr, uint32_t *uaddr2, uint32_t val3)
+{
+ return syscall(__NR_futex, uaddr, FUTEX_WAKE_OP, 1, 1,
+ uaddr2, val3);
+}
+
+typedef unsigned short sa_family_t;
+
+struct sockaddr {
+ sa_family_t sa_family;
+ char sa_data[14];
+};
+
+static int getsockname(int socket, struct sockaddr *sockaddr, int *socklen)
+{
+ return syscall(__NR_getsockname, socket, sockaddr, socklen);
+}
+
+static int open_file(void)
+{
+ return syscall(__NR_openat, 0, file, O_RDWR | O_CREAT, 0666);
+}
+
+static inline int readv(int fd, struct iovec *iov, int iovcnt)
+{
+ return syscall(__NR_readv, fd, iov, iovcnt);
+}
+
+static inline int writev(int fd, struct iovec *iov, int iovcnt)
+{
+ return syscall(__NR_writev, fd, iov, iovcnt);
+}
+
+static inline int sigaction(int sig, struct sigaction *old, struct sigaction *new)
+{
+ return syscall(__NR_rt_sigaction, sig, old, new, sizeof(sigset_t));
+}
+
+static inline int sysinfo(struct sysinfo *ptr)
+{
+ return syscall(__NR_sysinfo, ptr);
+}
+
+TEST(test_copy_to_user)
+{
+ struct sysinfo my_sysinfo;
+
+ ASSERT_EQ(sysinfo(&my_sysinfo), 0);
+
+ ASSERT_EQ(sysinfo(&my_sysinfo + sizeof(struct sysinfo)), -EFAULT);
+
+ ASSERT_EQ(sysinfo(cheri_tag_clear(&my_sysinfo)), -EFAULT);
+
+ ASSERT_EQ(sysinfo(cheri_perms_and(&my_sysinfo, 0)), -EFAULT);
+}
+
+/*
+ * sigaction() tests both copy_to_user and copy_from_user by copying
+ * into and from a pointer to struct sigaction. The resulting action
+ * in this case is a no-op, but this does not affect the validity of
+ * the test. sigaction attempts to copy the new action from user space
+ * and the old action to user space, if the arguments is valid in either
+ * case.
+ */
+TEST(test_copy_user)
+{
+ struct sigaction act;
+
+ ASSERT_EQ(sigaction(SIGSEGV, NULL, &act), 0);
+ ASSERT_EQ(sigaction(SIGSEGV, &act, NULL), 0);
+
+ ASSERT_EQ(sigaction(SIGSEGV, NULL, &act + sizeof(struct sigaction)), -EFAULT);
+ ASSERT_EQ(sigaction(SIGSEGV, &act + sizeof(struct sigaction), NULL), -EFAULT);
+
+ ASSERT_EQ(sigaction(SIGSEGV, NULL, cheri_tag_clear(&act)), -EFAULT);
+ ASSERT_EQ(sigaction(SIGSEGV, cheri_tag_clear(&act), NULL), -EFAULT);
+
+ ASSERT_EQ(sigaction(SIGSEGV, NULL, cheri_perms_and(&act, 0)), -EFAULT);
+ ASSERT_EQ(sigaction(SIGSEGV, cheri_perms_and(&act, 0), NULL), -EFAULT);
+}
+
+/*
+ * getsockname(2) uses both get_user and put_user to copy the addrlen argument
+ * from and to user space respectively. getsockname(2) calls move_addr_to_user
+ * to copy an address to user space. move_addr_to_user also uses copy_to_user;
+ * if copy_to_user is successful, but either of get_user or put_user fail then
+ * getsockname(2) will return -EFAULT appropriately.
+ */
+TEST(test_get_put_user)
+{
+ struct sockaddr sa;
+ int sa_len = sizeof(sa);
+ int my_socket;
+
+ // socket(AF_INET, SOCK_STREAM, 0)
+ my_socket = syscall(__NR_socket, 2, 1, 0);
+
+ // The socket must be successfully opened before proceeding.
+ ASSERT_GE(my_socket, 0);
+
+ ASSERT_EQ(getsockname(my_socket, &sa, &sa_len), 0);
+ ASSERT_EQ(getsockname(my_socket, &sa, &sa_len + sizeof(struct sockaddr)), -EFAULT);
+ ASSERT_EQ(getsockname(my_socket, &sa, cheri_tag_clear(&sa_len)), -EFAULT);
+ ASSERT_EQ(getsockname(my_socket, &sa, cheri_perms_and(&sa_len, 0)), -EFAULT);
+ close(my_socket);
+}
+
+/*
+ * The FUTEX_WAKE_OP operation in the futex(2) syscall is used to handle more
+ * than one futex at the same time. This is an atomic operation that attempts
+ * to read, modify and write a value at a second uaddr (uaddr2) and also wake
+ * up waiters on the futex words at uaddr1 and uaddr2. uaddr1 and uaddr2 are
+ * provided via capabilities, and so this futex tests the case of an atomic
+ * uaccess operation.
+ *
+ * When using FUTEX_WAKE_OP, val3 is an encoding of both the operation as
+ * well as the comparison to be performed on the futex word at uaddr2. If val3
+ * is set to 0, this represents an operation of FUTEX_OP_SET and a comparison
+ * FUTEX_OP_CMP_EQ, where both oparg and comparg are 0.
+ */
+TEST(test_futex)
+{
+ static struct futex_waitv waitv;
+ uint32_t futex = 0;
+
+ waitv.uaddr = (uintptr_t)&futex;
+ waitv.flags = FUTEX_32 | FUTEX_PRIVATE_FLAG;
+ waitv.val = futex;
+ waitv.__reserved = 0;
+
+ ASSERT_GE(futex_wake_op(&futex, &futex, 0), 0);
+ ASSERT_EQ(futex_wake_op(&futex, &futex + sizeof(uint32_t), 0), -EFAULT);
+ ASSERT_EQ(futex_wake_op(&futex, cheri_tag_clear(&futex), 0), -EFAULT);
+ ASSERT_EQ(futex_wake_op(&futex, cheri_perms_and(&futex, 0), 0),
+ -EFAULT);
+}
+
+/*
+ * Test explicit accesses used in iov_iter via readv and writev. Both
+ * syscalls use explicit checking on the iov_base field of struct iovec,
+ * so the metadata of the capability provided for iov_base is modified as
+ * per the needs of each individual test.
+ */
+TEST(test_explicit_iov_iter)
+{
+ char buf0[2];
+ char buf1[4];
+ char buf2[6];
+ const char *write_buf0 = "Hello I am the first char buffer!\n";
+ const char *write_buf1 = "Hello, I am the second char buffer.\n";
+ const char *write_buf2 = "Hello, I am the third and final char buffer.\n";
+ struct iovec iov[3];
+ int iovcnt;
+ int fd;
+
+ fd = open_file();
+ ASSERT_NE(fd, -1);
+
+ iov[0].iov_base = buf0;
+ iov[0].iov_len = sizeof(buf0);
+ iov[1].iov_base = buf1;
+ iov[1].iov_len = sizeof(buf1);
+ iov[2].iov_base = buf2;
+ iov[2].iov_len = sizeof(buf2);
+
+ iovcnt = sizeof(iov) / sizeof(struct iovec);
+
+ ASSERT_GE(readv(fd, iov, iovcnt), 0);
+
+ iov[0].iov_base = buf0 + sizeof(iov) * 100;
+ ASSERT_EQ(readv(fd, iov, iovcnt), -EFAULT);
+
+ iov[0].iov_base = cheri_tag_clear(buf0);
+ ASSERT_EQ(readv(fd, iov, iovcnt), -EFAULT);
+
+ iov[0].iov_base = cheri_perms_and(buf0, 0);
+ ASSERT_EQ(readv(fd, iov, iovcnt), -EFAULT);
+
+ iov[0].iov_base = (void *)write_buf0;
+ iov[0].iov_len = strlen(write_buf0);
+ iov[1].iov_base = (void *)write_buf1;
+ iov[1].iov_len = strlen(write_buf1);
+ iov[2].iov_base = (void *)write_buf2;
+ iov[2].iov_len = strlen(write_buf2);
+
+ ASSERT_GE(writev(fd, iov, iovcnt), 0);
+
+ iov[0].iov_base = (void *)write_buf0 + sizeof(iov) * 100;
+ ASSERT_EQ(writev(fd, iov, iovcnt), -EFAULT);
+
+ iov[0].iov_base = (void *)cheri_tag_clear(write_buf0);
+ ASSERT_EQ(writev(fd, iov, iovcnt), -EFAULT);
+
+ iov[0].iov_base = (void *)cheri_perms_and(write_buf0, 0);
+ ASSERT_EQ(writev(fd, iov, iovcnt), -EFAULT);
+
+ close(fd);
+}
+
+/*
+ * strlen_user() explicitly inspects an input capability, the behaviour
+ * of which must also be verified within these tests. The fsopen() syscall
+ * makes use of strlen_user() by duplicating a string (representing the name
+ * of a filesystem) with the strndup_user() function. strlen_user() can
+ * therefore be tested with a call to fsopen().
+ */
+TEST(test_strlen_user)
+{
+ const char *fsname = "my_nonexistent_filesystem";
+
+ // strndup_user() will still be called, so fsopen() fails after this.
+ ASSERT_EQ(fsopen(fsname), -ENODEV);
+
+ ASSERT_EQ(fsopen(fsname + strlen(fsname) * 10), -EFAULT);
+
+ ASSERT_EQ(fsopen(cheri_tag_clear(fsname)), -EFAULT);
+
+ ASSERT_EQ(fsopen(cheri_perms_and(fsname, 0)), -EFAULT);
+}
+
+int main(void)
+{
+ test_copy_to_user();
+ test_copy_user();
+ test_get_put_user();
+ test_futex();
+ test_explicit_iov_iter();
+ test_strlen_user();
+ return 0;
+}
--
2.34.1
Add tests for capability enforcement via the standard copy routines
(copy_{to, from}_user, {get, put}_user), futex operations and the
explicit uaccess checks used in iov_iter via readv and writev.
Signed-off-by: Akram Ahmad <Akram.Ahmad(a)arm.com>
---
Hi everyone,
This is v3 of the uaccess kselftest patch. The main changes from V2 to
V3 are:
- Corrections to function prototypes to remove void * in favour of the
actual struct pointer types and other minor corrections.
- Correction to the futex test, which now tests for a correctly enforced
uaccess routine when used in an atomic operation.
- Reintroduction of the out-of-bounds checking in tests.
V2 introduced the following changes:
- More concise formatting changes
- Corrections to various mistakes in tests
- Addition of a strlen_user() test
- Fixed futex test
The review branch can be found at:
https://git.morello-project.org/arkamnite/linux/-/commits/morello%2Fuaccess…
The related issue can also be found at:
https://git.morello-project.org/morello/kernel/linux/-/issues/58
Many thanks,
Akram
---
.../testing/selftests/arm64/morello/Makefile | 2 +-
.../testing/selftests/arm64/morello/uaccess.c | 271 ++++++++++++++++++
2 files changed, 272 insertions(+), 1 deletion(-)
create mode 100644 tools/testing/selftests/arm64/morello/uaccess.c
diff --git a/tools/testing/selftests/arm64/morello/Makefile b/tools/testing/selftests/arm64/morello/Makefile
index ecfa6fc5e989..7b794d3e436c 100644
--- a/tools/testing/selftests/arm64/morello/Makefile
+++ b/tools/testing/selftests/arm64/morello/Makefile
@@ -17,7 +17,7 @@ CFLAGS += $(CLANG_FLAGS) $(CFLAGS_PURECAP) $(CFLAGS_COMMON)
LDFLAGS += $(CLANG_LDFLAGS) $(CLANG_FLAGS) -nostdlib -static
SRCS := $(wildcard *.c) $(wildcard *.S)
-PROGS := bootstrap clone exit mmap read_write sched signal
+PROGS := bootstrap clone exit mmap read_write sched signal uaccess
DEPS := $(wildcard *.h)
# these are the final executables
diff --git a/tools/testing/selftests/arm64/morello/uaccess.c b/tools/testing/selftests/arm64/morello/uaccess.c
new file mode 100644
index 000000000000..d1624f61adcd
--- /dev/null
+++ b/tools/testing/selftests/arm64/morello/uaccess.c
@@ -0,0 +1,271 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) 2023 Arm Limited
+
+#include <stdint.h>
+#include <stddef.h>
+#include <stdbool.h>
+
+#include <asm/fcntl.h>
+#include <asm-generic/errno-base.h>
+#include <cheriintrin.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/futex.h>
+#include <linux/mman.h>
+#include <linux/signal.h>
+#include <linux/sysinfo.h>
+#include <linux/socket.h>
+#include <linux/time.h>
+#include <linux/uio.h>
+
+#include "freestanding.h"
+
+#define MSG_LEN 16
+#define NR_FUTEXES 5
+
+static const char file[] = "/my_file.txt";
+
+static inline int fsopen(const char *string)
+{
+ return syscall(__NR_fsopen, string);
+}
+
+/*
+ * FUTEX_WAKE_OP is an operation used to handle more than one futex at the same
+ * time. When using FUTEX_WAKE_OP, val3 is an encoding of both the operation as
+ * well as the comparison to be performed on the futex word at uaddr2. If val3
+ * is set to 0, this represents an operation of FUTEX_OP_SET and a comparison
+ * FUTEX_OP_CMP_EQ, where both oparg and comparg are 0.
+ */
+static inline int futex_wake(uint32_t *uaddr, uint32_t *uaddr2, uint32_t val3,
+ const struct timespec *timeout)
+{
+ return syscall(__NR_futex, uaddr, FUTEX_WAKE_OP, NR_FUTEXES, timeout,
+ uaddr2, val3);
+}
+
+typedef unsigned short sa_family_t;
+
+struct sockaddr {
+ sa_family_t sa_family;
+ char sa_data[14];
+};
+
+static int getsockname(int socket, struct sockaddr *sockaddr, int *socklen)
+{
+ return syscall(__NR_getsockname, socket, sockaddr, socklen);
+}
+
+static int open_file(void)
+{
+ return syscall(__NR_openat, 0, file, O_RDWR | O_CREAT, 0666);
+}
+
+static inline int readv(int fd, struct iovec *iov, int iovcnt)
+{
+ return syscall(__NR_readv, fd, iov, iovcnt);
+}
+
+static inline int writev(int fd, struct iovec *iov, int iovcnt)
+{
+ return syscall(__NR_writev, fd, iov, iovcnt);
+}
+
+static inline int sigaction(int sig, struct sigaction *old, struct sigaction *new)
+{
+ return syscall(__NR_rt_sigaction, sig, old, new, sizeof(sigset_t));
+}
+
+static inline int sysinfo(struct sysinfo *ptr)
+{
+ return syscall(__NR_sysinfo, ptr);
+}
+
+TEST(test_copy_to_user)
+{
+ struct sysinfo my_sysinfo;
+
+ ASSERT_EQ(sysinfo(&my_sysinfo), 0);
+
+ ASSERT_EQ(sysinfo(&my_sysinfo + sizeof(struct sysinfo) * 100), -EFAULT);
+
+ ASSERT_EQ(sysinfo(cheri_tag_clear(&my_sysinfo)), -EFAULT);
+
+ ASSERT_EQ(sysinfo(cheri_perms_and(&my_sysinfo, 0)), -EFAULT);
+}
+
+/*
+ * sigaction() tests both copy_to_user and copy_from_user by copying
+ * into and from a pointer to struct sigaction. The resulting action
+ * in this case is a no-op, but this does not affect the validity of
+ * the test. sigaction attempts to copy the new action from user space
+ * and the old action to user space, if the arguments is valid in either
+ * case.
+ */
+TEST(test_copy_user)
+{
+ struct sigaction act;
+
+ ASSERT_EQ(sigaction(SIGSEGV, NULL, &act), 0);
+ ASSERT_EQ(sigaction(SIGSEGV, &act, NULL), 0);
+
+ ASSERT_EQ(sigaction(SIGSEGV, NULL, &act + sizeof(struct sigaction) * 100), -EFAULT);
+ ASSERT_EQ(sigaction(SIGSEGV, &act + sizeof(struct sigaction) * 100, NULL), -EFAULT);
+
+ ASSERT_EQ(sigaction(SIGSEGV, NULL, cheri_tag_clear(&act)), -EFAULT);
+ ASSERT_EQ(sigaction(SIGSEGV, cheri_tag_clear(&act), NULL), -EFAULT);
+
+ ASSERT_EQ(sigaction(SIGSEGV, NULL, cheri_perms_and(&act, 0)), -EFAULT);
+ ASSERT_EQ(sigaction(SIGSEGV, cheri_perms_and(&act, 0), NULL), -EFAULT);
+}
+
+/*
+ * getsockname(2) uses both get_user and put_user to copy the addrlen argument
+ * from and to user space respectively. getsockname(2) calls move_addr_to_user
+ * to copy an address to user space. move_addr_to_user also uses copy_to_user;
+ * if copy_to_user is successful, but either of get_user or put_user fail then
+ * getsockname(2) will return -EFAULT appropriately.
+ */
+TEST(test_get_put_user)
+{
+ struct sockaddr sa;
+ int sa_len = sizeof(sa);
+ int my_socket;
+
+ // socket(AF_INET, SOCK_STREAM, 0)
+ my_socket = syscall(__NR_socket, 2, 1, 0);
+
+ // The socket must be successfully opened before proceeding.
+ ASSERT_GE(my_socket, 0);
+
+ ASSERT_EQ(getsockname(my_socket, &sa, &sa_len), 0)
+ ASSERT_EQ(getsockname(my_socket, &sa, &sa_len + sizeof(struct sockaddr) * 100), -EFAULT)
+ ASSERT_EQ(getsockname(my_socket, &sa, cheri_tag_clear(&sa_len)), -EFAULT)
+ ASSERT_EQ(getsockname(my_socket, &sa, cheri_perms_and(&sa_len, 0)), -EFAULT)
+ close(my_socket);
+}
+
+/*
+ * The FUTEX_WAKE_OP operation in the futex(2) syscall is used to handle more
+ * than one futex at the same time. This is an atomic operation that attempts
+ * to read, modify and write a value at a second uaddr (uaddr2) and also wake
+ * up waiters on the futex words at uaddr1 and uaddr2. uaddr1 and uaddr2 are
+ * provided via capabilities, and so this futex tests the case of an atomic
+ * uaccess operation.
+ */
+TEST(test_futex)
+{
+ static struct futex_waitv waitv[NR_FUTEXES];
+ uint32_t futexes[NR_FUTEXES] = {0};
+ struct timespec to = {.tv_sec = 0, .tv_nsec = 500000000};
+
+ for (int i = 0; i < NR_FUTEXES; i++) {
+ waitv[i].uaddr = (uintptr_t)&futexes[i];
+ waitv[i].flags = FUTEX_32 | FUTEX_PRIVATE_FLAG;
+ waitv[i].val = futexes[i];
+ waitv[i].__reserved = 0;
+ }
+
+ ASSERT_GE(futex_wake(&futexes[0], &futexes[0], 0, &to), 0);
+ ASSERT_EQ(futex_wake(&futexes[0],
+ &futexes[0] + sizeof(uint32_t) * NR_FUTEXES, 0,
+ &to),
+ -EFAULT);
+ ASSERT_EQ(futex_wake(&futexes[0], cheri_tag_clear(&futexes[0]), 0, &to),
+ -EFAULT);
+ ASSERT_EQ(futex_wake(&futexes[0], cheri_perms_and(&futexes[0], 0), 0,
+ &to),
+ -EFAULT);
+}
+
+/*
+ * Test explicit accesses used in iov_iter via readv and writev. Both
+ * syscalls use explicit checking on the iov_base field of struct iovec,
+ * so the metadata of the capability provided for iov_base is modified as
+ * per the needs of each individual test.
+ */
+TEST(test_explicit_iov_iter)
+{
+ char buf0[2];
+ char buf1[4];
+ char buf2[6];
+ char *write_buf0 = "Hello I am the first char buffer!\n";
+ char *write_buf1 = "Hello, I am the second char buffer.\n";
+ char *write_buf2 = "Hello, I am the third and final char buffer.\n";
+ struct iovec iov[3];
+ int iovcnt;
+ static int fd;
+
+ fd = open_file();
+ ASSERT_NE(fd, -1);
+
+ iov[0].iov_base = buf0;
+ iov[0].iov_len = sizeof(buf0);
+ iov[1].iov_base = buf1;
+ iov[1].iov_len = sizeof(buf1);
+ iov[2].iov_base = buf2;
+ iov[2].iov_len = sizeof(buf2);
+
+ iovcnt = sizeof(iov) / sizeof(struct iovec);
+
+ ASSERT_GE(readv(fd, iov, iovcnt), 0);
+
+ iov[0].iov_base = buf0 + sizeof(iov) * 100;
+ ASSERT_EQ(readv(fd, iov, iovcnt), -EFAULT);
+
+ iov[0].iov_base = cheri_tag_clear(buf0);
+ ASSERT_EQ(readv(fd, iov, iovcnt), -EFAULT);
+
+ iov[0].iov_base = cheri_perms_and(buf0, 0);
+ ASSERT_EQ(readv(fd, iov, iovcnt), -EFAULT);
+
+ iov[0].iov_base = write_buf0;
+ iov[0].iov_len = strlen(write_buf0);
+ iov[1].iov_base = write_buf1;
+ iov[1].iov_len = strlen(write_buf1);
+ iov[2].iov_base = write_buf2;
+ iov[2].iov_len = strlen(write_buf2);
+
+ ASSERT_GE(writev(fd, iov, iovcnt), 0);
+
+ iov[0].iov_base = write_buf0 + sizeof(iov) * 100;
+ ASSERT_EQ(writev(fd, iov, iovcnt), -EFAULT);
+
+ iov[0].iov_base = cheri_tag_clear(write_buf0);
+ ASSERT_EQ(writev(fd, iov, iovcnt), -EFAULT);
+
+ iov[0].iov_base = cheri_perms_and(write_buf0, 0);
+ ASSERT_EQ(writev(fd, iov, iovcnt), -EFAULT);
+}
+
+/*
+ * strlen_user() explicitly inspects an input capability, the behaviour
+ * of which must also be verified within these tests. The fsopen() syscall
+ * makes use of strlen_user() by duplicating a string (representing the name
+ * of a filesystem) with the strndup_user() function. strlen_user() can
+ * therefore be tested with a call to fsopen().
+ */
+TEST(test_strlen_user)
+{
+ const char *fsname = "my_nonexistent_filesystem";
+
+ // strndup_user() will still be called, so fsopen() fails after this.
+ ASSERT_EQ(fsopen(fsname), -ENODEV);
+
+ ASSERT_EQ(fsopen(fsname + strlen(fsname) * 10), -EFAULT);
+
+ ASSERT_EQ(fsopen(cheri_tag_clear(fsname)), -EFAULT);
+
+ ASSERT_EQ(fsopen(cheri_perms_and(fsname, 0)), -EFAULT);
+}
+
+int main(void)
+{
+ test_copy_to_user();
+ test_copy_user();
+ test_get_put_user();
+ test_futex();
+ test_explicit_iov_iter();
+ test_strlen_user();
+ return 0;
+}
--
2.34.1
Hi,
This series switches all uaccess routines to operate directly on the
user-provided capability pointer in PCuABI, instead of making the access
via the extracted 64-bit address. This means that all forms of uaccess
now enforce the capability metadata, through the use of capability-based
loads and stores, and will fail with -EFAULT following a failed
(hardware) capability check. The impacted routines are:
- *get_user* (including get_user_ptr)
- *put_user* (including put_user_ptr)
- *copy_from_user* (including copy_from_user_with_ptr)
- *copy_to_user* (including copy_to_user_with_ptr)
(Note that futex operations are already checked thanks to Luca's work [1].)
Enabling capability checks in uaccess exposed a variety of issues, both
in the kernel and in userspace. This series addresses the fallout in the
kernel before making the switch:
- Patch 1-3 overhaul strnlen_user() to prevent it from reading
beyond the bounds of the user pointer, while leaving its
implementation unchanged in !PCuABI.
- Patch 4-6 fixes various capability propagation issues (surprisingly
few of those!).
- Patch 7 fixes a bug in a Morello kselftest, detected through
capability checking in copy_from_user().
Finally, the switch to capability-based accesses is made:
- Patch 8-10 switch {get,put}_user and variants.
- Patch 11-12 switch copy_{from,to}_user and variants.
After this series, the Morello kselftests and Musl tests still pass, and
so do the liburing tests, however the LTP syscalls suite doesn't because
of various issues in LTP itself. I have addressed a number of them
upstream [2] (series already merged), and will post the remaining fixes
to linux-morello-ltp.
Because this precise uaccess checking cannot be achieved with most
existing sanitisers or hardware memory safety mechanisms, further bugs
are likely to be identified in low-level libraries and test suites
running in purecap (I have spotted a couple in the Bionic tests).
Issue:
https://git.morello-project.org/morello/kernel/linux/-/issues/5
Review branch:
https://git.morello-project.org/kbrodsky-arm/linux/-/commits/morello/checke…
Cheers,
Kevin
[1] https://op-lists.linaro.org/archives/list/linux-morello@op-lists.linaro.org…
[2] https://lore.kernel.org/ltp/20231023135647.2157030-1-kevin.brodsky@arm.com/
Kevin Brodsky (12):
linux/user_ptr.h: Introduce user_ptr_{base,limit}
lib: Refactor do_strnlen_user()
lib: Make strnlen_user() PCuABI-friendly
block: Eliminate invalid user pointer casts
tty: Convert another ioctl helper to take arg as user_uintptr_t
seccomp: Preserve pointers when reading sock_fprog from userspace
kselftests/arm64: morello: Fix message size in mmap test
arm64: morello: Implement user capability accessors inline
arm64: uaccess: Move macros from futex.h to uaccess.h
arm64: uaccess: Switch to capability-based {get,put}_user in PCuABI
arm64: lib: Simplify copy_*_user register allocation
arm64: lib: Switch to capability-based copy_*_user in PCuABI
Documentation/core-api/user_ptr.rst | 13 ++
arch/arm64/include/asm/assembler.h | 8 +
arch/arm64/include/asm/futex.h | 20 +--
arch/arm64/include/asm/gpr-num.h | 6 +-
arch/arm64/include/asm/morello.h | 4 -
arch/arm64/include/asm/uaccess.h | 49 +++++-
arch/arm64/lib/copy_from_user.S | 33 +++-
arch/arm64/lib/copy_template.S | 14 +-
arch/arm64/lib/copy_to_user.S | 17 +-
arch/arm64/lib/morello.S | 23 ---
block/blk-zoned.c | 6 +-
block/ioctl.c | 22 +--
drivers/tty/tty_ioctl.c | 2 +-
include/linux/blkdev.h | 8 +-
include/linux/tty.h | 2 +-
include/linux/user_ptr.h | 44 +++++
kernel/seccomp.c | 2 +-
lib/strnlen_user.c | 163 +++++++++++++++----
tools/testing/selftests/arm64/morello/mmap.c | 2 +-
19 files changed, 319 insertions(+), 119 deletions(-)
--
2.38.1
Add tests for capability enforcement via the standard copy routines
(copy_{to, from}_user, {get, put}_user), futex operations and the
explicit uaccess checks used in iov_iter via readv and writev.
Signed-off-by: Akram Ahmad <Akram.Ahmad(a)arm.com>
---
Hi everyone,
This is V2 of the uaccess kselftest patch. The main changes are:
- More concise formatting changes
- Corrections to various mistakes in tests
- Addition of a strlen_user() test case
One of the most important fixes is the fixed futex test. It turns
out that whilst optimisation was causing some varying results, the
overall problem was to do with a mistake in the use of the syscall.
I've decided to allow the syscall to timeout in the case of a valid
input capability, as I feel this tests the explicit uaccess check
sufficiently, but I am not sure if this is the ideal method.
Many thanks,
Akram
---
.../testing/selftests/arm64/morello/Makefile | 2 +-
.../testing/selftests/arm64/morello/uaccess.c | 294 ++++++++++++++++++
2 files changed, 295 insertions(+), 1 deletion(-)
create mode 100644 tools/testing/selftests/arm64/morello/uaccess.c
diff --git a/tools/testing/selftests/arm64/morello/Makefile b/tools/testing/selftests/arm64/morello/Makefile
index ecfa6fc5e989..7b794d3e436c 100644
--- a/tools/testing/selftests/arm64/morello/Makefile
+++ b/tools/testing/selftests/arm64/morello/Makefile
@@ -17,7 +17,7 @@ CFLAGS += $(CLANG_FLAGS) $(CFLAGS_PURECAP) $(CFLAGS_COMMON)
LDFLAGS += $(CLANG_LDFLAGS) $(CLANG_FLAGS) -nostdlib -static
SRCS := $(wildcard *.c) $(wildcard *.S)
-PROGS := bootstrap clone exit mmap read_write sched signal
+PROGS := bootstrap clone exit mmap read_write sched signal uaccess
DEPS := $(wildcard *.h)
# these are the final executables
diff --git a/tools/testing/selftests/arm64/morello/uaccess.c b/tools/testing/selftests/arm64/morello/uaccess.c
new file mode 100644
index 000000000000..e3c935df948a
--- /dev/null
+++ b/tools/testing/selftests/arm64/morello/uaccess.c
@@ -0,0 +1,294 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) 2023 Arm Limited
+
+#include <stdint.h>
+#include <stddef.h>
+#include <stdbool.h>
+
+#include <asm/fcntl.h>
+#include <asm-generic/errno-base.h>
+#include <cheriintrin.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/futex.h>
+#include <linux/mman.h>
+#include <linux/signal.h>
+#include <linux/sysinfo.h>
+#include <linux/socket.h>
+#include <linux/time.h>
+#include <linux/uio.h>
+
+#include "freestanding.h"
+
+#define MSG_LEN 16
+#define NR_FUTEXES 1
+
+static struct futex_waitv waitv[NR_FUTEXES];
+uint32_t futexes[NR_FUTEXES] = {1};
+
+static const char file[] = "/my_file.txt";
+static int fd;
+
+static inline int fsopen(void *string)
+{
+ return syscall(__NR_fsopen, string);
+}
+
+static inline int futex_waitv(struct futex_waitv *waiters, unsigned long nr_waiters,
+ unsigned long flags, struct timespec *timo, clockid_t clockid)
+{
+ return syscall(__NR_futex_waitv, waiters, nr_waiters, flags, timo, clockid);
+}
+
+static int getsockname(int socket, void *sockaddr, int *socklen)
+{
+ return syscall(__NR_getsockname, socket, sockaddr, socklen);
+}
+
+static int open_file(void)
+{
+ return syscall(__NR_openat, 0, file, O_RDWR | O_CREAT, 0666);
+}
+
+static inline int readv(int fd, struct iovec *iov, int iovcnt)
+{
+ return syscall(__NR_readv, fd, iov, iovcnt);
+}
+
+static inline int writev(int fd, struct iovec *iov, int iovcnt)
+{
+ return syscall(__NR_writev, fd, iov, iovcnt);
+}
+
+static inline int sigaction(int sig, void *old, void *new)
+{
+ return syscall(__NR_rt_sigaction, sig, old, new, sizeof(sigset_t));
+}
+
+static inline int sysinfo(void *ptr)
+{
+ return syscall(__NR_sysinfo, ptr);
+}
+
+TEST(test_copy_to_user)
+{
+ struct sysinfo my_sysinfo;
+
+ // Ensure that valid capabilities are successfully copied.
+ ASSERT_EQ(sysinfo(&my_sysinfo), 0);
+
+ // Test copying a capability which has an invalid tag.
+ ASSERT_EQ(sysinfo(cheri_tag_clear(&my_sysinfo)), -EFAULT);
+
+ // Test copying a capability which has invalid permissions.
+ ASSERT_EQ(sysinfo(cheri_perms_and(&my_sysinfo, 0)), -EFAULT);
+}
+
+/*
+ * sigaction() tests both copy_to_user and copy_from_user by copying
+ * into and from a pointer to struct sigaction. The resulting action
+ * in this case is a no-op, but this does not affect the validity of
+ * the test. sigaction attempts to copy the new action from user space
+ * and the old action to user space, if the arguments is valid in either
+ * case.
+ */
+TEST(test_copy_user)
+{
+ struct sigaction act;
+
+ // Check valid capabilities are successfully copied.
+ ASSERT_EQ(sigaction(SIGSEGV, NULL, &act), 0);
+ ASSERT_EQ(sigaction(SIGSEGV, &act, NULL), 0);
+
+ /*
+ * Capabilities with a cleared tag must not be successfully copied
+ * from nor into user space.
+ */
+ ASSERT_EQ(sigaction(SIGSEGV, NULL, cheri_tag_clear(&act)), -EFAULT);
+ ASSERT_EQ(sigaction(SIGSEGV, cheri_tag_clear(&act), NULL), -EFAULT);
+
+ /*
+ * Capabilities with invalid permissions (simulated here with 0) must
+ * not be successfully copied to or from user space.
+ */
+ ASSERT_EQ(sigaction(SIGSEGV, NULL, cheri_perms_and(&act, 0)), -EFAULT);
+ ASSERT_EQ(sigaction(SIGSEGV, cheri_perms_and(&act, 0), NULL), -EFAULT);
+}
+
+typedef unsigned short sa_family_t;
+
+struct sockaddr {
+ sa_family_t sa_family;
+ char sa_data[14];
+};
+
+/*
+ * getsockname(2) uses both get_user and put_user to copy the addrlen argument
+ * from and to user space respectively. If copy_to_user works successfully,
+ * getsockname(2) will return -EFAULT if either get_user or put_user fail.
+ */
+TEST(test_get_put_user)
+{
+ struct sockaddr sa;
+ int sa_len = sizeof(sa);
+ int my_socket;
+
+ // socket(AF_INET, SOCK_STREAM, 0)
+ my_socket = syscall(__NR_socket, 2, 1, 0);
+
+ // The socket must be successfully opened before proceeding.
+ ASSERT_GE(my_socket, 0);
+
+ ASSERT_EQ(getsockname(my_socket, &sa, &sa_len), 0)
+ goto cleanup;
+ // Test with cleared capability tag.
+ ASSERT_EQ(getsockname(my_socket, &sa, cheri_tag_clear(&sa_len)), -EFAULT)
+ goto cleanup;
+ // Test with invalid capability permissions.
+ ASSERT_EQ(getsockname(my_socket, &sa, cheri_perms_and(&sa_len, 0)), -EFAULT)
+ goto cleanup;
+
+ /*
+ * If any of the getsockname syscalls failed, then the socket is
+ * still open and must be closed before the test exits.
+ */
+cleanup:
+ ASSERT_EQ(close(my_socket), 0);
+}
+
+/*
+ * The futex_waitv syscall explicitly checks the uaddr field of the individual
+ * futex waiters which is provided by a capability. This capability enforcement
+ * is therefore tested here; in the case where the capability is valid, the
+ * syscall should return -ETIMEDOUT as the futex waiter requires another thread
+ * to signal that it should wake up using futex_wake(). futex_wait() is not
+ * called in this thread, thus causing a timeout to occur.
+ */
+TEST(test_futex)
+{
+ struct timespec to = {.tv_sec = 0, .tv_nsec = 500000000};
+ int res;
+
+ // Test with valid capabilities.
+ for (int i = 0; i < NR_FUTEXES; i++) {
+ waitv[i].uaddr = (uintptr_t)&futexes[i];
+ waitv[i].flags = FUTEX_32 | FUTEX_PRIVATE_FLAG;
+ waitv[i].val = futexes[i];
+ waitv[i].__reserved = 0;
+ }
+
+ res = futex_waitv(waitv, NR_FUTEXES, 0, &to, CLOCK_MONOTONIC);
+ ASSERT_EQ(res, -ETIMEDOUT);
+
+ for (int i = 0; i < NR_FUTEXES; i++)
+ waitv[i].uaddr = (uintptr_t)cheri_tag_clear(&futexes[i]);
+
+
+ res = futex_waitv(waitv, NR_FUTEXES, 0, &to, CLOCK_MONOTONIC);
+ ASSERT_EQ(res, -EFAULT);
+
+ for (int i = 0; i < NR_FUTEXES; i++)
+ waitv[i].uaddr = (uintptr_t)cheri_perms_and(&futexes[i], 0);
+
+
+ res = futex_waitv(waitv, NR_FUTEXES, 0, &to, CLOCK_MONOTONIC);
+ ASSERT_EQ(res, -EFAULT);
+}
+
+/*
+ * Test explicit accesses used in iov_iter via readv and writev. Both
+ * syscalls use explicit checking on the iov_base field of struct iovec,
+ * so the metadata of the capability provided for iov_base is modified as
+ * per the needs of each individual test.
+ */
+TEST(test_explicit_iov_iter)
+{
+ char buf0[2];
+ char buf1[4];
+ char buf2[6];
+ char *write_buf0 = "Hello I am the first char buffer!\n";
+ char *write_buf1 = "Hello, I am the second char buffer.\n";
+ char *write_buf2 = "Hello, I am the third and final char buffer.\n";
+ struct iovec iov[3];
+ int iovcnt;
+
+ fd = open_file();
+ ASSERT_NE(fd, -1);
+
+ // Set up buffers for reading.
+ iov[0].iov_base = buf0;
+ iov[0].iov_len = sizeof(buf0);
+ iov[1].iov_base = buf1;
+ iov[1].iov_len = sizeof(buf1);
+ iov[2].iov_base = buf2;
+ iov[2].iov_len = sizeof(buf2);
+
+ iovcnt = sizeof(iov) / sizeof(struct iovec);
+
+ // Make sure readv works as expected with intact buf0.
+ ASSERT_GE(readv(fd, iov, iovcnt), 0) goto cleanup;
+
+ // Clear the tag for the pointer to buf0.
+ iov[0].iov_base = cheri_tag_clear(buf0);
+ ASSERT_EQ(readv(fd, iov, iovcnt), -EFAULT) goto cleanup;
+
+ // Clear permissions, but restore the tag for buf0.
+ iov[0].iov_base = buf0;
+ iov[0].iov_base = cheri_perms_and(buf0, 0);
+ ASSERT_EQ(readv(fd, iov, iovcnt), -EFAULT) goto cleanup;
+
+ // Set up buffers for writing.
+ iov[0].iov_base = write_buf0;
+ iov[0].iov_len = sizeof(write_buf0);
+ iov[1].iov_base = write_buf1;
+ iov[1].iov_len = sizeof(write_buf1);
+ iov[2].iov_base = write_buf2;
+ iov[2].iov_len = sizeof(write_buf2);
+
+ // Make sure writev works as expected for buf0.
+ ASSERT_GE(writev(fd, iov, iovcnt), 0) goto cleanup;
+
+ // Clear the tag for the pointer to buf0.
+ iov[0].iov_base = cheri_tag_clear(write_buf0);
+ ASSERT_EQ(writev(fd, iov, iovcnt), -EFAULT) goto cleanup;
+
+ // Clear permissions, but restore the tag for buf0.
+ iov[0].iov_base = write_buf0;
+ iov[0].iov_base = cheri_perms_and(write_buf0, 0);
+ ASSERT_EQ(writev(fd, iov, iovcnt), -EFAULT) goto cleanup;
+
+cleanup:
+ close(fd);
+}
+
+/*
+ * strlen_user() explicitly inspects an input capability, the behaviour
+ * of which must also be verified within these tests. The fsopen() syscall
+ * makes use of strlen_user() by duplicating a string (representing the name
+ * of a filesystem) with the strndup_user() function. strlen_user() can
+ * therefore be tested with a call to fsopen().
+ */
+TEST(test_strlen_user)
+{
+ char *fsname = "my_nonexistent_filesystem";
+
+ // strndup_user() will still be called, so fsopen() fails after this.
+ ASSERT_EQ(fsopen(&fsname), -ENODEV);
+
+ // Clear the tag for the input capability to strndup_user().
+ ASSERT_EQ(fsopen(cheri_tag_clear(&fsname)), -EFAULT);
+
+ // Clear the permissions for the input capability to strndup_user().
+ ASSERT_EQ(fsopen(cheri_perms_and(&fsname, 0)), -EFAULT);
+}
+
+int main(void)
+{
+ test_copy_to_user();
+ test_copy_user();
+ test_get_put_user();
+ test_futex();
+ test_explicit_iov_iter();
+ test_strlen_user();
+ return 0;
+}
--
2.34.1
Hi All,
This patch series introduces the mm reservation interface to manage
the owning capability of the allocated addresses. As compared to the RFC
v1, this series adds reservation details in the VMA structure. Looking for
feedback regarding interface names, interface directory structure etc.
Patch [1-11] manages capability bounds via reservation interface. Patch [12-19]
adds support for managing capability permissions.
Details about several rules implemented can be found in PCuABI spec here [1].
Changes in RFC v2:
1) Removed separate maple tree structures for the reservation interface
and modified the vma structure to add the reservation details. As most of
the mmap/munmap operations happen per-vma so this reduced the code
churnings. However this approach will increase time-complexity of syscalls
which operate across vma's such as mlock, madvise etc. get_unmapped_area()
which generated free unmapped virtual address may now need more iterations.
2) Added Cheri base representability and length representability
modifications. Now get_unmapped_area() will generate CHERI
representable addresses.
3) Added new PCuABI changes for mincore() syscall.
4) Added changes for compat64.
Future works:
1) Users of vm_mmap/vm_munmap() i.e. filesystems, loaders, kexec etc to be
modified to preserve capability addresses.
2) Cover remaining memory addressing syscalls.
Testing:
1) All tests by Chaitanya in v4 selftests [2] passes.
2) Purecap/Compat Busybox boot passes after adding [WIP] patches present in [3].
The whole series can be found here [3].
[1]: https://git.morello-project.org/morello/kernel/linux/-/wikis/Morello-pure-c…
[2]: https://git.morello-project.org/chaitanya_prakash/linux.git review/purecap_mmap_testcases
[3]: https://git.morello-project.org/amitdaniel/linux.git review/purecap_mm_reservation_v2
Thanks,
Amit Daniel
Amit Daniel Kachhap (19):
uapi: errno.h: Introduce PCuABI memory reservation error
arm64: morello: Add VM_PCUABI_RESERVE flags
mm: Add capability reservation interfaces in vma for PCuABI
mm/cap_addr_mgmt: Add capability bound helpers for PCuABI
mm/mmap: Modify unmapped address space management code for PCuABI
mm/mmap: Use the PCuABI reservations in mmap/munmap
mm/mremap: Add the PCuABI reservation interfaces
mm/mprotect: Add the PCuABI reservation interfaces
mm/madvise: Add the PCuABI reservation interfaces
mm/mlock: Add the PCuABI reservation interfaces
mm/msync: Add the PCuABI reservation interfaces
uapi: mman-common.h: Macros for maximum capability permissions
mm/cap_addr_mgmt: Add capability permission helpers for PCuABI
mm/cap_addr_mgmt: Reduce the maximum protection check impact
mm/mmap: Disable MAP_GROWSDOWN mapping flag for PCuABI
mm/mmap: Add capability permission constraints for PCuABI
mm/mremap: Add capability permission constraints for PCuABI
mm/mprotect: Add capability permission constraints for PCuABI
mm/mincore: Add capability constraints for PCuABI
arch/arm64/include/asm/cap_addr_mgmt.h | 22 ++
include/linux/cap_addr_mgmt.h | 168 +++++++++++++
include/linux/cheri.h | 3 +
include/linux/mm.h | 29 ++-
include/linux/mm_types.h | 5 +
include/uapi/asm-generic/errno.h | 2 +
include/uapi/asm-generic/mman-common.h | 6 +
io_uring/advise.c | 2 +-
mm/Makefile | 2 +-
mm/cap_addr_mgmt.c | 314 +++++++++++++++++++++++++
mm/damon/vaddr.c | 2 +-
mm/madvise.c | 27 ++-
mm/mincore.c | 46 +++-
mm/mlock.c | 38 ++-
mm/mmap.c | 182 ++++++++++++--
mm/mprotect.c | 21 +-
mm/mremap.c | 109 +++++++--
mm/msync.c | 15 +-
mm/util.c | 10 +-
19 files changed, 919 insertions(+), 84 deletions(-)
create mode 100644 arch/arm64/include/asm/cap_addr_mgmt.h
create mode 100644 include/linux/cap_addr_mgmt.h
create mode 100644 mm/cap_addr_mgmt.c
--
2.25.1
Cast the return value of getauxptr() from void* to uintptr_t (which is
defined as __uintcap_t in purecap) to address compiler warnings.
Remove the unnecessary cast on getauxval(), as uintptr_t is defined as
unsigned long in non-purecap anyway.
Signed-off-by: Aditya Deshpande <aditya.deshpande(a)arm.com>
---
Hi all,
This is a minor follow up patch to the vDSO series which fixes a
compiler warning about implicit type casts. The current state of the
vDSO tests shouldn't cause any issues with the release as:
1. This is just a compiler warning - the test code is still functionally
correct.
2. The vDSO selftests are built standalone anyways.
Thanks,
Aditya
---
tools/testing/selftests/vDSO/parse_vdso.h | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/tools/testing/selftests/vDSO/parse_vdso.h b/tools/testing/selftests/vDSO/parse_vdso.h
index 54ad67f463cc..9c891a12be7d 100644
--- a/tools/testing/selftests/vDSO/parse_vdso.h
+++ b/tools/testing/selftests/vDSO/parse_vdso.h
@@ -38,9 +38,9 @@ void vdso_init_from_auxv(void *auxv);
static inline uintptr_t get_sysinfo_ehdr()
{
#ifdef __CHERI_PURE_CAPABILITY__
- return getauxptr(AT_SYSINFO_EHDR);
+ return (uintptr_t)getauxptr(AT_SYSINFO_EHDR);
#else
- return (uintptr_t)getauxval(AT_SYSINFO_EHDR);
+ return getauxval(AT_SYSINFO_EHDR);
#endif
}
--
2.25.1