For a 32-bit parent debugging a 32-bit child, add tests for reading the
TLS registers, and mangling with the mode bits in CPSR.
 
Signed-off-by: Dev Jain <dev.j...@arm.com>
---
 tools/testing/selftests/arm/abi/ptrace.c | 82 ++++++++++++++++++++++++
 tools/testing/selftests/arm/abi/ptrace.h | 57 ++++++++++++++++
 2 files changed, 139 insertions(+)
 create mode 100644 tools/testing/selftests/arm/abi/ptrace.c
 create mode 100644 tools/testing/selftests/arm/abi/ptrace.h

diff --git a/tools/testing/selftests/arm/abi/ptrace.c 
b/tools/testing/selftests/arm/abi/ptrace.c
new file mode 100644
index 000000000000..2079065c48fd
--- /dev/null
+++ b/tools/testing/selftests/arm/abi/ptrace.c
@@ -0,0 +1,82 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2024 ARM Limited.
+ */
+#include <errno.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/auxv.h>
+#include <sys/prctl.h>
+#include <sys/ptrace.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <sys/wait.h>
+#include <asm/sigcontext.h>
+#include <asm/ptrace.h>
+
+#include "ptrace.h"
+#include "../../kselftest.h"
+
+#define EXPECTED_TESTS 6
+#define NUM_TLS_REGS   2
+
+static void test_tpidr(pid_t child)
+{
+       unsigned long read_val[NUM_TLS_REGS];
+       struct iovec read_iov;
+       int ret;
+
+       read_iov.iov_base = read_val;
+
+       /* TLS registers must not be accessible */
+       read_iov.iov_len = 2 * sizeof(unsigned long);
+       ret = ptrace(PTRACE_GETREGSET, child, NT_ARM_TLS, &read_iov);
+       ksft_test_result(ret != 0, "cannot read TLS\n");
+}
+
+static void run_tests(pid_t child)
+{
+       test_tpidr(child);
+       test_user_regs(child);
+}
+
+static int do_child(void)
+{
+       if (ptrace(PTRACE_TRACEME, -1, NULL, NULL))
+               ksft_exit_fail_perror("PTRACE_TRACEME");
+
+       if (raise(SIGSTOP))
+               ksft_exit_fail_perror("raise(SIGSTOP)");
+
+       if (raise(SIGSTOP))
+               ksft_exit_fail_perror("raise(SIGSTOP)");
+
+       return EXIT_SUCCESS;
+}
+
+int main(void)
+{
+       int ret = EXIT_SUCCESS;
+       pid_t child;
+
+       srandom(getpid());
+
+       ksft_print_header();
+
+       ksft_set_plan(EXPECTED_TESTS);
+
+       child = fork();
+       if (!child)
+               return do_child();
+
+       if (do_parent(child))
+               ret = EXIT_FAILURE;
+
+       ksft_print_cnts();
+
+       return ret;
+}
diff --git a/tools/testing/selftests/arm/abi/ptrace.h 
b/tools/testing/selftests/arm/abi/ptrace.h
new file mode 100644
index 000000000000..17ba8aa32726
--- /dev/null
+++ b/tools/testing/selftests/arm/abi/ptrace.h
@@ -0,0 +1,57 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#include "../../arm64/abi/ptrace.h"
+
+/* Do not pull from asm/ptrace.h since the macro names change for 32-bit */
+#define PSR_MODE32_BIT 0x00000010
+#define PSR_MODE_EL1t  0x00000004
+
+static void test_user_regs(pid_t child)
+{
+       unsigned int read_val[18];
+       struct iovec read_iov;
+       int status;
+       int ret;
+
+       read_iov.iov_base = read_val;
+       read_iov.iov_len = 18 * sizeof(unsigned int);
+
+       ret = ptrace(PTRACE_GETREGSET, child, NT_PRSTATUS, &read_iov);
+       ksft_test_result(!ret, "read general-purpose registers\n");
+
+       /* Change a random user register */
+       read_val[2] = read_val[2] + 1;
+       ret = ptrace(PTRACE_SETREGSET, child, NT_PRSTATUS, &read_iov);
+       ksft_test_result(!ret, "set user register\n");
+
+       /* 16th register is the CPSR */
+       read_val[16] &= (~PSR_MODE32_BIT);
+
+       ret = ptrace(PTRACE_SETREGSET, child, NT_PRSTATUS,  &read_iov);
+       ksft_test_result(ret, "cannot toggle MODE32 bit\n");
+
+       ret = ptrace(PTRACE_CONT, child, NULL, 0);
+       if (ret) {
+               perror("ptrace");
+               goto error;
+       }
+
+       if (wait(&status) == -1) {
+               perror("wait");
+               goto error;
+       }
+
+       read_val[16] = 0;
+
+       ret = ptrace(PTRACE_GETREGSET, child, NT_PRSTATUS, &read_iov);
+       ksft_test_result(!ret, "read general-purpose registers again\n");
+
+       read_val[16] |= PSR_MODE_EL1t;
+       ret = ptrace(PTRACE_SETREGSET, child, NT_PRSTATUS, &read_iov);
+       ksft_test_result(ret, "cannot escalate privilege\n");
+       return;
+
+error:
+       kill(child, SIGKILL);
+}
+
+
-- 
2.39.2


Reply via email to