| Index: sandbox/linux/services/namespace_sandbox_unittest.cc | 
| diff --git a/sandbox/linux/services/namespace_sandbox_unittest.cc b/sandbox/linux/services/namespace_sandbox_unittest.cc | 
| index 8bb0bff83b7027d86d8b8e08ae82e0ea7ddd8c5a..547ef6728c871cb1972993f84d44b9b615fcb9d3 100644 | 
| --- a/sandbox/linux/services/namespace_sandbox_unittest.cc | 
| +++ b/sandbox/linux/services/namespace_sandbox_unittest.cc | 
| @@ -4,6 +4,7 @@ | 
|  | 
| #include "sandbox/linux/services/namespace_sandbox.h" | 
|  | 
| +#include <signal.h> | 
| #include <sys/types.h> | 
| #include <sys/wait.h> | 
| #include <unistd.h> | 
| @@ -118,6 +119,99 @@ TEST_F(NamespaceSandboxTest, NestedNamespaceSandbox) { | 
| TestProc("NestedNamespaceSandbox"); | 
| } | 
|  | 
| +const int kNormalExitCode = 0; | 
| +const int kSignalTerminationExitCode = 255; | 
| + | 
| +// Ensure that CHECK(false) is distinguishable from _exit(kNormalExitCode). | 
| +// Allowing noise since CHECK(false) will write a stack trace to stderr. | 
| +SANDBOX_TEST_ALLOW_NOISE(ForkInNewPidNamespace, CheckDoesNotReturnZero) { | 
| +  if (!Credentials::CanCreateProcessInNewUserNS()) { | 
| +    return; | 
| +  } | 
| + | 
| +  CHECK(sandbox::Credentials::MoveToNewUserNS()); | 
| +  const pid_t pid = NamespaceSandbox::ForkInNewPidNamespace( | 
| +      /*drop_capabilities_in_child=*/true); | 
| +  CHECK_GE(pid, 0); | 
| + | 
| +  if (pid == 0) { | 
| +    CHECK(false); | 
| +    _exit(kNormalExitCode); | 
| +  } | 
| + | 
| +  int status; | 
| +  PCHECK(waitpid(pid, &status, 0) == pid); | 
| +  if (WIFEXITED(status)) { | 
| +    CHECK_NE(kNormalExitCode, WEXITSTATUS(status)); | 
| +  } | 
| +} | 
| + | 
| +SANDBOX_TEST(ForkInNewPidNamespace, BasicUsage) { | 
| +  if (!Credentials::CanCreateProcessInNewUserNS()) { | 
| +    return; | 
| +  } | 
| + | 
| +  CHECK(sandbox::Credentials::MoveToNewUserNS()); | 
| +  const pid_t pid = NamespaceSandbox::ForkInNewPidNamespace( | 
| +      /*drop_capabilities_in_child=*/true); | 
| +  CHECK_GE(pid, 0); | 
| + | 
| +  if (pid == 0) { | 
| +    CHECK_EQ(1, getpid()); | 
| +    CHECK(!Credentials::HasAnyCapability()); | 
| +    _exit(kNormalExitCode); | 
| +  } | 
| + | 
| +  int status; | 
| +  PCHECK(waitpid(pid, &status, 0) == pid); | 
| +  CHECK(WIFEXITED(status)); | 
| +  CHECK_EQ(kNormalExitCode, WEXITSTATUS(status)); | 
| +} | 
| + | 
| +SANDBOX_TEST(ForkInNewPidNamespace, ExitWithSignal) { | 
| +  if (!Credentials::CanCreateProcessInNewUserNS()) { | 
| +    return; | 
| +  } | 
| + | 
| +  CHECK(sandbox::Credentials::MoveToNewUserNS()); | 
| +  const pid_t pid = NamespaceSandbox::ForkInNewPidNamespace( | 
| +      /*drop_capabilities_in_child=*/true); | 
| +  CHECK_GE(pid, 0); | 
| + | 
| +  if (pid == 0) { | 
| +    CHECK_EQ(1, getpid()); | 
| +    CHECK(!Credentials::HasAnyCapability()); | 
| +    CHECK(NamespaceSandbox::InstallTerminationSignalHandler( | 
| +        SIGTERM, kSignalTerminationExitCode)); | 
| +    while (true) { | 
| +      raise(SIGTERM); | 
| +    } | 
| +  } | 
| + | 
| +  int status; | 
| +  PCHECK(waitpid(pid, &status, 0) == pid); | 
| +  CHECK(WIFEXITED(status)); | 
| +  CHECK_EQ(kSignalTerminationExitCode, WEXITSTATUS(status)); | 
| +} | 
| + | 
| +volatile sig_atomic_t signal_handler_called; | 
| +void ExitSuccessfully(int sig) { | 
| +  signal_handler_called = 1; | 
| +} | 
| + | 
| +SANDBOX_TEST(InstallTerminationSignalHandler, DoesNotOverrideExistingHandlers) { | 
| +  struct sigaction action = {}; | 
| +  action.sa_handler = &ExitSuccessfully; | 
| +  PCHECK(sigaction(SIGUSR1, &action, nullptr) == 0); | 
| + | 
| +  NamespaceSandbox::InstallDefaultTerminationSignalHandlers(); | 
| +  CHECK(!NamespaceSandbox::InstallTerminationSignalHandler( | 
| +            SIGUSR1, kSignalTerminationExitCode)); | 
| + | 
| +  raise(SIGUSR1); | 
| +  CHECK_EQ(1, signal_handler_called); | 
| +} | 
| + | 
| }  // namespace | 
|  | 
| }  // namespace sandbox | 
|  |