Merge patch series "selftests/namespaces: Fix test hangs and false failures"

Ricardo B. Marlière <rbm@suse.com> says:

This series addresses three reliability problems in the namespaces selftest
suite that cause tests to hang or report incorrect results.

The first patch fixes a hang in nsid_test where the grandchild process is
not reaped during fixture teardown, leaving it alive and holding the TAP
pipe write-end open so the test runner blocks indefinitely waiting for EOF.

The second and third patches fix two problems in listns_efault_test: a
waitpid(-1) race that can cause the iterator child to be consumed during
namespace cleanup (leading to an indefinite block on the subsequent targeted
waitpid), and a false FAIL verdict on kernels that do not implement listns()
(the EFAULT tests should SKIP in that case, consistent with every other
listns test that already handles ENOSYS correctly).

Link: https://patch.msgid.link/20260407-selftests-namespaces_fixes-v1-0-59109909d88b@suse.com
Signed-off-by: Christian Brauner <brauner@kernel.org>
This commit is contained in:
Christian Brauner 2026-04-09 15:06:07 +02:00
commit a243a7b02e
No known key found for this signature in database
GPG Key ID: 91C61BC06578DCA2
2 changed files with 40 additions and 7 deletions

View File

@ -38,7 +38,7 @@ TEST(listns_partial_fault_with_ns_cleanup)
__u64 *ns_ids;
ssize_t ret;
long page_size;
pid_t pid, iter_pid;
pid_t pid, iter_pid, ns_pids[5];
int pidfds[5];
int sv[5][2];
int iter_pidfd;
@ -114,6 +114,7 @@ TEST(listns_partial_fault_with_ns_cleanup)
pid = create_child(&pidfds[i], CLONE_NEWNS);
ASSERT_NE(pid, -1);
ns_pids[i] = pid;
if (pid == 0) {
close(sv[i][0]); /* Close parent end */
@ -164,7 +165,7 @@ TEST(listns_partial_fault_with_ns_cleanup)
/* Wait for all mount namespace children to exit and cleanup */
for (i = 0; i < 5; i++) {
waitpid(-1, NULL, 0);
waitpid(ns_pids[i], NULL, 0);
close(sv[i][0]);
close(pidfds[i]);
}
@ -175,6 +176,12 @@ TEST(listns_partial_fault_with_ns_cleanup)
ASSERT_EQ(ret, iter_pid);
close(iter_pidfd);
/* If listns() is not supported the iterator exits cleanly via ENOSYS */
if (WIFEXITED(status) && WEXITSTATUS(status) == PIDFD_SKIP) {
munmap(map, page_size);
SKIP(return, "listns() not supported");
}
/* Should have been killed */
ASSERT_TRUE(WIFSIGNALED(status));
ASSERT_EQ(WTERMSIG(status), SIGKILL);
@ -250,7 +257,7 @@ TEST(listns_late_fault_with_ns_cleanup)
__u64 *ns_ids;
ssize_t ret;
long page_size;
pid_t pid, iter_pid;
pid_t pid, iter_pid, ns_pids[10];
int pidfds[10];
int sv[10][2];
int iter_pidfd;
@ -320,6 +327,7 @@ TEST(listns_late_fault_with_ns_cleanup)
pid = create_child(&pidfds[i], CLONE_NEWNS);
ASSERT_NE(pid, -1);
ns_pids[i] = pid;
if (pid == 0) {
close(sv[i][0]); /* Close parent end */
@ -373,7 +381,7 @@ TEST(listns_late_fault_with_ns_cleanup)
/* Wait for all children and cleanup */
for (i = 0; i < 10; i++) {
waitpid(-1, NULL, 0);
waitpid(ns_pids[i], NULL, 0);
close(sv[i][0]);
close(pidfds[i]);
}
@ -384,6 +392,12 @@ TEST(listns_late_fault_with_ns_cleanup)
ASSERT_EQ(ret, iter_pid);
close(iter_pidfd);
/* If listns() is not supported the iterator exits cleanly via ENOSYS */
if (WIFEXITED(status) && WEXITSTATUS(status) == PIDFD_SKIP) {
munmap(map, page_size);
SKIP(return, "listns() not supported");
}
/* Should have been killed */
ASSERT_TRUE(WIFSIGNALED(status));
ASSERT_EQ(WTERMSIG(status), SIGKILL);
@ -402,7 +416,7 @@ TEST(listns_mnt_ns_cleanup_on_fault)
__u64 *ns_ids;
ssize_t ret;
long page_size;
pid_t pid, iter_pid;
pid_t pid, iter_pid, ns_pids[8];
int pidfds[8];
int sv[8][2];
int iter_pidfd;
@ -462,6 +476,7 @@ TEST(listns_mnt_ns_cleanup_on_fault)
pid = create_child(&pidfds[i], CLONE_NEWNS);
ASSERT_NE(pid, -1);
ns_pids[i] = pid;
if (pid == 0) {
close(sv[i][0]); /* Close parent end */
@ -508,7 +523,7 @@ TEST(listns_mnt_ns_cleanup_on_fault)
/* Wait for children and cleanup */
for (i = 0; i < 8; i++) {
waitpid(-1, NULL, 0);
waitpid(ns_pids[i], NULL, 0);
close(sv[i][0]);
close(pidfds[i]);
}
@ -519,6 +534,12 @@ TEST(listns_mnt_ns_cleanup_on_fault)
ASSERT_EQ(ret, iter_pid);
close(iter_pidfd);
/* If listns() is not supported the iterator exits cleanly via ENOSYS */
if (WIFEXITED(status) && WEXITSTATUS(status) == PIDFD_SKIP) {
munmap(map, page_size);
SKIP(return, "listns() not supported");
}
/* Should have been killed */
ASSERT_TRUE(WIFSIGNALED(status));
ASSERT_EQ(WTERMSIG(status), SIGKILL);

View File

@ -25,14 +25,24 @@
/* Fixture for tests that create child processes */
FIXTURE(nsid) {
pid_t child_pid;
pid_t grandchild_pid;
};
FIXTURE_SETUP(nsid) {
self->child_pid = 0;
self->grandchild_pid = 0;
}
FIXTURE_TEARDOWN(nsid) {
/* Clean up any child process that may still be running */
/*
* Kill grandchild first: timens_separate and pidns_separate fork a
* grandchild that calls pause(). It is reparented to init on child
* exit and keeps the test runner's tap pipe open, hanging the runner.
*/
if (self->grandchild_pid > 0) {
kill(self->grandchild_pid, SIGKILL);
waitpid(self->grandchild_pid, NULL, 0);
}
if (self->child_pid > 0) {
kill(self->child_pid, SIGKILL);
waitpid(self->child_pid, NULL, 0);
@ -676,6 +686,7 @@ TEST_F(nsid, timens_separate)
pid_t grandchild_pid;
ASSERT_EQ(read(pipefd[0], &grandchild_pid, sizeof(grandchild_pid)), sizeof(grandchild_pid));
self->grandchild_pid = grandchild_pid;
close(pipefd[0]);
/* Open grandchild's time namespace */
@ -797,6 +808,7 @@ TEST_F(nsid, pidns_separate)
pid_t grandchild_pid;
ASSERT_EQ(read(pipefd[0], &grandchild_pid, sizeof(grandchild_pid)), sizeof(grandchild_pid));
self->grandchild_pid = grandchild_pid;
close(pipefd[0]);
/* Open grandchild's PID namespace */