diff options
Diffstat (limited to 'test/syscalls')
-rw-r--r-- | test/syscalls/linux/BUILD | 2 | ||||
-rw-r--r-- | test/syscalls/linux/wait.cc | 185 |
2 files changed, 133 insertions, 54 deletions
diff --git a/test/syscalls/linux/BUILD b/test/syscalls/linux/BUILD index e8caf31fc..014679ec5 100644 --- a/test/syscalls/linux/BUILD +++ b/test/syscalls/linux/BUILD @@ -3179,7 +3179,9 @@ cc_binary( "//test/util:signal_util", "//test/util:test_main", "//test/util:test_util", + "//test/util:thread_util", "@com_google_absl//absl/strings", + "@com_google_absl//absl/synchronization", "@com_google_absl//absl/time", "@com_google_googletest//:gtest", ], diff --git a/test/syscalls/linux/wait.cc b/test/syscalls/linux/wait.cc index 50d0725a7..da3b97828 100644 --- a/test/syscalls/linux/wait.cc +++ b/test/syscalls/linux/wait.cc @@ -21,11 +21,13 @@ #include <unistd.h> #include <functional> +#include <tuple> #include <vector> #include "gmock/gmock.h" #include "gtest/gtest.h" #include "absl/strings/str_cat.h" +#include "absl/synchronization/mutex.h" #include "absl/time/clock.h" #include "absl/time/time.h" #include "test/util/cleanup.h" @@ -34,6 +36,7 @@ #include "test/util/posix_error.h" #include "test/util/signal_util.h" #include "test/util/test_util.h" +#include "test/util/thread_util.h" using ::testing::UnorderedElementsAre; @@ -42,10 +45,8 @@ using ::testing::UnorderedElementsAre; // // NOTE(b/22640830,b/27680907,b/29049891): Some functionality is not tested as // it is not currently supported by gVisor: -// * UID in waitid(2) siginfo. // * Process groups. // * Core dump status (WCOREDUMP). -// * Linux only option __WNOTHREAD. // // Tests for waiting on stopped/continued children are in sigstop.cc. @@ -357,13 +358,22 @@ INSTANTIATE_TEST_SUITE_P( return static_cast<pid_t>(si.si_pid); })); -// Fixture for tests parameterized by a function that takes the PID of a -// specific child to wait for, waits for it to exit, and checks that it exits -// with the given code. +// Fixture for tests parameterized by a (sysno, function) tuple. The function +// takes the PID of a specific child to wait for, waits for it to exit, and +// checks that it exits with the given code. class WaitSpecificChildTest - : public ::testing::TestWithParam<std::function<PosixError(pid_t, int)>> { + : public ::testing::TestWithParam< + std::tuple<int, std::function<PosixError(pid_t, int, int)>>> { protected: - PosixError WaitFor(pid_t pid, int code) { return GetParam()(pid, code); } + int Sysno() { return std::get<0>(GetParam()); } + + PosixError WaitForWithOptions(pid_t pid, int options, int code) { + return std::get<1>(GetParam())(pid, options, code); + } + + PosixError WaitFor(pid_t pid, int code) { + return std::get<1>(GetParam())(pid, 0, code); + } }; // Wait for specific child to exit. @@ -432,6 +442,75 @@ TEST_P(WaitSpecificChildTest, AfterExit) { EXPECT_NO_ERRNO(WaitFor(child, 0)); } +// Wait for child of sibling thread. +TEST_P(WaitSpecificChildTest, SiblingChildren) { + absl::Mutex mu; + pid_t child; + bool ready = false; + bool stop = false; + + ScopedThread t([&] { + absl::MutexLock ml(&mu); + EXPECT_THAT(child = ForkAndExit(0, 0), SyscallSucceeds()); + ready = true; + mu.Await(absl::Condition(&stop)); + }); + + // N.B. This must be declared after ScopedThread, so it is destructed first, + // thus waking the thread. + absl::MutexLock ml(&mu); + mu.Await(absl::Condition(&ready)); + + EXPECT_NO_ERRNO(WaitFor(child, 0)); + + // Keep the sibling alive until after we've waited so the child isn't + // reparented. + stop = true; +} + +// Waiting for child of sibling thread not allowed with WNOTHREAD. +TEST_P(WaitSpecificChildTest, SiblingChildrenWNOTHREAD) { + // Linux added WNOTHREAD support to waitid(2) in + // 91c4e8ea8f05916df0c8a6f383508ac7c9e10dba ("wait: allow sys_waitid() to + // accept __WNOTHREAD/__WCLONE/__WALL"). i.e., Linux 4.7. + // + // Skip the test if it isn't supported yet. + if (Sysno() == SYS_waitid) { + int ret = waitid(P_ALL, 0, nullptr, WEXITED | WNOHANG | __WNOTHREAD); + SKIP_IF(ret < 0 && errno == EINVAL); + } + + absl::Mutex mu; + pid_t child; + bool ready = false; + bool stop = false; + + ScopedThread t([&] { + absl::MutexLock ml(&mu); + EXPECT_THAT(child = ForkAndExit(0, 0), SyscallSucceeds()); + ready = true; + mu.Await(absl::Condition(&stop)); + + // This thread can wait on child. + EXPECT_NO_ERRNO(WaitForWithOptions(child, __WNOTHREAD, 0)); + }); + + // N.B. This must be declared after ScopedThread, so it is destructed first, + // thus waking the thread. + absl::MutexLock ml(&mu); + mu.Await(absl::Condition(&ready)); + + // This thread can't wait on child. + EXPECT_THAT( + WaitForWithOptions(child, __WNOTHREAD, 0), + PosixErrorIs(ECHILD, ::testing::AnyOf(::testing::StrEq("waitid"), + ::testing::StrEq("wait4")))); + + // Keep the sibling alive until after we've waited so the child isn't + // reparented. + stop = true; +} + // Wait for specific child to exit. // A non-CLONE_THREAD child which sends SIGCHLD upon exit behaves much like // a forked process. @@ -551,55 +630,53 @@ TEST_P(WaitSpecificChildTest, AfterChildExecve) { EXPECT_NO_ERRNO(WaitFor(child, 0)); } -INSTANTIATE_TEST_SUITE_P( - Waiters, WaitSpecificChildTest, - ::testing::Values( - [](pid_t pid, int code) -> PosixError { - int status; - auto const rv = Wait4(pid, &status, 0, nullptr); - MaybeSave(); - if (rv < 0) { - return PosixError(errno, "wait4"); - } else if (rv != pid) { - return PosixError(EINVAL, absl::StrCat("unexpected pid: got ", rv, - ", wanted ", pid)); - } - if (!WIFEXITED(status) || WEXITSTATUS(status) != code) { - return PosixError( - EINVAL, absl::StrCat("unexpected wait status: got ", status, - ", wanted ", code)); - } - return NoError(); - }, - [](pid_t pid, int code) -> PosixError { - siginfo_t si; - auto const rv = Waitid(P_PID, pid, &si, WEXITED); - MaybeSave(); - if (rv < 0) { - return PosixError(errno, "waitid"); - } - if (si.si_pid != pid) { - return PosixError(EINVAL, - absl::StrCat("unexpected pid: got ", si.si_pid, +PosixError CheckWait4(pid_t pid, int options, int code) { + int status; + auto const rv = Wait4(pid, &status, options, nullptr); + MaybeSave(); + if (rv < 0) { + return PosixError(errno, "wait4"); + } else if (rv != pid) { + return PosixError( + EINVAL, absl::StrCat("unexpected pid: got ", rv, ", wanted ", pid)); + } + if (!WIFEXITED(status) || WEXITSTATUS(status) != code) { + return PosixError(EINVAL, absl::StrCat("unexpected wait status: got ", + status, ", wanted ", code)); + } + return NoError(); +}; + +PosixError CheckWaitid(pid_t pid, int options, int code) { + siginfo_t si; + auto const rv = Waitid(P_PID, pid, &si, options | WEXITED); + MaybeSave(); + if (rv < 0) { + return PosixError(errno, "waitid"); + } + if (si.si_pid != pid) { + return PosixError(EINVAL, absl::StrCat("unexpected pid: got ", si.si_pid, ", wanted ", pid)); - } - if (si.si_signo != SIGCHLD) { - return PosixError( - EINVAL, absl::StrCat("unexpected signo: got ", si.si_signo, - ", wanted ", SIGCHLD)); - } - if (si.si_status != code) { - return PosixError( - EINVAL, absl::StrCat("unexpected status: got ", si.si_status, - ", wanted ", code)); - } - if (si.si_code != CLD_EXITED) { - return PosixError(EINVAL, - absl::StrCat("unexpected code: got ", si.si_code, + } + if (si.si_signo != SIGCHLD) { + return PosixError(EINVAL, absl::StrCat("unexpected signo: got ", + si.si_signo, ", wanted ", SIGCHLD)); + } + if (si.si_status != code) { + return PosixError(EINVAL, absl::StrCat("unexpected status: got ", + si.si_status, ", wanted ", code)); + } + if (si.si_code != CLD_EXITED) { + return PosixError(EINVAL, absl::StrCat("unexpected code: got ", si.si_code, ", wanted ", CLD_EXITED)); - } - return NoError(); - })); + } + return NoError(); +} + +INSTANTIATE_TEST_SUITE_P( + Waiters, WaitSpecificChildTest, + ::testing::Values(std::make_tuple(SYS_wait4, CheckWait4), + std::make_tuple(SYS_waitid, CheckWaitid))); // WIFEXITED, WIFSIGNALED, WTERMSIG indicate signal exit. TEST(WaitTest, SignalExit) { |