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