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