mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Add WCLONE / WALL support to waitid
The previous commit adds WNOTHREAD support to waitid, so we may as well
complete the upstream change.
Linux added WCLONE, WALL, WNOTHREAD support to waitid(2) in
91c4e8ea8f05916df0c8a6f383508ac7c9e10dba ("wait: allow sys_waitid() to
accept __WNOTHREAD/__WCLONE/__WALL"). i.e., Linux 4.7.
PiperOrigin-RevId: 249560587
Change-Id: Iff177b0848a3f7bae6cb5592e44500c5a942fbeb
This commit is contained in:
@@ -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 {
|
||||
|
||||
+82
-42
@@ -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));
|
||||
|
||||
Reference in New Issue
Block a user