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/deadline_manager.h"
16
17 #include <sys/syscall.h>
18
19 #include <algorithm>
20 #include <csignal>
21 #include <cstdint>
22 #include <memory>
23 #include <string>
24
25 #include "absl/base/call_once.h"
26 #include "absl/flags/flag.h"
27 #include "absl/functional/function_ref.h"
28 #include "absl/log/check.h"
29 #include "absl/log/log.h"
30 #include "absl/strings/str_cat.h"
31 #include "absl/strings/string_view.h"
32 #include "absl/synchronization/mutex.h"
33 #include "absl/time/clock.h"
34 #include "absl/time/time.h"
35 #include "sandboxed_api/sandbox2/util.h"
36 #include "sandboxed_api/util/thread.h"
37
38 ABSL_FLAG(int, sandbox2_deadline_manager_signal, SIGRTMAX - 1,
39 "Signal to use for deadline notifications - must be not otherwise "
40 "used by the process (default: SIGRTMAX - 1)");
41
42 namespace sandbox2 {
43 namespace {
44 constexpr int kFailedNotificationsThreshold = 32;
45
RoundUpTo(absl::Time time,absl::Duration resolution)46 absl::Time RoundUpTo(absl::Time time, absl::Duration resolution) {
47 return time == absl::InfiniteFuture()
48 ? absl::InfiniteFuture()
49 : absl::UnixEpoch() +
50 absl::Ceil(time - absl::UnixEpoch(), resolution);
51 }
52 } // namespace
53
54 int DeadlineManager::signal_nr_ = -1;
55
DeadlineRegistration(DeadlineManager & manager)56 DeadlineRegistration::DeadlineRegistration(DeadlineManager& manager)
57 : manager_(manager) {
58 manager_.Register(*this);
59 }
60
~DeadlineRegistration()61 DeadlineRegistration::~DeadlineRegistration() { manager_.Unregister(*this); }
62
SetDeadline(absl::Time deadline)63 void DeadlineRegistration::SetDeadline(absl::Time deadline) {
64 if (deadline != last_deadline_) {
65 manager_.AdjustDeadline(*this, deadline);
66 last_deadline_ = deadline;
67 }
68 }
69
ExecuteBlockingSyscall(absl::FunctionRef<void ()> blocking_fn)70 void DeadlineRegistration::ExecuteBlockingSyscall(
71 absl::FunctionRef<void()> blocking_fn) {
72 {
73 absl::MutexLock lock(&data_->mutex);
74 data_->tid = util::Syscall(__NR_gettid);
75 if (data_->notification_attempt > 0 || data_->deadline <= absl::Now()) {
76 return;
77 }
78 data_->in_blocking_fn = true;
79 }
80 blocking_fn();
81 {
82 absl::MutexLock lock(&data_->mutex);
83 data_->in_blocking_fn = false;
84 }
85 }
86
DeadlineManager(absl::string_view name)87 DeadlineManager::DeadlineManager(absl::string_view name) {
88 RegisterSignalHandler();
89 thread_ = sapi::Thread(this, &DeadlineManager::Run, name);
90 }
91
~DeadlineManager()92 DeadlineManager::~DeadlineManager() {
93 {
94 absl::MutexLock lock(®istration_mutex_);
95 CHECK_EQ(registered_deadlines_, 0);
96 }
97 {
98 absl::MutexLock lock(&queue_mutex_);
99 cancelled_ = true;
100 }
101 if (thread_.IsJoinable()) {
102 thread_.Join();
103 }
104 }
105
SignalHandler(int signal)106 void DeadlineManager::SignalHandler(int signal) {}
107
AdjustDeadline(DeadlineRegistration & registration,absl::Time deadline)108 void DeadlineManager::AdjustDeadline(DeadlineRegistration& registration,
109 absl::Time deadline) {
110 absl::MutexLock lock(&queue_mutex_);
111 queue_.erase(registration.data_.get());
112 absl::MutexLock data_lock(®istration.data_->mutex);
113 registration.data_->notification_attempt = 0;
114 registration.data_->deadline = RoundUpTo(deadline, kResolution);
115 if (deadline != absl::InfiniteFuture()) {
116 queue_.insert(registration.data_.get());
117 }
118 }
119
RegisterSignalHandler()120 void DeadlineManager::RegisterSignalHandler() {
121 static absl::once_flag once;
122 absl::call_once(once, [] {
123 signal_nr_ = absl::GetFlag(FLAGS_sandbox2_deadline_manager_signal);
124 struct sigaction sa = {};
125 sa.sa_flags = 0;
126 sa.sa_handler = DeadlineManager::SignalHandler;
127 struct sigaction old = {};
128 PCHECK(sigaction(signal_nr_, &sa, &old) == 0);
129 // Verify that previously there was no handler set.
130 if (old.sa_flags & SA_SIGINFO) {
131 LOG_IF(WARNING, old.sa_sigaction != nullptr)
132 << "Signal handler was already registered";
133 } else {
134 LOG_IF(WARNING, old.sa_handler != nullptr)
135 << "Signal handler was already registered";
136 }
137 return true;
138 });
139 }
140
VerifySignalHandler()141 void DeadlineManager::VerifySignalHandler() {
142 struct sigaction old = {};
143 PCHECK(sigaction(signal_nr_, nullptr, &old) == 0);
144 auto describe = [](uintptr_t handler) -> std::string {
145 return absl::StrCat(absl::Hex(handler));
146 };
147 uintptr_t old_handler = (old.sa_flags & SA_SIGINFO)
148 ? reinterpret_cast<uintptr_t>(old.sa_sigaction)
149 : reinterpret_cast<uintptr_t>(old.sa_handler);
150 CHECK(old_handler ==
151 reinterpret_cast<uintptr_t>(DeadlineManager::SignalHandler))
152 << "Signal handler was overriden " << describe(old_handler);
153 CHECK((old.sa_flags & SA_RESTART) == 0)
154 << "SA_RESTART signal handler flag was overriden: "
155 << absl::Hex(old.sa_flags);
156 }
157
Run()158 void DeadlineManager::Run() {
159 absl::Time next_deadline;
160 auto next_deadline_changed_or_cancelled = [this, &next_deadline]() -> bool {
161 queue_mutex_.AssertReaderHeld();
162 return (!queue_.empty() && next_deadline != (*queue_.begin())->deadline) ||
163 cancelled_;
164 };
165 absl::MutexLock lock(&queue_mutex_);
166 while (!cancelled_) {
167 next_deadline = absl::InfiniteFuture();
168 if (!queue_.empty()) {
169 next_deadline = (*queue_.begin())->deadline;
170 }
171 if (queue_mutex_.AwaitWithDeadline(
172 absl::Condition(&next_deadline_changed_or_cancelled),
173 next_deadline)) {
174 continue;
175 }
176 while (!queue_.empty() && (*queue_.begin())->deadline <= next_deadline) {
177 DeadlineRegistration::Data* entry = *queue_.begin();
178 queue_.erase(queue_.begin());
179 absl::MutexLock lock(&entry->mutex);
180 if (++entry->notification_attempt > kFailedNotificationsThreshold) {
181 VerifySignalHandler();
182 }
183 if (entry->in_blocking_fn) {
184 constexpr int kExponentialBackoffRate = 8;
185 constexpr int kMaxExponentialBackoff = 10;
186 int exp_backoff =
187 1 << std::min(entry->notification_attempt / kExponentialBackoffRate,
188 kMaxExponentialBackoff);
189 absl::Time next_notification_time =
190 RoundUpTo(absl::Now() + kResolution * exp_backoff, kResolution);
191 util::Syscall(__NR_tgkill, getpid(), entry->tid, signal_nr_);
192 entry->deadline = next_notification_time;
193 queue_.insert(entry);
194 }
195 }
196 }
197 }
198
199 } // namespace sandbox2
200