1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "sandbox/linux/services/scoped_process.h"
6 
7 #include <fcntl.h>
8 #include <signal.h>
9 #include <sys/stat.h>
10 #include <sys/syscall.h>
11 #include <sys/types.h>
12 #include <sys/wait.h>
13 #include <unistd.h>
14 
15 #include "base/callback.h"
16 #include "base/logging.h"
17 #include "base/posix/eintr_wrapper.h"
18 #include "build/build_config.h"
19 #include "sandbox/linux/services/syscall_wrappers.h"
20 #include "sandbox/linux/services/thread_helpers.h"
21 
22 namespace sandbox {
23 
24 namespace {
25 
26 const char kSynchronisationChar[] = "D";
27 
WaitForever()28 void WaitForever() {
29   while(true) {
30     pause();
31   }
32 }
33 
34 }  // namespace
35 
ScopedProcess(const base::Closure & child_callback)36 ScopedProcess::ScopedProcess(const base::Closure& child_callback)
37     : child_process_id_(-1), process_id_(getpid()) {
38   PCHECK(0 == pipe(pipe_fds_));
39 #if !defined(THREAD_SANITIZER)
40   // Make sure that we can safely fork().
41   CHECK(ThreadHelpers::IsSingleThreaded());
42 #endif
43   child_process_id_ = fork();
44   PCHECK(0 <= child_process_id_);
45 
46   if (0 == child_process_id_) {
47     PCHECK(0 == IGNORE_EINTR(close(pipe_fds_[0])));
48     pipe_fds_[0] = -1;
49     child_callback.Run();
50     // Notify the parent that the closure has run.
51     CHECK_EQ(1, HANDLE_EINTR(write(pipe_fds_[1], kSynchronisationChar, 1)));
52     WaitForever();
53     NOTREACHED();
54     _exit(1);
55   }
56 
57   PCHECK(0 == IGNORE_EINTR(close(pipe_fds_[1])));
58   pipe_fds_[1] = -1;
59 }
60 
~ScopedProcess()61 ScopedProcess::~ScopedProcess() {
62   CHECK(IsOriginalProcess());
63   if (child_process_id_ >= 0) {
64     PCHECK(0 == kill(child_process_id_, SIGKILL));
65     siginfo_t process_info;
66 
67     PCHECK(0 == HANDLE_EINTR(
68                     waitid(P_PID, child_process_id_, &process_info, WEXITED)));
69   }
70   if (pipe_fds_[0] >= 0) {
71     PCHECK(0 == IGNORE_EINTR(close(pipe_fds_[0])));
72   }
73   if (pipe_fds_[1] >= 0) {
74     PCHECK(0 == IGNORE_EINTR(close(pipe_fds_[1])));
75   }
76 }
77 
WaitForExit(bool * got_signaled)78 int ScopedProcess::WaitForExit(bool* got_signaled) {
79   DCHECK(got_signaled);
80   CHECK(IsOriginalProcess());
81   siginfo_t process_info;
82   // WNOWAIT to make sure that the destructor can wait on the child.
83   int ret = HANDLE_EINTR(
84       waitid(P_PID, child_process_id_, &process_info, WEXITED | WNOWAIT));
85   PCHECK(0 == ret) << "Did something else wait on the child?";
86 
87   if (process_info.si_code == CLD_EXITED) {
88     *got_signaled = false;
89   } else if (process_info.si_code == CLD_KILLED ||
90              process_info.si_code == CLD_DUMPED) {
91     *got_signaled = true;
92   } else {
93     CHECK(false) << "ScopedProcess needs to be extended for si_code "
94                  << process_info.si_code;
95   }
96   return process_info.si_status;
97 }
98 
WaitForClosureToRun()99 bool ScopedProcess::WaitForClosureToRun() {
100   char c = 0;
101   int ret = HANDLE_EINTR(read(pipe_fds_[0], &c, 1));
102   PCHECK(ret >= 0);
103   if (0 == ret)
104     return false;
105 
106   CHECK_EQ(c, kSynchronisationChar[0]);
107   return true;
108 }
109 
110 // It would be problematic if after a fork(), another process would start using
111 // this object.
112 // This method allows to assert it is not happening.
IsOriginalProcess()113 bool ScopedProcess::IsOriginalProcess() {
114   // Make a direct syscall to bypass glibc caching of PIDs.
115   pid_t pid = sys_getpid();
116   return pid == process_id_;
117 }
118 
119 }  // namespace sandbox
120