Hello all,
Many of the Morello helper functions are implemented in assembly due
to historical reasons. This patch series aims to translate the helpers into
C functions to improve their readability, introducing additional helpers
where necessary such as to access capability-based system registers. The
newly translated helpers are defined directly in morello.c.
This patch series also removes the now empty morello.S file, and updates
the Makefile accordingly. It also tidies up definitions of assembly-only
values, removing these where necessary.
V1 separates the patch series into two patches; one to add the register
helpers, and another to translate the assembly helpers (and tidy up). V1
also corrects the more complicated helpers, whilst removing unnecessary
casts and finally implementing some feedback for improving code clarity.
The review branch is accessible here:
https://git.morello-project.org/arkamnite/linux/-/commits/morello%2Farm64_v1
The related issue is here:
https://git.morello-project.org/morello/kernel/linux/-/issues/61
Many thanks,
Akram
Akram Ahmad (2):
arm64: add {read, write, clear}_cap_sysreg helpers for Morello
arm64: rewrite Morello helpers in C
arch/arm64/include/asm/assembler.h | 10 --
arch/arm64/include/asm/morello.h | 4 +
arch/arm64/include/asm/sysreg.h | 18 +++
arch/arm64/kernel/asm-offsets.c | 8 --
arch/arm64/kernel/morello.c | 125 +++++++++++++++++-
arch/arm64/lib/Makefile | 3 +-
arch/arm64/lib/morello.S | 205 -----------------------------
7 files changed, 141 insertions(+), 232 deletions(-)
delete mode 100644 arch/arm64/lib/morello.S
--
2.34.1
%#lp results in the address being prefixed with 0x, like %#p, but
not if the capability is null-derived. Make sure the prefix is used
in all cases.
Fixes: ("lib/vsprintf: Add support for architectural capabilities")
Signed-off-by: Kevin Brodsky <kevin.brodsky(a)arm.com>
---
This is probably the result of some confusion during the review process
for the original patch, as the first version never printed the prefix.
Review branch:
https://git.morello-project.org/kbrodsky-arm/linux/-/commits/morello/printk…
lib/vsprintf.c | 6 +-----
1 file changed, 1 insertion(+), 5 deletions(-)
diff --git a/lib/vsprintf.c b/lib/vsprintf.c
index 0b551cf89880..1a2a01843775 100644
--- a/lib/vsprintf.c
+++ b/lib/vsprintf.c
@@ -2550,13 +2550,9 @@ char *capability(const char *fmt, char *buf, char *end, void * __capability cap,
* Same applies when hashing is active.
*/
if ((!cheri_tag_get(cap) && !__builtin_cheri_copy_from_high(cap)) ||
- (likely(!no_hash_pointers) && *fmt != 'x')) {
-
- /* Avoid adding prefix */
- spec.flags &= ~SPECIAL;
+ (likely(!no_hash_pointers) && *fmt != 'x'))
return pointer(fmt, buf, end, (void *)cheri_address_get(cap),
spec);
- }
if (spec.flags & SPECIAL) { /* Simplified format for capabilities */
int orig_field_width = spec.field_width;
--
2.43.0
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 preadv and writev.
Signed-off-by: Akram Ahmad <Akram.Ahmad(a)arm.com>
---
Hi everyone,
This is v6 of the uaccess kselftest patch. Aside from some minor
corrections, v6 fixes a bug where readv would not fail correctly
in some circumstances. It has been replaced with preadv and the
tests are all passing correctly now.
V4, V5 introduced the following changes:
- Simplification of the futex test to only use one futex
- Move FUTEX_WAKE_OP description to futex test
- Various minor corrections
- Tightening of bounds for out-of-bounds access test cases.
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_v6
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 | 257 ++++++++++++++++++
2 files changed, 258 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..c448aeda0aad
--- /dev/null
+++ b/tools/testing/selftests/arm64/morello/uaccess.c
@@ -0,0 +1,257 @@
+// 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 preadv(int fd, struct iovec *iov, int iovcnt)
+{
+ return syscall(__NR_preadv, fd, iov, iovcnt, 0);
+}
+
+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 + 1), -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 + 1), -EFAULT);
+ ASSERT_EQ(sigaction(SIGSEGV, &act + 1, 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 + 1), -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)
+{
+ uint32_t futex = 0;
+
+ ASSERT_GE(futex_wake_op(&futex, &futex, 0), 0);
+ ASSERT_EQ(futex_wake_op(&futex, &futex + 1, 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 preadv 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. preadv is used to ensure that the
+ * file is read with a file offset of 0, as the calls to writev modify the file
+ * offset.
+ */
+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 = (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);
+
+ iovcnt = sizeof(iov) / sizeof(struct iovec);
+ ASSERT_GE(writev(fd, iov, iovcnt), 0);
+
+ iov[0].iov_base = (void *)write_buf0 + 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);
+
+ 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);
+
+ ASSERT_GE(preadv(fd, iov, iovcnt), 0);
+
+ iov[0].iov_base = buf0 + 100;
+ ASSERT_EQ(preadv(fd, iov, iovcnt), -EFAULT);
+
+ iov[0].iov_base = cheri_tag_clear(buf0);
+ ASSERT_EQ(preadv(fd, iov, iovcnt), -EFAULT);
+
+ iov[0].iov_base = cheri_perms_and(buf0, 0);
+ ASSERT_EQ(preadv(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
Hi all,
Many of the Morello helper functions are implemented in assembly due
to historical reasons. This patch series aims to translate the helpers into
C functions to improve their readability, introducing additional helpers
where necessary such as to access capability-based system registers. The
newly translated helpers are defined directly in morello.c. I would like
to draw particular attention to my translation of
morello_build_any_user_cap, as I'm not sure if this a correct approach.
This patch series also removes the now empty morello.S file, and updates
the Makefile accordingly.
The review branch is accessible here:
https://git.morello-project.org/arkamnite/linux/-/commits/morello%2Farm64_v0
The related issue is here:
https://git.morello-project.org/morello/kernel/linux/-/issues/61
Many thanks,
Akram
Akram Ahmad (3):
arm64: rewrite Morello helpers in C
arm64: add system register helpers for __morello_thread_init_user
arm64: translate Morello helpers in assembly to C
arch/arm64/include/asm/morello.h | 16 +++
arch/arm64/kernel/morello.c | 108 +++++++++++++++-
arch/arm64/lib/Makefile | 3 +-
arch/arm64/lib/morello.S | 205 -------------------------------
4 files changed, 120 insertions(+), 212 deletions(-)
delete mode 100644 arch/arm64/lib/morello.S
--
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 v5 of the uaccess kselftest patch and introduces the following:
- Tighten bounds of the out-of-bounds access test cases.
- Tidy up the futex test case.
V4 introduced the following changes:
- 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_v5
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 | 256 ++++++++++++++++++
2 files changed, 257 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..489630864d4f
--- /dev/null
+++ b/tools/testing/selftests/arm64/morello/uaccess.c
@@ -0,0 +1,256 @@
+// 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 + 1), -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)
+{
+ uint32_t futex = 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 + 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 + 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
Hi,
This series is a collection of various cleanups for compat64:
- Patch 1 makes use of the new in_compat64_syscall() helper introduced
by Zach in his BPF series to unify the way we check whether we are
handling a compat64 syscall.
- Patch 2-4 aim to restore compat_binfmt_elf as close to its original
shape as possible, by using most of the existing definitions for
compat64 as well as compat32.
- Patch 5 converts remaining uses of #ifdef CONFIG_COMPAT to
CONFIG_COMPAT32 when the corresponding code is specific to AArch32.
- Patch 6 fixes the number of ASLR bits in compat64, which should be the
same as in native and not the AArch32 value.
Aside from making things a little clearer and removing some unused code
in compat64, patch 4 and 5 bring minor functional improvements. See
the commit messages for details.
Review branch:
https://git.morello-project.org/kbrodsky-arm/linux/-/commits/morello/compat…
Cheers,
Kevin
Kevin Brodsky (6):
treewide: Use in_compat64_syscall() for compat64 checks
arm64: Only define COMPAT_ELF_HWCAP* in compat32
arm64/elf: Only define compat_elf_read_implies_exec in compat32
fs/compat_binfmt_elf: Align compat64 with compat32
arm64: Disable remaining 32-bit handling in compat64
arm64: Kconfig: Use native mmap_rnd_bits in compat64
arch/arm64/Kconfig | 2 +-
arch/arm64/include/asm/arch_timer.h | 2 +-
arch/arm64/include/asm/elf.h | 50 ++++++++++-----------
arch/arm64/include/asm/fpsimd.h | 2 +-
arch/arm64/include/asm/hwcap.h | 4 +-
arch/arm64/include/asm/mmu.h | 2 +-
arch/arm64/include/asm/processor.h | 8 ++--
arch/arm64/include/asm/ptrace.h | 2 +-
arch/arm64/include/asm/seccomp.h | 2 +-
arch/arm64/include/asm/unistd.h | 6 ++-
arch/arm64/kernel/cpufeature.c | 14 +++---
arch/arm64/kernel/cpuinfo.c | 8 ++--
arch/arm64/kernel/entry-common.c | 6 +--
arch/arm64/kernel/traps.c | 4 +-
arch/arm64/mm/init.c | 2 +-
drivers/mmc/core/block.c | 11 ++---
fs/compat_binfmt_elf.c | 68 +++++++++++++----------------
include/linux/elfcore-compat.h | 7 +++
io_uring/io_uring.c | 2 +-
io_uring/uring_cmd.c | 2 +-
kernel/futex/syscalls.c | 2 +-
net/ipv4/tcp.c | 18 +++-----
22 files changed, 109 insertions(+), 115 deletions(-)
--
2.42.1
Tests to validate the capability's tag, bounds, range as well as
permissions have been added. As certain flags and syscalls
conflict with the reservation model or lack implementation, a check
to verify appropriate handling of the same has also been added. Lastly,
testcases to verify mmap/unmap of CHERI unreprentable address/length
have been added.
Review branch:
https://git.morello-project.org/chaitanya_prakash/linux/-/tree/review/morel…
This patch series has been tested on:
https://git.morello-project.org/amitdaniel/linux/-/tree/review/purecap_mm_r…
[1]https://git.morello-project.org/morello/kernel/linux/-/wikis/Morello-pure…
Changes in V6:
- Updated commit messages and in code comments as required.
- Moved struct initial_data back to bootstrap.c
- Defined __maybe_unused in freestanding.h
- Defined a fixed address to be shared among the tests.
- Modified negative madvise() test to make use of the common private
mapping.
- Renamed test_mmap_bounds_check and test_mremap_bounds_check testcases
to test_check_mmap_reservation and test_check_mremap_reservation
respectively.
- Modified the do-while loop used to generate unrepresentable
length/address.
- Added checks to validate that the bounds and length of ptr1 and ptr2
are of cheri representable length and their base is aligned according
to the alignment mask.
- Added a test to ensure mmap(owning_cap,..., MAP_FIXED fails if the
underlying reservation has been destroyed.
Changes in V5:
https://op-lists.linaro.org/archives/list/linux-morello@op-lists.linaro.org…
- Added representability testcase.
- Removed global struct reg_data and called get_pagesize() with auxv
passed in main().
- Removed VERIFY_ERRNO macro and made use of extended EXPECT_EQ
- As helper functions have been removed, the inline attribute line is of
no use and has been deleted.
- Used a common mapping to avoid creating and destroying mappings
repeatedly.
- Removed positive testcases as they are not unique to PCuABI
- Corrected the error code to reflect -ENOMEM instead of -ERESERVATION
when mremap() tries to move a mapping without MREMAP_MAYMOVE flag
- Reworded commit messages and restructured code.
Changes in V4:
https://op-lists.linaro.org/archives/list/linux-morello@op-lists.linaro.org…
-Corrected subject of cover letter
Changes in V3:
https://op-lists.linaro.org/archives/list/linux-morello@op-lists.linaro.org…
- Added get_pagesize() function and VERRIFY_ERRNO() macro
- Added LoadCap and StoreCap permissions testcase
- Added validity_tag_check testcases
- Added reservation tests
- Renamed variable "addr" to "ptr" to avoid confusion when manipulating
both addresses and capabilities
- Cleaned up syscall_mmap and syscall_mmap2 testcases
- Restructured code into testcases that check tags, range, bounds
and permissions
- Improved range_check testcases
- Improved commit messages
- Removed helper functions, tests directly written in testcase functions
- Removed signal handling and ddc register testcases
Changes in V2:
https://op-lists.linaro.org/archives/list/linux-morello@op-lists.linaro.org…
- Added link to the review branch
- Removed unnecessary whitespace
Changes in V1:
https://op-lists.linaro.org/archives/list/linux-morello@op-lists.linaro.org…
Amit Daniel Kachhap (1):
kselftests/arm64: morello: mmap: Test unrepresentable addresses
Chaitanya S Prakash (10):
kselftests/arm64: morello: Create wrapper functions for frequently
invoked syscalls
kselftests/arm64: morello: Add get_pagesize() function
kselftests/arm64: morello: mmap: Clean up existing testcases
kselftests/arm64: morello: mmap: Add MAP_GROWSDOWN testcase
kselftests/arm64: morello: mmap: Add validity tag check testcases
kselftests/arm64: morello: mmap: Add capability range testcases
kselftests/arm64: morello: mmap: Add mmap() reservation testcases
kselftests/arm64: morello: mmap: Add mremap() reservation check
testcases
kselftests/arm64: morello: mmap: Add permission check testcases
kselftests/arm64: morello: mmap: Add brk() testcase
.../selftests/arm64/morello/bootstrap.c | 6 -
.../selftests/arm64/morello/freestanding.c | 15 +
.../selftests/arm64/morello/freestanding.h | 68 ++-
tools/testing/selftests/arm64/morello/mmap.c | 549 +++++++++++++++++-
4 files changed, 600 insertions(+), 38 deletions(-)
--
2.34.1
Syscalls operating on memory mappings manage their address space via
owning capabilities. They must adhere to a certain set of rules[1] in
order to ensure memory safety. Address space management syscalls are
only allowed to manipulate mappings that are within the range of the
owning capability and have appropriate permissions.
Tests to validate the capability's tag, bounds, range as well as
permissions have been added. Finally, as certain flags and syscalls
conflict with the reservation model or lack implementation, a check
to verify appropriate handling of the same has also been added.
Review branch:
https://git.morello-project.org/chaitanya_prakash/linux/-/tree/review/purec…
This patch series has been tested on:
https://git.morello-project.org/amitdaniel/linux/-/tree/review/purecap_mm_r…
[1]https://git.morello-project.org/morello/kernel/linux/-/wikis/Morello-pure…
Changes in V5:
- Added representability testcase.
- Removed global struct reg_data and called get_pagesize() with auxv
passed in main().
- Removed VERIFY_ERRNO macro and made use of extended EXPECT_EQ
- As helper functions have been removed, the inline attribute line is of
no use and has been deleted.
- Used a common mapping to avoid creating and destroying mappings
repeatedly.
- Removed positive testcases as they are not unique to PCuABI
- Corrected the error code to reflect -ENOMEM instead of -ERESERVATION
when mremap() tries to move a mapping without MREMAP_MAYMOVE flag
- Reworded commit messages and restructured code.
Changes in V4:
https://op-lists.linaro.org/archives/list/linux-morello@op-lists.linaro.org…
-Corrected subject of cover letter
Changes in V3:
https://op-lists.linaro.org/archives/list/linux-morello@op-lists.linaro.org…
- Added get_pagesize() function and VERRIFY_ERRNO() macro
- Added LoadCap and StoreCap permissions testcase
- Added validity_tag_check testcases
- Added reservation tests
- Renamed variable "addr" to "ptr" to avoid confusion when manipulating
both addresses and capabilities
- Cleaned up syscall_mmap and syscall_mmap2 testcases
- Restructured code into testcases that check tags, range, bounds
and permissions
- Improved range_check testcases
- Improved commit messages
- Removed helper functions, tests directly written in testcase functions
- Removed signal handling and ddc register testcases
Changes in V2:
https://op-lists.linaro.org/archives/list/linux-morello@op-lists.linaro.org…
- Added link to the review branch
- Removed unnecessary whitespace
Changes in V1:
https://op-lists.linaro.org/archives/list/linux-morello@op-lists.linaro.org…
Amit Daniel Kachhap (1):
kselftests/arm64: morello: mmap: Test unrepresentable addresses
Chaitanya S Prakash (10):
kselftests/arm64: morello: Create wrapper functions for frequently
invoked syscalls
kselftests/arm64: morello: Add get_pagesize() function
kselftests/arm64: morello: mmap: Clean up existing testcases
kselftests/arm64: morello: mmap: Add MAP_GROWSDOWN testcase
kselftests/arm64: morello: mmap: Add validity tag check testcases
kselftests/arm64: morello: mmap: Add capability range testcases
kselftests/arm64: morello: mmap: Add mmap() bounds check testcases
kselftests/arm64: morello: mmap: Add mremap() bounds check testcases
kselftests/arm64: morello: mmap: Add permission check testcases
kselftests/arm64: morello: mmap: Add brk() testcase
.../selftests/arm64/morello/bootstrap.c | 13 -
.../selftests/arm64/morello/freestanding.c | 15 +
.../selftests/arm64/morello/freestanding.h | 74 ++-
tools/testing/selftests/arm64/morello/mmap.c | 533 +++++++++++++++++-
4 files changed, 590 insertions(+), 45 deletions(-)
--
2.34.1
struct io_uring_sqe::optval is a strange field, because in reality
it is part of the cmd payload (for IORING_OP_URING_CMD). The layout
of this payload is not converted before the SQE is submitted (in
convert_compat64_io_uring_sqe()), because it is specific to
each .uring_cmd handler. Even if we wanted to handle all the
conversions upfront, it would be difficult to achieve as the target
file (and thus the .uring_cmd handler) has not been determined at
that point.
Since optval is a user pointer, we need to perform the usual
conversion for compat64. This is done by getting a pointer to
the payload using io_uring_sqe_cmd(), and then interpreting that
payload as either __u64 or __kernel_uintptr_t. The optval field is
no longer used, as it is not meaningful in compat64. Since struct
io_uring_sqe is uapi, the field is retained for userspace usage.
Signed-off-by: Kevin Brodsky <kevin.brodsky(a)arm.com>
---
Another patch for the branch rebased on 6.7. Testing it with checked
uaccess revealed that additional handling was needed for compat64. The
OP_{G,S}ETSOCKOPT commands were introduced by [1].
The patch is available in my 6.7-based branch:
https://git.morello-project.org/kbrodsky-arm/linux/-/commits/morello/next-6…
[1] https://lore.kernel.org/all/20231016134750.1381153-1-leitao@debian.org/
io_uring/uring_cmd.c | 15 +++++++++++++--
1 file changed, 13 insertions(+), 2 deletions(-)
diff --git a/io_uring/uring_cmd.c b/io_uring/uring_cmd.c
index 4c4431e9616b..db184905c20d 100644
--- a/io_uring/uring_cmd.c
+++ b/io_uring/uring_cmd.c
@@ -214,6 +214,17 @@ int io_uring_cmd_import_fixed(void __user *ubuf, unsigned long len, int rw,
}
EXPORT_SYMBOL_GPL(io_uring_cmd_import_fixed);
+static inline void __user *sqe_get_optval(const struct io_uring_sqe *sqe)
+{
+ if (IS_ENABLED(CONFIG_COMPAT64) && in_compat_syscall()) {
+ const __u64 *val = io_uring_sqe_cmd(sqe);
+ return compat_ptr(READ_ONCE(*val));
+ } else {
+ const __kernel_uintptr_t *val = io_uring_sqe_cmd(sqe);
+ return (void __user *)READ_ONCE(*val);
+ }
+}
+
static inline int io_uring_cmd_getsockopt(struct socket *sock,
struct io_uring_cmd *cmd,
unsigned int issue_flags)
@@ -226,7 +237,7 @@ static inline int io_uring_cmd_getsockopt(struct socket *sock,
if (level != SOL_SOCKET)
return -EOPNOTSUPP;
- optval = (void __user *)READ_ONCE(cmd->sqe->optval);
+ optval = sqe_get_optval(cmd->sqe);
optname = READ_ONCE(cmd->sqe->optname);
optlen = READ_ONCE(cmd->sqe->optlen);
@@ -249,7 +260,7 @@ static inline int io_uring_cmd_setsockopt(struct socket *sock,
void __user *optval;
sockptr_t optval_s;
- optval = (void __user *)READ_ONCE(cmd->sqe->optval);
+ optval = sqe_get_optval(cmd->sqe);
optname = READ_ONCE(cmd->sqe->optname);
optlen = READ_ONCE(cmd->sqe->optlen);
level = READ_ONCE(cmd->sqe->level);
--
2.42.1