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 v4 1/3] thp04: group runtime state and skip when /proc/self/mem writes are blocked
Date: Wed, 12 Aug 2026 15:28:57 +0200	[thread overview]
Message-ID: <20260812132901.202632-2-japo@linux.ibm.com> (raw)
In-Reply-To: <20260812132901.202632-1-japo@linux.ibm.com>

Move thp04 runtime state from static globals into a heap-allocated
struct thp_state. Group write_thp, read_thp, write_ptr, read_ptr,
thp_size, writefd, readfd and fzsync_pair in one object instead of
spreading them across file-scope variables.

Also add a real /proc/self/mem write probe in setup(). If the write
fails with EIO, report TCONF and skip the test cleanly on kernels where
CONFIG_PROC_MEM_FORCE_PTRACE or CONFIG_PROC_MEM_NO_FORCE blocks
unforced /proc/self/mem self-writes.

Signed-off-by: Jan Polensky <japo@linux.ibm.com>
---
 testcases/kernel/mem/thp/thp04.c | 179 ++++++++++++++++++++++---------
 1 file changed, 127 insertions(+), 52 deletions(-)

diff --git a/testcases/kernel/mem/thp/thp04.c b/testcases/kernel/mem/thp/thp04.c
index a4b2fa7bc629..1fe75c3608f0 100644
--- a/testcases/kernel/mem/thp/thp04.c
+++ b/testcases/kernel/mem/thp/thp04.c
@@ -3,7 +3,7 @@
  * Copyright (c) 2019 SUSE LLC <mdoucha@suse.cz>
  */
 
-/*
+/*\
  * CVE-2017-1000405
  *
  * Check for the Huge Dirty Cow vulnerability which allows a userspace process
@@ -21,27 +21,40 @@
  * 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").
+ *
+ * This test uses direct writes to /proc/self/mem with fuzzy-sync to trigger
+ * the race condition. The test verifies that forced writes work before proceeding.
+ * If forced writes are blocked by kernel configuration, the test reports TCONF.
+ * For ptrace-based /proc/pid/mem testing, see testcases/kernel/syscalls/ptrace/.
  */
 
-#include "tst_test.h"
+#include <string.h>
+
 #include "lapi/mmap.h"
 #include "tst_fuzzy_sync.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;
+struct thp_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 void *alloc_zero_page(void *baseaddr)
+static struct thp_state *state;
+
+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 +80,68 @@ 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 thp_cleanup(void)
+{
+	if (!state)
+		return;
+
+	tst_fzsync_pair_cleanup(&state->fzsync_pair);
+
+	if (state->readfd >= 0)
+		SAFE_CLOSE(state->readfd);
+
+	if (state->writefd >= 0)
+		SAFE_CLOSE(state->writefd);
+
+	if (state->read_thp)
+		SAFE_MUNMAP(state->read_thp, state->thp_size);
+
+	if (state->write_thp)
+		SAFE_MUNMAP(state->write_thp, state->thp_size);
+}
+
+static void thp_setup(void)
 {
 	size_t i;
 
-	thp_size = tst_get_hugepage_size();
+	state->thp_size = tst_get_hugepage_size();
 
-	if (!thp_size)
+	if (!state->thp_size)
 		tst_brk(TCONF, "Kernel does not support huge pages");
 
-	write_thp = alloc_zero_page((void *)thp_size);
+	state->write_thp = alloc_zero_page((void *)state->thp_size,
+		state->thp_size);
 
-	for (i = 0; i < thp_size; i++) {
-		if (write_thp[i])
+	for (i = 0; i < state->thp_size; i++) {
+		if (state->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);
+	state->read_thp = alloc_zero_page(state->write_thp + 2 * state->thp_size,
+		state->thp_size);
+	state->write_ptr = (int *)(state->write_thp + state->thp_size - sizeof(int));
+	state->read_ptr = (int *)(state->read_thp + state->thp_size - sizeof(int));
 
-	fzsync_pair.exec_loops = 100000;
-	tst_fzsync_pair_init(&fzsync_pair);
+	state->writefd = SAFE_OPEN("/proc/self/mem", O_RDWR);
+	state->readfd = SAFE_OPEN("/proc/self/mem", O_RDWR);
+	state->fzsync_pair.exec_loops = 100000;
+	tst_fzsync_pair_init(&state->fzsync_pair);
 }
 
 static void *thread_run(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(&state->fzsync_pair)) {
+		tst_fzsync_start_race_b(&state->fzsync_pair);
+		madvise(state->write_thp, state->thp_size, MADV_DONTNEED);
+		memcpy(&c, state->write_ptr, sizeof(c));
+		SAFE_LSEEK(state->readfd, (off_t)state->write_ptr, SEEK_SET);
+		SAFE_READ(1, state->readfd, &c, sizeof(int));
+		tst_fzsync_end_race_b(&state->fzsync_pair);
 		/* Wait for dirty page handling before next madvise() */
 		usleep(10);
 	}
@@ -119,20 +153,20 @@ static void run(void)
 {
 	int c = 0xdeadbeef;
 
-	tst_fzsync_pair_reset(&fzsync_pair, thread_run);
+	tst_fzsync_pair_reset(&state->fzsync_pair, thread_run);
 
-	while (tst_fzsync_run_a(&fzsync_pair)) {
+	while (tst_fzsync_run_a(&state->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(&state->fzsync_pair);
+		SAFE_LSEEK(state->writefd, (off_t)state->write_ptr, SEEK_SET);
+		madvise(state->write_thp, state->thp_size, MADV_DONTNEED);
+		SAFE_WRITE(SAFE_WRITE_ALL, state->writefd, &c, sizeof(int));
+		tst_fzsync_end_race_a(&state->fzsync_pair);
 
 		/* Check the other huge zero page for pollution */
-		madvise(read_thp, thp_size, MADV_DONTNEED);
+		madvise(state->read_thp, state->thp_size, MADV_DONTNEED);
 
-		if (*read_ptr != 0) {
+		if (*state->read_ptr != 0) {
 			tst_res(TFAIL, "Huge zero page was polluted");
 			return;
 		}
@@ -141,20 +175,61 @@ static void run(void)
 	tst_res(TPASS, "Huge zero page is still clean");
 }
 
+static void setup(void)
+{
+	int test_val = 0xdeadbeef;
+
+	state = SAFE_MMAP(NULL, sizeof(*state), PROT_READ | PROT_WRITE,
+		MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+	memset(state, 0, sizeof(*state));
+	state->writefd = -1;
+	state->readfd = -1;
+
+	thp_setup();
+
+	if (*state->write_ptr != 0) {
+		tst_brk(TBROK, "write_ptr not zero initially: 0x%x",
+			*state->write_ptr);
+	}
+
+	SAFE_MPROTECT((void *)state->write_thp, state->thp_size,
+		PROT_READ | PROT_WRITE);
+	*state->write_ptr = 0x12345678;
+	SAFE_MPROTECT((void *)state->write_thp, state->thp_size, PROT_READ);
+
+	SAFE_LSEEK(state->writefd, (off_t)state->write_ptr, SEEK_SET);
+
+	TEST(write(state->writefd, &test_val, sizeof(test_val)));
+
+	if (TST_RET == -1 && TST_ERR == EIO) {
+		tst_brk(TCONF,
+			"Direct writes to /proc/self/mem disabled "
+			"(CONFIG_PROC_MEM_FORCE_PTRACE=y)");
+	}
+
+	if (TST_RET == -1)
+		tst_brk(TBROK | TTERRNO, "probe write to /proc/self/mem failed");
+
+	if (TST_RET != sizeof(test_val)) {
+		tst_brk(TBROK, "short write to /proc/self/mem: %ld bytes (expected %zu)",
+			TST_RET, sizeof(test_val));
+	}
+
+	if (*state->write_ptr != test_val) {
+		tst_brk(TBROK,
+			"write to /proc/self/mem did not modify memory: "
+			"expected 0x%x, got 0x%x", test_val, *state->write_ptr);
+	}
+
+	*state->write_ptr = 0;
+}
+
 static void cleanup(void)
 {
-	tst_fzsync_pair_cleanup(&fzsync_pair);
+	thp_cleanup();
 
-	if (readfd >= 0)
-		SAFE_CLOSE(readfd);
-
-	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 (state)
+		SAFE_MUNMAP(state, sizeof(*state));
 }
 
 static struct tst_test test = {
-- 
2.55.0


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

  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 ` Jan Polensky [this message]
2026-08-12 15:01   ` [LTP] thp04: group runtime state and skip when /proc/self/mem writes are blocked 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 ` [LTP] [PATCH v4 3/3] ptrace: add test for /proc/pid/mem writes under ptrace Jan Polensky

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-2-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.