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(¬ify_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(¬ify_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