From: Jan Polensky <japo@linux.ibm.com>
To: ltp@lists.linux.it
Subject: [LTP] [PATCH v4 3/3] ptrace: add test for /proc/pid/mem writes under ptrace
Date: Wed, 12 Aug 2026 15:28:59 +0200 [thread overview]
Message-ID: <20260812132901.202632-4-japo@linux.ibm.com> (raw)
In-Reply-To: <20260812132901.202632-1-japo@linux.ibm.com>
Add ptrace13 to verify that a tracer can write to a tracee through
/proc/pid/mem when CONFIG_PROC_MEM_FORCE_PTRACE requires ptrace access
checks.
The test forks a child, attaches to it with PTRACE_SEIZE, waits for the
child to stop, then repeatedly writes a value to the child's read-only
mapping via /proc/pid/mem and resumes it. The child verifies each write
after it is continued, covering the write-stop-continue cycle needed
for ptrace-mediated memory writes.
Signed-off-by: Jan Polensky <japo@linux.ibm.com>
---
runtest/syscalls | 1 +
testcases/kernel/syscalls/ptrace/.gitignore | 1 +
testcases/kernel/syscalls/ptrace/ptrace13.c | 202 ++++++++++++++++++++
3 files changed, 204 insertions(+)
create mode 100644 testcases/kernel/syscalls/ptrace/ptrace13.c
diff --git a/runtest/syscalls b/runtest/syscalls
index 7fc443247361..20d442ed4b99 100644
--- a/runtest/syscalls
+++ b/runtest/syscalls
@@ -1183,6 +1183,7 @@ ptrace10 ptrace10
ptrace11 ptrace11
ptrace12 ptrace12
+ptrace13 ptrace13
pwrite01 pwrite01
pwrite02 pwrite02
pwrite03 pwrite03
diff --git a/testcases/kernel/syscalls/ptrace/.gitignore b/testcases/kernel/syscalls/ptrace/.gitignore
index 8631219312d5..72f9cef98e22 100644
--- a/testcases/kernel/syscalls/ptrace/.gitignore
+++ b/testcases/kernel/syscalls/ptrace/.gitignore
@@ -10,3 +10,4 @@
/ptrace10
/ptrace11
/ptrace12
+/ptrace13
diff --git a/testcases/kernel/syscalls/ptrace/ptrace13.c b/testcases/kernel/syscalls/ptrace/ptrace13.c
new file mode 100644
index 000000000000..d88968273d21
--- /dev/null
+++ b/testcases/kernel/syscalls/ptrace/ptrace13.c
@@ -0,0 +1,202 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2026 IBM Corporation
+ */
+
+/*\
+ * Verify that a parent process can write to a traced child's memory
+ * via /proc/pid/mem when the child is in a stopped state.
+ *
+ * This test validates the ptrace-based memory write mechanism that
+ * becomes mandatory when CONFIG_PROC_MEM_FORCE_PTRACE=y is active.
+ *
+ * Test flow:
+ *
+ * 1. Parent forks a child process
+ * 2. Child signals readiness via checkpoint
+ * 3. Parent attaches with PTRACE_SEIZE
+ * 4. Parent issues PTRACE_INTERRUPT to create initial stop
+ * 5. Parent writes to child's memory via /proc/pid/mem
+ * 6. Parent continues child with PTRACE_CONT
+ * 7. Child verifies the write took effect
+ * 8. Child self-stops with raise(SIGSTOP) for next iteration
+ * 9. Repeat for multiple iterations
+ *
+ * The test uses checkpoint synchronization to prevent race conditions
+ * during ptrace attachment, and PTRACE_INTERRUPT for deterministic
+ * initial stop control.
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/ptrace.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "tst_test.h"
+#include "tst_checkpoint.h"
+
+#define TEST_ITERATIONS 100
+
+struct shared_state {
+ int *test_ptr;
+ int expected_val;
+};
+
+static struct shared_state *shared;
+static pid_t tracee_pid;
+
+static void tracee_main(void)
+{
+ int i;
+
+ shared->test_ptr = SAFE_MMAP(NULL, sizeof(int), PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ *shared->test_ptr = 0;
+
+ /* Force parent write through /proc/pid/mem to require FOLL_FORCE */
+ SAFE_MPROTECT((void *)shared->test_ptr, sizeof(int), PROT_READ);
+
+ TST_CHECKPOINT_WAKE(0);
+ TST_CHECKPOINT_WAIT(1);
+
+ for (i = 0; i < TEST_ITERATIONS; i++) {
+ if (*shared->test_ptr != shared->expected_val) {
+ tst_res(TFAIL,
+ "Iteration %d: expected 0x%x, got 0x%x",
+ i, shared->expected_val, *shared->test_ptr);
+ exit(1);
+ }
+
+ raise(SIGSTOP);
+ }
+
+ pause();
+ exit(0);
+}
+
+static void setup(void)
+{
+ /* Allocate shared memory for parent-child communication */
+ shared = SAFE_MMAP(NULL, sizeof(*shared), PROT_READ | PROT_WRITE,
+ MAP_SHARED | MAP_ANONYMOUS, -1, 0);
+}
+
+static void run(void)
+{
+ char path[64];
+ int memfd;
+ int status;
+ int i;
+
+ tracee_pid = SAFE_FORK();
+ if (!tracee_pid) {
+ tracee_main();
+ exit(0);
+ }
+
+ TST_CHECKPOINT_WAIT(0);
+ SAFE_PTRACE(PTRACE_SEIZE, tracee_pid, NULL, NULL);
+ TST_CHECKPOINT_WAKE(1);
+ SAFE_PTRACE(PTRACE_INTERRUPT, tracee_pid, NULL, NULL);
+ SAFE_WAITPID(tracee_pid, &status, 0);
+
+ if (WIFEXITED(status)) {
+ tst_brk(TBROK,
+ "Tracee exited unexpectedly at initial stop: %s",
+ tst_strstatus(status));
+ } else if (WIFSIGNALED(status)) {
+ tst_brk(TBROK,
+ "Tracee was killed at initial stop: %s",
+ tst_strstatus(status));
+ } else if (!WIFSTOPPED(status)) {
+ tst_brk(TBROK,
+ "Tracee not stopped at initial stop: %s",
+ tst_strstatus(status));
+ }
+
+ snprintf(path, sizeof(path), "/proc/%d/mem", tracee_pid);
+ memfd = SAFE_OPEN(path, O_RDWR);
+
+ for (i = 0; i < TEST_ITERATIONS; i++) {
+ int write_val = 0xdead0000 | i;
+
+ shared->expected_val = write_val;
+ SAFE_LSEEK(memfd, (off_t)shared->test_ptr, SEEK_SET);
+ SAFE_WRITE(SAFE_WRITE_ALL, memfd, &write_val, sizeof(write_val));
+ SAFE_PTRACE(PTRACE_CONT, tracee_pid, NULL, NULL);
+ SAFE_WAITPID(tracee_pid, &status, 0);
+
+ if (WIFEXITED(status)) {
+ SAFE_CLOSE(memfd);
+ tracee_pid = 0;
+
+ if (WEXITSTATUS(status) == 0) {
+ tst_brk(TBROK,
+ "Tracee exited unexpectedly at iteration %d: %s",
+ i, tst_strstatus(status));
+ } else {
+ return;
+ }
+ } else if (WIFSIGNALED(status)) {
+ tst_brk(TBROK,
+ "Tracee was killed at iteration %d: %s",
+ i, tst_strstatus(status));
+ } else if (!WIFSTOPPED(status) || WSTOPSIG(status) != SIGSTOP) {
+ tst_brk(TBROK,
+ "Tracee did not stop correctly at iteration %d: %s",
+ i, tst_strstatus(status));
+ }
+ }
+
+ SAFE_CLOSE(memfd);
+
+ tst_res(TPASS,
+ "Successfully wrote to tracee memory via /proc/pid/mem "
+ "for %d iterations", TEST_ITERATIONS);
+
+ SAFE_PTRACE(PTRACE_DETACH, tracee_pid, NULL, NULL);
+ SAFE_KILL(tracee_pid, SIGTERM);
+ SAFE_WAITPID(tracee_pid, &status, 0);
+
+ if (!WIFSIGNALED(status) || WTERMSIG(status) != SIGTERM) {
+ tst_res(TWARN, "Unexpected final wait status: %s",
+ tst_strstatus(status));
+ }
+
+ tracee_pid = 0;
+}
+
+static void cleanup(void)
+{
+ if (tracee_pid > 0) {
+ /* Kill tracee if still alive (e.g., test aborted) */
+ if (kill(tracee_pid, 0) == 0) {
+ SAFE_KILL(tracee_pid, SIGKILL);
+ tst_reap_children();
+ }
+ tracee_pid = 0;
+ }
+
+ if (shared)
+ SAFE_MUNMAP(shared, sizeof(*shared));
+}
+
+static struct tst_test test = {
+ .test_all = run,
+ .setup = setup,
+ .cleanup = cleanup,
+ .forks_child = 1,
+ .needs_checkpoints = 1,
+ .needs_kconfigs = (const char *[]) {
+ "CONFIG_PROC_MEM_FORCE_PTRACE=y",
+ NULL
+ },
+ .tags = (const struct tst_tag[]) {
+ {"linux-git", "41e8149c8892"},
+ {}
+ }
+};
--
2.55.0
--
Mailing list info: https://lists.linux.it/listinfo/ltp
prev parent reply other threads:[~2026-08-12 13:29 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-12 13:28 [LTP] [PATCH v4 0/3] Handle FORCE_PTRACE in thp04 and add ptrace coverage Jan Polensky
2026-08-12 13:28 ` [LTP] [PATCH v4 1/3] thp04: group runtime state and skip when /proc/self/mem writes are blocked Jan Polensky
2026-08-12 15:01 ` [LTP] " linuxtestproject.agent
2026-08-12 13:28 ` [LTP] [PATCH v4 2/3] ptrace: add test for /proc/self/mem write rejection Jan Polensky
2026-08-12 13:28 ` Jan Polensky [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260812132901.202632-4-japo@linux.ibm.com \
--to=japo@linux.ibm.com \
--cc=ltp@lists.linux.it \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.