2019-04-29 21:25:05 +00:00
|
|
|
// Copyright 2018 The gVisor Authors.
|
2018-12-10 22:41:40 +00:00
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
// You may obtain a copy of the License at
|
|
|
|
//
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
//
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
// limitations under the License.
|
|
|
|
|
|
|
|
#include <signal.h>
|
|
|
|
#include <sys/mman.h>
|
2019-05-29 01:02:07 +00:00
|
|
|
#include <sys/ptrace.h>
|
2018-12-10 22:41:40 +00:00
|
|
|
#include <sys/resource.h>
|
|
|
|
#include <sys/time.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/wait.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
|
|
|
|
#include <functional>
|
2019-05-22 22:53:13 +00:00
|
|
|
#include <tuple>
|
2018-12-10 22:41:40 +00:00
|
|
|
#include <vector>
|
|
|
|
|
|
|
|
#include "gmock/gmock.h"
|
|
|
|
#include "gtest/gtest.h"
|
|
|
|
#include "absl/strings/str_cat.h"
|
2019-05-22 22:53:13 +00:00
|
|
|
#include "absl/synchronization/mutex.h"
|
2018-12-10 22:41:40 +00:00
|
|
|
#include "absl/time/clock.h"
|
|
|
|
#include "absl/time/time.h"
|
|
|
|
#include "test/util/cleanup.h"
|
2019-05-29 01:02:07 +00:00
|
|
|
#include "test/util/file_descriptor.h"
|
2018-12-10 22:41:40 +00:00
|
|
|
#include "test/util/logging.h"
|
|
|
|
#include "test/util/multiprocess_util.h"
|
|
|
|
#include "test/util/posix_error.h"
|
|
|
|
#include "test/util/signal_util.h"
|
|
|
|
#include "test/util/test_util.h"
|
2019-05-22 22:53:13 +00:00
|
|
|
#include "test/util/thread_util.h"
|
2019-05-24 19:57:34 +00:00
|
|
|
#include "test/util/time_util.h"
|
2018-12-10 22:41:40 +00:00
|
|
|
|
|
|
|
using ::testing::UnorderedElementsAre;
|
|
|
|
|
|
|
|
// These unit tests focus on the wait4(2) system call, but include a basic
|
|
|
|
// checks for the i386 waitpid(2) syscall, which is a subset of wait4(2).
|
|
|
|
//
|
2019-04-29 21:03:04 +00:00
|
|
|
// NOTE(b/22640830,b/27680907,b/29049891): Some functionality is not tested as
|
2018-12-10 22:41:40 +00:00
|
|
|
// it is not currently supported by gVisor:
|
|
|
|
// * Process groups.
|
|
|
|
// * Core dump status (WCOREDUMP).
|
|
|
|
//
|
|
|
|
// Tests for waiting on stopped/continued children are in sigstop.cc.
|
|
|
|
|
|
|
|
namespace gvisor {
|
|
|
|
namespace testing {
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
// The CloneChild function seems to need more than one page of stack space.
|
|
|
|
static const size_t kStackSize = 2 * kPageSize;
|
|
|
|
|
|
|
|
// The child thread created in CloneAndExit runs this function.
|
|
|
|
// This child does not have the TLS setup, so it must not use glibc functions.
|
|
|
|
int CloneChild(void* priv) {
|
|
|
|
int64_t sleep = reinterpret_cast<int64_t>(priv);
|
|
|
|
SleepSafe(absl::Seconds(sleep));
|
|
|
|
|
|
|
|
// glibc's _exit(2) function wrapper will helpfully call exit_group(2),
|
|
|
|
// exiting the entire process.
|
|
|
|
syscall(__NR_exit, 0);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// ForkAndExit forks a child process which exits with exit_code, after
|
|
|
|
// sleeping for the specified duration (seconds).
|
|
|
|
pid_t ForkAndExit(int exit_code, int64_t sleep) {
|
|
|
|
pid_t child = fork();
|
|
|
|
if (child == 0) {
|
|
|
|
SleepSafe(absl::Seconds(sleep));
|
|
|
|
_exit(exit_code);
|
|
|
|
}
|
|
|
|
return child;
|
|
|
|
}
|
|
|
|
|
|
|
|
int64_t clock_gettime_nsecs(clockid_t id) {
|
|
|
|
struct timespec ts;
|
|
|
|
TEST_PCHECK(clock_gettime(id, &ts) == 0);
|
|
|
|
return (ts.tv_sec * 1000000000 + ts.tv_nsec);
|
|
|
|
}
|
|
|
|
|
|
|
|
void spin(int64_t sec) {
|
|
|
|
int64_t ns = sec * 1000000000;
|
|
|
|
int64_t start = clock_gettime_nsecs(CLOCK_THREAD_CPUTIME_ID);
|
|
|
|
int64_t end = start + ns;
|
|
|
|
|
|
|
|
do {
|
|
|
|
constexpr int kLoopCount = 1000000; // large and arbitrary
|
|
|
|
// volatile to prevent the compiler from skipping this loop.
|
|
|
|
for (volatile int i = 0; i < kLoopCount; i++) {
|
|
|
|
}
|
|
|
|
} while (clock_gettime_nsecs(CLOCK_THREAD_CPUTIME_ID) < end);
|
|
|
|
}
|
|
|
|
|
|
|
|
// ForkSpinAndExit forks a child process which exits with exit_code, after
|
|
|
|
// spinning for the specified duration (seconds).
|
|
|
|
pid_t ForkSpinAndExit(int exit_code, int64_t spintime) {
|
|
|
|
pid_t child = fork();
|
|
|
|
if (child == 0) {
|
|
|
|
spin(spintime);
|
|
|
|
_exit(exit_code);
|
|
|
|
}
|
|
|
|
return child;
|
|
|
|
}
|
|
|
|
|
|
|
|
absl::Duration RusageCpuTime(const struct rusage& ru) {
|
|
|
|
return absl::DurationFromTimeval(ru.ru_utime) +
|
|
|
|
absl::DurationFromTimeval(ru.ru_stime);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Returns the address of the top of the stack.
|
|
|
|
// Free with FreeStack.
|
|
|
|
uintptr_t AllocStack() {
|
|
|
|
void* addr = mmap(nullptr, kStackSize, PROT_READ | PROT_WRITE,
|
|
|
|
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
|
|
|
|
|
|
|
if (addr == MAP_FAILED) {
|
|
|
|
return reinterpret_cast<uintptr_t>(MAP_FAILED);
|
|
|
|
}
|
|
|
|
|
|
|
|
return reinterpret_cast<uintptr_t>(addr) + kStackSize;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Frees a stack page allocated with AllocStack.
|
|
|
|
int FreeStack(uintptr_t addr) {
|
|
|
|
addr -= kStackSize;
|
|
|
|
return munmap(reinterpret_cast<void*>(addr), kPageSize);
|
|
|
|
}
|
|
|
|
|
|
|
|
// CloneAndExit clones a child thread, which exits with 0 after sleeping for
|
|
|
|
// the specified duration (must be in seconds). extra_flags are ORed against
|
|
|
|
// the standard clone(2) flags.
|
|
|
|
int CloneAndExit(int64_t sleep, uintptr_t stack, int extra_flags) {
|
|
|
|
return clone(CloneChild, reinterpret_cast<void*>(stack),
|
|
|
|
CLONE_FILES | CLONE_FS | CLONE_SIGHAND | CLONE_VM | extra_flags,
|
|
|
|
reinterpret_cast<void*>(sleep));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Simple wrappers around wait4(2) and waitid(2) that ignore interrupts.
|
|
|
|
constexpr auto Wait4 = RetryEINTR(wait4);
|
|
|
|
constexpr auto Waitid = RetryEINTR(waitid);
|
|
|
|
|
|
|
|
// Fixture for tests parameterized by a function that waits for any child to
|
|
|
|
// exit with the given options, checks that it exited with the given code, and
|
|
|
|
// then returns its PID.
|
|
|
|
//
|
|
|
|
// N.B. These tests run in a multi-threaded environment. We assume that
|
|
|
|
// background threads do not create child processes and are not themselves
|
|
|
|
// created with clone(... | SIGCHLD). Either may cause these tests to
|
|
|
|
// erroneously wait on child processes/threads.
|
|
|
|
class WaitAnyChildTest : public ::testing::TestWithParam<
|
|
|
|
std::function<PosixErrorOr<pid_t>(int, int)>> {
|
|
|
|
protected:
|
|
|
|
PosixErrorOr<pid_t> WaitAny(int code) { return WaitAnyWithOptions(code, 0); }
|
|
|
|
|
|
|
|
PosixErrorOr<pid_t> WaitAnyWithOptions(int code, int options) {
|
|
|
|
return GetParam()(code, options);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
// Wait for any child to exit.
|
|
|
|
TEST_P(WaitAnyChildTest, Fork) {
|
|
|
|
pid_t child;
|
|
|
|
ASSERT_THAT(child = ForkAndExit(0, 0), SyscallSucceeds());
|
|
|
|
|
|
|
|
EXPECT_THAT(WaitAny(0), IsPosixErrorOkAndHolds(child));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Call wait4 for any process after the child has already exited.
|
|
|
|
TEST_P(WaitAnyChildTest, AfterExit) {
|
|
|
|
pid_t child;
|
|
|
|
ASSERT_THAT(child = ForkAndExit(0, 0), SyscallSucceeds());
|
|
|
|
|
|
|
|
absl::SleepFor(absl::Seconds(5));
|
|
|
|
|
|
|
|
EXPECT_THAT(WaitAny(0), IsPosixErrorOkAndHolds(child));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Wait for multiple children to exit, waiting for either at a time.
|
|
|
|
TEST_P(WaitAnyChildTest, MultipleFork) {
|
|
|
|
pid_t child1, child2;
|
|
|
|
ASSERT_THAT(child1 = ForkAndExit(0, 0), SyscallSucceeds());
|
|
|
|
ASSERT_THAT(child2 = ForkAndExit(0, 0), SyscallSucceeds());
|
|
|
|
|
|
|
|
std::vector<pid_t> pids;
|
|
|
|
pids.push_back(ASSERT_NO_ERRNO_AND_VALUE(WaitAny(0)));
|
|
|
|
pids.push_back(ASSERT_NO_ERRNO_AND_VALUE(WaitAny(0)));
|
|
|
|
EXPECT_THAT(pids, UnorderedElementsAre(child1, child2));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Wait for any child to exit.
|
|
|
|
// A non-CLONE_THREAD child which sends SIGCHLD upon exit behaves much like
|
|
|
|
// a forked process.
|
|
|
|
TEST_P(WaitAnyChildTest, CloneSIGCHLD) {
|
|
|
|
uintptr_t stack;
|
|
|
|
ASSERT_THAT(stack = AllocStack(), SyscallSucceeds());
|
2019-01-15 00:11:13 +00:00
|
|
|
auto free =
|
|
|
|
Cleanup([stack] { ASSERT_THAT(FreeStack(stack), SyscallSucceeds()); });
|
2018-12-10 22:41:40 +00:00
|
|
|
|
|
|
|
int child;
|
|
|
|
ASSERT_THAT(child = CloneAndExit(0, stack, SIGCHLD), SyscallSucceeds());
|
|
|
|
|
|
|
|
EXPECT_THAT(WaitAny(0), IsPosixErrorOkAndHolds(child));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Wait for a child thread and process.
|
|
|
|
TEST_P(WaitAnyChildTest, ForkAndClone) {
|
|
|
|
pid_t process;
|
|
|
|
ASSERT_THAT(process = ForkAndExit(0, 0), SyscallSucceeds());
|
|
|
|
|
|
|
|
uintptr_t stack;
|
|
|
|
ASSERT_THAT(stack = AllocStack(), SyscallSucceeds());
|
2019-01-15 00:11:13 +00:00
|
|
|
auto free =
|
|
|
|
Cleanup([stack] { ASSERT_THAT(FreeStack(stack), SyscallSucceeds()); });
|
2018-12-10 22:41:40 +00:00
|
|
|
|
|
|
|
int thread;
|
|
|
|
// Send SIGCHLD for normal wait semantics.
|
|
|
|
ASSERT_THAT(thread = CloneAndExit(0, stack, SIGCHLD), SyscallSucceeds());
|
|
|
|
|
|
|
|
std::vector<pid_t> pids;
|
|
|
|
pids.push_back(ASSERT_NO_ERRNO_AND_VALUE(WaitAny(0)));
|
|
|
|
pids.push_back(ASSERT_NO_ERRNO_AND_VALUE(WaitAny(0)));
|
|
|
|
EXPECT_THAT(pids, UnorderedElementsAre(process, thread));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Return immediately if no child has exited.
|
|
|
|
TEST_P(WaitAnyChildTest, WaitWNOHANG) {
|
2019-05-23 01:10:54 +00:00
|
|
|
EXPECT_THAT(WaitAnyWithOptions(0, WNOHANG),
|
|
|
|
PosixErrorIs(ECHILD, ::testing::_));
|
2018-12-10 22:41:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Bad options passed
|
|
|
|
TEST_P(WaitAnyChildTest, BadOption) {
|
2019-05-23 01:10:54 +00:00
|
|
|
EXPECT_THAT(WaitAnyWithOptions(0, 123456),
|
|
|
|
PosixErrorIs(EINVAL, ::testing::_));
|
2018-12-10 22:41:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
TEST_P(WaitAnyChildTest, WaitedChildRusage) {
|
|
|
|
struct rusage before;
|
|
|
|
ASSERT_THAT(getrusage(RUSAGE_CHILDREN, &before), SyscallSucceeds());
|
|
|
|
|
|
|
|
pid_t child;
|
|
|
|
constexpr absl::Duration kSpin = absl::Seconds(3);
|
|
|
|
ASSERT_THAT(child = ForkSpinAndExit(0, absl::ToInt64Seconds(kSpin)),
|
|
|
|
SyscallSucceeds());
|
|
|
|
ASSERT_THAT(WaitAny(0), IsPosixErrorOkAndHolds(child));
|
|
|
|
|
|
|
|
struct rusage after;
|
|
|
|
ASSERT_THAT(getrusage(RUSAGE_CHILDREN, &after), SyscallSucceeds());
|
|
|
|
|
|
|
|
EXPECT_GE(RusageCpuTime(after) - RusageCpuTime(before), kSpin);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_P(WaitAnyChildTest, IgnoredChildRusage) {
|
|
|
|
// "POSIX.1-2001 specifies that if the disposition of SIGCHLD is
|
|
|
|
// set to SIG_IGN or the SA_NOCLDWAIT flag is set for SIGCHLD (see
|
|
|
|
// sigaction(2)), then children that terminate do not become zombies and a
|
|
|
|
// call to wait() or waitpid() will block until all children have terminated,
|
|
|
|
// and then fail with errno set to ECHILD." - waitpid(2)
|
|
|
|
//
|
|
|
|
// "RUSAGE_CHILDREN: Return resource usage statistics for all children of the
|
|
|
|
// calling process that have terminated *and been waited for*." -
|
|
|
|
// getrusage(2), emphasis added
|
|
|
|
|
|
|
|
struct sigaction sa;
|
|
|
|
sa.sa_handler = SIG_IGN;
|
|
|
|
const auto cleanup_sigact =
|
|
|
|
ASSERT_NO_ERRNO_AND_VALUE(ScopedSigaction(SIGCHLD, sa));
|
|
|
|
|
|
|
|
struct rusage before;
|
|
|
|
ASSERT_THAT(getrusage(RUSAGE_CHILDREN, &before), SyscallSucceeds());
|
|
|
|
|
|
|
|
const absl::Duration start =
|
|
|
|
absl::Nanoseconds(clock_gettime_nsecs(CLOCK_MONOTONIC));
|
|
|
|
|
|
|
|
constexpr absl::Duration kSpin = absl::Seconds(3);
|
|
|
|
|
|
|
|
// ForkAndSpin uses CLOCK_THREAD_CPUTIME_ID, which is lower resolution than,
|
|
|
|
// and may diverge from, CLOCK_MONOTONIC, so we allow a small grace period but
|
|
|
|
// still check that we blocked for a while.
|
|
|
|
constexpr absl::Duration kSpinGrace = absl::Milliseconds(100);
|
|
|
|
|
|
|
|
pid_t child;
|
|
|
|
ASSERT_THAT(child = ForkSpinAndExit(0, absl::ToInt64Seconds(kSpin)),
|
|
|
|
SyscallSucceeds());
|
2019-05-23 01:10:54 +00:00
|
|
|
ASSERT_THAT(WaitAny(0), PosixErrorIs(ECHILD, ::testing::_));
|
2018-12-10 22:41:40 +00:00
|
|
|
const absl::Duration end =
|
|
|
|
absl::Nanoseconds(clock_gettime_nsecs(CLOCK_MONOTONIC));
|
|
|
|
EXPECT_GE(end - start, kSpin - kSpinGrace);
|
|
|
|
|
|
|
|
struct rusage after;
|
|
|
|
ASSERT_THAT(getrusage(RUSAGE_CHILDREN, &after), SyscallSucceeds());
|
|
|
|
EXPECT_EQ(before.ru_utime.tv_sec, after.ru_utime.tv_sec);
|
|
|
|
EXPECT_EQ(before.ru_utime.tv_usec, after.ru_utime.tv_usec);
|
|
|
|
EXPECT_EQ(before.ru_stime.tv_sec, after.ru_stime.tv_sec);
|
|
|
|
EXPECT_EQ(before.ru_stime.tv_usec, after.ru_stime.tv_usec);
|
|
|
|
}
|
|
|
|
|
2019-04-29 18:32:48 +00:00
|
|
|
INSTANTIATE_TEST_SUITE_P(
|
2018-12-10 22:41:40 +00:00
|
|
|
Waiters, WaitAnyChildTest,
|
|
|
|
::testing::Values(
|
|
|
|
[](int code, int options) -> PosixErrorOr<pid_t> {
|
|
|
|
int status;
|
|
|
|
auto const pid = Wait4(-1, &status, options, nullptr);
|
|
|
|
MaybeSave();
|
|
|
|
if (pid < 0) {
|
|
|
|
return PosixError(errno, "wait4");
|
|
|
|
}
|
|
|
|
if (!WIFEXITED(status) || WEXITSTATUS(status) != code) {
|
|
|
|
return PosixError(
|
|
|
|
EINVAL, absl::StrCat("unexpected wait status: got ", status,
|
|
|
|
", wanted ", code));
|
|
|
|
}
|
|
|
|
return static_cast<pid_t>(pid);
|
|
|
|
},
|
|
|
|
[](int code, int options) -> PosixErrorOr<pid_t> {
|
|
|
|
siginfo_t si;
|
|
|
|
auto const rv = Waitid(P_ALL, 0, &si, WEXITED | options);
|
|
|
|
MaybeSave();
|
|
|
|
if (rv < 0) {
|
|
|
|
return PosixError(errno, "waitid");
|
|
|
|
}
|
|
|
|
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));
|
|
|
|
}
|
|
|
|
auto const uid = getuid();
|
|
|
|
if (si.si_uid != uid) {
|
|
|
|
return PosixError(EINVAL,
|
|
|
|
absl::StrCat("unexpected uid: got ", si.si_uid,
|
|
|
|
", wanted ", uid));
|
|
|
|
}
|
|
|
|
return static_cast<pid_t>(si.si_pid);
|
|
|
|
}));
|
|
|
|
|
2019-05-22 22:53:13 +00:00
|
|
|
// 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.
|
2018-12-10 22:41:40 +00:00
|
|
|
class WaitSpecificChildTest
|
2019-05-22 22:53:13 +00:00
|
|
|
: public ::testing::TestWithParam<
|
|
|
|
std::tuple<int, std::function<PosixError(pid_t, int, int)>>> {
|
2018-12-10 22:41:40 +00:00
|
|
|
protected:
|
2019-05-22 22:53:13 +00:00
|
|
|
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);
|
|
|
|
}
|
2018-12-10 22:41:40 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
// Wait for specific child to exit.
|
|
|
|
TEST_P(WaitSpecificChildTest, Fork) {
|
|
|
|
pid_t child;
|
|
|
|
ASSERT_THAT(child = ForkAndExit(0, 0), SyscallSucceeds());
|
|
|
|
|
|
|
|
EXPECT_NO_ERRNO(WaitFor(child, 0));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Non-zero exit codes are correctly propagated.
|
|
|
|
TEST_P(WaitSpecificChildTest, NormalExit) {
|
|
|
|
pid_t child;
|
|
|
|
ASSERT_THAT(child = ForkAndExit(42, 0), SyscallSucceeds());
|
|
|
|
|
|
|
|
EXPECT_NO_ERRNO(WaitFor(child, 42));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Wait for multiple children to exit.
|
|
|
|
TEST_P(WaitSpecificChildTest, MultipleFork) {
|
|
|
|
pid_t child1, child2;
|
|
|
|
ASSERT_THAT(child1 = ForkAndExit(0, 0), SyscallSucceeds());
|
|
|
|
ASSERT_THAT(child2 = ForkAndExit(0, 0), SyscallSucceeds());
|
|
|
|
|
|
|
|
EXPECT_NO_ERRNO(WaitFor(child1, 0));
|
|
|
|
EXPECT_NO_ERRNO(WaitFor(child2, 0));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Wait for multiple children to exit, out of the order they were created.
|
|
|
|
TEST_P(WaitSpecificChildTest, MultipleForkOutOfOrder) {
|
|
|
|
pid_t child1, child2;
|
|
|
|
ASSERT_THAT(child1 = ForkAndExit(0, 0), SyscallSucceeds());
|
|
|
|
ASSERT_THAT(child2 = ForkAndExit(0, 0), SyscallSucceeds());
|
|
|
|
|
|
|
|
EXPECT_NO_ERRNO(WaitFor(child2, 0));
|
|
|
|
EXPECT_NO_ERRNO(WaitFor(child1, 0));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Wait for specific child to exit, entering wait4 before the exit occurs.
|
|
|
|
TEST_P(WaitSpecificChildTest, ForkSleep) {
|
|
|
|
pid_t child;
|
|
|
|
ASSERT_THAT(child = ForkAndExit(0, 5), SyscallSucceeds());
|
|
|
|
|
|
|
|
EXPECT_NO_ERRNO(WaitFor(child, 0));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Wait should block until the child exits.
|
|
|
|
TEST_P(WaitSpecificChildTest, ForkBlock) {
|
|
|
|
pid_t child;
|
|
|
|
|
|
|
|
auto start = absl::Now();
|
|
|
|
ASSERT_THAT(child = ForkAndExit(0, 5), SyscallSucceeds());
|
|
|
|
|
|
|
|
EXPECT_NO_ERRNO(WaitFor(child, 0));
|
|
|
|
|
|
|
|
EXPECT_GE(absl::Now() - start, absl::Seconds(5));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Waiting after the child has already exited returns immediately.
|
|
|
|
TEST_P(WaitSpecificChildTest, AfterExit) {
|
|
|
|
pid_t child;
|
|
|
|
ASSERT_THAT(child = ForkAndExit(0, 0), SyscallSucceeds());
|
|
|
|
|
|
|
|
absl::SleepFor(absl::Seconds(5));
|
|
|
|
|
|
|
|
EXPECT_NO_ERRNO(WaitFor(child, 0));
|
|
|
|
}
|
|
|
|
|
2019-05-22 22:53:13 +00:00
|
|
|
// 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.
|
2019-05-23 01:10:54 +00:00
|
|
|
EXPECT_THAT(WaitForWithOptions(child, __WNOTHREAD, 0),
|
|
|
|
PosixErrorIs(ECHILD, ::testing::_));
|
2019-05-22 22:53:13 +00:00
|
|
|
|
|
|
|
// Keep the sibling alive until after we've waited so the child isn't
|
|
|
|
// reparented.
|
|
|
|
stop = true;
|
|
|
|
}
|
|
|
|
|
2018-12-10 22:41:40 +00:00
|
|
|
// Wait for specific child to exit.
|
|
|
|
// A non-CLONE_THREAD child which sends SIGCHLD upon exit behaves much like
|
|
|
|
// a forked process.
|
|
|
|
TEST_P(WaitSpecificChildTest, CloneSIGCHLD) {
|
|
|
|
uintptr_t stack;
|
|
|
|
ASSERT_THAT(stack = AllocStack(), SyscallSucceeds());
|
2019-01-15 00:11:13 +00:00
|
|
|
auto free =
|
|
|
|
Cleanup([stack] { ASSERT_THAT(FreeStack(stack), SyscallSucceeds()); });
|
2018-12-10 22:41:40 +00:00
|
|
|
|
|
|
|
int child;
|
|
|
|
ASSERT_THAT(child = CloneAndExit(0, stack, SIGCHLD), SyscallSucceeds());
|
|
|
|
|
|
|
|
EXPECT_NO_ERRNO(WaitFor(child, 0));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Wait for specific child to exit.
|
|
|
|
// A non-CLONE_THREAD child which does not send SIGCHLD upon exit can be waited
|
|
|
|
// on, but returns ECHILD.
|
|
|
|
TEST_P(WaitSpecificChildTest, CloneNoSIGCHLD) {
|
|
|
|
uintptr_t stack;
|
|
|
|
ASSERT_THAT(stack = AllocStack(), SyscallSucceeds());
|
2019-01-15 00:11:13 +00:00
|
|
|
auto free =
|
|
|
|
Cleanup([stack] { ASSERT_THAT(FreeStack(stack), SyscallSucceeds()); });
|
2018-12-10 22:41:40 +00:00
|
|
|
|
|
|
|
int child;
|
|
|
|
ASSERT_THAT(child = CloneAndExit(0, stack, 0), SyscallSucceeds());
|
|
|
|
|
2019-05-23 01:10:54 +00:00
|
|
|
EXPECT_THAT(WaitFor(child, 0), PosixErrorIs(ECHILD, ::testing::_));
|
2018-12-10 22:41:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Waiting after the child has already exited returns immediately.
|
|
|
|
TEST_P(WaitSpecificChildTest, CloneAfterExit) {
|
|
|
|
uintptr_t stack;
|
|
|
|
ASSERT_THAT(stack = AllocStack(), SyscallSucceeds());
|
2019-01-15 00:11:13 +00:00
|
|
|
auto free =
|
|
|
|
Cleanup([stack] { ASSERT_THAT(FreeStack(stack), SyscallSucceeds()); });
|
2018-12-10 22:41:40 +00:00
|
|
|
|
|
|
|
int child;
|
|
|
|
// Send SIGCHLD for normal wait semantics.
|
|
|
|
ASSERT_THAT(child = CloneAndExit(0, stack, SIGCHLD), SyscallSucceeds());
|
|
|
|
|
|
|
|
absl::SleepFor(absl::Seconds(5));
|
|
|
|
|
|
|
|
EXPECT_NO_ERRNO(WaitFor(child, 0));
|
|
|
|
}
|
|
|
|
|
|
|
|
// A CLONE_THREAD child cannot be waited on.
|
|
|
|
TEST_P(WaitSpecificChildTest, CloneThread) {
|
|
|
|
uintptr_t stack;
|
|
|
|
ASSERT_THAT(stack = AllocStack(), SyscallSucceeds());
|
2019-01-15 00:11:13 +00:00
|
|
|
auto free =
|
|
|
|
Cleanup([stack] { ASSERT_THAT(FreeStack(stack), SyscallSucceeds()); });
|
2018-12-10 22:41:40 +00:00
|
|
|
|
|
|
|
int child;
|
|
|
|
ASSERT_THAT(child = CloneAndExit(15, stack, CLONE_THREAD), SyscallSucceeds());
|
|
|
|
auto start = absl::Now();
|
|
|
|
|
2019-05-23 01:10:54 +00:00
|
|
|
EXPECT_THAT(WaitFor(child, 0), PosixErrorIs(ECHILD, ::testing::_));
|
2018-12-10 22:41:40 +00:00
|
|
|
|
|
|
|
// Ensure wait4 didn't block.
|
|
|
|
EXPECT_LE(absl::Now() - start, absl::Seconds(10));
|
|
|
|
|
|
|
|
// Since we can't wait on the child, we sleep to try to avoid freeing its
|
|
|
|
// stack before it exits.
|
|
|
|
absl::SleepFor(absl::Seconds(5));
|
|
|
|
}
|
|
|
|
|
2019-05-23 01:10:54 +00:00
|
|
|
// 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));
|
|
|
|
}
|
|
|
|
|
2018-12-10 22:41:40 +00:00
|
|
|
// Return ECHILD for bad child.
|
|
|
|
TEST_P(WaitSpecificChildTest, BadChild) {
|
2019-05-23 01:10:54 +00:00
|
|
|
EXPECT_THAT(WaitFor(42, 0), PosixErrorIs(ECHILD, ::testing::_));
|
2018-12-10 22:41:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Wait for a child process that only exits after calling execve(2) from a
|
|
|
|
// non-leader thread.
|
|
|
|
TEST_P(WaitSpecificChildTest, AfterChildExecve) {
|
|
|
|
ExecveArray const owned_child_argv = {"/bin/true"};
|
|
|
|
char* const* const child_argv = owned_child_argv.get();
|
|
|
|
|
|
|
|
uintptr_t stack;
|
|
|
|
ASSERT_THAT(stack = AllocStack(), SyscallSucceeds());
|
2019-01-15 00:11:13 +00:00
|
|
|
auto free =
|
|
|
|
Cleanup([stack] { ASSERT_THAT(FreeStack(stack), SyscallSucceeds()); });
|
2018-12-10 22:41:40 +00:00
|
|
|
|
|
|
|
pid_t const child = fork();
|
|
|
|
if (child == 0) {
|
|
|
|
// Give the parent some time to start waiting.
|
|
|
|
SleepSafe(absl::Seconds(5));
|
|
|
|
// Pass CLONE_VFORK to block the original thread in the child process until
|
|
|
|
// the clone thread calls execve, annihilating them both. (This means that
|
|
|
|
// if clone returns at all, something went wrong.)
|
|
|
|
//
|
|
|
|
// N.B. clone(2) is not officially async-signal-safe, but at minimum glibc's
|
|
|
|
// x86_64 implementation is safe. See glibc
|
|
|
|
// sysdeps/unix/sysv/linux/x86_64/clone.S.
|
|
|
|
clone(
|
|
|
|
+[](void* arg) {
|
|
|
|
auto child_argv = static_cast<char* const*>(arg);
|
|
|
|
execve(child_argv[0], child_argv, /* envp = */ nullptr);
|
|
|
|
return errno;
|
|
|
|
},
|
|
|
|
reinterpret_cast<void*>(stack),
|
|
|
|
CLONE_FILES | CLONE_FS | CLONE_SIGHAND | CLONE_THREAD | CLONE_VM |
|
|
|
|
CLONE_VFORK,
|
|
|
|
const_cast<char**>(child_argv));
|
|
|
|
_exit(errno);
|
|
|
|
}
|
2019-01-11 22:47:45 +00:00
|
|
|
ASSERT_THAT(child, SyscallSucceeds());
|
2018-12-10 22:41:40 +00:00
|
|
|
EXPECT_NO_ERRNO(WaitFor(child, 0));
|
|
|
|
}
|
|
|
|
|
2019-05-22 22:53:13 +00:00
|
|
|
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,
|
2018-12-10 22:41:40 +00:00
|
|
|
", wanted ", pid));
|
2019-05-22 22:53:13 +00:00
|
|
|
}
|
|
|
|
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,
|
2018-12-10 22:41:40 +00:00
|
|
|
", wanted ", CLD_EXITED));
|
2019-05-22 22:53:13 +00:00
|
|
|
}
|
|
|
|
return NoError();
|
|
|
|
}
|
|
|
|
|
|
|
|
INSTANTIATE_TEST_SUITE_P(
|
|
|
|
Waiters, WaitSpecificChildTest,
|
|
|
|
::testing::Values(std::make_tuple(SYS_wait4, CheckWait4),
|
|
|
|
std::make_tuple(SYS_waitid, CheckWaitid)));
|
2018-12-10 22:41:40 +00:00
|
|
|
|
|
|
|
// WIFEXITED, WIFSIGNALED, WTERMSIG indicate signal exit.
|
|
|
|
TEST(WaitTest, SignalExit) {
|
|
|
|
pid_t child;
|
|
|
|
ASSERT_THAT(child = ForkAndExit(0, 10), SyscallSucceeds());
|
|
|
|
|
|
|
|
EXPECT_THAT(kill(child, SIGKILL), SyscallSucceeds());
|
|
|
|
|
|
|
|
int status;
|
|
|
|
EXPECT_THAT(Wait4(child, &status, 0, nullptr),
|
|
|
|
SyscallSucceedsWithValue(child));
|
|
|
|
|
|
|
|
EXPECT_FALSE(WIFEXITED(status));
|
|
|
|
EXPECT_TRUE(WIFSIGNALED(status));
|
|
|
|
EXPECT_EQ(SIGKILL, WTERMSIG(status));
|
|
|
|
}
|
|
|
|
|
|
|
|
// waitid requires at least one option.
|
|
|
|
TEST(WaitTest, WaitidOptions) {
|
|
|
|
EXPECT_THAT(Waitid(P_ALL, 0, nullptr, 0), SyscallFailsWithErrno(EINVAL));
|
|
|
|
}
|
|
|
|
|
|
|
|
// waitid does not wait for a child to exit if not passed WEXITED.
|
|
|
|
TEST(WaitTest, WaitidNoWEXITED) {
|
|
|
|
pid_t child;
|
|
|
|
ASSERT_THAT(child = ForkAndExit(0, 0), SyscallSucceeds());
|
|
|
|
EXPECT_THAT(Waitid(P_ALL, 0, nullptr, WSTOPPED),
|
|
|
|
SyscallFailsWithErrno(ECHILD));
|
|
|
|
EXPECT_THAT(Waitid(P_ALL, 0, nullptr, WEXITED), SyscallSucceeds());
|
|
|
|
}
|
|
|
|
|
|
|
|
// WNOWAIT allows the same wait result to be returned again.
|
|
|
|
TEST(WaitTest, WaitidWNOWAIT) {
|
|
|
|
pid_t child;
|
|
|
|
ASSERT_THAT(child = ForkAndExit(42, 0), SyscallSucceeds());
|
|
|
|
|
|
|
|
siginfo_t info;
|
|
|
|
ASSERT_THAT(Waitid(P_PID, child, &info, WEXITED | WNOWAIT),
|
|
|
|
SyscallSucceeds());
|
|
|
|
EXPECT_EQ(child, info.si_pid);
|
|
|
|
EXPECT_EQ(SIGCHLD, info.si_signo);
|
|
|
|
EXPECT_EQ(CLD_EXITED, info.si_code);
|
|
|
|
EXPECT_EQ(42, info.si_status);
|
|
|
|
|
|
|
|
ASSERT_THAT(Waitid(P_PID, child, &info, WEXITED), SyscallSucceeds());
|
|
|
|
EXPECT_EQ(child, info.si_pid);
|
|
|
|
EXPECT_EQ(SIGCHLD, info.si_signo);
|
|
|
|
EXPECT_EQ(CLD_EXITED, info.si_code);
|
|
|
|
EXPECT_EQ(42, info.si_status);
|
|
|
|
|
|
|
|
EXPECT_THAT(Waitid(P_PID, child, &info, WEXITED),
|
|
|
|
SyscallFailsWithErrno(ECHILD));
|
|
|
|
}
|
|
|
|
|
|
|
|
// waitpid(pid, status, options) is equivalent to
|
|
|
|
// wait4(pid, status, options, nullptr).
|
|
|
|
// This is a dedicated syscall on i386, glibc maps it to wait4 on amd64.
|
|
|
|
TEST(WaitTest, WaitPid) {
|
|
|
|
pid_t child;
|
|
|
|
ASSERT_THAT(child = ForkAndExit(42, 0), SyscallSucceeds());
|
|
|
|
|
|
|
|
int status;
|
|
|
|
EXPECT_THAT(RetryEINTR(waitpid)(child, &status, 0),
|
|
|
|
SyscallSucceedsWithValue(child));
|
|
|
|
|
|
|
|
EXPECT_TRUE(WIFEXITED(status));
|
|
|
|
EXPECT_EQ(42, WEXITSTATUS(status));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Test that signaling a zombie succeeds. This is a signals test that is in this
|
|
|
|
// file for some reason.
|
|
|
|
TEST(WaitTest, KillZombie) {
|
|
|
|
pid_t child;
|
|
|
|
ASSERT_THAT(child = ForkAndExit(42, 0), SyscallSucceeds());
|
|
|
|
|
|
|
|
// Sleep for three seconds to ensure the child has exited.
|
|
|
|
absl::SleepFor(absl::Seconds(3));
|
|
|
|
|
|
|
|
// The child is now a zombie. Check that killing it returns 0.
|
|
|
|
EXPECT_THAT(kill(child, SIGTERM), SyscallSucceeds());
|
|
|
|
EXPECT_THAT(kill(child, 0), SyscallSucceeds());
|
|
|
|
|
|
|
|
EXPECT_THAT(Wait4(child, nullptr, 0, nullptr),
|
|
|
|
SyscallSucceedsWithValue(child));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(WaitTest, Wait4Rusage) {
|
|
|
|
pid_t child;
|
|
|
|
constexpr absl::Duration kSpin = absl::Seconds(3);
|
|
|
|
ASSERT_THAT(child = ForkSpinAndExit(21, absl::ToInt64Seconds(kSpin)),
|
|
|
|
SyscallSucceeds());
|
|
|
|
|
|
|
|
int status;
|
|
|
|
struct rusage rusage = {};
|
|
|
|
ASSERT_THAT(Wait4(child, &status, 0, &rusage),
|
|
|
|
SyscallSucceedsWithValue(child));
|
|
|
|
|
|
|
|
EXPECT_TRUE(WIFEXITED(status));
|
|
|
|
EXPECT_EQ(21, WEXITSTATUS(status));
|
|
|
|
|
|
|
|
EXPECT_GE(RusageCpuTime(rusage), kSpin);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(WaitTest, WaitidRusage) {
|
|
|
|
pid_t child;
|
|
|
|
constexpr absl::Duration kSpin = absl::Seconds(3);
|
|
|
|
ASSERT_THAT(child = ForkSpinAndExit(27, absl::ToInt64Seconds(kSpin)),
|
|
|
|
SyscallSucceeds());
|
|
|
|
|
|
|
|
siginfo_t si = {};
|
|
|
|
struct rusage rusage = {};
|
|
|
|
|
|
|
|
// From waitid(2):
|
|
|
|
// The raw waitid() system call takes a fifth argument, of type
|
|
|
|
// struct rusage *. If this argument is non-NULL, then it is used
|
|
|
|
// to return resource usage information about the child, in the
|
|
|
|
// same manner as wait4(2).
|
|
|
|
EXPECT_THAT(
|
|
|
|
RetryEINTR(syscall)(SYS_waitid, P_PID, child, &si, WEXITED, &rusage),
|
|
|
|
SyscallSucceeds());
|
|
|
|
EXPECT_EQ(si.si_signo, SIGCHLD);
|
|
|
|
EXPECT_EQ(si.si_code, CLD_EXITED);
|
|
|
|
EXPECT_EQ(si.si_status, 27);
|
|
|
|
EXPECT_EQ(si.si_pid, child);
|
|
|
|
|
|
|
|
EXPECT_GE(RusageCpuTime(rusage), kSpin);
|
|
|
|
}
|
|
|
|
|
2019-05-29 01:02:07 +00:00
|
|
|
// After bf959931ddb88c4e4366e96dd22e68fa0db9527c ("wait/ptrace: assume __WALL
|
|
|
|
// if the child is traced") (Linux 4.7), tracees are always eligible for
|
|
|
|
// waiting, regardless of type.
|
|
|
|
TEST(WaitTest, TraceeWALL) {
|
|
|
|
int fds[2];
|
|
|
|
ASSERT_THAT(pipe(fds), SyscallSucceeds());
|
|
|
|
FileDescriptor rfd(fds[0]);
|
|
|
|
FileDescriptor wfd(fds[1]);
|
|
|
|
|
|
|
|
pid_t child = fork();
|
|
|
|
if (child == 0) {
|
|
|
|
// Child.
|
|
|
|
rfd.reset();
|
|
|
|
|
|
|
|
TEST_PCHECK(ptrace(PTRACE_TRACEME, 0, nullptr, nullptr) == 0);
|
|
|
|
|
|
|
|
// Notify parent that we're now a tracee.
|
|
|
|
wfd.reset();
|
|
|
|
|
|
|
|
_exit(0);
|
|
|
|
}
|
|
|
|
ASSERT_THAT(child, SyscallSucceeds());
|
|
|
|
|
|
|
|
wfd.reset();
|
|
|
|
|
|
|
|
// Wait for child to become tracee.
|
|
|
|
char c;
|
|
|
|
EXPECT_THAT(ReadFd(rfd.get(), &c, sizeof(c)), SyscallSucceedsWithValue(0));
|
|
|
|
|
|
|
|
// We can wait on the fork child with WCLONE, as it is a tracee.
|
|
|
|
int status;
|
|
|
|
if (IsRunningOnGvisor()) {
|
|
|
|
ASSERT_THAT(Wait4(child, &status, __WCLONE, nullptr),
|
|
|
|
SyscallSucceedsWithValue(child));
|
|
|
|
|
|
|
|
EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) << status;
|
|
|
|
} else {
|
|
|
|
// On older versions of Linux, we may get ECHILD.
|
|
|
|
ASSERT_THAT(Wait4(child, &status, __WCLONE, nullptr),
|
|
|
|
::testing::AnyOf(SyscallSucceedsWithValue(child),
|
|
|
|
SyscallFailsWithErrno(ECHILD)));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-10 22:41:40 +00:00
|
|
|
} // namespace
|
|
|
|
|
|
|
|
} // namespace testing
|
|
|
|
} // namespace gvisor
|