summaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2026-08-18 11:57:17 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2026-08-18 11:57:17 -0700
commit85cdaca6970028bf6f544c355c90035586836ddf (patch)
tree77f5b379507d6f0e410daa5862065d5a6409a9ea /tools
parentd24f5cdbeff8b6a063fca92d0a1f94122a799b59 (diff)
parent26b77009ee74f6122a067a4f9926ff6e09fffb94 (diff)
Merge tag 'arm64-upstream' of git://git.kernel.org/pub/scm/linux/kernel/git/arm64/linux
Pull arm64 updates from Will Deacon: "There's a reasonable amount of stuff here, including a bunch of updates to the perf PMU drivers and some MPAM updates to expose the memory bandwidth counters via resctrl. On the architecture side, some highlights include support for BBML3 and steps towards support for an architectural NMI solution, all wrapped up in a web of fixes for latent issues identified by Sashiko. ACPI: - Combine reads of AMU counters into a single FFH feedback counter op Confidential computing: - Fix smp_processor_id() in preemptible context when retrieving an attestation token inside a realm - Convert pKVM over to a "CC platform" - Clean-up our SWIOTLB configuration in preparation for reworking the handling of encrypted/decryped DMA buffers in the dma-mapping tree CPU errata handling: - Work around broken device memory ordering on NVIDIA Olympus cores - Fix broken 'nospectre_bhb' command-line option - Select the idle loop backend instruction on the command-line CPU features: - Replace our BBML2-noabort feature with the new architectural BBML3 feature - Disable in-kernel BTI for recent versions of Clang due to issues with livepatch that are still being investigated - Clean-up documentation describing which ID register fields are exposed to userspace Interrupts: - Preliminary work towards supporting FEAT_NMI, which cleans up our IRQ entry code and fixes some latent issues with pseudo-NMI - Support for an SDEI backend to trigger an NMI backtrace Memory management: - Treat all devices as coherent when CLIDR_EL1.LoC == 0 - Fix no-map handling of sub-page-sized regions - Second attempt at unmapping the linear aliases of the kernel data and bss sections - Fix EFI runtime calls when software-PAN is enabled Miscellaneous: - Add Mark Rutland as a reviewer! - Tidy-up our futex cmpxchg logic when using the new LSUI instructions - Drop the requirement on DYNAMIC_FTRACE_WITH_CALL_OPS when selecting HAVE_DYNAMIC_FTRACE_WITH_DIRECT_CALLS - Fix a false-positive KCSCAN splat in the delay loop - Use a portable typedef for 128-bit scalar types in our UAPI headers - Non-critical fixes for Sashiko reports all over MPAM: - Hook MPAM memory bandwidth counters into resctrl's counter assignment interface - Fix a quirk in the MPAM bandwidth counting on Nvidia T241 so that it also applies to 63 bit counters Perf: - Workarounds for hardware issues in the CMN-S3 PMU (Graviton 5) and CPU PMU (NVIDIA Olympus again!) - Add support for the DDR PMU on Marvell CN20K SoCs - Add support for Picoheart implementations of the DCW PCIe PMU - Add support for Channel/Rank/Bank filtering in the CXL PMU driver - Add support for 64-bit counters in the CSPMU device - Add support for revision 2 of the CMN S3 PMU Ptrace: - Fix a decade-old bug in our handling of seccomp and tracing on syscall entry - Fix regset handling for inactive SVE and SSVE registers Selftests - Add some tests for the decade-old bug that we just tried to fix in our syscall entry path - Fix SVE test crash on SME-only CPUs" * tag 'arm64-upstream' of git://git.kernel.org/pub/scm/linux/kernel/git/arm64/linux: (95 commits) arm64/efi: Avoid voluntary preemption with efi_mm installed arm64: bti: Disable in-kernel BTI with recent versions of Clang arm64: entry: Avoid unnecessary local_irq_disable() on kernel exit irqchip/gic-v3: make the unmasking of pseudo-NMIs explicit when handling IRQs arm64: Disable KCSAN instrumentation in delay.o arm_mpam: Disable driver unbind to avoid UAF arm_mpam: Fix a NULL pointer dereference on unbinding after an error interrupt perf: arm_pmuv3: Zero initialize hw_id branch stack field arm64: mm: Unmap kernel data/bss entirely from the linear map iommu/arm-smmu-v3-sva: Use system_supports_bbml3() to detect CPU feature perf/arm-cmn: Support CMN S3 r2 perf/arm-cmn: Plumb in new filter types perf/arm-cmn: Refactor event filter data perf/arm-cmn: Refactor event filter programming perf/arm-cmn: Rename filter variables for clarity arm64: mm: fix accidental linear mapping of no-map reserved memory tools: Ensure tools copy of linux/filter.h exports the UAPI kselftest/arm64: Fix abi test compilation errors arch: arm64: add early_param idle=<wfi|yield|nop> arm64: entry: mask DAIF before returning from C EL1 handlers ...
Diffstat (limited to 'tools')
-rw-r--r--tools/include/linux/filter.h1
-rw-r--r--tools/include/linux/types.h5
-rw-r--r--tools/testing/selftests/arm64/abi/.gitignore2
-rw-r--r--tools/testing/selftests/arm64/abi/Makefile4
-rw-r--r--tools/testing/selftests/arm64/abi/seccomp_ptrace_x0_bypass.c192
-rw-r--r--tools/testing/selftests/arm64/abi/seccomp_ret_trace_x0_bypass.c201
-rw-r--r--tools/testing/selftests/arm64/fp/fp-ptrace.c47
-rw-r--r--tools/testing/selftests/arm64/fp/sve-test.S7
8 files changed, 451 insertions, 8 deletions
diff --git a/tools/include/linux/filter.h b/tools/include/linux/filter.h
index bcc6df79301a..4ead4e72097c 100644
--- a/tools/include/linux/filter.h
+++ b/tools/include/linux/filter.h
@@ -6,6 +6,7 @@
#define __TOOLS_LINUX_FILTER_H
#include <linux/bpf.h>
+#include <uapi/linux/filter.h>
/* ArgX, context and stack frame pointer register positions. Note,
* Arg1, Arg2, Arg3, etc are used as argument mappings of function
diff --git a/tools/include/linux/types.h b/tools/include/linux/types.h
index d41f8a261bce..b6c473b7920d 100644
--- a/tools/include/linux/types.h
+++ b/tools/include/linux/types.h
@@ -23,6 +23,11 @@ typedef enum {
__GFP_HIGH
} gfp_t;
+#ifdef __SIZEOF_INT128__
+typedef __signed__ __int128 __s128 __attribute__((aligned(16)));
+typedef unsigned __int128 __u128 __attribute__((aligned(16)));
+#endif
+
/*
* We define u64 as uint64_t for every architecture
* so that we can print it with "%"PRIx64 without getting warnings.
diff --git a/tools/testing/selftests/arm64/abi/.gitignore b/tools/testing/selftests/arm64/abi/.gitignore
index 44f8b80f37e3..491a80db9dff 100644
--- a/tools/testing/selftests/arm64/abi/.gitignore
+++ b/tools/testing/selftests/arm64/abi/.gitignore
@@ -1,4 +1,6 @@
hwcap
ptrace
+seccomp_ptrace_x0_bypass
+seccomp_ret_trace_x0_bypass
syscall-abi
tpidr2
diff --git a/tools/testing/selftests/arm64/abi/Makefile b/tools/testing/selftests/arm64/abi/Makefile
index 483488f8c2ad..e91d4cdf17ad 100644
--- a/tools/testing/selftests/arm64/abi/Makefile
+++ b/tools/testing/selftests/arm64/abi/Makefile
@@ -1,7 +1,9 @@
# SPDX-License-Identifier: GPL-2.0
# Copyright (C) 2021 ARM Limited
-TEST_GEN_PROGS := hwcap ptrace syscall-abi tpidr2
+CFLAGS += $(KHDR_INCLUDES) -I$(top_srcdir)/tools/include
+
+TEST_GEN_PROGS := hwcap ptrace syscall-abi tpidr2 seccomp_ptrace_x0_bypass seccomp_ret_trace_x0_bypass
include ../../lib.mk
diff --git a/tools/testing/selftests/arm64/abi/seccomp_ptrace_x0_bypass.c b/tools/testing/selftests/arm64/abi/seccomp_ptrace_x0_bypass.c
new file mode 100644
index 000000000000..4ee8e5aaad6f
--- /dev/null
+++ b/tools/testing/selftests/arm64/abi/seccomp_ptrace_x0_bypass.c
@@ -0,0 +1,192 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Test that seccomp, tracepoints and audit observe the correct syscall
+ * arguments after a ptracer has modified them at syscall-enter-stop.
+ *
+ * On arm64, both the first argument and the return value of a syscall
+ * are passed in register x0. The original x0 is saved in
+ * pt_regs::orig_x0 during syscall entry and returned as the first
+ * argument by syscall_get_arguments(). Because ptrace modifications
+ * to x0 are not automatically reflected in orig_x0, seccomp, tracepoints
+ * and audit may see a stale value unless orig_x0 is explicitly
+ * re-synchronised after a ptrace stop.
+ *
+ * This test sets up a seccomp filter that allows write(2, ...) but kills
+ * the task for any other fd. A ptracer changes the fd argument from 2
+ * to 1 at the syscall-enter stop. If the orig_x0 re-sync works, seccomp
+ * sees the modified argument (fd=1) and kills the child with SIGSYS
+ * (test passes). If orig_x0 is not re-synced, seccomp sees the original
+ * fd=2, the write succeeds and the child exits normally (test fails,
+ * vulnerability present).
+ */
+#include <errno.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/prctl.h>
+#include <sys/ptrace.h>
+#include <sys/uio.h>
+#include <sys/wait.h>
+#include <asm/ptrace.h>
+#include <linux/elf.h>
+#include <linux/filter.h>
+#include <linux/seccomp.h>
+#include <asm/unistd.h>
+
+#include "kselftest.h"
+
+#define EXPECTED_TESTS 1
+
+#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+#define ARG0_OFFSET (offsetof(struct seccomp_data, args))
+#else
+#define ARG0_OFFSET (offsetof(struct seccomp_data, args) + 4)
+#endif
+
+static int do_child(void)
+{
+ if (ptrace(PTRACE_TRACEME, 0, NULL, NULL))
+ ksft_exit_fail_perror("PTRACE_TRACEME");
+
+ if (raise(SIGSTOP))
+ ksft_exit_fail_perror("raise(SIGSTOP)");
+
+ /*
+ * Seccomp filter:
+ * If syscall is not write -> ALLOW
+ * If syscall is write:
+ * - If args[0] (fd) == 2 -> ALLOW
+ * - Otherwise -> KILL
+ */
+ struct sock_filter filter[] = {
+ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, nr)), /* nr */
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_write, 0, 3),
+ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, ARG0_OFFSET), /* args[0] */
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 2, 1, 0),
+ BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_KILL),
+ BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW),
+ };
+ struct sock_fprog prog = {
+ .len = ARRAY_SIZE(filter),
+ .filter = filter,
+ };
+
+ if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0))
+ ksft_exit_fail_perror("prctl NO_NEW_PRIVS");
+
+ if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog))
+ ksft_exit_fail_perror("prctl SECCOMP");
+
+ /*
+ * Invoke write(2, ...) while the tracer will change the first
+ * argument (fd) from 2 to 1 at syscall entry.
+ */
+ syscall(__NR_write, 2, NULL, 0);
+ _exit(0);
+}
+
+static int do_parent(pid_t child)
+{
+ bool bypass = false;
+ int status;
+
+ /* Wait for the initial SIGSTOP */
+ if (waitpid(child, &status, 0) != child)
+ ksft_exit_fail_msg("waitpid failed");
+
+ if (!WIFSTOPPED(status) || WSTOPSIG(status) != SIGSTOP)
+ ksft_exit_fail_msg("unexpected stop status");
+
+ if (ptrace(PTRACE_SETOPTIONS, child, 0, PTRACE_O_TRACESYSGOOD | PTRACE_O_EXITKILL))
+ ksft_exit_fail_perror("PTRACE_SETOPTIONS");
+
+ if (ptrace(PTRACE_SYSCALL, child, 0, 0))
+ ksft_exit_fail_perror("PTRACE_SYSCALL");
+
+ while (1) {
+ int sig;
+
+ if (waitpid(child, &status, 0) != child)
+ ksft_exit_fail_msg("waitpid lost child");
+
+ if (WIFEXITED(status)) {
+ /* Child exited normally – bypass succeeded */
+ bypass = true;
+ break;
+ }
+
+ if (WIFSIGNALED(status)) {
+ sig = WTERMSIG(status);
+ if (sig == SIGSYS)
+ break;
+ ksft_exit_fail_msg("child died unexpectedly from signal %d (%s)",
+ sig, strsignal(sig));
+ }
+
+ if (!WIFSTOPPED(status))
+ ksft_exit_fail_msg("unexpected wait status");
+
+ sig = WSTOPSIG(status);
+
+ if (sig == (SIGTRAP | 0x80)) {
+ struct user_regs_struct regs;
+ struct iovec iov = {
+ .iov_base = &regs,
+ .iov_len = sizeof(regs),
+ };
+
+ if (ptrace(PTRACE_GETREGSET, child, NT_PRSTATUS, &iov))
+ ksft_exit_fail_perror("PTRACE_GETREGSET");
+
+ unsigned long syscall_nr = regs.regs[8];
+ unsigned long x0 = regs.regs[0];
+
+ /* Modify fd from 2 to 1 at write entry */
+ if (syscall_nr == __NR_write && x0 == 2) {
+ regs.regs[0] = 1;
+ if (ptrace(PTRACE_SETREGSET, child, NT_PRSTATUS, &iov))
+ ksft_exit_fail_perror("PTRACE_SETREGSET");
+ }
+
+ if (ptrace(PTRACE_SYSCALL, child, 0, 0))
+ ksft_exit_fail_perror("PTRACE_SYSCALL");
+ } else {
+ /* Forward other signals */
+ if (ptrace(PTRACE_SYSCALL, child, 0, sig))
+ ksft_exit_fail_perror("PTRACE_SYSCALL");
+ }
+ }
+
+ /* bypass == true means vulnerability exists -> test fails */
+ return bypass ? EXIT_FAILURE : EXIT_SUCCESS;
+}
+
+int main(void)
+{
+ pid_t child;
+
+ ksft_print_header();
+ ksft_set_plan(EXPECTED_TESTS);
+
+ child = fork();
+ if (child < 0)
+ ksft_exit_fail_msg("fork failed: %s", strerror(errno));
+
+ if (!child)
+ return do_child();
+
+ /*
+ * do_parent() returns EXIT_SUCCESS if the child was killed by
+ * SIGSYS (i.e. seccomp correctly saw the modified argument),
+ * and EXIT_FAILURE if the child exited normally (bypass).
+ */
+ int result = do_parent(child);
+
+ ksft_test_result(result == EXIT_SUCCESS, "seccomp_ptrace_x0_bypass\n");
+
+ ksft_print_cnts();
+ return result;
+}
diff --git a/tools/testing/selftests/arm64/abi/seccomp_ret_trace_x0_bypass.c b/tools/testing/selftests/arm64/abi/seccomp_ret_trace_x0_bypass.c
new file mode 100644
index 000000000000..a23081763328
--- /dev/null
+++ b/tools/testing/selftests/arm64/abi/seccomp_ret_trace_x0_bypass.c
@@ -0,0 +1,201 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Test for SECCOMP_RET_TRACE argument modification bypass
+ * via stale orig_x0 during filter re-evaluation.
+ *
+ * On arm64, syscall_get_arguments() reads the first argument from
+ * regs->orig_x0. When a seccomp filter returns SECCOMP_RET_TRACE,
+ * ptrace may modify regs->regs[0] while orig_x0 remains unchanged.
+ * The kernel then re-evaluates the filter; if it sees the stale
+ * orig_x0, it may incorrectly allow a syscall that the tracer intended
+ * to block.
+ *
+ * This test installs a filter that:
+ * - TRACEs write() when fd == 2
+ * - returns ERRNO(EPERM) when fd == 1
+ * - allows all other syscalls
+ *
+ * The child calls write(2, ...). The parent catches the SECCOMP stop,
+ * changes x0 (fd) from 2 to 1, and resumes the child.
+ *
+ * If re-evaluation sees the old fd=2 (stale orig_x0), the filter
+ * returns TRACE again; because recheck_after_trace is true, the kernel
+ * allows the syscall to proceed. write(1, ...) succeeds, child exits 0.
+ * -> test FAIL (bypass detected).
+ *
+ * If re-evaluation sees the new fd=1 (synced orig_x0), the filter
+ * returns ERRNO(EPERM), write fails, child exits 1.
+ * -> test PASS (no bypass).
+ *
+ * No special privileges required beyond CAP_SYS_PTRACE.
+ */
+#include <errno.h>
+#include <signal.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/prctl.h>
+#include <sys/ptrace.h>
+#include <sys/uio.h>
+#include <sys/wait.h>
+#include <linux/elf.h>
+#include <linux/filter.h>
+#include <linux/seccomp.h>
+#include <linux/ptrace.h>
+#include <asm/unistd.h>
+
+#include "kselftest.h"
+
+#define PTRACE_EVENT_MASK(status) ((status) >> 16)
+
+#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+#define ARG0_OFFSET (offsetof(struct seccomp_data, args))
+#else
+#define ARG0_OFFSET (offsetof(struct seccomp_data, args) + 4)
+#endif
+
+static int do_child(void)
+{
+ long ret;
+
+ if (ptrace(PTRACE_TRACEME, 0, NULL, NULL))
+ _exit(2);
+
+ raise(SIGSTOP); /* synchronize with parent */
+
+ /*
+ * Filter:
+ * if syscall == write:
+ * if fd == 2 -> TRACE
+ * if fd == 1 -> ERRNO(EPERM)
+ * else -> ALLOW
+ * else -> ALLOW
+ */
+ struct sock_filter filter[] = {
+ /* Load syscall number */
+ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, nr)),
+ /* If not write, allow */
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_write, 0, 5),
+ /* Load first argument (fd) */
+ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, ARG0_OFFSET),
+ /* fd == 2 ? */
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 2, 0, 1),
+ /* Yes: TRACE */
+ BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_TRACE),
+ /* fd == 1 ? */
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 1, 0, 1),
+ /* Yes: ERRNO(EPERM) */
+ BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ERRNO | (EPERM & SECCOMP_RET_DATA)),
+ /* Other fd: ALLOW */
+ BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW),
+ };
+
+ struct sock_fprog prog = {
+ .len = ARRAY_SIZE(filter),
+ .filter = filter,
+ };
+
+ if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0))
+ _exit(3);
+ if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog))
+ _exit(4);
+
+ /*
+ * write(2, ...) triggers TRACE, parent changes fd to 1.
+ * If re-eval sees fd=1 -> ERRNO -> write fails, ret = -EPERM.
+ * If re-eval sees fd=2 -> TRACE again -> allowed -> write succeeds.
+ */
+ ret = syscall(__NR_write, 2, "", 0);
+ _exit(ret == 0 ? 0 : 1);
+}
+
+int main(void)
+{
+ struct user_pt_regs regs;
+ struct iovec iov = { .iov_base = &regs, .iov_len = sizeof(regs) };
+ pid_t child;
+ int status;
+
+ ksft_print_header();
+ ksft_set_plan(1);
+
+ child = fork();
+ if (child < 0)
+ ksft_exit_fail_msg("fork failed: %s", strerror(errno));
+
+ if (!child)
+ return do_child();
+
+ /* 1. Wait for initial SIGSTOP */
+ if (waitpid(child, &status, 0) != child)
+ ksft_exit_fail_msg("waitpid SIGSTOP");
+ if (!WIFSTOPPED(status) || WSTOPSIG(status) != SIGSTOP)
+ ksft_exit_fail_msg("unexpected initial stop");
+
+ /* 2. Enable SECCOMP ptrace events */
+ if (ptrace(PTRACE_SETOPTIONS, child, 0, PTRACE_O_TRACESECCOMP))
+ ksft_exit_fail_msg("PTRACE_SETOPTIONS");
+
+ /* 3. Continue child to hit SECCOMP stop */
+ if (ptrace(PTRACE_CONT, child, 0, 0))
+ ksft_exit_fail_msg("PTRACE_CONT");
+
+ /* 4. Wait for SECCOMP stop */
+ while (1) {
+ if (waitpid(child, &status, 0) != child)
+ ksft_exit_fail_msg("waitpid SECCOMP");
+ if (WIFEXITED(status)) {
+ ksft_test_result_fail("child exited before SECCOMP stop\n");
+ goto out;
+ }
+ if (WIFSIGNALED(status)) {
+ ksft_test_result_fail("child killed unexpectedly\n");
+ goto out;
+ }
+ if (WIFSTOPPED(status) &&
+ WSTOPSIG(status) == SIGTRAP &&
+ PTRACE_EVENT_MASK(status) == PTRACE_EVENT_SECCOMP)
+ break;
+ ptrace(PTRACE_CONT, child, 0, WSTOPSIG(status));
+ }
+
+ /* 5. Modify x0 (fd) from 2 to 1 */
+ if (ptrace(PTRACE_GETREGSET, child, NT_PRSTATUS, &iov))
+ ksft_exit_fail_perror("GETREGSET");
+ if (regs.regs[8] != __NR_write || regs.regs[0] != 2) {
+ ksft_test_result_fail("unexpected regs: syscall=%llu, x0=%llu\n",
+ regs.regs[8], regs.regs[0]);
+ goto out;
+ }
+ regs.regs[0] = 1;
+ if (ptrace(PTRACE_SETREGSET, child, NT_PRSTATUS, &iov))
+ ksft_exit_fail_perror("SETREGSET");
+
+ /* 6. Resume child */
+ if (ptrace(PTRACE_CONT, child, 0, 0))
+ ksft_exit_fail_perror("PTRACE_CONT");
+
+ /* 7. Reap child – must exit normally */
+ if (waitpid(child, &status, 0) != child)
+ ksft_exit_fail_msg("final waitpid");
+
+ if (!WIFEXITED(status)) {
+ ksft_test_result_fail("child did not exit normally\n");
+ goto out;
+ }
+
+ if (WEXITSTATUS(status) != 0)
+ ksft_test_result_pass("seccomp correctly denied modified syscall\n");
+ else
+ ksft_test_result_fail("write succeeded, orig_x0 bypass likely\n");
+
+out:
+ if (child > 0) {
+ kill(child, SIGKILL);
+ waitpid(child, NULL, 0);
+ }
+ ksft_print_cnts();
+ return ksft_get_fail_cnt() ? EXIT_FAILURE : EXIT_SUCCESS;
+}
diff --git a/tools/testing/selftests/arm64/fp/fp-ptrace.c b/tools/testing/selftests/arm64/fp/fp-ptrace.c
index 22c584b78be5..b435837c8c0e 100644
--- a/tools/testing/selftests/arm64/fp/fp-ptrace.c
+++ b/tools/testing/selftests/arm64/fp/fp-ptrace.c
@@ -65,6 +65,9 @@
/* VL 128..2048 in powers of 2 */
#define MAX_NUM_VLS 5
+/* Sentinel for detecting buffer bytes the kernel did not write */
+#define REGSET_SENTINEL 0xa5
+
/*
* FPMR bits we can set without doing feature checks to see if values
* are valid.
@@ -181,6 +184,20 @@ static bool compare_buffer(const char *name, void *out,
return false;
}
+static bool buffer_is_filled(const void *buffer, size_t size,
+ unsigned char value)
+{
+ const unsigned char *bytes = buffer;
+ size_t i;
+
+ for (i = 0; i < size; i++) {
+ if (bytes[i] != value)
+ return false;
+ }
+
+ return true;
+}
+
struct test_config {
int sve_vl_in;
int sve_vl_expected;
@@ -401,6 +418,7 @@ static bool check_ptrace_values_sve(pid_t child, struct test_config *config)
struct user_sve_header *sve;
struct user_fpsimd_state *fpsimd;
struct iovec iov;
+ size_t buf_size;
int ret, vq;
bool pass = true;
@@ -409,14 +427,16 @@ static bool check_ptrace_values_sve(pid_t child, struct test_config *config)
vq = __sve_vq_from_vl(config->sve_vl_in);
- iov.iov_len = SVE_PT_SVE_OFFSET + SVE_PT_SVE_SIZE(vq, SVE_PT_REGS_SVE);
- iov.iov_base = malloc(iov.iov_len);
+ buf_size = SVE_PT_SVE_OFFSET + SVE_PT_SVE_SIZE(vq, SVE_PT_REGS_SVE);
+ iov.iov_len = buf_size;
+ iov.iov_base = malloc(buf_size);
if (!iov.iov_base) {
ksft_print_msg("OOM allocating %lu byte SVE buffer\n",
iov.iov_len);
return false;
}
+ memset(iov.iov_base, REGSET_SENTINEL, buf_size);
ret = ptrace(PTRACE_GETREGSET, child, NT_ARM_SVE, &iov);
if (ret != 0) {
ksft_print_msg("Failed to read initial SVE: %s (%d)\n",
@@ -440,10 +460,16 @@ static bool check_ptrace_values_sve(pid_t child, struct test_config *config)
}
if (svcr_in & SVCR_SM) {
- if (sve->size != sizeof(sve)) {
+ if (sve->size != sizeof(*sve)) {
ksft_print_msg("NT_ARM_SVE reports data with PSTATE.SM\n");
pass = false;
}
+ if (!buffer_is_filled(iov.iov_base + sizeof(*sve),
+ buf_size - sizeof(*sve), REGSET_SENTINEL)) {
+ ksft_print_msg("NT_ARM_SVE wrote beyond its header with PSTATE.SM\n");
+ pass = false;
+ }
+ goto out;
} else {
if (sve->size != SVE_PT_SIZE(vq, sve->flags)) {
ksft_print_msg("Mismatch in SVE header size: %d != %lu\n",
@@ -485,6 +511,7 @@ static bool check_ptrace_values_ssve(pid_t child, struct test_config *config)
struct user_sve_header *sve;
struct user_fpsimd_state *fpsimd;
struct iovec iov;
+ size_t buf_size;
int ret, vq;
bool pass = true;
@@ -493,14 +520,16 @@ static bool check_ptrace_values_ssve(pid_t child, struct test_config *config)
vq = __sve_vq_from_vl(config->sme_vl_in);
- iov.iov_len = SVE_PT_SVE_OFFSET + SVE_PT_SVE_SIZE(vq, SVE_PT_REGS_SVE);
- iov.iov_base = malloc(iov.iov_len);
+ buf_size = SVE_PT_SVE_OFFSET + SVE_PT_SVE_SIZE(vq, SVE_PT_REGS_SVE);
+ iov.iov_len = buf_size;
+ iov.iov_base = malloc(buf_size);
if (!iov.iov_base) {
ksft_print_msg("OOM allocating %lu byte SSVE buffer\n",
iov.iov_len);
return false;
}
+ memset(iov.iov_base, REGSET_SENTINEL, buf_size);
ret = ptrace(PTRACE_GETREGSET, child, NT_ARM_SSVE, &iov);
if (ret != 0) {
ksft_print_msg("Failed to read initial SSVE: %s (%d)\n",
@@ -523,10 +552,16 @@ static bool check_ptrace_values_ssve(pid_t child, struct test_config *config)
}
if (!(svcr_in & SVCR_SM)) {
- if (sve->size != sizeof(sve)) {
+ if (sve->size != sizeof(*sve)) {
ksft_print_msg("NT_ARM_SSVE reports data without PSTATE.SM\n");
pass = false;
}
+ if (!buffer_is_filled(iov.iov_base + sizeof(*sve),
+ buf_size - sizeof(*sve), REGSET_SENTINEL)) {
+ ksft_print_msg("NT_ARM_SSVE wrote beyond its header without PSTATE.SM\n");
+ pass = false;
+ }
+ goto out;
} else {
if (sve->size != SVE_PT_SIZE(vq, sve->flags)) {
ksft_print_msg("Mismatch in SSVE header size: %d != %lu\n",
diff --git a/tools/testing/selftests/arm64/fp/sve-test.S b/tools/testing/selftests/arm64/fp/sve-test.S
index 80e072f221cd..7ef7835389e7 100644
--- a/tools/testing/selftests/arm64/fp/sve-test.S
+++ b/tools/testing/selftests/arm64/fp/sve-test.S
@@ -298,15 +298,20 @@ function irritator_handler
add x0, x0, #1
str x0, [x2, #ucontext_regs + 8 * 23]
+#ifndef SSVE
// Corrupt some random Z-regs
movi v0.8b, #1
movi v9.16b, #2
movi v31.8b, #3
// And P0
ptrue p0.d
-#ifndef SSVE
// And FFR
wrffr p15.b
+#else
+ // Enter and exit streaming mode, will reset all of the V, Z, P
+ // and FFR registers that the system has.
+ smstart_sm
+ smstop
#endif
ret