Source code

Revision control

Other Tools

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 "SandboxLaunch.h"
8
9
#include <fcntl.h>
10
#include <sched.h>
11
#include <setjmp.h>
12
#include <signal.h>
13
#include <sys/socket.h>
14
#include <sys/syscall.h>
15
#include <unistd.h>
16
17
#include "LinuxCapabilities.h"
18
#include "LinuxSched.h"
19
#include "SandboxChrootProto.h"
20
#include "SandboxInfo.h"
21
#include "SandboxLogging.h"
22
#include "base/eintr_wrapper.h"
23
#include "base/strings/safe_sprintf.h"
24
#include "mozilla/Array.h"
25
#include "mozilla/ArrayUtils.h"
26
#include "mozilla/Assertions.h"
27
#include "mozilla/Attributes.h"
28
#include "mozilla/Move.h"
29
#include "mozilla/Preferences.h"
30
#include "mozilla/SandboxReporter.h"
31
#include "mozilla/SandboxSettings.h"
32
#include "mozilla/Services.h"
33
#include "mozilla/Unused.h"
34
#include "nsCOMPtr.h"
35
#include "nsDebug.h"
36
#include "nsIGfxInfo.h"
37
#include "nsString.h"
38
#include "nsThreadUtils.h"
39
#include "prenv.h"
40
#include "sandbox/linux/system_headers/linux_syscalls.h"
41
42
#ifdef MOZ_X11
43
# ifndef MOZ_WIDGET_GTK
44
# error "Unknown toolkit"
45
# endif
46
# include <gdk/gdk.h>
47
# include <gdk/gdkx.h>
48
# include "X11UndefineNone.h"
49
# include "gfxPlatform.h"
50
#endif
51
52
namespace mozilla {
53
54
// Returns true if graphics will work from a content process
55
// started in a new network namespace. Specifically, named
56
// Unix-domain sockets will work, but TCP/IP will not, even if it's a
57
// connection to localhost: the child process has its own private
58
// loopback interface.
59
//
60
// (Longer-term we intend to either proxy or remove X11 access from
61
// content processes, at which point this will stop being an issue.)
62
static bool IsDisplayLocal() {
63
// For X11, check whether the parent's connection is a Unix-domain
64
// socket. This is done instead of trying to parse the display name
65
// because an empty hostname (e.g., ":0") will fall back to TCP in
66
// case of failure to connect using Unix-domain sockets.
67
#ifdef MOZ_X11
68
// First, ensure that the parent process's graphics are initialized.
69
Unused << gfxPlatform::GetPlatform();
70
71
const auto display = gdk_display_get_default();
72
if (NS_WARN_IF(display == nullptr)) {
73
return false;
74
}
75
if (GDK_IS_X11_DISPLAY(display)) {
76
const int xSocketFd = ConnectionNumber(GDK_DISPLAY_XDISPLAY(display));
77
if (NS_WARN_IF(xSocketFd < 0)) {
78
return false;
79
}
80
81
int domain;
82
socklen_t optlen = static_cast<socklen_t>(sizeof(domain));
83
int rv = getsockopt(xSocketFd, SOL_SOCKET, SO_DOMAIN, &domain, &optlen);
84
if (NS_WARN_IF(rv != 0)) {
85
return false;
86
}
87
MOZ_RELEASE_ASSERT(static_cast<size_t>(optlen) == sizeof(domain));
88
if (domain != AF_LOCAL) {
89
return false;
90
}
91
// There's one more complication: Xorg listens on named sockets
92
// (actual filesystem nodes) as well as abstract addresses (opaque
93
// octet strings scoped to the network namespace; this is a Linux
94
// extension).
95
//
96
// Inside a container environment (e.g., when running as a Snap
97
// package), it's possible that only the abstract addresses are
98
// accessible. In that case, the display must be considered
99
// remote. See also bug 1450740.
100
//
101
// Unfortunately, the Xorg client libraries prefer the abstract
102
// addresses, so this isn't directly detectable by inspecting the
103
// parent process's socket. Instead, parse the DISPLAY env var
104
// (which was updated if necessary in nsAppRunner.cpp) to get the
105
// display number and construct the socket path, falling back to
106
// testing the directory in case that doesn't work. (See bug
107
// 1565972 and bug 1559368 for cases where we need to test the
108
// specific socket.)
109
const char* const displayStr = PR_GetEnv("DISPLAY");
110
nsAutoCString socketPath("/tmp/.X11-unix");
111
int accessFlags = X_OK;
112
int displayNum;
113
// sscanf ignores trailing text, so display names with a screen
114
// number (e.g., ":0.2") will parse correctly.
115
if (displayStr && (sscanf(displayStr, ":%d", &displayNum) == 1 ||
116
sscanf(displayStr, "unix:%d", &displayNum) == 1)) {
117
socketPath.AppendPrintf("/X%d", displayNum);
118
accessFlags = R_OK | W_OK;
119
}
120
if (access(socketPath.get(), accessFlags) != 0) {
121
SANDBOX_LOG_ERROR(
122
"%s is inaccessible (%s); can't isolate network namespace in"
123
" content processes",
124
socketPath.get(), strerror(errno));
125
return false;
126
}
127
}
128
#endif
129
130
// Assume that other backends (e.g., Wayland) will not use the
131
// network namespace.
132
return true;
133
}
134
135
bool HasAtiDrivers() {
136
nsCOMPtr<nsIGfxInfo> gfxInfo = services::GetGfxInfo();
137
nsAutoString vendorID;
138
static const Array<nsresult (nsIGfxInfo::*)(nsAString&), 2> kMethods = {
139
&nsIGfxInfo::GetAdapterVendorID,
140
&nsIGfxInfo::GetAdapterVendorID2,
141
};
142
for (const auto method : kMethods) {
143
if (NS_SUCCEEDED((gfxInfo->*method)(vendorID))) {
144
// This test is based on telemetry data. The proprietary ATI
145
// drivers seem to use this vendor string, including for some
146
// newer devices that have AMD branding in the device name, such
147
// as those using AMDGPU-PRO drivers.
148
// The open-source drivers integrated into Mesa appear to use
149
// the vendor ID "X.Org" instead.
150
if (vendorID.EqualsLiteral("ATI Technologies Inc.")) {
151
return true;
152
}
153
}
154
}
155
156
return false;
157
}
158
159
// Content processes may need direct access to SysV IPC in certain
160
// uncommon use cases.
161
static bool ContentNeedsSysVIPC() {
162
// The ALSA dmix plugin uses SysV semaphores and shared memory to
163
// coordinate software mixing.
164
#ifdef MOZ_ALSA
165
if (!Preferences::GetBool("media.cubeb.sandbox")) {
166
return true;
167
}
168
#endif
169
170
// Bug 1438391: VirtualGL uses SysV shm for images and configuration.
171
if (PR_GetEnv("VGL_ISACTIVE") != nullptr) {
172
return true;
173
}
174
175
// The fglrx (ATI Catalyst) GPU drivers use SysV IPC.
176
if (HasAtiDrivers()) {
177
return true;
178
}
179
180
return false;
181
}
182
183
static void PreloadSandboxLib(base::environment_map* aEnv) {
184
// Preload libmozsandbox.so so that sandbox-related interpositions
185
// can be defined there instead of in the executable.
186
// (This could be made conditional on intent to use sandboxing, but
187
// it's harmless for non-sandboxed processes.)
188
nsAutoCString preload;
189
// Prepend this, because people can and do preload libpthread.
190
// (See bug 1222500.)
191
preload.AssignLiteral("libmozsandbox.so");
192
if (const char* oldPreload = PR_GetEnv("LD_PRELOAD")) {
193
// Doesn't matter if oldPreload is ""; extra separators are ignored.
194
preload.Append(' ');
195
preload.Append(oldPreload);
196
(*aEnv)["MOZ_ORIG_LD_PRELOAD"] = oldPreload;
197
}
198
MOZ_ASSERT(aEnv->count("LD_PRELOAD") == 0);
199
(*aEnv)["LD_PRELOAD"] = preload.get();
200
}
201
202
static void AttachSandboxReporter(base::file_handle_mapping_vector* aFdMap) {
203
int srcFd, dstFd;
204
SandboxReporter::Singleton()->GetClientFileDescriptorMapping(&srcFd, &dstFd);
205
aFdMap->push_back({srcFd, dstFd});
206
}
207
208
class SandboxFork : public base::LaunchOptions::ForkDelegate {
209
public:
210
explicit SandboxFork(int aFlags, bool aChroot);
211
virtual ~SandboxFork();
212
213
void PrepareMapping(base::file_handle_mapping_vector* aMap);
214
pid_t Fork() override;
215
216
private:
217
int mFlags;
218
int mChrootServer;
219
int mChrootClient;
220
221
void StartChrootServer();
222
SandboxFork(const SandboxFork&) = delete;
223
SandboxFork& operator=(const SandboxFork&) = delete;
224
};
225
226
static int GetEffectiveSandboxLevel(GeckoProcessType aType) {
227
auto info = SandboxInfo::Get();
228
switch (aType) {
229
case GeckoProcessType_GMPlugin:
230
if (info.Test(SandboxInfo::kEnabledForMedia)) {
231
return 1;
232
}
233
return 0;
234
case GeckoProcessType_Content:
235
// GetEffectiveContentSandboxLevel is main-thread-only due to prefs.
236
MOZ_ASSERT(NS_IsMainThread());
237
if (info.Test(SandboxInfo::kEnabledForContent)) {
238
return GetEffectiveContentSandboxLevel();
239
}
240
return 0;
241
case GeckoProcessType_RDD:
242
return PR_GetEnv("MOZ_DISABLE_RDD_SANDBOX") == nullptr ? 1 : 0;
243
default:
244
return 0;
245
}
246
}
247
248
void SandboxLaunchPrepare(GeckoProcessType aType,
249
base::LaunchOptions* aOptions) {
250
auto info = SandboxInfo::Get();
251
252
// We won't try any kind of sandboxing without seccomp-bpf.
253
if (!info.Test(SandboxInfo::kHasSeccompBPF)) {
254
return;
255
}
256
257
// Check prefs (and env vars) controlling sandbox use.
258
int level = GetEffectiveSandboxLevel(aType);
259
if (level == 0) {
260
return;
261
}
262
263
// At this point, we know we'll be using sandboxing; generic
264
// sandboxing support goes here. The MOZ_SANDBOXED env var tells
265
// the child process whether this is the case.
266
aOptions->env_map["MOZ_SANDBOXED"] = "1";
267
PreloadSandboxLib(&aOptions->env_map);
268
AttachSandboxReporter(&aOptions->fds_to_remap);
269
270
bool canChroot = false;
271
int flags = 0;
272
273
if (aType == GeckoProcessType_Content && level >= 1) {
274
static const bool needSysV = ContentNeedsSysVIPC();
275
if (needSysV) {
276
// Tell the child process so it can adjust its seccomp-bpf
277
// policy.
278
aOptions->env_map["MOZ_SANDBOX_ALLOW_SYSV"] = "1";
279
} else {
280
flags |= CLONE_NEWIPC;
281
}
282
}
283
284
// Anything below this requires unprivileged user namespaces.
285
if (!info.Test(SandboxInfo::kHasUserNamespaces)) {
286
return;
287
}
288
289
switch (aType) {
290
case GeckoProcessType_GMPlugin:
291
case GeckoProcessType_RDD:
292
if (level >= 1) {
293
canChroot = true;
294
flags |= CLONE_NEWNET | CLONE_NEWIPC;
295
}
296
break;
297
case GeckoProcessType_Content:
298
if (level >= 4) {
299
canChroot = true;
300
// Unshare network namespace if allowed by graphics; see
301
// function definition above for details. (The display
302
// local-ness is cached because it won't change.)
303
static const bool canCloneNet =
304
IsDisplayLocal() && !PR_GetEnv("RENDERDOC_CAPTUREOPTS");
305
if (canCloneNet) {
306
flags |= CLONE_NEWNET;
307
}
308
}
309
// Hidden pref to allow testing user namespaces separately, even
310
// if there's nothing that would require them.
311
if (Preferences::GetBool("security.sandbox.content.force-namespace",
312
false)) {
313
flags |= CLONE_NEWUSER;
314
}
315
break;
316
default:
317
// Nothing yet.
318
break;
319
}
320
321
if (canChroot || flags != 0) {
322
auto forker = MakeUnique<SandboxFork>(flags | CLONE_NEWUSER, canChroot);
323
forker->PrepareMapping(&aOptions->fds_to_remap);
324
aOptions->fork_delegate = std::move(forker);
325
if (canChroot) {
326
aOptions->env_map[kSandboxChrootEnvFlag] = "1";
327
}
328
}
329
}
330
331
SandboxFork::SandboxFork(int aFlags, bool aChroot)
332
: mFlags(aFlags), mChrootServer(-1), mChrootClient(-1) {
333
if (aChroot) {
334
int fds[2];
335
int rv = socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, fds);
336
if (rv != 0) {
337
SANDBOX_LOG_ERROR("socketpair: %s", strerror(errno));
338
MOZ_CRASH("socketpair failed");
339
}
340
mChrootClient = fds[0];
341
mChrootServer = fds[1];
342
}
343
}
344
345
void SandboxFork::PrepareMapping(base::file_handle_mapping_vector* aMap) {
346
if (mChrootClient >= 0) {
347
aMap->push_back({mChrootClient, kSandboxChrootClientFd});
348
}
349
}
350
351
SandboxFork::~SandboxFork() {
352
if (mChrootClient >= 0) {
353
close(mChrootClient);
354
}
355
if (mChrootServer >= 0) {
356
close(mChrootServer);
357
}
358
}
359
360
static void BlockAllSignals(sigset_t* aOldSigs) {
361
sigset_t allSigs;
362
int rv = sigfillset(&allSigs);
363
MOZ_RELEASE_ASSERT(rv == 0);
364
rv = pthread_sigmask(SIG_BLOCK, &allSigs, aOldSigs);
365
if (rv != 0) {
366
SANDBOX_LOG_ERROR("pthread_sigmask (block all): %s", strerror(rv));
367
MOZ_CRASH("pthread_sigmask");
368
}
369
}
370
371
static void RestoreSignals(const sigset_t* aOldSigs) {
372
// Assuming that pthread_sigmask is a thin layer over rt_sigprocmask
373
// and doesn't try to touch TLS, which may be in an "interesting"
374
// state right now:
375
int rv = pthread_sigmask(SIG_SETMASK, aOldSigs, nullptr);
376
if (rv != 0) {
377
SANDBOX_LOG_ERROR("pthread_sigmask (restore): %s", strerror(-rv));
378
MOZ_CRASH("pthread_sigmask");
379
}
380
}
381
382
static void ResetSignalHandlers() {
383
for (int signum = 1; signum <= SIGRTMAX; ++signum) {
384
if (signal(signum, SIG_DFL) == SIG_ERR) {
385
MOZ_DIAGNOSTIC_ASSERT(errno == EINVAL);
386
}
387
}
388
}
389
390
namespace {
391
392
// The libc clone() routine insists on calling a provided function on
393
// a new stack, even if the address space isn't shared and it would be
394
// safe to expose the underlying system call's fork()-like behavior.
395
// So, we work around this by longjmp()ing back onto the original stack;
396
// this technique is also used by Chromium.
397
//
398
// In theory, the clone syscall could be used directly if we ensure
399
// that functions like raise() are never used in the child, including
400
// by inherited signal handlers, but the longjmp approach isn't much
401
// extra code and avoids a class of potential bugs.
402
static int CloneCallee(void* aPtr) {
403
auto ctxPtr = reinterpret_cast<jmp_buf*>(aPtr);
404
longjmp(*ctxPtr, 1);
405
MOZ_CRASH("unreachable");
406
return 1;
407
}
408
409
// According to the Chromium developers, builds with FORTIFY_SOURCE
410
// require that longjump move the stack pointer towards the root
411
// function of the call stack. Therefore, we must ensure that the
412
// clone callee stack is leafward of the stack pointer captured in
413
// setjmp() below by using this no-inline helper function.
414
//
415
// ASan apparently also causes problems, by the combination of
416
// allocating the large stack-allocated buffer outside of the actual
417
// stack and then assuming that longjmp is used only to unwind a
418
// stack, not switch stacks.
419
//
420
// Valgrind would disapprove of using clone() without CLONE_VM;
421
// Chromium uses the raw syscall as a workaround in that case, but
422
// we don't currently support sandboxing under valgrind.
423
MOZ_NEVER_INLINE MOZ_ASAN_BLACKLIST static pid_t DoClone(int aFlags,
424
jmp_buf* aCtx) {
425
uint8_t miniStack[PTHREAD_STACK_MIN];
426
#ifdef __hppa__
427
void* stackPtr = miniStack;
428
#else
429
void* stackPtr = ArrayEnd(miniStack);
430
#endif
431
return clone(CloneCallee, stackPtr, aFlags, aCtx);
432
}
433
434
} // namespace
435
436
// Similar to fork(), but allows passing flags to clone() and does not
437
// run pthread_atfork hooks.
438
static pid_t ForkWithFlags(int aFlags) {
439
// Don't allow flags that would share the address space, or
440
// require clone() arguments we're not passing:
441
static const int kBadFlags = CLONE_VM | CLONE_VFORK | CLONE_SETTLS |
442
CLONE_PARENT_SETTID | CLONE_CHILD_SETTID |
443
CLONE_CHILD_CLEARTID;
444
MOZ_RELEASE_ASSERT((aFlags & kBadFlags) == 0);
445
446
jmp_buf ctx;
447
if (setjmp(ctx) == 0) {
448
// In the parent and just called setjmp:
449
return DoClone(aFlags | SIGCHLD, &ctx);
450
}
451
// In the child and have longjmp'ed:
452
return 0;
453
}
454
455
static bool WriteStringToFile(const char* aPath, const char* aStr,
456
const size_t aLen) {
457
int fd = open(aPath, O_WRONLY);
458
if (fd < 0) {
459
return false;
460
}
461
ssize_t written = write(fd, aStr, aLen);
462
if (close(fd) != 0 || written != ssize_t(aLen)) {
463
return false;
464
}
465
return true;
466
}
467
468
// This function sets up uid/gid mappings that preserve the
469
// process's previous ids. Mapping the uid/gid to something is
470
// necessary in order to nest user namespaces (not currently being
471
// used, but could be useful), and leaving the ids unchanged is
472
// likely to minimize unexpected side-effects.
473
static void ConfigureUserNamespace(uid_t uid, gid_t gid) {
474
using base::strings::SafeSPrintf;
475
char buf[sizeof("18446744073709551615 18446744073709551615 1")];
476
size_t len;
477
478
len = static_cast<size_t>(SafeSPrintf(buf, "%d %d 1", uid, uid));
479
MOZ_RELEASE_ASSERT(len < sizeof(buf));
480
if (!WriteStringToFile("/proc/self/uid_map", buf, len)) {
481
MOZ_CRASH("Failed to write /proc/self/uid_map");
482
}
483
484
// In recent kernels (3.19, 3.18.2, 3.17.8), for security reasons,
485
// establishing gid mappings will fail unless the process first
486
// revokes its ability to call setgroups() by using a /proc node
487
// added in the same set of patches.
488
Unused << WriteStringToFile("/proc/self/setgroups", "deny", 4);
489
490
len = static_cast<size_t>(SafeSPrintf(buf, "%d %d 1", gid, gid));
491
MOZ_RELEASE_ASSERT(len < sizeof(buf));
492
if (!WriteStringToFile("/proc/self/gid_map", buf, len)) {
493
MOZ_CRASH("Failed to write /proc/self/gid_map");
494
}
495
}
496
497
static void DropAllCaps() {
498
if (!LinuxCapabilities().SetCurrent()) {
499
SANDBOX_LOG_ERROR("capset (drop all): %s", strerror(errno));
500
}
501
}
502
503
pid_t SandboxFork::Fork() {
504
if (mFlags == 0) {
505
MOZ_ASSERT(mChrootServer < 0);
506
return fork();
507
}
508
509
uid_t uid = getuid();
510
gid_t gid = getgid();
511
512
// Block signals so that the handlers can be safely reset in the
513
// child process without races, and so that repeated SIGPROF from
514
// the profiler won't prevent clone() from making progress. (The
515
// profiler uses pthread_atfork to do that, but ForkWithFlags
516
// can't run atfork hooks.)
517
sigset_t oldSigs;
518
BlockAllSignals(&oldSigs);
519
pid_t pid = ForkWithFlags(mFlags);
520
if (pid != 0) {
521
RestoreSignals(&oldSigs);
522
return pid;
523
}
524
525
// WARNING: all code from this point on (and in StartChrootServer)
526
// must be async signal safe. In particular, it cannot do anything
527
// that could allocate heap memory or use mutexes.
528
529
// Clear signal handlers in the child, under the assumption that any
530
// actions they would take (running the crash reporter, manipulating
531
// the Gecko profile, etc.) wouldn't work correctly in the child.
532
ResetSignalHandlers();
533
RestoreSignals(&oldSigs);
534
ConfigureUserNamespace(uid, gid);
535
536
if (mChrootServer >= 0) {
537
StartChrootServer();
538
}
539
540
// execve() will drop capabilities, but it seems best to also drop
541
// them here in case they'd do something unexpected in the generic
542
// post-fork code.
543
DropAllCaps();
544
return 0;
545
}
546
547
void SandboxFork::StartChrootServer() {
548
// Run the rest of this function in a separate process that can
549
// chroot() on behalf of this process after it's sandboxed.
550
pid_t pid = ForkWithFlags(CLONE_FS);
551
if (pid < 0) {
552
MOZ_CRASH("failed to clone chroot helper process");
553
}
554
if (pid > 0) {
555
return;
556
}
557
558
LinuxCapabilities caps;
559
caps.Effective(CAP_SYS_CHROOT) = true;
560
if (!caps.SetCurrent()) {
561
SANDBOX_LOG_ERROR("capset (chroot helper): %s", strerror(errno));
562
MOZ_DIAGNOSTIC_ASSERT(false);
563
}
564
565
base::CloseSuperfluousFds(this, [](void* aCtx, int aFd) {
566
return aFd == static_cast<decltype(this)>(aCtx)->mChrootServer;
567
});
568
569
char msg;
570
ssize_t msgLen = HANDLE_EINTR(read(mChrootServer, &msg, 1));
571
if (msgLen == 0) {
572
// Process exited before chrooting (or chose not to chroot?).
573
_exit(0);
574
}
575
MOZ_RELEASE_ASSERT(msgLen == 1);
576
MOZ_RELEASE_ASSERT(msg == kSandboxChrootRequest);
577
578
// This chroots both processes to this process's procfs fdinfo
579
// directory, which becomes empty and unlinked when this process
580
// exits at the end of this function, and which is always
581
// unwriteable.
582
int rv = chroot("/proc/self/fdinfo");
583
MOZ_RELEASE_ASSERT(rv == 0);
584
585
// Drop CAP_SYS_CHROOT ASAP. This must happen before responding;
586
// the main child won't be able to waitpid(), so it could start
587
// handling hostile content before this process finishes exiting.
588
DropAllCaps();
589
590
// The working directory still grant access to the real filesystem;
591
// remove that. (Note: if the process can obtain directory fds, for
592
// example via SandboxBroker, it must be blocked from using fchdir.)
593
rv = chdir("/");
594
MOZ_RELEASE_ASSERT(rv == 0);
595
596
msg = kSandboxChrootResponse;
597
msgLen = HANDLE_EINTR(write(mChrootServer, &msg, 1));
598
MOZ_RELEASE_ASSERT(msgLen == 1);
599
_exit(0);
600
}
601
602
} // namespace mozilla