diff mbox series

[v3,7/9] selftests/arm: Add ptrace test

Message ID 20240625122408.1439097-8-dev.jain@arm.com (mailing list archive)
State New, archived
Headers show
Series A new selftests/ directory for arm compatibility testing | expand

Commit Message

Dev Jain June 25, 2024, 12:24 p.m. UTC
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.jain@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 mbox series

Patch

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);
+}
+
+