• 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/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(&registration_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(&registration.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