[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