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