1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 #include "Sandbox.h"
8
9 #include "LinuxSched.h"
10 #include "SandboxBrokerClient.h"
11 #include "SandboxChrootProto.h"
12 #include "SandboxFilter.h"
13 #include "SandboxInternal.h"
14 #include "SandboxLogging.h"
15 #include "SandboxOpenedFiles.h"
16 #include "SandboxReporterClient.h"
17
18 #include <dirent.h>
19 #ifdef NIGHTLY_BUILD
20 # include "dlfcn.h"
21 #endif
22 #include <errno.h>
23 #include <fcntl.h>
24 #include <linux/futex.h>
25 #include <pthread.h>
26 #include <signal.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <sys/mman.h>
31 #include <sys/prctl.h>
32 #include <sys/ptrace.h>
33 #include <sys/syscall.h>
34 #include <sys/time.h>
35 #include <unistd.h>
36
37 #include "mozilla/Array.h"
38 #include "mozilla/Atomics.h"
39 #include "mozilla/Range.h"
40 #include "mozilla/SandboxInfo.h"
41 #include "mozilla/Span.h"
42 #include "mozilla/UniquePtr.h"
43 #include "mozilla/Unused.h"
44 #include "prenv.h"
45 #include "base/posix/eintr_wrapper.h"
46 #include "sandbox/linux/bpf_dsl/codegen.h"
47 #include "sandbox/linux/bpf_dsl/dump_bpf.h"
48 #include "sandbox/linux/bpf_dsl/policy.h"
49 #include "sandbox/linux/bpf_dsl/policy_compiler.h"
50 #include "sandbox/linux/bpf_dsl/seccomp_macros.h"
51 #include "sandbox/linux/seccomp-bpf/trap.h"
52 #include "sandbox/linux/system_headers/linux_filter.h"
53 #include "sandbox/linux/system_headers/linux_seccomp.h"
54 #include "sandbox/linux/system_headers/linux_syscalls.h"
55 #if defined(ANDROID)
56 # include "sandbox/linux/system_headers/linux_ucontext.h"
57 #endif
58
59 #ifdef MOZ_ASAN
60 // Copy libsanitizer declarations to avoid depending on ASAN headers.
61 // See also bug 1081242 comment #4.
62 extern "C" {
63 namespace __sanitizer {
64 // Win64 uses long long, but this is Linux.
65 typedef signed long sptr;
66 } // namespace __sanitizer
67
68 typedef struct {
69 int coverage_sandboxed;
70 __sanitizer::sptr coverage_fd;
71 unsigned int coverage_max_block_size;
72 } __sanitizer_sandbox_arguments;
73
74 MOZ_IMPORT_API void __sanitizer_sandbox_on_notify(
75 __sanitizer_sandbox_arguments* args);
76 } // extern "C"
77 #endif // MOZ_ASAN
78
79 // Signal number used to enable seccomp on each thread.
80 mozilla::Atomic<int> gSeccompTsyncBroadcastSignum(0);
81
82 namespace mozilla {
83
84 static bool gSandboxCrashOnError = false;
85
86 // This is initialized by SandboxSetCrashFunc().
87 SandboxCrashFunc gSandboxCrashFunc;
88
89 static SandboxReporterClient* gSandboxReporterClient;
90 static void (*gChromiumSigSysHandler)(int, siginfo_t*, void*);
91
92 // Test whether a ucontext, interpreted as the state after a syscall,
93 // indicates the given error. See also sandbox::Syscall::PutValueInUcontext.
ContextIsError(const ucontext_t * aContext,int aError)94 static bool ContextIsError(const ucontext_t* aContext, int aError) {
95 // Avoid integer promotion warnings. (The unary addition makes
96 // the decltype not evaluate to a reference type.)
97 typedef decltype(+SECCOMP_RESULT(aContext)) reg_t;
98
99 #ifdef __mips__
100 return SECCOMP_PARM4(aContext) != 0 &&
101 SECCOMP_RESULT(aContext) == static_cast<reg_t>(aError);
102 #else
103 return SECCOMP_RESULT(aContext) == static_cast<reg_t>(-aError);
104 #endif
105 }
106
107 /**
108 * This is the SIGSYS handler function. It delegates to the Chromium
109 * TrapRegistry handler (see InstallSigSysHandler, below) and, if the
110 * trap handler installed by the policy would fail with ENOSYS,
111 * crashes the process. This allows unintentional policy failures to
112 * be reported as crash dumps and fixed. It also logs information
113 * about the failed system call.
114 *
115 * Note that this could be invoked in parallel on multiple threads and
116 * that it could be in async signal context (e.g., intercepting an
117 * open() called from an async signal handler).
118 */
SigSysHandler(int nr,siginfo_t * info,void * void_context)119 static void SigSysHandler(int nr, siginfo_t* info, void* void_context) {
120 ucontext_t* ctx = static_cast<ucontext_t*>(void_context);
121 // This shouldn't ever be null, but the Chromium handler checks for
122 // that and refrains from crashing, so let's not crash release builds:
123 MOZ_DIAGNOSTIC_ASSERT(ctx);
124 if (!ctx) {
125 return;
126 }
127
128 // Save a copy of the context before invoking the trap handler,
129 // which will overwrite one or more registers with the return value.
130 ucontext_t savedCtx = *ctx;
131
132 gChromiumSigSysHandler(nr, info, ctx);
133 if (!ContextIsError(ctx, ENOSYS)) {
134 return;
135 }
136
137 SandboxReport report = gSandboxReporterClient->MakeReportAndSend(&savedCtx);
138
139 // TODO, someday when this is enabled on MIPS: include the two extra
140 // args in the error message.
141 SANDBOX_LOG_ERROR(
142 "seccomp sandbox violation: pid %d, tid %d, syscall %d,"
143 " args %d %d %d %d %d %d.%s",
144 report.mPid, report.mTid, report.mSyscall, report.mArgs[0],
145 report.mArgs[1], report.mArgs[2], report.mArgs[3], report.mArgs[4],
146 report.mArgs[5], gSandboxCrashOnError ? " Killing process." : "");
147
148 if (gSandboxCrashOnError) {
149 // Bug 1017393: record syscall number somewhere useful.
150 info->si_addr = reinterpret_cast<void*>(report.mSyscall);
151
152 gSandboxCrashFunc(nr, info, &savedCtx);
153 _exit(127);
154 }
155 }
156
157 /**
158 * This function installs the SIGSYS handler. This is slightly
159 * complicated because we want to use Chromium's handler to dispatch
160 * to specific trap handlers defined in the policy, but we also need
161 * the full original signal context to give to Breakpad for crash
162 * dumps. So we install Chromium's handler first, then retrieve its
163 * address so our replacement can delegate to it.
164 */
InstallSigSysHandler(void)165 static void InstallSigSysHandler(void) {
166 struct sigaction act;
167
168 // Ensure that the Chromium handler is installed.
169 Unused << sandbox::Trap::Registry();
170
171 // If the signal handling state isn't as expected, crash now instead
172 // of crashing later (and more confusingly) when SIGSYS happens.
173
174 if (sigaction(SIGSYS, nullptr, &act) != 0) {
175 MOZ_CRASH("Couldn't read old SIGSYS disposition");
176 }
177 if ((act.sa_flags & SA_SIGINFO) != SA_SIGINFO) {
178 MOZ_CRASH("SIGSYS not already set to a siginfo handler?");
179 }
180 MOZ_RELEASE_ASSERT(act.sa_sigaction);
181 gChromiumSigSysHandler = act.sa_sigaction;
182 act.sa_sigaction = SigSysHandler;
183 // Currently, SA_NODEFER should already be set by the Chromium code,
184 // but it's harmless to ensure that it's set:
185 MOZ_ASSERT(act.sa_flags & SA_NODEFER);
186 act.sa_flags |= SA_NODEFER;
187 if (sigaction(SIGSYS, &act, nullptr) < 0) {
188 MOZ_CRASH("Couldn't change SIGSYS disposition");
189 }
190 }
191
192 /**
193 * This function installs the syscall filter, a.k.a. seccomp. The
194 * aUseTSync flag indicates whether this should apply to all threads
195 * in the process -- which will fail if the kernel doesn't support
196 * that -- or only the current thread.
197 *
198 * SECCOMP_MODE_FILTER is the "bpf" mode of seccomp which allows
199 * to pass a bpf program (in our case, it contains a syscall
200 * whitelist).
201 *
202 * PR_SET_NO_NEW_PRIVS ensures that it is impossible to grant more
203 * syscalls to the process beyond this point (even after fork()), and
204 * prevents gaining capabilities (e.g., by exec'ing a setuid root
205 * program). The kernel won't allow seccomp-bpf without doing this,
206 * because otherwise it could be used for privilege escalation attacks.
207 *
208 * Returns false if the filter was already installed (see the
209 * PR_SET_NO_NEW_PRIVS rule in SandboxFilter.cpp). Crashes on any
210 * other error condition.
211 *
212 * @see SandboxInfo
213 * @see BroadcastSetThreadSandbox
214 */
InstallSyscallFilter(const sock_fprog * aProg,bool aUseTSync)215 static bool MOZ_MUST_USE InstallSyscallFilter(const sock_fprog* aProg,
216 bool aUseTSync) {
217 if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) {
218 if (!aUseTSync && errno == ETXTBSY) {
219 return false;
220 }
221 SANDBOX_LOG_ERROR("prctl(PR_SET_NO_NEW_PRIVS) failed: %s", strerror(errno));
222 MOZ_CRASH("prctl(PR_SET_NO_NEW_PRIVS)");
223 }
224
225 if (aUseTSync) {
226 if (syscall(__NR_seccomp, SECCOMP_SET_MODE_FILTER,
227 SECCOMP_FILTER_FLAG_TSYNC, aProg) != 0) {
228 SANDBOX_LOG_ERROR("thread-synchronized seccomp failed: %s",
229 strerror(errno));
230 MOZ_CRASH("seccomp+tsync failed, but kernel supports tsync");
231 }
232 } else {
233 if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, (unsigned long)aProg, 0,
234 0)) {
235 SANDBOX_LOG_ERROR("prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER) failed: %s",
236 strerror(errno));
237 MOZ_CRASH("prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER)");
238 }
239 }
240 return true;
241 }
242
243 // Use signals for permissions that need to be set per-thread.
244 // The communication channel from the signal handler back to the main thread.
245 static mozilla::Atomic<int> gSetSandboxDone;
246 // Pass the filter itself through a global.
247 const sock_fprog* gSetSandboxFilter;
248
249 // We have to dynamically allocate the signal number; see bug 1038900.
250 // This function returns the first realtime signal currently set to
251 // default handling (i.e., not in use), or 0 if none could be found.
252 //
253 // WARNING: if this function or anything similar to it (including in
254 // external libraries) is used on multiple threads concurrently, there
255 // will be a race condition.
FindFreeSignalNumber()256 static int FindFreeSignalNumber() {
257 for (int signum = SIGRTMAX; signum >= SIGRTMIN; --signum) {
258 struct sigaction sa;
259
260 if (sigaction(signum, nullptr, &sa) == 0 &&
261 (sa.sa_flags & SA_SIGINFO) == 0 && sa.sa_handler == SIG_DFL) {
262 return signum;
263 }
264 }
265 return 0;
266 }
267
268 // Returns true if sandboxing was enabled, or false if sandboxing
269 // already was enabled. Crashes if sandboxing could not be enabled.
SetThreadSandbox()270 static bool SetThreadSandbox() {
271 return InstallSyscallFilter(gSetSandboxFilter, false);
272 }
273
SetThreadSandboxHandler(int signum)274 static void SetThreadSandboxHandler(int signum) {
275 // The non-zero number sent back to the main thread indicates
276 // whether action was taken.
277 if (SetThreadSandbox()) {
278 gSetSandboxDone = 2;
279 } else {
280 gSetSandboxDone = 1;
281 }
282 // Wake up the main thread. See the FUTEX_WAIT call, below, for an
283 // explanation.
284 syscall(__NR_futex, reinterpret_cast<int*>(&gSetSandboxDone), FUTEX_WAKE, 1);
285 }
286
EnterChroot()287 static void EnterChroot() {
288 const char* env = PR_GetEnv(kSandboxChrootEnvFlag);
289 if (!env || !*env || *env == '0') {
290 return;
291 }
292 char msg = kSandboxChrootRequest;
293 ssize_t msg_len = HANDLE_EINTR(write(kSandboxChrootClientFd, &msg, 1));
294 MOZ_RELEASE_ASSERT(msg_len == 1);
295 msg_len = HANDLE_EINTR(read(kSandboxChrootClientFd, &msg, 1));
296 MOZ_RELEASE_ASSERT(msg_len == 1);
297 MOZ_RELEASE_ASSERT(msg == kSandboxChrootResponse);
298 close(kSandboxChrootClientFd);
299 }
300
BroadcastSetThreadSandbox(const sock_fprog * aFilter)301 static void BroadcastSetThreadSandbox(const sock_fprog* aFilter) {
302 pid_t pid, tid, myTid;
303 DIR* taskdp;
304 struct dirent* de;
305
306 // This function does not own *aFilter, so this global needs to
307 // always be zeroed before returning.
308 gSetSandboxFilter = aFilter;
309
310 static_assert(sizeof(mozilla::Atomic<int>) == sizeof(int),
311 "mozilla::Atomic<int> isn't represented by an int");
312 pid = getpid();
313 myTid = syscall(__NR_gettid);
314 taskdp = opendir("/proc/self/task");
315 if (taskdp == nullptr) {
316 SANDBOX_LOG_ERROR("opendir /proc/self/task: %s\n", strerror(errno));
317 MOZ_CRASH("failed while trying to open directory /proc/self/task");
318 }
319
320 // In case this races with a not-yet-deprivileged thread cloning
321 // itself, repeat iterating over all threads until we find none
322 // that are still privileged.
323 bool sandboxProgress;
324 const int tsyncSignum = gSeccompTsyncBroadcastSignum;
325 do {
326 sandboxProgress = false;
327 // For each thread...
328 while ((de = readdir(taskdp))) {
329 char* endptr;
330 tid = strtol(de->d_name, &endptr, 10);
331 if (*endptr != '\0' || tid <= 0) {
332 // Not a task ID.
333 continue;
334 }
335 if (tid == myTid) {
336 // Drop this thread's privileges last, below, so we can
337 // continue to signal other threads.
338 continue;
339 }
340
341 MOZ_RELEASE_ASSERT(tsyncSignum != 0);
342
343 // Reset the futex cell and signal.
344 gSetSandboxDone = 0;
345 if (syscall(__NR_tgkill, pid, tid, tsyncSignum) != 0) {
346 if (errno == ESRCH) {
347 SANDBOX_LOG_ERROR("Thread %d unexpectedly exited.", tid);
348 // Rescan threads, in case it forked before exiting.
349 sandboxProgress = true;
350 continue;
351 }
352 SANDBOX_LOG_ERROR("tgkill(%d,%d): %s\n", pid, tid, strerror(errno));
353 MOZ_CRASH("failed while trying to send a signal to a thread");
354 }
355 // It's unlikely, but if the thread somehow manages to exit
356 // after receiving the signal but before entering the signal
357 // handler, we need to avoid blocking forever.
358 //
359 // Using futex directly lets the signal handler send the wakeup
360 // from an async signal handler (pthread mutex/condvar calls
361 // aren't allowed), and to use a relative timeout that isn't
362 // affected by changes to the system clock (not possible with
363 // POSIX semaphores).
364 //
365 // If a thread doesn't respond within a reasonable amount of
366 // time, but still exists, we crash -- the alternative is either
367 // blocking forever or silently losing security, and it
368 // shouldn't actually happen.
369 static const int crashDelay = 10; // seconds
370 struct timespec timeLimit;
371 clock_gettime(CLOCK_MONOTONIC, &timeLimit);
372 timeLimit.tv_sec += crashDelay;
373 while (true) {
374 static const struct timespec futexTimeout = {0,
375 10 * 1000 * 1000}; // 10ms
376 // Atomically: if gSetSandboxDone == 0, then sleep.
377 if (syscall(__NR_futex, reinterpret_cast<int*>(&gSetSandboxDone),
378 FUTEX_WAIT, 0, &futexTimeout) != 0) {
379 if (errno != EWOULDBLOCK && errno != ETIMEDOUT && errno != EINTR) {
380 SANDBOX_LOG_ERROR("FUTEX_WAIT: %s\n", strerror(errno));
381 MOZ_CRASH("failed during FUTEX_WAIT");
382 }
383 }
384 // Did the handler finish?
385 if (gSetSandboxDone > 0) {
386 if (gSetSandboxDone == 2) {
387 sandboxProgress = true;
388 }
389 break;
390 }
391 // Has the thread ceased to exist?
392 if (syscall(__NR_tgkill, pid, tid, 0) != 0) {
393 if (errno == ESRCH) {
394 SANDBOX_LOG_ERROR("Thread %d unexpectedly exited.", tid);
395 }
396 // Rescan threads, in case it forked before exiting.
397 // Also, if it somehow failed in a way that wasn't ESRCH,
398 // and still exists, that will be handled on the next pass.
399 sandboxProgress = true;
400 break;
401 }
402 struct timespec now;
403 clock_gettime(CLOCK_MONOTONIC, &now);
404 if (now.tv_sec > timeLimit.tv_sec ||
405 (now.tv_sec == timeLimit.tv_sec &&
406 now.tv_nsec > timeLimit.tv_nsec)) {
407 SANDBOX_LOG_ERROR(
408 "Thread %d unresponsive for %d seconds."
409 " Killing process.",
410 tid, crashDelay);
411
412 MOZ_CRASH("failed while waiting for unresponsive thread");
413 }
414 }
415 }
416 rewinddir(taskdp);
417 } while (sandboxProgress);
418
419 void (*oldHandler)(int);
420 oldHandler = signal(tsyncSignum, SIG_DFL);
421 if (oldHandler != SetThreadSandboxHandler) {
422 // See the comment on FindFreeSignalNumber about race conditions.
423 SANDBOX_LOG_ERROR("handler for signal %d was changed to %p!", tsyncSignum,
424 oldHandler);
425 MOZ_CRASH("handler for the signal was changed to another");
426 }
427 gSeccompTsyncBroadcastSignum = 0;
428 Unused << closedir(taskdp);
429 // And now, deprivilege the main thread:
430 SetThreadSandbox();
431 gSetSandboxFilter = nullptr;
432 }
433
ApplySandboxWithTSync(sock_fprog * aFilter)434 static void ApplySandboxWithTSync(sock_fprog* aFilter) {
435 // At this point we're committed to using tsync, because we'd have
436 // needed to allocate a signal and prevent it from being blocked on
437 // other threads (see SandboxHooks.cpp), so there's no attempt to
438 // fall back to the non-tsync path.
439 if (!InstallSyscallFilter(aFilter, true)) {
440 MOZ_CRASH("failed while trying to install syscall filter");
441 }
442 }
443
444 #ifdef NIGHTLY_BUILD
IsLibPresent(const char * aName)445 static bool IsLibPresent(const char* aName) {
446 if (const auto handle = dlopen(aName, RTLD_LAZY | RTLD_NOLOAD)) {
447 dlclose(handle);
448 return true;
449 }
450 return false;
451 }
452
453 static const Array<const char*, 1> kLibsThatWillCrash{
454 "libesets_pac.so",
455 };
456 #endif // NIGHTLY_BUILD
457
SandboxEarlyInit()458 void SandboxEarlyInit() {
459 if (PR_GetEnv("MOZ_SANDBOXED") == nullptr) {
460 return;
461 }
462
463 // Fix LD_PRELOAD for any child processes. See bug 1434392 comment #10;
464 // this can probably go away when audio remoting is mandatory.
465 const char* oldPreload = PR_GetEnv("MOZ_ORIG_LD_PRELOAD");
466 char* preloadEntry;
467 // This string is "leaked" because the environment takes ownership.
468 if (asprintf(&preloadEntry, "LD_PRELOAD=%s", oldPreload ? oldPreload : "") !=
469 -1) {
470 PR_SetEnv(preloadEntry);
471 }
472
473 // If TSYNC is not supported, set up signal handler
474 // used to enable seccomp on each thread.
475 if (!SandboxInfo::Get().Test(SandboxInfo::kHasSeccompTSync)) {
476 // The signal number has to be chosen early, so that the
477 // interceptions in SandboxHooks.cpp can prevent it from being
478 // masked.
479 const int tsyncSignum = FindFreeSignalNumber();
480 if (tsyncSignum == 0) {
481 SANDBOX_LOG_ERROR("No available signal numbers!");
482 MOZ_CRASH("failed while trying to find a free signal number");
483 }
484 gSeccompTsyncBroadcastSignum = tsyncSignum;
485
486 // ...and the signal handler also needs to be installed now, to
487 // indicate to anything else looking for free signals that it's
488 // claimed.
489 void (*oldHandler)(int);
490 oldHandler = signal(tsyncSignum, SetThreadSandboxHandler);
491 if (oldHandler != SIG_DFL) {
492 // See the comment on FindFreeSignalNumber about race conditions.
493 if (oldHandler == SIG_ERR) {
494 MOZ_CRASH("failed while registering the signal handler");
495 } else {
496 MOZ_CRASH("failed because the signal is in use by another handler");
497 }
498 SANDBOX_LOG_ERROR("signal %d in use by handler %p!\n", tsyncSignum,
499 oldHandler);
500 }
501 }
502 }
503
RunGlibcLazyInitializers()504 static void RunGlibcLazyInitializers() {
505 // Make glibc's lazy initialization of shm_open() run before sandboxing
506 int fd = shm_open("/dummy", O_RDONLY, 0);
507 if (fd > 0) {
508 close(fd); // In the unlikely case we actually opened something
509 }
510 }
511
SandboxLateInit()512 static void SandboxLateInit() {
513 #ifdef NIGHTLY_BUILD
514 gSandboxCrashOnError = true;
515 for (const char* name : kLibsThatWillCrash) {
516 if (IsLibPresent(name)) {
517 gSandboxCrashOnError = false;
518 break;
519 }
520 }
521 #endif
522
523 if (const char* envVar = PR_GetEnv("MOZ_SANDBOX_CRASH_ON_ERROR")) {
524 if (envVar[0]) {
525 gSandboxCrashOnError = envVar[0] != '0';
526 }
527 }
528
529 RunGlibcLazyInitializers();
530 }
531
532 // Common code for sandbox startup.
SetCurrentProcessSandbox(UniquePtr<sandbox::bpf_dsl::Policy> aPolicy)533 static void SetCurrentProcessSandbox(
534 UniquePtr<sandbox::bpf_dsl::Policy> aPolicy) {
535 MOZ_ASSERT(gSandboxCrashFunc);
536 MOZ_RELEASE_ASSERT(gSandboxReporterClient != nullptr);
537 SandboxLateInit();
538
539 // Auto-collect child processes -- mainly the chroot helper if
540 // present, but also anything setns()ed into the pid namespace (not
541 // yet implemented). This process won't be able to waitpid them
542 // after the seccomp-bpf policy is applied.
543 signal(SIGCHLD, SIG_IGN);
544
545 // Note: PolicyCompiler borrows the policy and registry for its
546 // lifetime, but does not take ownership of them.
547 sandbox::bpf_dsl::PolicyCompiler compiler(aPolicy.get(),
548 sandbox::Trap::Registry());
549 sandbox::CodeGen::Program program = compiler.Compile();
550 if (SandboxInfo::Get().Test(SandboxInfo::kVerbose)) {
551 sandbox::bpf_dsl::DumpBPF::PrintProgram(program);
552 }
553
554 InstallSigSysHandler();
555
556 #ifdef MOZ_ASAN
557 __sanitizer_sandbox_arguments asanArgs;
558 asanArgs.coverage_sandboxed = 1;
559 asanArgs.coverage_fd = -1;
560 asanArgs.coverage_max_block_size = 0;
561 __sanitizer_sandbox_on_notify(&asanArgs);
562 #endif
563
564 // The syscall takes a C-style array, so copy the vector into one.
565 size_t programLen = program.size();
566 UniquePtr<sock_filter[]> flatProgram(new sock_filter[programLen]);
567 for (auto i = program.begin(); i != program.end(); ++i) {
568 flatProgram[i - program.begin()] = *i;
569 }
570
571 sock_fprog fprog;
572 fprog.filter = flatProgram.get();
573 fprog.len = static_cast<unsigned short>(programLen);
574 MOZ_RELEASE_ASSERT(static_cast<size_t>(fprog.len) == programLen);
575
576 const SandboxInfo info = SandboxInfo::Get();
577 if (info.Test(SandboxInfo::kHasSeccompTSync)) {
578 if (info.Test(SandboxInfo::kVerbose)) {
579 SANDBOX_LOG_ERROR("using seccomp tsync");
580 }
581 ApplySandboxWithTSync(&fprog);
582 } else {
583 if (info.Test(SandboxInfo::kVerbose)) {
584 SANDBOX_LOG_ERROR("no tsync support; using signal broadcast");
585 }
586 BroadcastSetThreadSandbox(&fprog);
587 }
588
589 // Now that all threads' filesystem accesses are being intercepted
590 // (if a broker is used) it's safe to chroot the process:
591 EnterChroot();
592 }
593
594 /**
595 * Starts the seccomp sandbox for a content process. Should be called
596 * only once, and before any potentially harmful content is loaded.
597 *
598 * Will normally make the process exit on failure.
599 */
SetContentProcessSandbox(ContentProcessSandboxParams && aParams)600 bool SetContentProcessSandbox(ContentProcessSandboxParams&& aParams) {
601 int brokerFd = aParams.mBrokerFd;
602 aParams.mBrokerFd = -1;
603
604 if (!SandboxInfo::Get().Test(SandboxInfo::kEnabledForContent)) {
605 if (brokerFd >= 0) {
606 close(brokerFd);
607 }
608 return false;
609 }
610
611 auto procType = aParams.mFileProcess ? SandboxReport::ProcType::FILE
612 : SandboxReport::ProcType::CONTENT;
613 gSandboxReporterClient = new SandboxReporterClient(procType);
614
615 // This needs to live until the process exits.
616 static SandboxBrokerClient* sBroker;
617 if (brokerFd >= 0) {
618 sBroker = new SandboxBrokerClient(brokerFd);
619 }
620
621 SetCurrentProcessSandbox(
622 GetContentSandboxPolicy(sBroker, std::move(aParams)));
623 return true;
624 }
625 /**
626 * Starts the seccomp sandbox for a media plugin process. Should be
627 * called only once, and before any potentially harmful content is
628 * loaded -- including the plugin itself, if it's considered untrusted.
629 *
630 * The file indicated by aFilePath, if non-null, can be open()ed
631 * read-only, once, after the sandbox starts; it should be the .so
632 * file implementing the not-yet-loaded plugin.
633 *
634 * Will normally make the process exit on failure.
635 */
SetMediaPluginSandbox(const char * aFilePath)636 void SetMediaPluginSandbox(const char* aFilePath) {
637 MOZ_RELEASE_ASSERT(aFilePath != nullptr);
638 if (!SandboxInfo::Get().Test(SandboxInfo::kEnabledForMedia)) {
639 return;
640 }
641
642 gSandboxReporterClient =
643 new SandboxReporterClient(SandboxReport::ProcType::MEDIA_PLUGIN);
644
645 SandboxOpenedFile plugin(aFilePath);
646 if (!plugin.IsOpen()) {
647 SANDBOX_LOG_ERROR("failed to open plugin file %s: %s", aFilePath,
648 strerror(errno));
649 MOZ_CRASH("failed while trying to open the plugin file ");
650 }
651
652 auto files = new SandboxOpenedFiles();
653 files->Add(std::move(plugin));
654 files->Add("/dev/urandom", true);
655 files->Add("/etc/ld.so.cache"); // Needed for NSS in clearkey.
656 files->Add("/sys/devices/system/cpu/cpu0/tsc_freq_khz");
657 files->Add("/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq");
658 files->Add("/proc/cpuinfo"); // Info also available via CPUID instruction.
659 files->Add("/proc/sys/crypto/fips_enabled"); // Needed for NSS in clearkey.
660 #ifdef __i386__
661 files->Add("/proc/self/auxv"); // Info also in process's address space.
662 #endif
663
664 // Finally, start the sandbox.
665 SetCurrentProcessSandbox(GetMediaSandboxPolicy(files));
666 }
667
SetRemoteDataDecoderSandbox(int aBroker)668 void SetRemoteDataDecoderSandbox(int aBroker) {
669 if (!SandboxInfo::Get().Test(SandboxInfo::kHasSeccompBPF) ||
670 PR_GetEnv("MOZ_DISABLE_RDD_SANDBOX")) {
671 if (aBroker >= 0) {
672 close(aBroker);
673 }
674 return;
675 }
676
677 gSandboxReporterClient =
678 new SandboxReporterClient(SandboxReport::ProcType::RDD);
679
680 // FIXME(bug 1513773): merge this with the one for content?
681 static SandboxBrokerClient* sBroker;
682 if (aBroker >= 0) {
683 sBroker = new SandboxBrokerClient(aBroker);
684 }
685
686 SetCurrentProcessSandbox(GetDecoderSandboxPolicy(sBroker));
687 }
688
SetSocketProcessSandbox(int aBroker)689 void SetSocketProcessSandbox(int aBroker) {
690 if (!SandboxInfo::Get().Test(SandboxInfo::kHasSeccompBPF) ||
691 PR_GetEnv("MOZ_DISABLE_SOCKET_PROCESS_SANDBOX")) {
692 if (aBroker >= 0) {
693 close(aBroker);
694 }
695 return;
696 }
697
698 gSandboxReporterClient =
699 new SandboxReporterClient(SandboxReport::ProcType::SOCKET_PROCESS);
700
701 static SandboxBrokerClient* sBroker;
702 if (aBroker >= 0) {
703 sBroker = new SandboxBrokerClient(aBroker);
704 }
705
706 SetCurrentProcessSandbox(GetSocketProcessSandboxPolicy(sBroker));
707 }
708
709 } // namespace mozilla
710