| Index: sandbox/linux/suid/sandbox.c
|
| diff --git a/sandbox/linux/suid/sandbox.c b/sandbox/linux/suid/sandbox.c
|
| index f2867eca6381cddc041461c8989fe8d9ad5fd259..24360928900d09fed14393fc2dc310d85717c552 100644
|
| --- a/sandbox/linux/suid/sandbox.c
|
| +++ b/sandbox/linux/suid/sandbox.c
|
| @@ -26,6 +26,7 @@
|
| #include <sys/time.h>
|
| #include <sys/types.h>
|
| #include <sys/vfs.h>
|
| +#include <sys/wait.h>
|
| #include <unistd.h>
|
|
|
| #include "linux_util.h"
|
| @@ -41,12 +42,18 @@
|
|
|
| static const char kSandboxDescriptorEnvironmentVarName[] = "SBX_D";
|
| static const char kSandboxHelperPidEnvironmentVarName[] = "SBX_HELPER_PID";
|
| +// This number must be kept in sync with common/zygote_commands_linux.h
|
| +static const int kZygoteIdFd = 7;
|
|
|
| // These are the magic byte values which the sandboxed process uses to request
|
| // that it be chrooted.
|
| static const char kMsgChrootMe = 'C';
|
| static const char kMsgChrootSuccessful = 'O';
|
|
|
| +static bool DropRoot();
|
| +
|
| +#define HANDLE_EINTR(x) TEMP_FAILURE_RETRY(x)
|
| +
|
| static void FatalError(const char *msg, ...)
|
| __attribute__((noreturn, format(printf, 1, 2)));
|
|
|
| @@ -57,6 +64,7 @@ static void FatalError(const char *msg, ...) {
|
| vfprintf(stderr, msg, ap);
|
| fprintf(stderr, ": %s\n", strerror(errno));
|
| fflush(stderr);
|
| + va_end(ap);
|
| _exit(1);
|
| }
|
|
|
| @@ -192,6 +200,25 @@ static bool SpawnChrootHelper() {
|
| return true;
|
| }
|
|
|
| +// Block until child_pid exits, then exit. Try to preserve the exit code.
|
| +static void WaitForChildAndExit(pid_t child_pid) {
|
| + int exit_code = -1;
|
| + siginfo_t reaped_child_info;
|
| +
|
| + int wait_ret =
|
| + HANDLE_EINTR(waitid(P_PID, child_pid, &reaped_child_info, WEXITED));
|
| +
|
| + if (!wait_ret && reaped_child_info.si_pid == child_pid) {
|
| + if (reaped_child_info.si_code == CLD_EXITED) {
|
| + exit_code = reaped_child_info.si_status;
|
| + } else {
|
| + // Exit with code 0 if the child got signaled.
|
| + exit_code = 0;
|
| + }
|
| + }
|
| + _exit(exit_code);
|
| +}
|
| +
|
| static bool MoveToNewNamespaces() {
|
| // These are the sets of flags which we'll try, in order.
|
| const int kCloneExtraFlags[] = {
|
| @@ -199,15 +226,53 @@ static bool MoveToNewNamespaces() {
|
| CLONE_NEWPID,
|
| };
|
|
|
| + // We need to close kZygoteIdFd before the child can continue. We use this
|
| + // socketpair to tell the child when to continue;
|
| + int sync_fds[2];
|
| + if (socketpair(AF_UNIX, SOCK_STREAM, 0, sync_fds)) {
|
| + FatalError("Failed to create a socketpair");
|
| + }
|
| +
|
| for (size_t i = 0;
|
| i < sizeof(kCloneExtraFlags) / sizeof(kCloneExtraFlags[0]);
|
| i++) {
|
| pid_t pid = syscall(__NR_clone, SIGCHLD | kCloneExtraFlags[i], 0, 0, 0);
|
|
|
| - if (pid > 0)
|
| - _exit(0);
|
| + if (pid > 0) {
|
| + if (!DropRoot()) {
|
| + FatalError("Could not drop privileges");
|
| + } else {
|
| + if (close(sync_fds[0]) || shutdown(sync_fds[1], SHUT_RD))
|
| + FatalError("Could not close socketpair");
|
| + // The kZygoteIdFd needs to be closed in the parent before
|
| + // Zygote gets started.
|
| + if (close(kZygoteIdFd))
|
| + FatalError("close");
|
| + // Tell our child to continue
|
| + if (HANDLE_EINTR(send(sync_fds[1], "C", 1, MSG_NOSIGNAL)) != 1)
|
| + FatalError("send");
|
| + if (close(sync_fds[1]))
|
| + FatalError("close");
|
| + // We want to keep a full process tree and we don't want our childs to
|
| + // be reparented to (the outer PID namespace) init. So we wait for it.
|
| + WaitForChildAndExit(pid);
|
| + }
|
| + // NOTREACHED
|
| + FatalError("Not reached");
|
| + }
|
|
|
| if (pid == 0) {
|
| + if (close(sync_fds[1]) || shutdown(sync_fds[0], SHUT_WR))
|
| + FatalError("Could not close socketpair");
|
| +
|
| + // Wait for the parent to confirm it closed kZygoteIdFd before we
|
| + // continue
|
| + char should_continue;
|
| + if (HANDLE_EINTR(read(sync_fds[0], &should_continue, 1)) != 1)
|
| + FatalError("Read on socketpair");
|
| + if (close(sync_fds[0]))
|
| + FatalError("close");
|
| +
|
| if (kCloneExtraFlags[i] & CLONE_NEWPID) {
|
| setenv("SBX_PID_NS", "", 1 /* overwrite */);
|
| } else {
|
|
|