trap.cc revision effb81e5f8246d0db0270817048dc992db66e9fb
1// Copyright (c) 2012 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/seccomp-bpf/trap.h"
6
7#include <errno.h>
8#include <signal.h>
9#include <string.h>
10#include <sys/prctl.h>
11#include <sys/syscall.h>
12
13#include <limits>
14
15#include "base/logging.h"
16#include "sandbox/linux/seccomp-bpf/codegen.h"
17#include "sandbox/linux/seccomp-bpf/die.h"
18#include "sandbox/linux/seccomp-bpf/syscall.h"
19
20// Android's signal.h doesn't define ucontext etc.
21#if defined(OS_ANDROID)
22#include "sandbox/linux/services/android_ucontext.h"
23#endif
24
25namespace {
26
27const int kCapacityIncrement = 20;
28
29// Unsafe traps can only be turned on, if the user explicitly allowed them
30// by setting the CHROME_SANDBOX_DEBUGGING environment variable.
31const char kSandboxDebuggingEnv[] = "CHROME_SANDBOX_DEBUGGING";
32
33// We need to tell whether we are performing a "normal" callback, or
34// whether we were called recursively from within a UnsafeTrap() callback.
35// This is a little tricky to do, because we need to somehow get access to
36// per-thread data from within a signal context. Normal TLS storage is not
37// safely accessible at this time. We could roll our own, but that involves
38// a lot of complexity. Instead, we co-opt one bit in the signal mask.
39// If BUS is blocked, we assume that we have been called recursively.
40// There is a possibility for collision with other code that needs to do
41// this, but in practice the risks are low.
42// If SIGBUS turns out to be a problem, we could instead co-opt one of the
43// realtime signals. There are plenty of them. Unfortunately, there is no
44// way to mark a signal as allocated. So, the potential for collision is
45// possibly even worse.
46bool GetIsInSigHandler(const ucontext_t* ctx) {
47  // Note: on Android, sigismember does not take a pointer to const.
48  return sigismember(const_cast<sigset_t*>(&ctx->uc_sigmask), SIGBUS);
49}
50
51void SetIsInSigHandler() {
52  sigset_t mask;
53  if (sigemptyset(&mask) || sigaddset(&mask, SIGBUS) ||
54      sigprocmask(SIG_BLOCK, &mask, NULL)) {
55    SANDBOX_DIE("Failed to block SIGBUS");
56  }
57}
58
59bool IsDefaultSignalAction(const struct sigaction& sa) {
60  if (sa.sa_flags & SA_SIGINFO || sa.sa_handler != SIG_DFL) {
61    return false;
62  }
63  return true;
64}
65
66}  // namespace
67
68namespace sandbox {
69
70Trap::Trap()
71    : trap_array_(NULL),
72      trap_array_size_(0),
73      trap_array_capacity_(0),
74      has_unsafe_traps_(false) {
75  // Set new SIGSYS handler
76  struct sigaction sa = {};
77  sa.sa_sigaction = SigSysAction;
78  sa.sa_flags = SA_SIGINFO | SA_NODEFER;
79  struct sigaction old_sa;
80  if (sigaction(SIGSYS, &sa, &old_sa) < 0) {
81    SANDBOX_DIE("Failed to configure SIGSYS handler");
82  }
83
84  if (!IsDefaultSignalAction(old_sa)) {
85    static const char kExistingSIGSYSMsg[] =
86        "Existing signal handler when trying to install SIGSYS. SIGSYS needs "
87        "to be reserved for seccomp-bpf.";
88    DLOG(FATAL) << kExistingSIGSYSMsg;
89    LOG(ERROR) << kExistingSIGSYSMsg;
90  }
91
92  // Unmask SIGSYS
93  sigset_t mask;
94  if (sigemptyset(&mask) || sigaddset(&mask, SIGSYS) ||
95      sigprocmask(SIG_UNBLOCK, &mask, NULL)) {
96    SANDBOX_DIE("Failed to configure SIGSYS handler");
97  }
98}
99
100Trap* Trap::GetInstance() {
101  // Note: This class is not thread safe. It is the caller's responsibility
102  // to avoid race conditions. Normally, this is a non-issue as the sandbox
103  // can only be initialized if there are no other threads present.
104  // Also, this is not a normal singleton. Once created, the global trap
105  // object must never be destroyed again.
106  if (!global_trap_) {
107    global_trap_ = new Trap();
108    if (!global_trap_) {
109      SANDBOX_DIE("Failed to allocate global trap handler");
110    }
111  }
112  return global_trap_;
113}
114
115void Trap::SigSysAction(int nr, siginfo_t* info, void* void_context) {
116  if (!global_trap_) {
117    RAW_SANDBOX_DIE(
118        "This can't happen. Found no global singleton instance "
119        "for Trap() handling.");
120  }
121  global_trap_->SigSys(nr, info, void_context);
122}
123
124void Trap::SigSys(int nr, siginfo_t* info, void* void_context) {
125  // Signal handlers should always preserve "errno". Otherwise, we could
126  // trigger really subtle bugs.
127  const int old_errno = errno;
128
129  // Various sanity checks to make sure we actually received a signal
130  // triggered by a BPF filter. If something else triggered SIGSYS
131  // (e.g. kill()), there is really nothing we can do with this signal.
132  if (nr != SIGSYS || info->si_code != SYS_SECCOMP || !void_context ||
133      info->si_errno <= 0 ||
134      static_cast<size_t>(info->si_errno) > trap_array_size_) {
135    // ATI drivers seem to send SIGSYS, so this cannot be FATAL.
136    // See crbug.com/178166.
137    // TODO(jln): add a DCHECK or move back to FATAL.
138    RAW_LOG(ERROR, "Unexpected SIGSYS received.");
139    errno = old_errno;
140    return;
141  }
142
143  // Obtain the signal context. This, most notably, gives us access to
144  // all CPU registers at the time of the signal.
145  ucontext_t* ctx = reinterpret_cast<ucontext_t*>(void_context);
146
147  // Obtain the siginfo information that is specific to SIGSYS. Unfortunately,
148  // most versions of glibc don't include this information in siginfo_t. So,
149  // we need to explicitly copy it into a arch_sigsys structure.
150  struct arch_sigsys sigsys;
151  memcpy(&sigsys, &info->_sifields, sizeof(sigsys));
152
153  // Some more sanity checks.
154  if (sigsys.ip != reinterpret_cast<void*>(SECCOMP_IP(ctx)) ||
155      sigsys.nr != static_cast<int>(SECCOMP_SYSCALL(ctx)) ||
156      sigsys.arch != SECCOMP_ARCH) {
157    // TODO(markus):
158    // SANDBOX_DIE() can call LOG(FATAL). This is not normally async-signal
159    // safe and can lead to bugs. We should eventually implement a different
160    // logging and reporting mechanism that is safe to be called from
161    // the sigSys() handler.
162    RAW_SANDBOX_DIE("Sanity checks are failing after receiving SIGSYS.");
163  }
164
165  intptr_t rc;
166  if (has_unsafe_traps_ && GetIsInSigHandler(ctx)) {
167    errno = old_errno;
168    if (sigsys.nr == __NR_clone) {
169      RAW_SANDBOX_DIE("Cannot call clone() from an UnsafeTrap() handler.");
170    }
171    rc = SandboxSyscall(sigsys.nr,
172                        SECCOMP_PARM1(ctx),
173                        SECCOMP_PARM2(ctx),
174                        SECCOMP_PARM3(ctx),
175                        SECCOMP_PARM4(ctx),
176                        SECCOMP_PARM5(ctx),
177                        SECCOMP_PARM6(ctx));
178  } else {
179    const ErrorCode& err = trap_array_[info->si_errno - 1];
180    if (!err.safe_) {
181      SetIsInSigHandler();
182    }
183
184    // Copy the seccomp-specific data into a arch_seccomp_data structure. This
185    // is what we are showing to TrapFnc callbacks that the system call
186    // evaluator registered with the sandbox.
187    struct arch_seccomp_data data = {
188        sigsys.nr, SECCOMP_ARCH, reinterpret_cast<uint64_t>(sigsys.ip),
189        {static_cast<uint64_t>(SECCOMP_PARM1(ctx)),
190         static_cast<uint64_t>(SECCOMP_PARM2(ctx)),
191         static_cast<uint64_t>(SECCOMP_PARM3(ctx)),
192         static_cast<uint64_t>(SECCOMP_PARM4(ctx)),
193         static_cast<uint64_t>(SECCOMP_PARM5(ctx)),
194         static_cast<uint64_t>(SECCOMP_PARM6(ctx))}};
195
196    // Now call the TrapFnc callback associated with this particular instance
197    // of SECCOMP_RET_TRAP.
198    rc = err.fnc_(data, err.aux_);
199  }
200
201  // Update the CPU register that stores the return code of the system call
202  // that we just handled, and restore "errno" to the value that it had
203  // before entering the signal handler.
204  SECCOMP_RESULT(ctx) = static_cast<greg_t>(rc);
205  errno = old_errno;
206
207  return;
208}
209
210bool Trap::TrapKey::operator<(const TrapKey& o) const {
211  if (fnc != o.fnc) {
212    return fnc < o.fnc;
213  } else if (aux != o.aux) {
214    return aux < o.aux;
215  } else {
216    return safe < o.safe;
217  }
218}
219
220ErrorCode Trap::MakeTrap(TrapFnc fnc, const void* aux, bool safe) {
221  return GetInstance()->MakeTrapImpl(fnc, aux, safe);
222}
223
224ErrorCode Trap::MakeTrapImpl(TrapFnc fnc, const void* aux, bool safe) {
225  if (!safe && !SandboxDebuggingAllowedByUser()) {
226    // Unless the user set the CHROME_SANDBOX_DEBUGGING environment variable,
227    // we never return an ErrorCode that is marked as "unsafe". This also
228    // means, the BPF compiler will never emit code that allow unsafe system
229    // calls to by-pass the filter (because they use the magic return address
230    // from SandboxSyscall(-1)).
231
232    // This SANDBOX_DIE() can optionally be removed. It won't break security,
233    // but it might make error messages from the BPF compiler a little harder
234    // to understand. Removing the SANDBOX_DIE() allows callers to easyly check
235    // whether unsafe traps are supported (by checking whether the returned
236    // ErrorCode is ET_INVALID).
237    SANDBOX_DIE(
238        "Cannot use unsafe traps unless CHROME_SANDBOX_DEBUGGING "
239        "is enabled");
240
241    return ErrorCode();
242  }
243
244  // Each unique pair of TrapFnc and auxiliary data make up a distinct instance
245  // of a SECCOMP_RET_TRAP.
246  TrapKey key(fnc, aux, safe);
247  TrapIds::const_iterator iter = trap_ids_.find(key);
248
249  // We return unique identifiers together with SECCOMP_RET_TRAP. This allows
250  // us to associate trap with the appropriate handler. The kernel allows us
251  // identifiers in the range from 0 to SECCOMP_RET_DATA (0xFFFF). We want to
252  // avoid 0, as it could be confused for a trap without any specific id.
253  // The nice thing about sequentially numbered identifiers is that we can also
254  // trivially look them up from our signal handler without making any system
255  // calls that might be async-signal-unsafe.
256  // In order to do so, we store all of our traps in a C-style trap_array_.
257  uint16_t id;
258  if (iter != trap_ids_.end()) {
259    // We have seen this pair before. Return the same id that we assigned
260    // earlier.
261    id = iter->second;
262  } else {
263    // This is a new pair. Remember it and assign a new id.
264    if (trap_array_size_ >= SECCOMP_RET_DATA /* 0xFFFF */ ||
265        trap_array_size_ >= std::numeric_limits<typeof(id)>::max()) {
266      // In practice, this is pretty much impossible to trigger, as there
267      // are other kernel limitations that restrict overall BPF program sizes.
268      SANDBOX_DIE("Too many SECCOMP_RET_TRAP callback instances");
269    }
270    id = trap_array_size_ + 1;
271
272    // Our callers ensure that there are no other threads accessing trap_array_
273    // concurrently (typically this is done by ensuring that we are single-
274    // threaded while the sandbox is being set up). But we nonetheless are
275    // modifying a life data structure that could be accessed any time a
276    // system call is made; as system calls could be triggering SIGSYS.
277    // So, we have to be extra careful that we update trap_array_ atomically.
278    // In particular, this means we shouldn't be using realloc() to resize it.
279    // Instead, we allocate a new array, copy the values, and then switch the
280    // pointer. We only really care about the pointer being updated atomically
281    // and the data that is pointed to being valid, as these are the only
282    // values accessed from the signal handler. It is OK if trap_array_size_
283    // is inconsistent with the pointer, as it is monotonously increasing.
284    // Also, we only care about compiler barriers, as the signal handler is
285    // triggered synchronously from a system call. We don't have to protect
286    // against issues with the memory model or with completely asynchronous
287    // events.
288    if (trap_array_size_ >= trap_array_capacity_) {
289      trap_array_capacity_ += kCapacityIncrement;
290      ErrorCode* old_trap_array = trap_array_;
291      ErrorCode* new_trap_array = new ErrorCode[trap_array_capacity_];
292
293      // Language specs are unclear on whether the compiler is allowed to move
294      // the "delete[]" above our preceding assignments and/or memory moves,
295      // iff the compiler believes that "delete[]" doesn't have any other
296      // global side-effects.
297      // We insert optimization barriers to prevent this from happening.
298      // The first barrier is probably not needed, but better be explicit in
299      // what we want to tell the compiler.
300      // The clang developer mailing list couldn't answer whether this is a
301      // legitimate worry; but they at least thought that the barrier is
302      // sufficient to prevent the (so far hypothetical) problem of re-ordering
303      // of instructions by the compiler.
304      memcpy(new_trap_array, trap_array_, trap_array_size_ * sizeof(ErrorCode));
305      asm volatile("" : "=r"(new_trap_array) : "0"(new_trap_array) : "memory");
306      trap_array_ = new_trap_array;
307      asm volatile("" : "=r"(trap_array_) : "0"(trap_array_) : "memory");
308
309      delete[] old_trap_array;
310    }
311    trap_ids_[key] = id;
312    trap_array_[trap_array_size_] = ErrorCode(fnc, aux, safe, id);
313    return trap_array_[trap_array_size_++];
314  }
315
316  return ErrorCode(fnc, aux, safe, id);
317}
318
319bool Trap::SandboxDebuggingAllowedByUser() const {
320  const char* debug_flag = getenv(kSandboxDebuggingEnv);
321  return debug_flag && *debug_flag;
322}
323
324bool Trap::EnableUnsafeTrapsInSigSysHandler() {
325  Trap* trap = GetInstance();
326  if (!trap->has_unsafe_traps_) {
327    // Unsafe traps are a one-way fuse. Once enabled, they can never be turned
328    // off again.
329    // We only allow enabling unsafe traps, if the user explicitly set an
330    // appropriate environment variable. This prevents bugs that accidentally
331    // disable all sandboxing for all users.
332    if (trap->SandboxDebuggingAllowedByUser()) {
333      // We only ever print this message once, when we enable unsafe traps the
334      // first time.
335      SANDBOX_INFO("WARNING! Disabling sandbox for debugging purposes");
336      trap->has_unsafe_traps_ = true;
337    } else {
338      SANDBOX_INFO(
339          "Cannot disable sandbox and use unsafe traps unless "
340          "CHROME_SANDBOX_DEBUGGING is turned on first");
341    }
342  }
343  // Returns the, possibly updated, value of has_unsafe_traps_.
344  return trap->has_unsafe_traps_;
345}
346
347ErrorCode Trap::ErrorCodeFromTrapId(uint16_t id) {
348  if (global_trap_ && id > 0 && id <= global_trap_->trap_array_size_) {
349    return global_trap_->trap_array_[id - 1];
350  } else {
351    return ErrorCode();
352  }
353}
354
355Trap* Trap::global_trap_;
356
357}  // namespace sandbox
358