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