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