* [LTP] [PATCH v4 1/1] thp04: Add ptrace support for CONFIG_PROC_MEM_FORCE_PTRACE=y
@ 2026-07-07 16:00 Jan Polensky
2026-07-07 16:43 ` [LTP] " linuxtestproject.agent
0 siblings, 1 reply; 3+ messages in thread
From: Jan Polensky @ 2026-07-07 16:00 UTC (permalink / raw)
To: ltp; +Cc: Li Wang
When CONFIG_PROC_MEM_FORCE_PTRACE=y is set, direct writes to
/proc/self/mem are disabled and require ptrace attachment. This patch
adds support for this configuration by implementing a ptrace-based test
mode that writes to /proc/pid/mem while the tracee is stopped.
The implementation uses a write-stop-continue cycle where the parent
writes to /proc/pid/mem while the tracee is stopped, then continues
the tracee to run madvise operations. This serialization is required
because /proc/pid/mem writes need PTRACE_MODE_ATTACH (tracee must be
in stopped state).
However, this serialization eliminates the concurrent execution window
that CVE-2017-1000405 exploits. The ptrace mode tests kernel handling
of writes to stopped processes, but does not test the actual race
condition between concurrent writes and MADV_DONTNEED operations.
The test now:
- Probes /proc/self/mem write capability at setup
- Falls back to ptrace mode if direct writes fail with EIO
- Respects proc_mem.force_override kernel cmdline parameter
- Documents the race limitation in both modes
- Supports re-invocation with -i N by spawning fresh tracee per run()
- Restores lapi/mmap.h for MADV_* fallback definitions
Signed-off-by: Jan Polensky <japo@linux.ibm.com>
---
Link: https://lore.kernel.org/all/20260701111242.97520-1-japo@linux.ibm.com/
Changes since v3:
- Restore lapi/mmap.h include for MADV_* fallback definitions (Andrea Cervesato)
- Move tracee lifecycle into run() to support re-invocation with -i N (Andrea Cervesato)
* Each run() invocation spawns fresh tracee via setup_ptrace_tracee()
* Tracee runs infinite loop, parent controls termination
* After 1000 iterations: PTRACE_DETACH → SIGKILL → SAFE_WAITPID
* cleanup() only handles error cases (uses tst_reap_children())
- Validated on s390x with -i 2 and -i 5 (both PASS)
Changes since v2:
- Added documentation clarifying that ptrace mode serialization eliminates the CVE race
- Fixed missing newline at end of file
- Addressed review feedback on race condition testing limitations
Changes since v1:
- detect proc_mem.force_override / kernel config instead of relying only on a probe write
- fix kernel parameter naming per review
- address feedback from Cyril Hrubis in previous thread
testcases/kernel/mem/thp/thp04.c | 375 ++++++++++++++++++++++++++-----
1 file changed, 323 insertions(+), 52 deletions(-)
diff --git a/testcases/kernel/mem/thp/thp04.c b/testcases/kernel/mem/thp/thp04.c
index 16d766c349b7..d520c0f630d2 100644
--- a/testcases/kernel/mem/thp/thp04.c
+++ b/testcases/kernel/mem/thp/thp04.c
@@ -21,27 +21,67 @@
* On old kernel such as 4.9, it has fixed the Dirty Cow bug but a similar check
* in huge_memory.c was forgotten. As a result, remote memory writes to ro regions
* of memory backed by transparent huge pages cause an infinite loop in the kernel.
- * While in this state the process is stil SIGKILLable, but little else works.
+ * While in this state the process is still SIGKILLable, but little else works.
* It is also a regression test about kernel
* commit 8310d48b125d("huge_memory.c: respect FOLL_FORCE/FOLL_COW for thp").
+ *
+ * Test Modes:
+ *
+ * PROC_MEM_ALWAYS: Direct writes to /proc/self/mem (default on most systems)
+ * - Child process writes to its own memory via /proc/self/mem
+ * - Concurrent execution: writes race with madvise() calls
+ * - Uses fuzzy-sync to maximize race window
+ *
+ * PROC_MEM_PTRACE: Ptrace-based writes to /proc/pid/mem (CONFIG_PROC_MEM_FORCE_PTRACE=y)
+ * - Parent writes to tracee's memory via /proc/pid/mem
+ * - Write-stop-continue cycle: tracee must be STOPPED for writes to succeed
+ * - Alternating execution: parent writes → tracee runs madvise → tracee stops → repeat
+ * - Required because /proc/pid/mem writes need PTRACE_MODE_ATTACH (stopped state)
+ * - Note: Serialization eliminates the race; tests write functionality, not the CVE race
*/
-#include "tst_test.h"
+#include <signal.h>
+#include <sys/ptrace.h>
+#include <sys/wait.h>
+
#include "lapi/mmap.h"
+
+/* PTRACE mode is serialized and therefore uses fewer iterations. */
+#define PTRACE_TEST_ITERATIONS 1000
+#include "tst_checkpoint.h"
#include "tst_fuzzy_sync.h"
+#include "tst_kconfig.h"
+#include "tst_test.h"
-static char *write_thp, *read_thp;
-static int *write_ptr, *read_ptr;
-static size_t thp_size;
-static int writefd = -1, readfd = -1;
-static struct tst_fzsync_pair fzsync_pair;
+enum proc_mem_mode {
+ PROC_MEM_ALWAYS,
+ PROC_MEM_PTRACE,
+ PROC_MEM_NEVER,
+};
-static void *alloc_zero_page(void *baseaddr)
+struct child_state {
+ char *write_thp;
+ char *read_thp;
+ int *write_ptr;
+ int *read_ptr;
+ size_t thp_size;
+ int writefd;
+ int readfd;
+ struct tst_fzsync_pair fzsync_pair;
+};
+
+static pid_t tracee_pid;
+/* Mode requested by kernel command line parsing. Do not mutate during probing. */
+static enum proc_mem_mode user_mode = PROC_MEM_ALWAYS;
+/* Runtime mode selected after checking whether direct /proc/self/mem writes work. */
+static enum proc_mem_mode effective_mode = PROC_MEM_ALWAYS;
+static struct child_state *child;
+
+static void *alloc_zero_page(void *baseaddr, size_t thp_size)
{
int i;
void *ret;
- /* Find aligned chunk of address space. MAP_HUGETLB doesn't work. */
for (i = 0; i < 16; i++, baseaddr += thp_size) {
ret = mmap(baseaddr, thp_size, PROT_READ,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
@@ -67,47 +107,78 @@ static void *alloc_zero_page(void *baseaddr)
}
tst_brk(TBROK, "Cannot map huge zero page near the specified address");
- return NULL; /* Silence compiler warning */
+ return NULL;
}
-static void setup(void)
+static void child_cleanup(void)
+{
+ if (!child)
+ return;
+
+ if (effective_mode == PROC_MEM_ALWAYS)
+ tst_fzsync_pair_cleanup(&child->fzsync_pair);
+
+ if (child->readfd >= 0)
+ SAFE_CLOSE(child->readfd);
+
+ if (child->writefd >= 0)
+ SAFE_CLOSE(child->writefd);
+
+ if (child->read_thp)
+ SAFE_MUNMAP(child->read_thp, child->thp_size);
+
+ if (child->write_thp)
+ SAFE_MUNMAP(child->write_thp, child->thp_size);
+}
+
+static void child_setup(void)
{
size_t i;
- thp_size = tst_get_hugepage_size();
+ child->thp_size = tst_get_hugepage_size();
- if (!thp_size)
+ if (!child->thp_size)
tst_brk(TCONF, "Kernel does not support huge pages");
- write_thp = alloc_zero_page((void *)thp_size);
+ child->write_thp = alloc_zero_page((void *)child->thp_size,
+ child->thp_size);
- for (i = 0; i < thp_size; i++) {
- if (write_thp[i])
+ for (i = 0; i < child->thp_size; i++) {
+ if (child->write_thp[i])
tst_brk(TCONF, "Huge zero page is pre-polluted");
}
- /* leave a hole between read and write THP to prevent merge */
- read_thp = alloc_zero_page(write_thp + 2 * thp_size);
- write_ptr = (int *)(write_thp + thp_size - sizeof(int));
- read_ptr = (int *)(read_thp + thp_size - sizeof(int));
- writefd = SAFE_OPEN("/proc/self/mem", O_RDWR);
- readfd = SAFE_OPEN("/proc/self/mem", O_RDWR);
+ child->read_thp = alloc_zero_page(child->write_thp + 2 * child->thp_size,
+ child->thp_size);
+ /* write_ptr points to last int in write_thp page */
+ child->write_ptr = (int *)(child->write_thp + child->thp_size - sizeof(int));
+ /* read_ptr points to last int in read_thp page */
+ child->read_ptr = (int *)(child->read_thp + child->thp_size - sizeof(int));
- fzsync_pair.exec_loops = 100000;
- tst_fzsync_pair_init(&fzsync_pair);
+ /* In ptrace mode, parent opens /proc/<pid>/mem, not child */
+ if (effective_mode == PROC_MEM_ALWAYS) {
+ child->writefd = SAFE_OPEN("/proc/self/mem", O_RDWR);
+ child->readfd = SAFE_OPEN("/proc/self/mem", O_RDWR);
+ child->fzsync_pair.exec_loops = 100000;
+ tst_fzsync_pair_init(&child->fzsync_pair);
+ } else {
+ child->writefd = -1;
+ child->readfd = -1;
+ }
}
-static void *thread_run(void *arg)
+/* Preserve the original fuzzy-sync race: this is the actual CVE trigger mechanism. */
+static void *thread_run_always(void *arg)
{
int c;
- while (tst_fzsync_run_b(&fzsync_pair)) {
- tst_fzsync_start_race_b(&fzsync_pair);
- madvise(write_thp, thp_size, MADV_DONTNEED);
- memcpy(&c, write_ptr, sizeof(c));
- SAFE_LSEEK(readfd, (off_t)write_ptr, SEEK_SET);
- SAFE_READ(1, readfd, &c, sizeof(int));
- tst_fzsync_end_race_b(&fzsync_pair);
+ while (tst_fzsync_run_b(&child->fzsync_pair)) {
+ tst_fzsync_start_race_b(&child->fzsync_pair);
+ madvise(child->write_thp, child->thp_size, MADV_DONTNEED);
+ memcpy(&c, child->write_ptr, sizeof(c));
+ SAFE_LSEEK(child->readfd, (off_t)child->write_ptr, SEEK_SET);
+ SAFE_READ(1, child->readfd, &c, sizeof(int));
+ tst_fzsync_end_race_b(&child->fzsync_pair);
/* Wait for dirty page handling before next madvise() */
usleep(10);
}
@@ -115,24 +186,24 @@ static void *thread_run(void *arg)
return arg;
}
-static void run(void)
+static void run_proc_mem_always_race(void)
{
int c = 0xdeadbeef;
- tst_fzsync_pair_reset(&fzsync_pair, thread_run);
+ tst_fzsync_pair_reset(&child->fzsync_pair, thread_run_always);
- while (tst_fzsync_run_a(&fzsync_pair)) {
+ while (tst_fzsync_run_a(&child->fzsync_pair)) {
/* Write into the main huge page */
- tst_fzsync_start_race_a(&fzsync_pair);
- SAFE_LSEEK(writefd, (off_t)write_ptr, SEEK_SET);
- madvise(write_thp, thp_size, MADV_DONTNEED);
- SAFE_WRITE(SAFE_WRITE_ALL, writefd, &c, sizeof(int));
- tst_fzsync_end_race_a(&fzsync_pair);
+ tst_fzsync_start_race_a(&child->fzsync_pair);
+ SAFE_LSEEK(child->writefd, (off_t)child->write_ptr, SEEK_SET);
+ madvise(child->write_thp, child->thp_size, MADV_DONTNEED);
+ SAFE_WRITE(SAFE_WRITE_ALL, child->writefd, &c, sizeof(int));
+ tst_fzsync_end_race_a(&child->fzsync_pair);
/* Check the other huge zero page for pollution */
- madvise(read_thp, thp_size, MADV_DONTNEED);
+ madvise(child->read_thp, child->thp_size, MADV_DONTNEED);
- if (*read_ptr != 0) {
+ if (*child->read_ptr != 0) {
tst_res(TFAIL, "Huge zero page was polluted");
return;
}
@@ -141,20 +212,218 @@ static void run(void)
tst_res(TPASS, "Huge zero page is still clean");
}
+static void tracee_main(void)
+{
+ /* child struct is already mapped by parent before fork */
+ child->writefd = -1;
+ child->readfd = -1;
+
+ child_setup();
+
+ TST_CHECKPOINT_WAKE(0);
+
+ /* Parent will PTRACE_CONT us when ready - no checkpoint needed */
+
+ /*
+ * Ptrace mode: writes and madvise are serialized (no race).
+ * Tests kernel handling of writes to stopped processes, not the CVE race.
+ * The stop/continue cycle is required because /proc/pid/mem writes need
+ * PTRACE_MODE_ATTACH (tracee must be in stopped state).
+ *
+ * Run indefinitely - parent controls termination via PTRACE_DETACH.
+ */
+ for (;;) {
+ madvise(child->write_thp, child->thp_size, MADV_DONTNEED);
+ madvise(child->read_thp, child->thp_size, MADV_DONTNEED);
+
+ if (*child->read_ptr != 0) {
+ tst_res(TFAIL, "Huge zero page was polluted");
+ child_cleanup();
+ exit(1);
+ }
+
+ /* Stop self to let parent write next iteration */
+ raise(SIGSTOP);
+ }
+}
+
+static void setup_ptrace_tracee(void)
+{
+ int status;
+
+ /* Test-loop state is initialized once and read-only afterwards. */
+ child = SAFE_MMAP(NULL, sizeof(*child), PROT_READ | PROT_WRITE,
+ MAP_SHARED | MAP_ANONYMOUS, -1, 0);
+ memset(child, 0, sizeof(*child));
+ child->writefd = -1;
+ child->readfd = -1;
+
+ tracee_pid = SAFE_FORK();
+ if (!tracee_pid) {
+ tracee_main();
+ exit(0);
+ }
+
+ TST_CHECKPOINT_WAIT(0);
+
+ SAFE_PTRACE(PTRACE_SEIZE, tracee_pid, NULL, NULL);
+
+ /*
+ * Tracee will run first iteration (madvise + check) and self-stop with raise(SIGSTOP).
+ * Wait for that SIGSTOP - this is iteration 0.
+ */
+ SAFE_WAITPID(tracee_pid, &status, 0);
+
+ if (!WIFSTOPPED(status) || WSTOPSIG(status) != SIGSTOP)
+ tst_brk(TBROK, "Tracee did not self-stop with SIGSTOP: %s",
+ tst_strstatus(status));
+}
+
+static void setup(void)
+{
+ int test_val = 0;
+ int explicit_mode = 0;
+
+ static struct tst_kcmdline_var params[] = {
+ TST_KCMDLINE_INIT("proc_mem.force_override"),
+ };
+
+ tst_kcmdline_parse(params, ARRAY_SIZE(params));
+
+ if (params[0].found) {
+ explicit_mode = 1;
+
+ if (!strcmp(params[0].value, "always")) {
+ user_mode = PROC_MEM_ALWAYS;
+ } else if (!strcmp(params[0].value, "ptrace")) {
+ user_mode = PROC_MEM_PTRACE;
+ } else {
+ user_mode = PROC_MEM_NEVER;
+ tst_brk(TCONF,
+ "Writes to /proc/self/mem disabled on kernel cmdline");
+ }
+ }
+
+ effective_mode = user_mode;
+
+ /* Probe direct /proc/self/mem writes only if not explicitly set to ptrace */
+ if (!explicit_mode || user_mode == PROC_MEM_ALWAYS) {
+ child = SAFE_MMAP(NULL, sizeof(*child), PROT_READ | PROT_WRITE,
+ MAP_SHARED | MAP_ANONYMOUS, -1, 0);
+ memset(child, 0, sizeof(*child));
+ child->writefd = -1;
+ child->readfd = -1;
+
+ child_setup();
+
+ TEST(lseek(child->writefd, (off_t)child->write_ptr, SEEK_SET));
+ if (TST_RET == -1)
+ tst_brk(TBROK | TTERRNO, "lseek on /proc/self/mem failed");
+
+ TEST(write(child->writefd, &test_val, sizeof(test_val)));
+
+ if (TST_RET == sizeof(test_val)) {
+ /* Direct write succeeded */
+ effective_mode = PROC_MEM_ALWAYS;
+ return;
+ }
+
+ if (TST_RET == -1 && TST_ERR != EIO)
+ tst_brk(TBROK | TTERRNO, "test write to /proc/self/mem failed");
+
+ /* /proc/self/mem write failed, cleanup and try ptrace mode */
+ child_cleanup();
+ SAFE_MUNMAP(child, sizeof(*child));
+ child = NULL;
+
+ if (explicit_mode && user_mode == PROC_MEM_ALWAYS)
+ tst_brk(TCONF,
+ "Writes to /proc/self/mem disabled despite always mode");
+
+ effective_mode = PROC_MEM_PTRACE;
+ }
+
+ /* Ptrace mode: tracee will be spawned in run() for each iteration */
+ if (effective_mode == PROC_MEM_PTRACE)
+ return;
+
+ tst_brk(TCONF, "Writes to /proc/self/mem disabled in kernel policy");
+}
+
+static void run_proc_mem_ptrace_race(void)
+{
+ char path[64];
+ int writefd;
+ int c = 0xdeadbeef;
+ int i;
+ int status;
+
+ snprintf(path, sizeof(path), "/proc/%d/mem", tracee_pid);
+ writefd = SAFE_OPEN(path, O_RDWR);
+
+ /*
+ * Tracee is already in SIGSTOP state from setup_ptrace_tracee().
+ * Write-stop-continue cycle: tracee is stopped, ready for writes.
+ */
+ for (i = 0; i < PTRACE_TEST_ITERATIONS; i++) {
+ /* Write to /proc/pid/mem while tracee is stopped */
+ SAFE_LSEEK(writefd, (off_t)child->write_ptr, SEEK_SET);
+ SAFE_WRITE(SAFE_WRITE_ALL, writefd, &c, sizeof(int));
+
+ /* Let tracee run one iteration (madvise + check + raise(SIGSTOP)) */
+ SAFE_PTRACE(PTRACE_CONT, tracee_pid, NULL, NULL);
+
+ /* Wait for tracee to stop itself with raise(SIGSTOP) */
+ SAFE_WAITPID(tracee_pid, &status, 0);
+
+ if (!WIFSTOPPED(status) || WSTOPSIG(status) != SIGSTOP)
+ tst_brk(TBROK, "Tracee did not stop with SIGSTOP: %s",
+ tst_strstatus(status));
+ }
+
+ SAFE_CLOSE(writefd);
+
+ /* Test passed - huge zero page remained clean through PTRACE_TEST_ITERATIONS iterations */
+ tst_res(TPASS, "Huge zero page is still clean");
+
+ /* Detach and terminate tracee */
+ SAFE_PTRACE(PTRACE_DETACH, tracee_pid, NULL, NULL);
+ SAFE_WAITPID(tracee_pid, &status, WNOHANG);
+ SAFE_KILL(tracee_pid, SIGKILL);
+ SAFE_WAITPID(tracee_pid, &status, 0);
+ tracee_pid = 0;
+}
+
+static void run(void)
+{
+ if (effective_mode == PROC_MEM_ALWAYS) {
+ run_proc_mem_always_race();
+ } else if (effective_mode == PROC_MEM_PTRACE) {
+ /* Spawn fresh tracee for each iteration */
+ setup_ptrace_tracee();
+ run_proc_mem_ptrace_race();
+ } else {
+ tst_brk(TBROK, "Unknown proc mem mode");
+ }
+}
+
static void cleanup(void)
{
- tst_fzsync_pair_cleanup(&fzsync_pair);
+ if (tracee_pid > 0) {
+ /*
+ * Tracee is normally reaped in run_proc_mem_ptrace_race().
+ * Only kill if still alive (e.g., test aborted with TBROK).
+ */
+ if (kill(tracee_pid, 0) == 0) {
+ SAFE_KILL(tracee_pid, SIGKILL);
+ tst_reap_children();
+ }
+ }
- if (readfd >= 0)
- SAFE_CLOSE(readfd);
+ child_cleanup();
- if (writefd >= 0)
- SAFE_CLOSE(writefd);
-
- if (read_thp)
- SAFE_MUNMAP(read_thp, thp_size);
- if (write_thp)
- SAFE_MUNMAP(write_thp, thp_size);
+ if (child)
+ SAFE_MUNMAP(child, sizeof(*child));
}
static struct tst_test test = {
@@ -162,6 +431,8 @@ static struct tst_test test = {
.setup = setup,
.cleanup = cleanup,
.runtime = 150,
+ .forks_child = 1,
+ .needs_checkpoints = 1,
.tags = (const struct tst_tag[]) {
{"linux-git", "a8f97366452e"},
{"linux-git", "8310d48b125d"},
--
2.54.0
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply related [flat|nested] 3+ messages in thread
* Re: [LTP] thp04: Add ptrace support for CONFIG_PROC_MEM_FORCE_PTRACE=y
2026-07-07 16:00 [LTP] [PATCH v4 1/1] thp04: Add ptrace support for CONFIG_PROC_MEM_FORCE_PTRACE=y Jan Polensky
@ 2026-07-07 16:43 ` linuxtestproject.agent
2026-07-09 14:35 ` Jan Polensky
0 siblings, 1 reply; 3+ messages in thread
From: linuxtestproject.agent @ 2026-07-07 16:43 UTC (permalink / raw)
To: Jan Polensky; +Cc: ltp
Hi Jan,
On Tue, 7 Jul 2026 18:00:22 +0200, Jan Polensky wrote:
> thp04: Add ptrace support for CONFIG_PROC_MEM_FORCE_PTRACE=y
> - Restores lapi/mmap.h for MADV_* fallback definitions
The diff shows lapi/mmap.h was already present in the original file
and was not removed or re-added by this patch. "Restores" is
inaccurate here.
> +static void setup_ptrace_tracee(void)
> +{
> + int status;
> +
> + /* Test-loop state is initialized once and read-only afterwards. */
> + child = SAFE_MMAP(NULL, sizeof(*child), PROT_READ | PROT_WRITE,
> + MAP_SHARED | MAP_ANONYMOUS, -1, 0);
> + memset(child, 0, sizeof(*child));
> + child->writefd = -1;
> + child->readfd = -1;
> +
> + tracee_pid = SAFE_FORK();
> + if (!tracee_pid) {
> + tracee_main();
> + exit(0);
> + }
> +
> + TST_CHECKPOINT_WAIT(0);
> +
> + SAFE_PTRACE(PTRACE_SEIZE, tracee_pid, NULL, NULL);
> +
> + /*
> + * Tracee will run first iteration (madvise + check) and self-stop
> + * with raise(SIGSTOP). Wait for that SIGSTOP - this is iteration 0.
> + */
> + SAFE_WAITPID(tracee_pid, &status, 0);
There is a race here. After TST_CHECKPOINT_WAKE(0) the tracee runs the
for(;;) body immediately and calls raise(SIGSTOP). If that raise()
fires before PTRACE_SEIZE executes in the parent, the SIGSTOP causes a
job-control stop (T state), not a ptrace-stop. PTRACE_SEIZE on an
already-T-stopped process does not generate a new ptrace event.
SAFE_WAITPID(..., 0) without WUNTRACED cannot see a job-control stop,
so the parent hangs indefinitely.
The comment "Parent will PTRACE_CONT us when ready - no checkpoint
needed" assumes PTRACE_SEIZE always wins the race, which is not
guaranteed, especially on a loaded system.
A second checkpoint (wake from parent after PTRACE_SEIZE completes,
wait in tracee before the first loop iteration) would close the window.
> +/*
> * CVE-2017-1000405
> *
> [...]
> + * Test Modes:
> + *
> + * PROC_MEM_ALWAYS: Direct writes to /proc/self/mem ...
The patch modifies this description block. The required opening for an
LTP high-level description block is /*\ (with a backslash), not /*.
The block is exported to the test catalog and must use the /*\ form.
Verdict - Needs revision
---
Note:
The agent can sometimes produce false positives although often its
findings are genuine. If you find issues with the review, please
comment this email or ignore the suggestions.
Regards,
LTP AI Reviewer
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [LTP] thp04: Add ptrace support for CONFIG_PROC_MEM_FORCE_PTRACE=y
2026-07-07 16:43 ` [LTP] " linuxtestproject.agent
@ 2026-07-09 14:35 ` Jan Polensky
0 siblings, 0 replies; 3+ messages in thread
From: Jan Polensky @ 2026-07-09 14:35 UTC (permalink / raw)
To: linuxtestproject.agent; +Cc: ltp
On Tue, Jul 07, 2026 at 04:43:04PM +0000, linuxtestproject.agent@gmail.com wrote:
> Hi Jan,
>
> On Tue, 7 Jul 2026 18:00:22 +0200, Jan Polensky wrote:
> > thp04: Add ptrace support for CONFIG_PROC_MEM_FORCE_PTRACE=y
>
> > - Restores lapi/mmap.h for MADV_* fallback definitions
>
> The diff shows lapi/mmap.h was already present in the original file
> and was not removed or re-added by this patch. "Restores" is
> inaccurate here.
>
> > +static void setup_ptrace_tracee(void)
> > +{
> > + int status;
> > +
> > + /* Test-loop state is initialized once and read-only afterwards. */
> > + child = SAFE_MMAP(NULL, sizeof(*child), PROT_READ | PROT_WRITE,
> > + MAP_SHARED | MAP_ANONYMOUS, -1, 0);
> > + memset(child, 0, sizeof(*child));
> > + child->writefd = -1;
> > + child->readfd = -1;
> > +
> > + tracee_pid = SAFE_FORK();
> > + if (!tracee_pid) {
> > + tracee_main();
> > + exit(0);
> > + }
> > +
> > + TST_CHECKPOINT_WAIT(0);
> > +
> > + SAFE_PTRACE(PTRACE_SEIZE, tracee_pid, NULL, NULL);
> > +
> > + /*
> > + * Tracee will run first iteration (madvise + check) and self-stop
> > + * with raise(SIGSTOP). Wait for that SIGSTOP - this is iteration 0.
> > + */
> > + SAFE_WAITPID(tracee_pid, &status, 0);
>
> There is a race here. After TST_CHECKPOINT_WAKE(0) the tracee runs the
> for(;;) body immediately and calls raise(SIGSTOP). If that raise()
> fires before PTRACE_SEIZE executes in the parent, the SIGSTOP causes a
> job-control stop (T state), not a ptrace-stop. PTRACE_SEIZE on an
> already-T-stopped process does not generate a new ptrace event.
>
> SAFE_WAITPID(..., 0) without WUNTRACED cannot see a job-control stop,
> so the parent hangs indefinitely.
>
> The comment "Parent will PTRACE_CONT us when ready - no checkpoint
> needed" assumes PTRACE_SEIZE always wins the race, which is not
> guaranteed, especially on a loaded system.
>
> A second checkpoint (wake from parent after PTRACE_SEIZE completes,
> wait in tracee before the first loop iteration) would close the window.
>
> > +/*
> > * CVE-2017-1000405
> > *
> > [...]
> > + * Test Modes:
> > + *
> > + * PROC_MEM_ALWAYS: Direct writes to /proc/self/mem ...
>
> The patch modifies this description block. The required opening for an
> LTP high-level description block is /*\ (with a backslash), not /*.
> The block is exported to the test catalog and must use the /*\ form.
>
> Verdict - Needs revision
>
> ---
> Note:
>
> The agent can sometimes produce false positives although often its
> findings are genuine. If you find issues with the review, please
> comment this email or ignore the suggestions.
>
> Regards,
> LTP AI Reviewer
Send v5 soon.
Thanks
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-07-09 14:35 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-07 16:00 [LTP] [PATCH v4 1/1] thp04: Add ptrace support for CONFIG_PROC_MEM_FORCE_PTRACE=y Jan Polensky
2026-07-07 16:43 ` [LTP] " linuxtestproject.agent
2026-07-09 14:35 ` Jan Polensky
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.