summaryrefslogtreecommitdiffhomepage
path: root/test/syscalls
diff options
context:
space:
mode:
Diffstat (limited to 'test/syscalls')
-rw-r--r--test/syscalls/linux/BUILD2
-rw-r--r--test/syscalls/linux/wait.cc185
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) {