Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(593)

Side by Side Diff: sandbox/linux/seccomp-bpf/trap.cc

Issue 572753002: Decouple Trap from ErrorCode (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Fix indenting; revert volatile changes Created 6 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « sandbox/linux/seccomp-bpf/trap.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "sandbox/linux/seccomp-bpf/trap.h" 5 #include "sandbox/linux/seccomp-bpf/trap.h"
6 6
7 #include <errno.h> 7 #include <errno.h>
8 #include <signal.h> 8 #include <signal.h>
9 #include <string.h> 9 #include <string.h>
10 #include <sys/prctl.h>
11 #include <sys/syscall.h> 10 #include <sys/syscall.h>
12 11
12 #include <algorithm>
13 #include <limits> 13 #include <limits>
14 14
15 #include "base/logging.h" 15 #include "base/logging.h"
16 #include "sandbox/linux/seccomp-bpf/codegen.h" 16 #include "build/build_config.h"
17 #include "sandbox/linux/seccomp-bpf/die.h" 17 #include "sandbox/linux/seccomp-bpf/die.h"
18 #include "sandbox/linux/seccomp-bpf/linux_seccomp.h"
18 #include "sandbox/linux/seccomp-bpf/syscall.h" 19 #include "sandbox/linux/seccomp-bpf/syscall.h"
19 20
20 // Android's signal.h doesn't define ucontext etc. 21 // Android's signal.h doesn't define ucontext etc.
21 #if defined(OS_ANDROID) 22 #if defined(OS_ANDROID)
22 #include "sandbox/linux/services/android_ucontext.h" 23 #include "sandbox/linux/services/android_ucontext.h"
23 #endif 24 #endif
24 25
25 namespace { 26 namespace {
26 27
28 struct arch_sigsys {
29 void* ip;
30 int nr;
31 unsigned int arch;
32 };
33
27 const int kCapacityIncrement = 20; 34 const int kCapacityIncrement = 20;
28 35
29 // Unsafe traps can only be turned on, if the user explicitly allowed them 36 // Unsafe traps can only be turned on, if the user explicitly allowed them
30 // by setting the CHROME_SANDBOX_DEBUGGING environment variable. 37 // by setting the CHROME_SANDBOX_DEBUGGING environment variable.
31 const char kSandboxDebuggingEnv[] = "CHROME_SANDBOX_DEBUGGING"; 38 const char kSandboxDebuggingEnv[] = "CHROME_SANDBOX_DEBUGGING";
32 39
33 // We need to tell whether we are performing a "normal" callback, or 40 // We need to tell whether we are performing a "normal" callback, or
34 // whether we were called recursively from within a UnsafeTrap() callback. 41 // 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 42 // 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 43 // per-thread data from within a signal context. Normal TLS storage is not
(...skipping 156 matching lines...) Expand 10 before | Expand all | Expand 10 after
193 #else 200 #else
194 rc = Syscall::Call(SECCOMP_SYSCALL(ctx), 201 rc = Syscall::Call(SECCOMP_SYSCALL(ctx),
195 SECCOMP_PARM1(ctx), 202 SECCOMP_PARM1(ctx),
196 SECCOMP_PARM2(ctx), 203 SECCOMP_PARM2(ctx),
197 SECCOMP_PARM3(ctx), 204 SECCOMP_PARM3(ctx),
198 SECCOMP_PARM4(ctx), 205 SECCOMP_PARM4(ctx),
199 SECCOMP_PARM5(ctx), 206 SECCOMP_PARM5(ctx),
200 SECCOMP_PARM6(ctx)); 207 SECCOMP_PARM6(ctx));
201 #endif // defined(__mips__) 208 #endif // defined(__mips__)
202 } else { 209 } else {
203 const ErrorCode& err = trap_array_[info->si_errno - 1]; 210 const TrapKey& trap = trap_array_[info->si_errno - 1];
204 if (!err.safe_) { 211 if (!trap.safe) {
205 SetIsInSigHandler(); 212 SetIsInSigHandler();
206 } 213 }
207 214
208 // Copy the seccomp-specific data into a arch_seccomp_data structure. This 215 // Copy the seccomp-specific data into a arch_seccomp_data structure. This
209 // is what we are showing to TrapFnc callbacks that the system call 216 // is what we are showing to TrapFnc callbacks that the system call
210 // evaluator registered with the sandbox. 217 // evaluator registered with the sandbox.
211 struct arch_seccomp_data data = { 218 struct arch_seccomp_data data = {
212 static_cast<int>(SECCOMP_SYSCALL(ctx)), 219 static_cast<int>(SECCOMP_SYSCALL(ctx)),
213 SECCOMP_ARCH, 220 SECCOMP_ARCH,
214 reinterpret_cast<uint64_t>(sigsys.ip), 221 reinterpret_cast<uint64_t>(sigsys.ip),
215 {static_cast<uint64_t>(SECCOMP_PARM1(ctx)), 222 {static_cast<uint64_t>(SECCOMP_PARM1(ctx)),
216 static_cast<uint64_t>(SECCOMP_PARM2(ctx)), 223 static_cast<uint64_t>(SECCOMP_PARM2(ctx)),
217 static_cast<uint64_t>(SECCOMP_PARM3(ctx)), 224 static_cast<uint64_t>(SECCOMP_PARM3(ctx)),
218 static_cast<uint64_t>(SECCOMP_PARM4(ctx)), 225 static_cast<uint64_t>(SECCOMP_PARM4(ctx)),
219 static_cast<uint64_t>(SECCOMP_PARM5(ctx)), 226 static_cast<uint64_t>(SECCOMP_PARM5(ctx)),
220 static_cast<uint64_t>(SECCOMP_PARM6(ctx))}}; 227 static_cast<uint64_t>(SECCOMP_PARM6(ctx))}};
221 228
222 // Now call the TrapFnc callback associated with this particular instance 229 // Now call the TrapFnc callback associated with this particular instance
223 // of SECCOMP_RET_TRAP. 230 // of SECCOMP_RET_TRAP.
224 rc = err.fnc_(data, err.aux_); 231 rc = trap.fnc(data, const_cast<void*>(trap.aux));
225 } 232 }
226 233
227 // Update the CPU register that stores the return code of the system call 234 // Update the CPU register that stores the return code of the system call
228 // that we just handled, and restore "errno" to the value that it had 235 // that we just handled, and restore "errno" to the value that it had
229 // before entering the signal handler. 236 // before entering the signal handler.
230 Syscall::PutValueInUcontext(rc, ctx); 237 Syscall::PutValueInUcontext(rc, ctx);
231 errno = old_errno; 238 errno = old_errno;
232 239
233 return; 240 return;
234 } 241 }
235 242
236 bool Trap::TrapKey::operator<(const TrapKey& o) const { 243 bool Trap::TrapKey::operator<(const TrapKey& o) const {
237 if (fnc != o.fnc) { 244 if (fnc != o.fnc) {
238 return fnc < o.fnc; 245 return fnc < o.fnc;
239 } else if (aux != o.aux) { 246 } else if (aux != o.aux) {
240 return aux < o.aux; 247 return aux < o.aux;
241 } else { 248 } else {
242 return safe < o.safe; 249 return safe < o.safe;
243 } 250 }
244 } 251 }
245 252
246 ErrorCode Trap::MakeTrap(TrapFnc fnc, const void* aux, bool safe) { 253 uint16_t Trap::MakeTrap(TrapFnc fnc, const void* aux, bool safe) {
247 return GetInstance()->MakeTrapImpl(fnc, aux, safe); 254 return GetInstance()->MakeTrapImpl(fnc, aux, safe);
248 } 255 }
249 256
250 ErrorCode Trap::MakeTrapImpl(TrapFnc fnc, const void* aux, bool safe) { 257 uint16_t Trap::MakeTrapImpl(TrapFnc fnc, const void* aux, bool safe) {
251 if (!safe && !SandboxDebuggingAllowedByUser()) { 258 if (!safe && !SandboxDebuggingAllowedByUser()) {
252 // Unless the user set the CHROME_SANDBOX_DEBUGGING environment variable, 259 // Unless the user set the CHROME_SANDBOX_DEBUGGING environment variable,
253 // we never return an ErrorCode that is marked as "unsafe". This also 260 // we never return an ErrorCode that is marked as "unsafe". This also
254 // means, the BPF compiler will never emit code that allow unsafe system 261 // means, the BPF compiler will never emit code that allow unsafe system
255 // calls to by-pass the filter (because they use the magic return address 262 // calls to by-pass the filter (because they use the magic return address
256 // from Syscall::Call(-1)). 263 // from Syscall::Call(-1)).
257 264
258 // This SANDBOX_DIE() can optionally be removed. It won't break security, 265 // This SANDBOX_DIE() can optionally be removed. It won't break security,
259 // but it might make error messages from the BPF compiler a little harder 266 // but it might make error messages from the BPF compiler a little harder
260 // to understand. Removing the SANDBOX_DIE() allows callers to easyly check 267 // to understand. Removing the SANDBOX_DIE() allows callers to easily check
261 // whether unsafe traps are supported (by checking whether the returned 268 // whether unsafe traps are supported (by checking whether the returned
262 // ErrorCode is ET_INVALID). 269 // ErrorCode is ET_INVALID).
263 SANDBOX_DIE( 270 SANDBOX_DIE(
264 "Cannot use unsafe traps unless CHROME_SANDBOX_DEBUGGING " 271 "Cannot use unsafe traps unless CHROME_SANDBOX_DEBUGGING "
265 "is enabled"); 272 "is enabled");
266 273
267 return ErrorCode(); 274 return 0;
268 } 275 }
269 276
270 // Each unique pair of TrapFnc and auxiliary data make up a distinct instance 277 // Each unique pair of TrapFnc and auxiliary data make up a distinct instance
271 // of a SECCOMP_RET_TRAP. 278 // of a SECCOMP_RET_TRAP.
272 TrapKey key(fnc, aux, safe); 279 TrapKey key(fnc, aux, safe);
273 TrapIds::const_iterator iter = trap_ids_.find(key);
274 280
275 // We return unique identifiers together with SECCOMP_RET_TRAP. This allows 281 // We return unique identifiers together with SECCOMP_RET_TRAP. This allows
276 // us to associate trap with the appropriate handler. The kernel allows us 282 // us to associate trap with the appropriate handler. The kernel allows us
277 // identifiers in the range from 0 to SECCOMP_RET_DATA (0xFFFF). We want to 283 // identifiers in the range from 0 to SECCOMP_RET_DATA (0xFFFF). We want to
278 // avoid 0, as it could be confused for a trap without any specific id. 284 // avoid 0, as it could be confused for a trap without any specific id.
279 // The nice thing about sequentially numbered identifiers is that we can also 285 // The nice thing about sequentially numbered identifiers is that we can also
280 // trivially look them up from our signal handler without making any system 286 // trivially look them up from our signal handler without making any system
281 // calls that might be async-signal-unsafe. 287 // calls that might be async-signal-unsafe.
282 // In order to do so, we store all of our traps in a C-style trap_array_. 288 // In order to do so, we store all of our traps in a C-style trap_array_.
283 uint16_t id; 289
290 TrapIds::const_iterator iter = trap_ids_.find(key);
284 if (iter != trap_ids_.end()) { 291 if (iter != trap_ids_.end()) {
285 // We have seen this pair before. Return the same id that we assigned 292 // We have seen this pair before. Return the same id that we assigned
286 // earlier. 293 // earlier.
287 id = iter->second; 294 return iter->second;
288 } else {
289 // This is a new pair. Remember it and assign a new id.
290 if (trap_array_size_ >= SECCOMP_RET_DATA /* 0xFFFF */ ||
291 trap_array_size_ >= std::numeric_limits<typeof(id)>::max()) {
292 // In practice, this is pretty much impossible to trigger, as there
293 // are other kernel limitations that restrict overall BPF program sizes.
294 SANDBOX_DIE("Too many SECCOMP_RET_TRAP callback instances");
295 }
296 id = trap_array_size_ + 1;
297
298 // Our callers ensure that there are no other threads accessing trap_array_
299 // concurrently (typically this is done by ensuring that we are single-
300 // threaded while the sandbox is being set up). But we nonetheless are
301 // modifying a life data structure that could be accessed any time a
302 // system call is made; as system calls could be triggering SIGSYS.
303 // So, we have to be extra careful that we update trap_array_ atomically.
304 // In particular, this means we shouldn't be using realloc() to resize it.
305 // Instead, we allocate a new array, copy the values, and then switch the
306 // pointer. We only really care about the pointer being updated atomically
307 // and the data that is pointed to being valid, as these are the only
308 // values accessed from the signal handler. It is OK if trap_array_size_
309 // is inconsistent with the pointer, as it is monotonously increasing.
310 // Also, we only care about compiler barriers, as the signal handler is
311 // triggered synchronously from a system call. We don't have to protect
312 // against issues with the memory model or with completely asynchronous
313 // events.
314 if (trap_array_size_ >= trap_array_capacity_) {
315 trap_array_capacity_ += kCapacityIncrement;
316 ErrorCode* old_trap_array = trap_array_;
317 ErrorCode* new_trap_array = new ErrorCode[trap_array_capacity_];
318
319 // Language specs are unclear on whether the compiler is allowed to move
320 // the "delete[]" above our preceding assignments and/or memory moves,
321 // iff the compiler believes that "delete[]" doesn't have any other
322 // global side-effects.
323 // We insert optimization barriers to prevent this from happening.
324 // The first barrier is probably not needed, but better be explicit in
325 // what we want to tell the compiler.
326 // The clang developer mailing list couldn't answer whether this is a
327 // legitimate worry; but they at least thought that the barrier is
328 // sufficient to prevent the (so far hypothetical) problem of re-ordering
329 // of instructions by the compiler.
330 memcpy(new_trap_array, trap_array_, trap_array_size_ * sizeof(ErrorCode));
331 asm volatile("" : "=r"(new_trap_array) : "0"(new_trap_array) : "memory");
332 trap_array_ = new_trap_array;
333 asm volatile("" : "=r"(trap_array_) : "0"(trap_array_) : "memory");
334
335 delete[] old_trap_array;
336 }
337 trap_ids_[key] = id;
338 trap_array_[trap_array_size_] = ErrorCode(fnc, aux, safe, id);
339 return trap_array_[trap_array_size_++];
340 } 295 }
341 296
342 return ErrorCode(fnc, aux, safe, id); 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
jln (very slow on Chromium) 2014/09/16 17:47:46 It doesn't change much here, but we should also st
mdempsky 2014/09/16 18:48:20 Yep, I'll followup on the crbug with more details.
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;
343 } 353 }
344 354
345 bool Trap::SandboxDebuggingAllowedByUser() const { 355 bool Trap::SandboxDebuggingAllowedByUser() const {
346 const char* debug_flag = getenv(kSandboxDebuggingEnv); 356 const char* debug_flag = getenv(kSandboxDebuggingEnv);
347 return debug_flag && *debug_flag; 357 return debug_flag && *debug_flag;
348 } 358 }
349 359
350 bool Trap::EnableUnsafeTrapsInSigSysHandler() { 360 bool Trap::EnableUnsafeTrapsInSigSysHandler() {
351 Trap* trap = GetInstance(); 361 Trap* trap = GetInstance();
352 if (!trap->has_unsafe_traps_) { 362 if (!trap->has_unsafe_traps_) {
(...skipping 10 matching lines...) Expand all
363 } else { 373 } else {
364 SANDBOX_INFO( 374 SANDBOX_INFO(
365 "Cannot disable sandbox and use unsafe traps unless " 375 "Cannot disable sandbox and use unsafe traps unless "
366 "CHROME_SANDBOX_DEBUGGING is turned on first"); 376 "CHROME_SANDBOX_DEBUGGING is turned on first");
367 } 377 }
368 } 378 }
369 // Returns the, possibly updated, value of has_unsafe_traps_. 379 // Returns the, possibly updated, value of has_unsafe_traps_.
370 return trap->has_unsafe_traps_; 380 return trap->has_unsafe_traps_;
371 } 381 }
372 382
373 ErrorCode Trap::ErrorCodeFromTrapId(uint16_t id) { 383 bool Trap::IsSafeTrapId(uint16_t id) {
374 if (global_trap_ && id > 0 && id <= global_trap_->trap_array_size_) { 384 if (global_trap_ && id > 0 && id <= global_trap_->trap_array_size_) {
375 return global_trap_->trap_array_[id - 1]; 385 return global_trap_->trap_array_[id - 1].safe;
376 } else {
377 return ErrorCode();
378 } 386 }
387 return false;
379 } 388 }
380 389
381 Trap* Trap::global_trap_; 390 Trap* Trap::global_trap_;
382 391
383 } // namespace sandbox 392 } // namespace sandbox
OLDNEW
« no previous file with comments | « sandbox/linux/seccomp-bpf/trap.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698