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

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: 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
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 { 295 }
289 // This is a new pair. Remember it and assign a new id. 296
290 if (trap_array_size_ >= SECCOMP_RET_DATA /* 0xFFFF */ || 297 // This is a new pair. Remember it and assign a new id.
291 trap_array_size_ >= std::numeric_limits<typeof(id)>::max()) { 298 if (trap_array_size_ >= SECCOMP_RET_DATA /* 0xFFFF */ ||
299 trap_array_size_ >= std::numeric_limits<uint16_t>::max()) {
292 // In practice, this is pretty much impossible to trigger, as there 300 // In practice, this is pretty much impossible to trigger, as there
293 // are other kernel limitations that restrict overall BPF program sizes. 301 // are other kernel limitations that restrict overall BPF program sizes.
294 SANDBOX_DIE("Too many SECCOMP_RET_TRAP callback instances"); 302 SANDBOX_DIE("Too many SECCOMP_RET_TRAP callback instances");
295 } 303 }
296 id = trap_array_size_ + 1;
297 304
298 // Our callers ensure that there are no other threads accessing trap_array_ 305 // Our callers ensure that there are no other threads accessing trap_array_
299 // concurrently (typically this is done by ensuring that we are single- 306 // concurrently (typically this is done by ensuring that we are single-
300 // threaded while the sandbox is being set up). But we nonetheless are 307 // 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 308 // modifying a live data structure that could be accessed any time a
302 // system call is made; as system calls could be triggering SIGSYS. 309 // 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. 310 // 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. 311 // 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 312 // 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 313 // 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 314 // 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_ 315 // values accessed from the signal handler. It is OK if trap_array_size_
309 // is inconsistent with the pointer, as it is monotonously increasing. 316 // is inconsistent with the pointer, as it is monotonously increasing.
310 // Also, we only care about compiler barriers, as the signal handler is 317 // 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 318 // triggered synchronously from a system call. We don't have to protect
312 // against issues with the memory model or with completely asynchronous 319 // against issues with the memory model or with completely asynchronous
313 // events. 320 // events.
leecam 2014/09/16 12:36:44 This code is to ensure that we can add new Traps a
mdempsky 2014/09/16 18:48:19 Yeah, more or less.
314 if (trap_array_size_ >= trap_array_capacity_) { 321 if (trap_array_size_ >= trap_array_capacity_) {
315 trap_array_capacity_ += kCapacityIncrement; 322 trap_array_capacity_ += kCapacityIncrement;
316 ErrorCode* old_trap_array = trap_array_; 323 TrapKey* old_trap_array = trap_array_;
317 ErrorCode* new_trap_array = new ErrorCode[trap_array_capacity_]; 324 TrapKey* new_trap_array = new TrapKey[trap_array_capacity_];
325 std::copy_n(old_trap_array, trap_array_size_, new_trap_array);
318 326
319 // Language specs are unclear on whether the compiler is allowed to move 327 // Language specs are unclear on whether the compiler is allowed to move
320 // the "delete[]" above our preceding assignments and/or memory moves, 328 // the "delete[]" above our preceding assignments and/or memory moves,
321 // iff the compiler believes that "delete[]" doesn't have any other 329 // iff the compiler believes that "delete[]" doesn't have any other
322 // global side-effects. 330 // global side-effects.
323 // We insert optimization barriers to prevent this from happening. 331 // We insert optimization barriers to prevent this from happening.
324 // The first barrier is probably not needed, but better be explicit in 332 // The first barrier is probably not needed, but better be explicit in
325 // what we want to tell the compiler. 333 // what we want to tell the compiler.
326 // The clang developer mailing list couldn't answer whether this is a 334 // The clang developer mailing list couldn't answer whether this is a
327 // legitimate worry; but they at least thought that the barrier is 335 // legitimate worry; but they at least thought that the barrier is
328 // sufficient to prevent the (so far hypothetical) problem of re-ordering 336 // sufficient to prevent the (so far hypothetical) problem of re-ordering
329 // of instructions by the compiler. 337 // of instructions by the compiler.
330 memcpy(new_trap_array, trap_array_, trap_array_size_ * sizeof(ErrorCode)); 338 // TODO(mdempsky): Replace with sequentially-consistent atomic store.
331 asm volatile("" : "=r"(new_trap_array) : "0"(new_trap_array) : "memory"); 339 asm volatile("" : "=r"(new_trap_array) : "0"(new_trap_array) : "memory");
332 trap_array_ = new_trap_array; 340 trap_array_ = new_trap_array;
333 asm volatile("" : "=r"(trap_array_) : "0"(trap_array_) : "memory"); 341 asm volatile("" : "=r"(trap_array_) : "0"(trap_array_) : "memory");
334 342
335 delete[] old_trap_array; 343 delete[] old_trap_array;
336 } 344 }
345
346 uint16_t id = trap_array_size_ + 1;
337 trap_ids_[key] = id; 347 trap_ids_[key] = id;
338 trap_array_[trap_array_size_] = ErrorCode(fnc, aux, safe, id); 348 trap_array_[trap_array_size_] = key;
339 return trap_array_[trap_array_size_++]; 349 // TODO(mdempsky): Atomic increment.
340 } 350 trap_array_size_++;
leecam 2014/09/16 12:36:44 why don't we just make this an atomic and fix the
mdempsky 2014/09/16 18:48:19 Just because I think doing that correctly is actua
341 351 return id;
342 return ErrorCode(fnc, aux, safe, id);
343 } 352 }
344 353
345 bool Trap::SandboxDebuggingAllowedByUser() const { 354 bool Trap::SandboxDebuggingAllowedByUser() const {
346 const char* debug_flag = getenv(kSandboxDebuggingEnv); 355 const char* debug_flag = getenv(kSandboxDebuggingEnv);
347 return debug_flag && *debug_flag; 356 return debug_flag && *debug_flag;
348 } 357 }
349 358
350 bool Trap::EnableUnsafeTrapsInSigSysHandler() { 359 bool Trap::EnableUnsafeTrapsInSigSysHandler() {
351 Trap* trap = GetInstance(); 360 Trap* trap = GetInstance();
352 if (!trap->has_unsafe_traps_) { 361 if (!trap->has_unsafe_traps_) {
(...skipping 10 matching lines...) Expand all
363 } else { 372 } else {
364 SANDBOX_INFO( 373 SANDBOX_INFO(
365 "Cannot disable sandbox and use unsafe traps unless " 374 "Cannot disable sandbox and use unsafe traps unless "
366 "CHROME_SANDBOX_DEBUGGING is turned on first"); 375 "CHROME_SANDBOX_DEBUGGING is turned on first");
367 } 376 }
368 } 377 }
369 // Returns the, possibly updated, value of has_unsafe_traps_. 378 // Returns the, possibly updated, value of has_unsafe_traps_.
370 return trap->has_unsafe_traps_; 379 return trap->has_unsafe_traps_;
371 } 380 }
372 381
373 ErrorCode Trap::ErrorCodeFromTrapId(uint16_t id) { 382 bool Trap::IsSafeTrapId(uint16_t id) {
374 if (global_trap_ && id > 0 && id <= global_trap_->trap_array_size_) { 383 if (global_trap_ && id > 0 && id <= global_trap_->trap_array_size_) {
375 return global_trap_->trap_array_[id - 1]; 384 return global_trap_->trap_array_[id - 1].safe;
376 } else {
377 return ErrorCode();
378 } 385 }
386 return false;
379 } 387 }
380 388
381 Trap* Trap::global_trap_; 389 Trap* Trap::global_trap_;
382 390
383 } // namespace sandbox 391 } // namespace sandbox
OLDNEW
« sandbox/linux/seccomp-bpf/trap.h ('K') | « sandbox/linux/seccomp-bpf/trap.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698