summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--pkg/sentry/syscalls/linux/sys_thread.go67
-rw-r--r--test/syscalls/linux/wait.cc124
2 files changed, 117 insertions, 74 deletions
diff --git a/pkg/sentry/syscalls/linux/sys_thread.go b/pkg/sentry/syscalls/linux/sys_thread.go
index 14fa7ef92..26f7e8ead 100644
--- a/pkg/sentry/syscalls/linux/sys_thread.go
+++ b/pkg/sentry/syscalls/linux/sys_thread.go
@@ -181,6 +181,32 @@ func Vfork(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall
return clone(t, syscall.CLONE_VM|syscall.CLONE_VFORK|int(syscall.SIGCHLD), 0, 0, 0, 0)
}
+// parseCommonWaitOptions applies the options common to wait4 and waitid to
+// wopts.
+func parseCommonWaitOptions(wopts *kernel.WaitOptions, options int) error {
+ switch options & (linux.WCLONE | linux.WALL) {
+ case 0:
+ wopts.NonCloneTasks = true
+ case linux.WCLONE:
+ wopts.CloneTasks = true
+ case linux.WALL:
+ wopts.NonCloneTasks = true
+ wopts.CloneTasks = true
+ default:
+ return syscall.EINVAL
+ }
+ if options&linux.WCONTINUED != 0 {
+ wopts.Events |= kernel.EventGroupContinue
+ }
+ if options&linux.WNOHANG == 0 {
+ wopts.BlockInterruptErr = kernel.ERESTARTSYS
+ }
+ if options&linux.WNOTHREAD == 0 {
+ wopts.SiblingChildren = true
+ }
+ return nil
+}
+
// wait4 waits for the given child process to exit.
func wait4(t *kernel.Task, pid int, statusAddr usermem.Addr, options int, rusageAddr usermem.Addr) (uintptr, error) {
if options&^(linux.WNOHANG|linux.WUNTRACED|linux.WCONTINUED|linux.WNOTHREAD|linux.WALL|linux.WCLONE) != 0 {
@@ -207,29 +233,12 @@ func wait4(t *kernel.Task, pid int, statusAddr usermem.Addr, options int, rusage
wopts.SpecificTID = kernel.ThreadID(pid)
}
- switch options & (linux.WCLONE | linux.WALL) {
- case 0:
- wopts.NonCloneTasks = true
- case linux.WCLONE:
- wopts.CloneTasks = true
- case linux.WALL:
- wopts.NonCloneTasks = true
- wopts.CloneTasks = true
- default:
- return 0, syscall.EINVAL
+ if err := parseCommonWaitOptions(&wopts, options); err != nil {
+ return 0, err
}
if options&linux.WUNTRACED != 0 {
wopts.Events |= kernel.EventChildGroupStop
}
- if options&linux.WCONTINUED != 0 {
- wopts.Events |= kernel.EventGroupContinue
- }
- if options&linux.WNOHANG == 0 {
- wopts.BlockInterruptErr = kernel.ERESTARTSYS
- }
- if options&linux.WNOTHREAD == 0 {
- wopts.SiblingChildren = true
- }
wr, err := t.Wait(&wopts)
if err != nil {
@@ -281,16 +290,15 @@ func Waitid(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscal
options := int(args[3].Uint())
rusageAddr := args[4].Pointer()
- if options&^(linux.WNOHANG|linux.WEXITED|linux.WSTOPPED|linux.WCONTINUED|linux.WNOWAIT|linux.WNOTHREAD) != 0 {
+ if options&^(linux.WNOHANG|linux.WEXITED|linux.WSTOPPED|linux.WCONTINUED|linux.WNOWAIT|linux.WNOTHREAD|linux.WALL|linux.WCLONE) != 0 {
return 0, nil, syscall.EINVAL
}
if options&(linux.WEXITED|linux.WSTOPPED|linux.WCONTINUED) == 0 {
return 0, nil, syscall.EINVAL
}
wopts := kernel.WaitOptions{
- NonCloneTasks: true,
- Events: kernel.EventTraceeStop,
- ConsumeEvent: options&linux.WNOWAIT == 0,
+ Events: kernel.EventTraceeStop,
+ ConsumeEvent: options&linux.WNOWAIT == 0,
}
switch idtype {
case linux.P_ALL:
@@ -301,21 +309,16 @@ func Waitid(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscal
default:
return 0, nil, syscall.EINVAL
}
+
+ if err := parseCommonWaitOptions(&wopts, options); err != nil {
+ return 0, nil, err
+ }
if options&linux.WEXITED != 0 {
wopts.Events |= kernel.EventExit
}
if options&linux.WSTOPPED != 0 {
wopts.Events |= kernel.EventChildGroupStop
}
- if options&linux.WCONTINUED != 0 {
- wopts.Events |= kernel.EventGroupContinue
- }
- if options&linux.WNOHANG == 0 {
- wopts.BlockInterruptErr = kernel.ERESTARTSYS
- }
- if options&linux.WNOTHREAD == 0 {
- wopts.SiblingChildren = true
- }
wr, err := t.Wait(&wopts)
if err != nil {
diff --git a/test/syscalls/linux/wait.cc b/test/syscalls/linux/wait.cc
index da3b97828..f413ee6ae 100644
--- a/test/syscalls/linux/wait.cc
+++ b/test/syscalls/linux/wait.cc
@@ -233,18 +233,14 @@ TEST_P(WaitAnyChildTest, ForkAndClone) {
// Return immediately if no child has exited.
TEST_P(WaitAnyChildTest, WaitWNOHANG) {
- EXPECT_THAT(
- WaitAnyWithOptions(0, WNOHANG),
- PosixErrorIs(ECHILD, ::testing::AnyOf(::testing::StrEq("waitid"),
- ::testing::StrEq("wait4"))));
+ EXPECT_THAT(WaitAnyWithOptions(0, WNOHANG),
+ PosixErrorIs(ECHILD, ::testing::_));
}
// Bad options passed
TEST_P(WaitAnyChildTest, BadOption) {
- EXPECT_THAT(
- WaitAnyWithOptions(0, 123456),
- PosixErrorIs(EINVAL, ::testing::AnyOf(::testing::StrEq("waitid"),
- ::testing::StrEq("wait4"))));
+ EXPECT_THAT(WaitAnyWithOptions(0, 123456),
+ PosixErrorIs(EINVAL, ::testing::_));
}
TEST_P(WaitAnyChildTest, WaitedChildRusage) {
@@ -295,9 +291,7 @@ TEST_P(WaitAnyChildTest, IgnoredChildRusage) {
pid_t child;
ASSERT_THAT(child = ForkSpinAndExit(0, absl::ToInt64Seconds(kSpin)),
SyscallSucceeds());
- ASSERT_THAT(WaitAny(0), PosixErrorIs(ECHILD, ::testing::AnyOf(
- ::testing::StrEq("waitid"),
- ::testing::StrEq("wait4"))));
+ ASSERT_THAT(WaitAny(0), PosixErrorIs(ECHILD, ::testing::_));
const absl::Duration end =
absl::Nanoseconds(clock_gettime_nsecs(CLOCK_MONOTONIC));
EXPECT_GE(end - start, kSpin - kSpinGrace);
@@ -501,10 +495,8 @@ TEST_P(WaitSpecificChildTest, SiblingChildrenWNOTHREAD) {
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"))));
+ EXPECT_THAT(WaitForWithOptions(child, __WNOTHREAD, 0),
+ PosixErrorIs(ECHILD, ::testing::_));
// Keep the sibling alive until after we've waited so the child isn't
// reparented.
@@ -538,10 +530,7 @@ TEST_P(WaitSpecificChildTest, CloneNoSIGCHLD) {
int child;
ASSERT_THAT(child = CloneAndExit(0, stack, 0), SyscallSucceeds());
- EXPECT_THAT(
- WaitFor(child, 0),
- PosixErrorIs(ECHILD, ::testing::AnyOf(::testing::StrEq("waitid"),
- ::testing::StrEq("wait4"))));
+ EXPECT_THAT(WaitFor(child, 0), PosixErrorIs(ECHILD, ::testing::_));
}
// Waiting after the child has already exited returns immediately.
@@ -571,10 +560,7 @@ TEST_P(WaitSpecificChildTest, CloneThread) {
ASSERT_THAT(child = CloneAndExit(15, stack, CLONE_THREAD), SyscallSucceeds());
auto start = absl::Now();
- EXPECT_THAT(
- WaitFor(child, 0),
- PosixErrorIs(ECHILD, ::testing::AnyOf(::testing::StrEq("waitid"),
- ::testing::StrEq("wait4"))));
+ EXPECT_THAT(WaitFor(child, 0), PosixErrorIs(ECHILD, ::testing::_));
// Ensure wait4 didn't block.
EXPECT_LE(absl::Now() - start, absl::Seconds(10));
@@ -584,12 +570,81 @@ TEST_P(WaitSpecificChildTest, CloneThread) {
absl::SleepFor(absl::Seconds(5));
}
+// A child that does not send a SIGCHLD on exit may be waited on with
+// the __WCLONE flag.
+TEST_P(WaitSpecificChildTest, CloneWCLONE) {
+ // Linux added WCLONE 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 | __WCLONE);
+ SKIP_IF(ret < 0 && errno == EINVAL);
+ }
+
+ uintptr_t stack;
+ ASSERT_THAT(stack = AllocStack(), SyscallSucceeds());
+ auto free =
+ Cleanup([stack] { ASSERT_THAT(FreeStack(stack), SyscallSucceeds()); });
+
+ int child;
+ ASSERT_THAT(child = CloneAndExit(0, stack, 0), SyscallSucceeds());
+
+ EXPECT_NO_ERRNO(WaitForWithOptions(child, __WCLONE, 0));
+}
+
+// A forked child cannot be waited on with WCLONE.
+TEST_P(WaitSpecificChildTest, ForkWCLONE) {
+ // Linux added WCLONE 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 | __WCLONE);
+ SKIP_IF(ret < 0 && errno == EINVAL);
+ }
+
+ pid_t child;
+ ASSERT_THAT(child = ForkAndExit(0, 0), SyscallSucceeds());
+
+ EXPECT_THAT(WaitForWithOptions(child, WNOHANG | __WCLONE, 0),
+ PosixErrorIs(ECHILD, ::testing::_));
+
+ EXPECT_NO_ERRNO(WaitFor(child, 0));
+}
+
+// Any type of child can be waited on with WALL.
+TEST_P(WaitSpecificChildTest, WALL) {
+ // Linux added WALL 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 | __WALL);
+ SKIP_IF(ret < 0 && errno == EINVAL);
+ }
+
+ pid_t child;
+ ASSERT_THAT(child = ForkAndExit(0, 0), SyscallSucceeds());
+
+ EXPECT_NO_ERRNO(WaitForWithOptions(child, __WALL, 0));
+
+ uintptr_t stack;
+ ASSERT_THAT(stack = AllocStack(), SyscallSucceeds());
+ auto free =
+ Cleanup([stack] { ASSERT_THAT(FreeStack(stack), SyscallSucceeds()); });
+
+ ASSERT_THAT(child = CloneAndExit(0, stack, 0), SyscallSucceeds());
+
+ EXPECT_NO_ERRNO(WaitForWithOptions(child, __WALL, 0));
+}
+
// Return ECHILD for bad child.
TEST_P(WaitSpecificChildTest, BadChild) {
- EXPECT_THAT(
- WaitFor(42, 0),
- PosixErrorIs(ECHILD, ::testing::AnyOf(::testing::StrEq("waitid"),
- ::testing::StrEq("wait4"))));
+ EXPECT_THAT(WaitFor(42, 0), PosixErrorIs(ECHILD, ::testing::_));
}
// Wait for a child process that only exits after calling execve(2) from a
@@ -694,21 +749,6 @@ TEST(WaitTest, SignalExit) {
EXPECT_EQ(SIGKILL, WTERMSIG(status));
}
-// A child that does not send a SIGCHLD on exit may be waited on with
-// the __WCLONE flag.
-TEST(WaitTest, CloneWCLONE) {
- uintptr_t stack;
- ASSERT_THAT(stack = AllocStack(), SyscallSucceeds());
- auto free =
- Cleanup([stack] { ASSERT_THAT(FreeStack(stack), SyscallSucceeds()); });
-
- int child;
- ASSERT_THAT(child = CloneAndExit(0, stack, 0), SyscallSucceeds());
-
- EXPECT_THAT(Wait4(child, nullptr, __WCLONE, nullptr),
- SyscallSucceedsWithValue(child));
-}
-
// waitid requires at least one option.
TEST(WaitTest, WaitidOptions) {
EXPECT_THAT(Waitid(P_ALL, 0, nullptr, 0), SyscallFailsWithErrno(EINVAL));