On 15/01/2024 17:53, Akram Ahmad wrote:
[...]
+/*
- 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.
I think it would be more useful to explain this where the function is called, as this is where the mysterious literal 0 appears. The wrapper itself is pretty self-explanatory, the reader can easily find out about FUTEX_WAKE_OP themselves.
- */
+static inline int futex_wake(uint32_t *uaddr, uint32_t *uaddr2, uint32_t val3,
Let's call it futex_wake_op(), because otherwise it suggests we're using FUTEX_WAKE.
const struct timespec *timeout)
+{
- return syscall(__NR_futex, uaddr, FUTEX_WAKE_OP, NR_FUTEXES, timeout,
val is the number of waiters to wake, not a number of futexes like in futex_waitv. We're not actually going to wake anything so it doesn't matter, we could just pass 1. In fact the same applies to val2, it is the number of waiters to wake for the second futex, not a timeout.
On that note we don't need an array of futexes any more, just one is enough.
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);
This actually results in a pointer at an offset (in bytes) of sizeof(struct sysinfo) * sizeof(struct sysinfo) * 100, considering the type of my_sysinfo.
Generally speaking all these structs are small enough that their bounds are exactly representable, so we don't need a big offset, incrementing the pointer by 1 (i.e. the size of the struct) is enough. This way we know for sure that the bounds remain representable (the tag won't be cleared), and we also check that the kernel's bounds check is precise.
- ASSERT_EQ(sysinfo(cheri_tag_clear(&my_sysinfo)), -EFAULT);
- ASSERT_EQ(sysinfo(cheri_perms_and(&my_sysinfo, 0)), -EFAULT);
+}
[...]
+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";
Should be const.
- struct iovec iov[3];
- int iovcnt;
- static int fd;
fd shouldn't need to be static, also we should close it at the end.
Kevin
[...]