All of lore.kernel.org
 help / color / mirror / Atom feed
From: Jan Polensky <japo@linux.ibm.com>
To: ltp@lists.linux.it
Subject: [LTP] [PATCH v3 3/3] ptrace: add test for /proc/pid/mem writes under ptrace
Date: Tue, 21 Jul 2026 22:24:51 +0200	[thread overview]
Message-ID: <20260721202452.315581-4-japo@linux.ibm.com> (raw)
In-Reply-To: <20260721202452.315581-1-japo@linux.ibm.com>

Add ptrace13 to verify that a tracer can write to tracee memory via
/proc/pid/mem when CONFIG_PROC_MEM_FORCE_PTRACE=y is active.

The test uses PTRACE_SEIZE to attach to a child process and writes to
its read-only memory pages via /proc/pid/mem. This validates that:
- FOLL_FORCE is allowed when actively ptracing
- Parent can modify child's read-only memory
- Write-stop-continue cycle works correctly (100 iterations)

Test validates kernel commit 41e8149c8892 ("proc: add config & param to
block forcing mem 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 | 204 ++++++++++++++++++++
 3 files changed, 206 insertions(+)
 create mode 100644 testcases/kernel/syscalls/ptrace/ptrace13.c

diff --git a/runtest/syscalls b/runtest/syscalls
index 9df4684cca41..476c69d4c5f0 100644
--- a/runtest/syscalls
+++ b/runtest/syscalls
@@ -1177,6 +1177,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..a052fab81cb6
--- /dev/null
+++ b/testcases/kernel/syscalls/ptrace/ptrace13.c
@@ -0,0 +1,204 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2025 SUSE LLC <japo@suse.cz>
+ */
+
+/*\
+ * 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. Child self-stops with raise(SIGSTOP)
+ * 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. Repeat for multiple iterations
+ *
+ * The test uses checkpoint synchronization to prevent race conditions
+ * during ptrace attachment.
+ */
+
+#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);
+	raise(SIGSTOP);
+
+	for (i = 0; i < TEST_ITERATIONS; i++) {
+		if (*shared->test_ptr != shared->expected_val) {
+			tst_res(TFAIL,
+				"Iteration %d: expected 0x%x (written in iteration %d), got 0x%x",
+				i, shared->expected_val, i > 0 ? i-1 : 0, *shared->test_ptr);
+			exit(1);
+		}
+
+		raise(SIGSTOP);
+	}
+
+	while (1)
+		pause();
+}
+
+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);
+	memset(shared, 0, sizeof(*shared));
+}
+
+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_WAITPID(tracee_pid, &status, 0);
+
+	if (WIFEXITED(status)) {
+		tst_brk(TBROK,
+			"Tracee exited unexpectedly before first SIGSTOP: %s",
+			tst_strstatus(status));
+	}
+
+	if (WIFSIGNALED(status)) {
+		tst_brk(TBROK,
+			"Tracee was killed before first SIGSTOP: %s",
+			tst_strstatus(status));
+	}
+
+	if (!WIFSTOPPED(status) || WSTOPSIG(status) != SIGSTOP) {
+		tst_brk(TBROK,
+			"Tracee did not self-stop with SIGSTOP: %s",
+			tst_strstatus(status));
+	}
+
+	snprintf(path, sizeof(path), "/proc/%d/mem", tracee_pid);
+	memfd = SAFE_OPEN(path, O_RDWR);
+
+	/*
+	 * Write-stop-continue cycle: tracee is already stopped from initial raise(SIGSTOP).
+	 * Pattern: write → continue → tracee verifies → tracee stops → repeat
+	 */
+	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)) {
+			if (WEXITSTATUS(status) == 0) {
+				tst_brk(TBROK,
+					"Tracee exited unexpectedly at iteration %d: %s",
+					i, tst_strstatus(status));
+			}
+			/* Tracee reported failure and exited */
+			SAFE_CLOSE(memfd);
+			tracee_pid = 0;
+			return;
+		}
+
+		if (WIFSIGNALED(status)) {
+			tst_brk(TBROK,
+				"Tracee was killed at iteration %d: %s",
+				i, tst_strstatus(status));
+		}
+
+		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);
+
+	/* Test passed - all writes were successful and tracee verified them */
+	tst_res(TPASS,
+		"Successfully wrote to tracee memory via /proc/pid/mem "
+		"for %d iterations", TEST_ITERATIONS);
+
+	/* Detach and terminate tracee */
+	SAFE_PTRACE(PTRACE_DETACH, tracee_pid, NULL, NULL);
+	SAFE_KILL(tracee_pid, SIGKILL);
+	SAFE_WAITPID(tracee_pid, &status, 0);
+	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,
+	.tags = (const struct tst_tag[]) {
+		{"linux-git", "41e8149c8892"},
+		{}
+	}
+};
-- 
2.55.0


-- 
Mailing list info: https://lists.linux.it/listinfo/ltp

      parent reply	other threads:[~2026-07-21 20:26 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-07-21 20:24 [LTP] [PATCH v3 0/3] Separate ptrace tests for CONFIG_PROC_MEM_FORCE_PTRACE Jan Polensky
2026-07-21 20:24 ` [LTP] [PATCH v3 1/3] thp04: Simplify to focus on CVE-2017-1000405 race test only Jan Polensky
2026-07-21 21:21   ` [LTP] " linuxtestproject.agent
2026-07-22  7:31     ` Andrea Cervesato via ltp
2026-07-21 20:24 ` [LTP] [PATCH v3 2/3] ptrace: add test for /proc/self/mem write rejection Jan Polensky
2026-07-21 20:24 ` 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=20260721202452.315581-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.