[LTP] [PATCH v1] Fix: dirtyc0w_shmem child process exit with error due to uninitialized lib_pid

Wei Gao wegao@suse.com
Sat May 10 22:53:24 CEST 2025


The dirtyc0w_shmem test spawns a child process using execlp. This child process then calls tst_brk(), which exits early
with a non-zero status because execlp does not inherit the parent's lib_pid variable. Consequently, the parent process
incorrectly reports an "Invalid child exit value".

This commit addresses this by ensuring the child process has access to the necessary lib_pid and main_pid by passing
them through a shared results structure. This prevents the premature exit in the child and the subsequent error report
in the parent.

Related commit:
commit a1f82704c28d9e027ca899f5ca2841e7fe49de72
lib/tst_test.c: Fix tst_brk() handling

Detail failure log:
tst_tmpdir.c:317: TINFO: Using /tmp/LTP_dirSOGVBC as tmpdir (btrfs filesystem)
tst_test.c:1938: TINFO: LTP version: 20250507.4a0e3a8fa
tst_test.c:1942: TINFO: Tested kernel: 6.4.0-150700.51-default #1 SMP Wed Apr 30 21:35:43 UTC 2025 (6930611) s390x
tst_kconfig.c:88: TINFO: Parsing kernel config '/proc/config.gz'
tst_kconfig.c:678: TINFO: CONFIG_FAULT_INJECTION kernel option detected which might slow the execution
tst_test.c:1760: TINFO: Overall timeout per run is 0h 04m 00s
dirtyc0w_shmem.c:54: TINFO: Mounting tmp_dirtyc0w_shmem to /tmp/LTP_dirSOGVBC/tmp_dirtyc0w_shmem fstyp=tmpfs flags=0
dirtyc0w_shmem_child.c:160: TCONF: System does not have userfaultfd minor fault support for shmem <<<<<<<<<< 1
tst_test.c:481: TBROK: Invalid child (8163) exit value 32 <<<<<<<< 2
dirtyc0w_shmem.c:102: TINFO: Umounting /tmp/LTP_dirSOGVBC/tmp_dirtyc0w_shmem

tmp_dirtyc0w_shmem.c call execlp to create new process run dirtyc0w_shmem_child bin.

SAFE_EXECLP("dirtyc0w_shmem_child", "dirtyc0w_shmem_child", NULL)

Within dirtyc0w_shmem_child.c trigger

tst_brk(TCONF, "System does not have userfaultfd minor fault support for shmem")

Since execlp does not inherit the parent process's variables lib_pid, so it will return TCONF(32) directly.

void tst_vbrk_(const char *file, const int lineno, int ttype, const char *fmt,
               va_list va)
{
...
        if (!lib_pid)
                exit(TTYPE_RESULT(ttype));   <<<<<
...
}

So finally captured by check_child_status report an error.

static void check_child_status(pid_t pid, int status)
{
...
        if (WEXITSTATUS(status))
                tst_brk(TBROK, "Invalid child (%i) exit value %i", pid, WEXITSTATUS(status));  <<<<
}

Signed-off-by: Wei Gao <wegao@suse.com>
---
 lib/tst_test.c | 25 ++++++++++++++-----------
 1 file changed, 14 insertions(+), 11 deletions(-)

diff --git a/lib/tst_test.c b/lib/tst_test.c
index 2bb4519dd..b666715b9 100644
--- a/lib/tst_test.c
+++ b/lib/tst_test.c
@@ -59,7 +59,7 @@ static const char *tid;
 static int iterations = 1;
 static float duration = -1;
 static float timeout_mul = -1;
-static pid_t main_pid, lib_pid;
+static pid_t lib_pid;
 static int mntpoint_mounted;
 static int ovl_mounted;
 static struct timespec tst_start_time; /* valid only for test pid */
@@ -78,6 +78,8 @@ struct results {
 	int abort_flag;
 	unsigned int runtime;
 	unsigned int overall_time;
+	pid_t lib_pid;
+	pid_t main_pid;
 };
 
 static struct results *results;
@@ -141,6 +143,7 @@ static void setup_ipc(void)
 		tst_futexes = (char *)results + sizeof(struct results);
 		tst_max_futexes = (size - sizeof(struct results))/sizeof(futex_t);
 	}
+	results->lib_pid = lib_pid;
 }
 
 static void cleanup_ipc(void)
@@ -391,7 +394,7 @@ void tst_vbrk_(const char *file, const int lineno, int ttype, const char *fmt,
 	 * If tst_brk() is called from some of the C helpers even before the
 	 * library was initialized, just exit.
 	 */
-	if (!lib_pid)
+	if (!results->lib_pid)
 		exit(TTYPE_RESULT(ttype));
 
 	update_results(TTYPE_RESULT(ttype));
@@ -402,13 +405,13 @@ void tst_vbrk_(const char *file, const int lineno, int ttype, const char *fmt,
 	 * specified but CLONE_THREAD is not. Use direct syscall to avoid
 	 * cleanup running in the child.
 	 */
-	if (tst_getpid() == main_pid)
+	if (tst_getpid() == results->main_pid)
 		do_test_cleanup();
 
 	/*
 	 * The test library process reports result statistics and exits.
 	 */
-	if (getpid() == lib_pid)
+	if (getpid() == results->lib_pid)
 		do_exit(TTYPE_RESULT(ttype));
 
 	/*
@@ -426,9 +429,9 @@ void tst_vbrk_(const char *file, const int lineno, int ttype, const char *fmt,
 		 * the main test process. That in turn triggers the code that
 		 * kills leftover children once the main test process did exit.
 		 */
-		if (main_pid && tst_getpid() != main_pid) {
+		if (results->main_pid && tst_getpid() != results->main_pid) {
 			tst_res(TINFO, "Child process reported TBROK killing the test");
-			kill(main_pid, SIGKILL);
+			kill(results->main_pid, SIGKILL);
 		}
 	}
 
@@ -1501,7 +1504,7 @@ static void do_setup(int argc, char *argv[])
 
 static void do_test_setup(void)
 {
-	main_pid = getpid();
+	results->main_pid = getpid();
 
 	if (!tst_test->all_filesystems && tst_test->skip_filesystems) {
 		long fs_type = tst_fs_type(".");
@@ -1521,7 +1524,7 @@ static void do_test_setup(void)
 	if (tst_test->setup)
 		tst_test->setup();
 
-	if (main_pid != tst_getpid())
+	if (results->main_pid != tst_getpid())
 		tst_brk(TBROK, "Runaway child in setup()!");
 
 	if (tst_test->caps)
@@ -1584,7 +1587,7 @@ static void run_tests(void)
 		heartbeat();
 		tst_test->test_all();
 
-		if (tst_getpid() != main_pid)
+		if (tst_getpid() != results->main_pid)
 			exit(0);
 
 		tst_reap_children();
@@ -1600,7 +1603,7 @@ static void run_tests(void)
 		heartbeat();
 		tst_test->test(i);
 
-		if (tst_getpid() != main_pid)
+		if (tst_getpid() != results->main_pid)
 			exit(0);
 
 		tst_reap_children();
@@ -1930,7 +1933,7 @@ void tst_run_tcases(int argc, char *argv[], struct tst_test *self)
 	tst_test = self;
 
 	do_setup(argc, argv);
-	tst_enable_oom_protection(lib_pid);
+	tst_enable_oom_protection(results->lib_pid);
 
 	SAFE_SIGNAL(SIGALRM, alarm_handler);
 	SAFE_SIGNAL(SIGUSR1, heartbeat_handler);
-- 
2.49.0



More information about the ltp mailing list