diff options
Diffstat (limited to 'security/sandbox/linux/SandboxInfo.cpp')
-rw-r--r-- | security/sandbox/linux/SandboxInfo.cpp | 272 |
1 files changed, 0 insertions, 272 deletions
diff --git a/security/sandbox/linux/SandboxInfo.cpp b/security/sandbox/linux/SandboxInfo.cpp deleted file mode 100644 index f813fb026..000000000 --- a/security/sandbox/linux/SandboxInfo.cpp +++ /dev/null @@ -1,272 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=8 sts=2 et sw=2 tw=80: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "SandboxInfo.h" -#include "SandboxLogging.h" -#include "LinuxSched.h" - -#include <errno.h> -#include <stdlib.h> -#include <sys/prctl.h> -#include <sys/stat.h> -#include <sys/syscall.h> -#include <sys/wait.h> -#include <unistd.h> - -#include "base/posix/eintr_wrapper.h" -#include "mozilla/Assertions.h" -#include "mozilla/ArrayUtils.h" -#include "sandbox/linux/system_headers/linux_seccomp.h" -#include "sandbox/linux/system_headers/linux_syscalls.h" - -#ifdef MOZ_VALGRIND -#include <valgrind/valgrind.h> -#endif - - -// A note about assertions: in general, the worst thing this module -// should be able to do is disable sandboxing features, so release -// asserts or MOZ_CRASH should be avoided, even for seeming -// impossibilities like an unimplemented syscall returning success -// (which has happened: https://crbug.com/439795 ). -// -// MOZ_DIAGNOSTIC_ASSERT (debug builds, plus Nightly/Aurora non-debug) -// is probably the best choice for conditions that shouldn't be able -// to fail without the help of bugs in the kernel or system libraries. -// -// Regardless of assertion type, whatever condition caused it to fail -// should generally also disable the corresponding feature on builds -// that omit the assertion. - -namespace mozilla { - -// Bug 1229136: this is copied from ../SandboxUtil.cpp to avoid -// complicated build issues; renamespaced to avoid the possibility of -// symbol conflict. -namespace { - -static bool -IsSingleThreaded() -{ - // This detects the thread count indirectly. /proc/<pid>/task has a - // subdirectory for each thread in <pid>'s thread group, and the - // link count on the "task" directory follows Unix expectations: the - // link from its parent, the "." link from itself, and the ".." link - // from each subdirectory; thus, 2+N links for N threads. - struct stat sb; - if (stat("/proc/self/task", &sb) < 0) { - MOZ_DIAGNOSTIC_ASSERT(false, "Couldn't access /proc/self/task!"); - return false; - } - MOZ_DIAGNOSTIC_ASSERT(sb.st_nlink >= 3); - return sb.st_nlink == 3; -} - -} // anonymous namespace - -static bool -HasSeccompBPF() -{ - // Allow simulating the absence of seccomp-bpf support, for testing. - if (getenv("MOZ_FAKE_NO_SANDBOX")) { - return false; - } - - // Valgrind and the sandbox don't interact well, probably because Valgrind - // does various system calls which aren't allowed, even if Firefox itself - // is playing by the rules. -# if defined(MOZ_VALGRIND) - if (RUNNING_ON_VALGRIND) { - return false; - } -# endif - - // Determine whether seccomp-bpf is supported by trying to - // enable it with an invalid pointer for the filter. This will - // fail with EFAULT if supported and EINVAL if not, without - // changing the process's state. - - int rv = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, nullptr); - MOZ_DIAGNOSTIC_ASSERT(rv == -1, "prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER," - " nullptr) didn't fail"); - MOZ_DIAGNOSTIC_ASSERT(errno == EFAULT || errno == EINVAL); - return rv == -1 && errno == EFAULT; -} - -static bool -HasSeccompTSync() -{ - // Similar to above, but for thread-sync mode. See also Chromium's - // sandbox::SandboxBPF::SupportsSeccompThreadFilterSynchronization - if (getenv("MOZ_FAKE_NO_SECCOMP_TSYNC")) { - return false; - } - int rv = syscall(__NR_seccomp, SECCOMP_SET_MODE_FILTER, - SECCOMP_FILTER_FLAG_TSYNC, nullptr); - MOZ_DIAGNOSTIC_ASSERT(rv == -1, "seccomp(..., SECCOMP_FILTER_FLAG_TSYNC," - " nullptr) didn't fail"); - MOZ_DIAGNOSTIC_ASSERT(errno == EFAULT || errno == EINVAL || errno == ENOSYS); - return rv == -1 && errno == EFAULT; -} - -static bool -HasUserNamespaceSupport() -{ - // Note: the /proc/<pid>/ns/* files track setns(2) support, which in - // some cases (e.g., pid) significantly postdates kernel support for - // the namespace type, so in general this type of check could be a - // false negative. However, for user namespaces, any kernel new - // enough for the feature to be usable for us has setns support - // (v3.8), so this is okay. - // - // The non-user namespaces all default to "y" in init/Kconfig, but - // check them explicitly in case someone has a weird custom config. - static const char* const paths[] = { - "/proc/self/ns/user", - "/proc/self/ns/pid", - "/proc/self/ns/net", - "/proc/self/ns/ipc", - }; - for (size_t i = 0; i < ArrayLength(paths); ++i) { - if (access(paths[i], F_OK) == -1) { - MOZ_ASSERT(errno == ENOENT); - return false; - } - } - return true; -} - -static bool -CanCreateUserNamespace() -{ - // Unfortunately, the only way to verify that this process can - // create a new user namespace is to actually create one; because - // this process's namespaces shouldn't be side-effected (yet), it's - // necessary to clone (and collect) a child process. See also - // Chromium's sandbox::Credentials::SupportsNewUserNS. - // - // This is somewhat more expensive than the other tests, so it's - // cached in the environment to prevent child processes from having - // to re-run the test. - // - // This is run at static initializer time, while single-threaded, so - // locking isn't needed to access the environment. - static const char kCacheEnvName[] = "MOZ_ASSUME_USER_NS"; - const char* cached = getenv(kCacheEnvName); - if (cached) { - return cached[0] > '0'; - } - - // Valgrind might allow the clone, but doesn't know what to do with - // unshare. Check for that by unsharing nothing. (Valgrind will - // probably need sandboxing disabled entirely, but no need to break - // things worse than strictly necessary.) - if (syscall(__NR_unshare, 0) != 0) { -#ifdef MOZ_VALGRIND - MOZ_ASSERT(errno == ENOSYS); -#else - // If something else can cause that call to fail, we's like to know - // about it; the right way to handle it might not be the same. - MOZ_ASSERT(false); -#endif - return false; - } - - pid_t pid = syscall(__NR_clone, SIGCHLD | CLONE_NEWUSER, - nullptr, nullptr, nullptr, nullptr); - if (pid == 0) { - // In the child. Do as little as possible. - _exit(0); - } - if (pid == -1) { - // Failure. - MOZ_ASSERT(errno == EINVAL || // unsupported - errno == EPERM || // root-only, or we're already chrooted - errno == EUSERS); // already at user namespace nesting limit - setenv(kCacheEnvName, "0", 1); - return false; - } - // Otherwise, in the parent and successful. - bool waitpid_ok = HANDLE_EINTR(waitpid(pid, nullptr, 0)) == pid; - MOZ_ASSERT(waitpid_ok); - if (!waitpid_ok) { - return false; - } - setenv(kCacheEnvName, "1", 1); - return true; -} - -/* static */ -SandboxInfo SandboxInfo::sSingleton = SandboxInfo(); - -SandboxInfo::SandboxInfo() { - int flags = 0; - static_assert(sizeof(flags) >= sizeof(Flags), "enum Flags fits in an int"); - - if (HasSeccompBPF()) { - flags |= kHasSeccompBPF; - if (HasSeccompTSync()) { - flags |= kHasSeccompTSync; - } - } - - // Detect the threading-problem signal from the parent process. - if (getenv("MOZ_SANDBOX_UNEXPECTED_THREADS")) { - flags |= kUnexpectedThreads; - } else { - if (HasUserNamespaceSupport()) { - flags |= kHasPrivilegedUserNamespaces; - if (CanCreateUserNamespace()) { - flags |= kHasUserNamespaces; - } - } - } - -#ifdef MOZ_CONTENT_SANDBOX - if (!getenv("MOZ_DISABLE_CONTENT_SANDBOX")) { - flags |= kEnabledForContent; - } - if (getenv("MOZ_PERMISSIVE_CONTENT_SANDBOX")) { - flags |= kPermissive; - } -#endif -#ifdef MOZ_GMP_SANDBOX - if (!getenv("MOZ_DISABLE_GMP_SANDBOX")) { - flags |= kEnabledForMedia; - } -#endif - if (getenv("MOZ_SANDBOX_VERBOSE")) { - flags |= kVerbose; - } - - mFlags = static_cast<Flags>(flags); -} - -/* static */ void -SandboxInfo::ThreadingCheck() -{ - // Allow MOZ_SANDBOX_UNEXPECTED_THREADS to be set manually for testing. - if (IsSingleThreaded() && - !getenv("MOZ_SANDBOX_UNEXPECTED_THREADS")) { - return; - } - SANDBOX_LOG_ERROR("unexpected multithreading found; this prevents using" - " namespace sandboxing.%s", - // getenv isn't thread-safe, but see below. - getenv("LD_PRELOAD") ? " (If you're LD_PRELOAD'ing" - " nVidia GL: that's not necessary for Gecko.)" : ""); - - // Propagate this information for use by child processes. (setenv - // isn't thread-safe, but other threads are from non-Gecko code so - // they wouldn't be using NSPR; we have to hope for the best.) - setenv("MOZ_SANDBOX_UNEXPECTED_THREADS", "1", 0); - int flags = sSingleton.mFlags; - flags |= kUnexpectedThreads; - flags &= ~(kHasUserNamespaces | kHasPrivilegedUserNamespaces); - sSingleton.mFlags = static_cast<Flags>(flags); -} - -} // namespace mozilla |