Add a new test to ensure that atomic futex uaccess succeeds on memory mapped with a non-default POIndex/pkey.
In the absence of FEAT_LSUI, atomic futex uaccess operations such as those triggered by FUTEX_WAKE_OP use privileged atomic load/store instructions and thus cannot have user permission overlays applied (as per POR_EL0). In case the kernel enabled POE at EL1, it is worth checking that PIR_EL1 isn't mistakenly configured to have kernel overlays (POR_EL1) applied instead, as that would fail for non-zero pkeys. Note that if such misconfiguration occurs, futex_wake_op() may get stuck in an infinite loop because futex_atomic_op_inuser() will fail but fault_in_user_writeable() will still report success. Signed-off-by: Kevin Brodsky <[email protected]> --- tools/testing/selftests/arm64/Makefile | 2 +- tools/testing/selftests/arm64/poe/.gitignore | 2 + tools/testing/selftests/arm64/poe/Makefile | 6 +++ tools/testing/selftests/arm64/poe/poe_futex.c | 62 +++++++++++++++++++++++++++ 4 files changed, 71 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/arm64/Makefile b/tools/testing/selftests/arm64/Makefile index e456f3b62fa1..bad5c3b33dce 100644 --- a/tools/testing/selftests/arm64/Makefile +++ b/tools/testing/selftests/arm64/Makefile @@ -4,7 +4,7 @@ ARCH ?= $(shell uname -m 2>/dev/null || echo not) ifneq (,$(filter $(ARCH),aarch64 arm64)) -ARM64_SUBTARGETS ?= tags signal pauth fp mte bti abi gcs +ARM64_SUBTARGETS ?= tags signal pauth fp mte bti abi gcs poe else ARM64_SUBTARGETS := endif diff --git a/tools/testing/selftests/arm64/poe/.gitignore b/tools/testing/selftests/arm64/poe/.gitignore new file mode 100644 index 000000000000..0dce4a3aa38b --- /dev/null +++ b/tools/testing/selftests/arm64/poe/.gitignore @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0-only +poe_futex diff --git a/tools/testing/selftests/arm64/poe/Makefile b/tools/testing/selftests/arm64/poe/Makefile new file mode 100644 index 000000000000..2af2bbf3f6d3 --- /dev/null +++ b/tools/testing/selftests/arm64/poe/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0 + +CFLAGS += $(KHDR_INCLUDES) +TEST_GEN_PROGS := poe_futex + +include ../../lib.mk diff --git a/tools/testing/selftests/arm64/poe/poe_futex.c b/tools/testing/selftests/arm64/poe/poe_futex.c new file mode 100644 index 000000000000..21a2e109ee43 --- /dev/null +++ b/tools/testing/selftests/arm64/poe/poe_futex.c @@ -0,0 +1,62 @@ +// SPDX-License-Identifier: GPL-2.0 +#include <stdint.h> +#include <stdlib.h> +#include <unistd.h> + +#include <linux/futex.h> +#include <sys/syscall.h> + +#include "kselftest_harness.h" + +static int sys_pkey_alloc(unsigned long flags, unsigned long init_val) +{ + return syscall(__NR_pkey_alloc, flags, init_val); +} + +static int sys_pkey_mprotect(void *ptr, size_t size, int prot, int pkey) +{ + return syscall(__NR_pkey_mprotect, ptr, size, prot, pkey); +} + +static int futex_wake_op(uint32_t *uaddr, uint32_t val, uint32_t val2, + uint32_t *uaddr2, uint32_t val3) +{ + return syscall(SYS_futex, uaddr, FUTEX_WAKE_OP, val, val2, + uaddr2, val3); +} + +/* + * Trigger some atomic uaccess on a page mapped with a non-default pkey. + * + * This ensures that such access is not mistakenly checked against the + * kernel's POR_EL1 register. + */ +TEST(poe_futex) +{ + int ret, pkey; + void *ptr; + size_t size = getpagesize(); + + pkey = sys_pkey_alloc(0, 0); + + if (pkey == -1 && errno == ENOSPC) + SKIP(return, "pkeys are not supported"); + + ASSERT_GT(pkey, 0); + + ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + ASSERT_NE(ptr, MAP_FAILED); + + ret = sys_pkey_mprotect(ptr, size, PROT_READ | PROT_WRITE, pkey); + ASSERT_EQ(ret, 0); + + /* + * There is no one to wake up so this syscall boils down to *(ptr+4) = 0 + * (arch_futex_atomic_op_inuser() called with FUTEX_OP_SET and op_arg=0). + */ + ret = futex_wake_op(ptr, 1, 1, ptr + sizeof(uint32_t), 0); + ASSERT_EQ(ret, 0); +} + +TEST_HARNESS_MAIN -- 2.51.2

