• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2024 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "sandboxed_api/sandbox2/util/pid_waiter.h"
16 
17 #include <sys/wait.h>
18 
19 #include <cerrno>
20 #include <memory>
21 
22 #include "absl/cleanup/cleanup.h"
23 #include "absl/synchronization/mutex.h"
24 #include "absl/time/time.h"
25 #include "sandboxed_api/sandbox2/util/deadline_manager.h"
26 
27 namespace sandbox2 {
28 
29 namespace {
30 
31 class OsWaitPid : public PidWaiter::WaitPidInterface {
32  public:
WaitPid(pid_t pid,int * status,int flags)33   int WaitPid(pid_t pid, int* status, int flags) override {
34     return waitpid(pid, status, flags);
35   }
36 };
37 
38 }  // namespace
39 
PidWaiter(pid_t priority_pid)40 PidWaiter::PidWaiter(pid_t priority_pid)
41     : PidWaiter(priority_pid, std::make_unique<OsWaitPid>()) {}
42 
Wait(int * status)43 int PidWaiter::Wait(int* status) {
44   RefillStatuses();
45 
46   if (statuses_.empty()) {
47     if (last_errno_ == 0) return 0;
48     errno = last_errno_;
49     last_errno_ = 0;
50     return -1;
51   }
52 
53   const auto& entry = statuses_.front();
54   pid_t pid = entry.first;
55   *status = entry.second;
56   statuses_.pop_front();
57   return pid;
58 }
59 
CheckStatus(pid_t pid,bool blocking)60 bool PidWaiter::CheckStatus(pid_t pid, bool blocking) {
61   int status;
62   int flags = __WNOTHREAD | __WALL | WUNTRACED;
63   if (!blocking) {
64     // It should be a non-blocking operation (hence WNOHANG), so this function
65     // returns quickly if there are no events to be processed.
66     flags |= WNOHANG;
67   }
68   pid_t ret = wait_pid_iface_->WaitPid(pid, &status, flags);
69   if (ret < 0) {
70     last_errno_ = errno;
71     return true;
72   }
73   if (ret == 0) {
74     return false;
75   }
76   statuses_.emplace_back(ret, status);
77   return true;
78 }
79 
RefillStatuses()80 void PidWaiter::RefillStatuses() {
81   constexpr int kMaxIterations = 1000;
82   constexpr int kPriorityCheckPeriod = 100;
83   absl::Cleanup notify = [this] {
84     absl::MutexLock lock(&notify_mutex_);
85     notified_ = false;
86   };
87   if (!statuses_.empty()) {
88     return;
89   }
90   for (int i = 0; last_errno_ == 0 && i < kMaxIterations; ++i) {
91     bool should_check_priority =
92         priority_pid_ != -1 && (i % kPriorityCheckPeriod) == 0;
93     if (should_check_priority && CheckStatus(priority_pid_)) {
94       return;
95     }
96     if (!CheckStatus(-1)) {
97       break;
98     }
99   }
100   if (statuses_.empty()) {
101     DeadlineRegistration* deadline_registration = nullptr;
102     {
103       absl::MutexLock lock(&notify_mutex_);
104       if (deadline_ == absl::InfinitePast() || notified_) {
105         return;
106       }
107       if (!deadline_registration_.has_value()) {
108         deadline_registration_.emplace(DeadlineManager::instance());
109       }
110       deadline_registration_->SetDeadline(deadline_);
111       // DeadlineRegistration is only constructed once, so it's safe to use it
112       // outside of the lock.
113       deadline_registration = &*deadline_registration_;
114     }
115     deadline_registration->ExecuteBlockingSyscall(
116         [&] { CheckStatus(-1, /*blocking=*/true); });
117   }
118 }
119 
120 }  // namespace sandbox2
121