diff options
Diffstat (limited to 'www/firefox/files/patch-bug1440203')
-rw-r--r-- | www/firefox/files/patch-bug1440203 | 788 |
1 files changed, 0 insertions, 788 deletions
diff --git a/www/firefox/files/patch-bug1440203 b/www/firefox/files/patch-bug1440203 deleted file mode 100644 index d66aebf5ff72..000000000000 --- a/www/firefox/files/patch-bug1440203 +++ /dev/null @@ -1,788 +0,0 @@ -commit cc6e7ab13380 -Author: Jed Davis <jld@mozilla.com> -Date: Thu Oct 22 21:23:32 2020 +0000 - - Bug 1440203 - Support memfd_create in IPC shared memory. r=glandium - - This commit also allows `memfd_create` in the seccomp-bpf policy for all - process types. - - `memfd_create` is an API added in Linux 3.17 (and adopted by FreeBSD - for the upcoming version 13) for creating anonymous shared memory - not connected to any filesystem. Supporting it means that sandboxed - child processes on Linux can create shared memory directly instead of - messaging a broker, which is unavoidably slower, and it should avoid - the problems we'd been seeing with overly small `/dev/shm` in container - environments (which were causing serious problems for using Firefox for - automated testing of frontend projects). - - `memfd_create` also introduces the related operation of file seals: - irrevocably preventing types of modifications to a file. Unfortunately, - the most useful one, `F_SEAL_WRITE`, can't be relied on; see the large - comment in `SharedMemory:ReadOnlyCopy` for details. So we still use - the applicable seals as defense in depth, but read-only copies are - implemented on Linux by using procfs (and see the comments on the - `ReadOnlyCopy` function in `shared_memory_posix.cc` for the subtleties - there). - - There's also a FreeBSD implementation, using `cap_rights_limit` for - read-only copies, if the build host is new enough to have the - `memfd_create` function. - - The support code for Android, which doesn't support shm_open and can't - use the memfd backend because of issues with its SELinux policy (see bug - 1670277), has been reorganized to reflect that we'll always use its own - API, ashmem, in that case. - - Differential Revision: https://phabricator.services.mozilla.com/D90605 ---- - build/moz.configure/headers.configure | 8 + - config/system-headers.mozbuild | 5 + - ipc/chromium/src/base/linux_memfd_defs.h | 69 +++++ - ipc/chromium/src/base/shared_memory.h | 3 +- - ipc/chromium/src/base/shared_memory_posix.cc | 428 +++++++++++++++++++++------ - ipc/gtest/TestSharedMemory.cpp | 46 +++ - security/sandbox/linux/SandboxFilter.cpp | 9 +- - 7 files changed, 472 insertions(+), 96 deletions(-) - -diff --git build/moz.configure/headers.configure build/moz.configure/headers.configure -index 9445f10a4d53..0f4455d8eeea 100644 ---- build/moz.configure/headers.configure -+++ build/moz.configure/headers.configure -@@ -65,6 +65,14 @@ check_headers( - 'byteswap.h', - ) - -+# memfd_create(2) -- Note that older versions of the Linux man-pages -+# project incorrectly cite <sys/memfd.h>, which doesn't exist; this -+# was fixed in the man-pages-5.00 release. -+set_define('HAVE_MEMFD_CREATE', -+ try_compile(includes=['sys/mman.h'], -+ body='memfd_create("", 0);', -+ check_msg='for memfd_create in sys/mman.h')) -+ - # TODO: Move these checks to file specific to --enable-project=js. - have_perf_event_h = check_header('linux/perf_event.h', - when=building_linux) -diff --git config/system-headers.mozbuild config/system-headers.mozbuild -index 1dfd3f0a48b8..3c355e3f1d71 100644 ---- config/system-headers.mozbuild -+++ config/system-headers.mozbuild -@@ -1352,5 +1352,10 @@ if CONFIG['OS_TARGET'] == 'Linux' and CONFIG['CPU_ARCH'].startswith('mips'): - 'sys/cachectl.h', - ] - -+if CONFIG['OS_TARGET'] == 'FreeBSD': -+ system_headers += [ -+ 'sys/capsicum.h', -+ ] -+ - if CONFIG['MOZ_APP_SYSTEM_HEADERS']: - include("../" + CONFIG['MOZ_BUILD_APP'] + "/app-system-headers.mozbuild") -diff --git ipc/chromium/src/base/linux_memfd_defs.h ipc/chromium/src/base/linux_memfd_defs.h -new file mode 100644 -index 000000000000..f5b0de1de853 ---- /dev/null -+++ ipc/chromium/src/base/linux_memfd_defs.h -@@ -0,0 +1,69 @@ -+/* -*- 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 https://mozilla.org/MPL/2.0/. */ -+ -+#ifndef BASE_LINUX_MEMFD_DEFS_H -+#define BASE_LINUX_MEMFD_DEFS_H -+ -+#include <sys/syscall.h> -+ -+// glibc before 2.27 didn't have a memfd_create wrapper, and if the -+// build system is old enough then it won't have the syscall number -+// and various related constants either. -+ -+#if defined(__x86_64__) -+# define MEMFD_CREATE_NR 319 -+#elif defined(__i386__) -+# define MEMFD_CREATE_NR 356 -+#elif defined(__aarch64__) -+# define MEMFD_CREATE_NR 279 -+#elif defined(__arm__) -+# define MEMFD_CREATE_NR 385 -+#elif defined(__powerpc__) -+# define MEMFD_CREATE_NR 360 -+#elif defined(__s390__) -+# define MEMFD_CREATE_NR 350 -+#elif defined(__mips__) -+# include <sgidefs.h> -+# if _MIPS_SIM == _MIPS_SIM_ABI32 -+# define MEMFD_CREATE_NR 4354 -+# elif _MIPS_SIM == _MIPS_SIM_ABI64 -+# define MEMFD_CREATE_NR 5314 -+# elif _MIPS_SIM == _MIPS_SIM_NABI32 -+# define MEMFD_CREATE_NR 6318 -+# endif // mips subarch -+#endif // arch -+ -+#ifdef MEMFD_CREATE_NR -+# ifdef SYS_memfd_create -+static_assert(MEMFD_CREATE_NR == SYS_memfd_create, -+ "MEMFD_CREATE_NR should match the actual SYS_memfd_create value"); -+# else // defined here but not in system headers -+# define SYS_memfd_create MEMFD_CREATE_NR -+# endif -+#endif -+ -+#ifndef MFD_CLOEXEC -+# define MFD_CLOEXEC 0x0001U -+# define MFD_ALLOW_SEALING 0x0002U -+#endif -+ -+#ifndef F_ADD_SEALS -+# ifndef F_LINUX_SPECIFIC_BASE -+# define F_LINUX_SPECIFIC_BASE 1024 -+# endif -+# define F_ADD_SEALS (F_LINUX_SPECIFIC_BASE + 9) -+# define F_GET_SEALS (F_LINUX_SPECIFIC_BASE + 10) -+# define F_SEAL_SEAL 0x0001 /* prevent further seals from being set */ -+# define F_SEAL_SHRINK 0x0002 /* prevent file from shrinking */ -+# define F_SEAL_GROW 0x0004 /* prevent file from growing */ -+# define F_SEAL_WRITE 0x0008 /* prevent writes */ -+#endif -+ -+#ifndef F_SEAL_FUTURE_WRITE -+# define F_SEAL_FUTURE_WRITE 0x0010 -+#endif -+ -+#endif // BASE_LINUX_MEMFD_DEFS_H -diff --git ipc/chromium/src/base/shared_memory.h ipc/chromium/src/base/shared_memory.h -index 93372f4ab333..49d614164a0b 100644 ---- ipc/chromium/src/base/shared_memory.h -+++ ipc/chromium/src/base/shared_memory.h -@@ -216,8 +216,9 @@ class SharedMemory { - // If true indicates this came from an external source so needs extra checks - // before being mapped. - bool external_section_ = false; --#elif defined(OS_POSIX) -+#elif defined(OS_POSIX) && !defined(ANDROID) - mozilla::UniqueFileHandle frozen_file_; -+ bool is_memfd_ = false; - #endif - bool read_only_ = false; - bool freezeable_ = false; -diff --git ipc/chromium/src/base/shared_memory_posix.cc ipc/chromium/src/base/shared_memory_posix.cc -index d5f734ac91b8..e36805aedb89 100644 ---- ipc/chromium/src/base/shared_memory_posix.cc -+++ ipc/chromium/src/base/shared_memory_posix.cc -@@ -16,6 +16,18 @@ - # include "mozilla/Ashmem.h" - #endif - -+#ifdef OS_LINUX -+# include "linux_memfd_defs.h" -+#endif -+ -+#ifdef __FreeBSD__ -+# include <sys/capsicum.h> -+#endif -+ -+#ifdef MOZ_VALGRIND -+# include <valgrind/valgrind.h> -+#endif -+ - #include "base/eintr_wrapper.h" - #include "base/logging.h" - #include "base/string_util.h" -@@ -46,11 +58,14 @@ SharedMemory::~SharedMemory() { - - bool SharedMemory::SetHandle(SharedMemoryHandle handle, bool read_only) { - DCHECK(!mapped_file_); -+#ifndef ANDROID - DCHECK(!frozen_file_); -+#endif - - freezeable_ = false; - mapped_file_.reset(handle.fd); - read_only_ = read_only; -+ // is_memfd_ only matters for freezing, which isn't possible - return true; - } - -@@ -62,11 +77,187 @@ bool SharedMemory::IsHandleValid(const SharedMemoryHandle& handle) { - // static - SharedMemoryHandle SharedMemory::NULLHandle() { return SharedMemoryHandle(); } - --// static -+#ifdef ANDROID -+ -+// Android has its own shared memory API, ashmem. It doesn't support -+// POSIX shm_open, and the memfd support (see below) also doesn't work -+// because its SELinux policy prevents the procfs operations we'd use -+// (see bug 1670277 for more details). -+ - bool SharedMemory::AppendPosixShmPrefix(std::string* str, pid_t pid) { --#if defined(ANDROID) - return false; --#else -+} -+ -+bool SharedMemory::CreateInternal(size_t size, bool freezeable) { -+ read_only_ = false; -+ -+ DCHECK(size > 0); -+ DCHECK(!mapped_file_); -+ -+ int fd = mozilla::android::ashmem_create(nullptr, size); -+ if (fd < 0) { -+ CHROMIUM_LOG(WARNING) << "failed to open shm: " << strerror(errno); -+ return false; -+ } -+ -+ mapped_file_.reset(fd); -+ max_size_ = size; -+ freezeable_ = freezeable; -+ return true; -+} -+ -+bool SharedMemory::ReadOnlyCopy(SharedMemory* ro_out) { -+ DCHECK(mapped_file_); -+ DCHECK(!read_only_); -+ CHECK(freezeable_); -+ -+ if (ro_out == this) { -+ DCHECK(!memory_); -+ } -+ -+ if (mozilla::android::ashmem_setProt(mapped_file_.get(), PROT_READ) != 0) { -+ CHROMIUM_LOG(WARNING) << "failed to set ashmem read-only: " -+ << strerror(errno); -+ return false; -+ } -+ -+ mozilla::UniqueFileHandle ro_file = std::move(mapped_file_); -+ -+ freezeable_ = false; -+ ro_out->Close(); -+ ro_out->mapped_file_ = std::move(ro_file); -+ ro_out->max_size_ = max_size_; -+ ro_out->read_only_ = true; -+ ro_out->freezeable_ = false; -+ -+ return true; -+} -+ -+#else // not Android -+ -+// memfd_create is a nonstandard interface for creating anonymous -+// shared memory accessible as a file descriptor but not tied to any -+// filesystem. It first appeared in Linux 3.17, and was adopted by -+// FreeBSD in version 13. -+ -+# if !defined(HAVE_MEMFD_CREATE) && defined(OS_LINUX) && \ -+ defined(SYS_memfd_create) -+ -+// Older libc versions (e.g., glibc before 2.27) don't have the -+// wrapper, but we can supply our own; see `linux_memfd_defs.h`. -+ -+static int memfd_create(const char* name, unsigned int flags) { -+ return syscall(SYS_memfd_create, name, flags); -+} -+ -+# define HAVE_MEMFD_CREATE 1 -+# endif -+ -+// memfd supports having "seals" applied to the file, to prevent -+// various types of changes (which apply to all fds referencing the -+// file). Unfortunately, we can't rely on F_SEAL_WRITE to implement -+// Freeze(); see the comments in ReadOnlyCopy() below. -+// -+// Instead, to prevent a child process from regaining write access to -+// a read-only copy, the OS must also provide a way to remove write -+// permissions at the file descriptor level. This next section -+// attempts to accomplish that. -+ -+# ifdef HAVE_MEMFD_CREATE -+# ifdef XP_LINUX -+# define USE_MEMFD_CREATE 1 -+ -+// To create a read-only duplicate of an fd, we can use procfs; the -+// same operation could restore write access, but sandboxing prevents -+// child processes from accessing /proc. -+// -+// (Note: if this ever changes to not use /proc, also reconsider how -+// and if HaveMemfd should check whether this works.) -+ -+static int DupReadOnly(int fd) { -+ std::string path = StringPrintf("/proc/self/fd/%d", fd); -+ // procfs opens probably won't EINTR, but checking for it can't hurt -+ return HANDLE_EINTR(open(path.c_str(), O_RDONLY | O_CLOEXEC)); -+} -+ -+# elif defined(__FreeBSD__) -+# define USE_MEMFD_CREATE 1 -+ -+// FreeBSD's Capsicum framework allows irrevocably restricting the -+// operations permitted on a file descriptor. -+ -+static int DupReadOnly(int fd) { -+ int rofd = dup(fd); -+ if (rofd < 0) { -+ return -1; -+ } -+ -+ cap_rights_t rights; -+ cap_rights_init(&rights, CAP_FSTAT, CAP_MMAP_R); -+ if (cap_rights_limit(rofd, &rights) < 0) { -+ int err = errno; -+ close(rofd); -+ errno = err; -+ return -1; -+ } -+ -+ return rofd; -+} -+ -+# else // unhandled OS -+# warning "OS has memfd_create but no DupReadOnly implementation" -+# endif // OS selection -+# endif // HAVE_MEMFD_CREATE -+ -+// Runtime detection for memfd support. -+static bool HaveMemfd() { -+# ifdef USE_MEMFD_CREATE -+ static const bool kHave = [] { -+ mozilla::UniqueFileHandle fd( -+ memfd_create("mozilla-ipc-test", MFD_CLOEXEC | MFD_ALLOW_SEALING)); -+ if (!fd) { -+ DCHECK_EQ(errno, ENOSYS); -+ return false; -+ } -+ -+ // Verify that DupReadOnly works; on Linux it's known to fail if: -+ // -+ // * SELinux assigns the memfd a type for which this process's -+ // domain doesn't have "open" permission; this is always the -+ // case on Android but could occur on desktop as well -+ // -+ // * /proc (used by the DupReadOnly implementation) isn't mounted, -+ // which is a configuration that the Tor Browser project is -+ // interested in as a way to reduce fingerprinting risk -+ // -+ // Sandboxed processes on Linux also can't use it if sandboxing -+ // has already been started, but that's expected. It should be -+ // safe for sandboxed child processes to use memfd even if an -+ // unsandboxed process couldn't freeze them, because freezing -+ // isn't allowed (or meaningful) for memory created by another -+ // process. -+ -+ if (!PR_GetEnv("MOZ_SANDBOXED")) { -+ mozilla::UniqueFileHandle rofd(DupReadOnly(fd.get())); -+ if (!rofd) { -+ CHROMIUM_LOG(WARNING) << "read-only dup failed (" << strerror(errno) -+ << "); not using memfd"; -+ return false; -+ } -+ } -+ return true; -+ }(); -+ return kHave; -+# else -+ return false; -+# endif // USE_MEMFD_CREATE -+} -+ -+// static -+bool SharedMemory::AppendPosixShmPrefix(std::string* str, pid_t pid) { -+ if (HaveMemfd()) { -+ return false; -+ } - *str += '/'; - # ifdef OS_LINUX - // The Snap package environment doesn't provide a private /dev/shm -@@ -90,7 +281,6 @@ bool SharedMemory::AppendPosixShmPrefix(std::string* str, pid_t pid) { - // enough for this. - StringAppendF(str, "org.mozilla.ipc.%d.", static_cast<int>(pid)); - return true; --#endif // !ANDROID - } - - bool SharedMemory::CreateInternal(size_t size, bool freezeable) { -@@ -102,104 +292,118 @@ bool SharedMemory::CreateInternal(size_t size, bool freezeable) { - - mozilla::UniqueFileHandle fd; - mozilla::UniqueFileHandle frozen_fd; -- bool needs_truncate = true; -+ bool is_memfd = false; -+ -+# ifdef USE_MEMFD_CREATE -+ if (HaveMemfd()) { -+ const unsigned flags = MFD_CLOEXEC | (freezeable ? MFD_ALLOW_SEALING : 0); -+ fd.reset(memfd_create("mozilla-ipc", flags)); -+ if (!fd) { -+ // In general it's too late to fall back here -- in a sandboxed -+ // child process, shm_open is already blocked. And it shouldn't -+ // be necessary. -+ CHROMIUM_LOG(WARNING) << "failed to create memfd: " << strerror(errno); -+ return false; -+ } -+ is_memfd = true; -+ if (freezeable) { -+ frozen_fd.reset(DupReadOnly(fd.get())); -+ if (!frozen_fd) { -+ CHROMIUM_LOG(WARNING) -+ << "failed to create read-only memfd: " << strerror(errno); -+ return false; -+ } -+ } -+ } -+# endif - --#ifdef ANDROID -- // Android has its own shared memory facility: -- fd.reset(mozilla::android::ashmem_create(nullptr, size)); - if (!fd) { -- CHROMIUM_LOG(WARNING) << "failed to open shm: " << strerror(errno); -- return false; -- } -- needs_truncate = false; --#else -- // Generic Unix: shm_open + shm_unlink -- do { -- // The names don't need to be unique, but it saves time if they -- // usually are. -- static mozilla::Atomic<size_t> sNameCounter; -- std::string name; -- CHECK(AppendPosixShmPrefix(&name, getpid())); -- StringAppendF(&name, "%zu", sNameCounter++); -- // O_EXCL means the names being predictable shouldn't be a problem. -- fd.reset( -- HANDLE_EINTR(shm_open(name.c_str(), O_RDWR | O_CREAT | O_EXCL, 0600))); -- if (fd) { -- if (freezeable) { -- frozen_fd.reset(HANDLE_EINTR(shm_open(name.c_str(), O_RDONLY, 0400))); -- if (!frozen_fd) { -- int open_err = errno; -- shm_unlink(name.c_str()); -- DLOG(FATAL) << "failed to re-open freezeable shm: " -- << strerror(open_err); -+ // Generic Unix: shm_open + shm_unlink -+ do { -+ // The names don't need to be unique, but it saves time if they -+ // usually are. -+ static mozilla::Atomic<size_t> sNameCounter; -+ std::string name; -+ CHECK(AppendPosixShmPrefix(&name, getpid())); -+ StringAppendF(&name, "%zu", sNameCounter++); -+ // O_EXCL means the names being predictable shouldn't be a problem. -+ fd.reset(HANDLE_EINTR( -+ shm_open(name.c_str(), O_RDWR | O_CREAT | O_EXCL, 0600))); -+ if (fd) { -+ if (freezeable) { -+ frozen_fd.reset(HANDLE_EINTR(shm_open(name.c_str(), O_RDONLY, 0400))); -+ if (!frozen_fd) { -+ int open_err = errno; -+ shm_unlink(name.c_str()); -+ DLOG(FATAL) << "failed to re-open freezeable shm: " -+ << strerror(open_err); -+ return false; -+ } -+ } -+ if (shm_unlink(name.c_str()) != 0) { -+ // This shouldn't happen, but if it does: assume the file is -+ // in fact leaked, and bail out now while it's still 0-length. -+ DLOG(FATAL) << "failed to unlink shm: " << strerror(errno); - return false; - } - } -- if (shm_unlink(name.c_str()) != 0) { -- // This shouldn't happen, but if it does: assume the file is -- // in fact leaked, and bail out now while it's still 0-length. -- DLOG(FATAL) << "failed to unlink shm: " << strerror(errno); -- return false; -- } -- } -- } while (!fd && errno == EEXIST); --#endif -+ } while (!fd && errno == EEXIST); -+ } - - if (!fd) { - CHROMIUM_LOG(WARNING) << "failed to open shm: " << strerror(errno); - return false; - } - -- if (needs_truncate) { --#if defined(HAVE_POSIX_FALLOCATE) -- // Using posix_fallocate will ensure that there's actually space for this -- // file. Otherwise we end up with a sparse file that can give SIGBUS if we -- // run out of space while writing to it. -- int rv; -- { -- // Avoid repeated interruptions of posix_fallocate by the profiler's -- // SIGPROF sampling signal. Indicating "thread sleep" here means we'll -- // get up to one interruption but not more. See bug 1658847 for more. -- // This has to be scoped outside the HANDLE_RV_EINTR retry loop. -- AUTO_PROFILER_THREAD_SLEEP; -- rv = HANDLE_RV_EINTR( -- posix_fallocate(fd.get(), 0, static_cast<off_t>(size))); -- } -- if (rv != 0) { -- if (rv == EOPNOTSUPP || rv == EINVAL || rv == ENODEV) { -- // Some filesystems have trouble with posix_fallocate. For now, we must -- // fallback ftruncate and accept the allocation failures like we do -- // without posix_fallocate. -- // See https://bugzilla.mozilla.org/show_bug.cgi?id=1618914 -- int fallocate_errno = rv; -- rv = HANDLE_EINTR(ftruncate(fd.get(), static_cast<off_t>(size))); -- if (rv != 0) { -- CHROMIUM_LOG(WARNING) << "fallocate failed to set shm size: " -- << strerror(fallocate_errno); -- CHROMIUM_LOG(WARNING) -- << "ftruncate failed to set shm size: " << strerror(errno); -- return false; -- } -- } else { -+# if defined(HAVE_POSIX_FALLOCATE) -+ // Using posix_fallocate will ensure that there's actually space for this -+ // file. Otherwise we end up with a sparse file that can give SIGBUS if we -+ // run out of space while writing to it. -+ int rv; -+ { -+ // Avoid repeated interruptions of posix_fallocate by the profiler's -+ // SIGPROF sampling signal. Indicating "thread sleep" here means we'll -+ // get up to one interruption but not more. See bug 1658847 for more. -+ // This has to be scoped outside the HANDLE_RV_EINTR retry loop. -+ AUTO_PROFILER_THREAD_SLEEP; -+ rv = -+ HANDLE_RV_EINTR(posix_fallocate(fd.get(), 0, static_cast<off_t>(size))); -+ } -+ if (rv != 0) { -+ if (rv == EOPNOTSUPP || rv == EINVAL || rv == ENODEV) { -+ // Some filesystems have trouble with posix_fallocate. For now, we must -+ // fallback ftruncate and accept the allocation failures like we do -+ // without posix_fallocate. -+ // See https://bugzilla.mozilla.org/show_bug.cgi?id=1618914 -+ int fallocate_errno = rv; -+ rv = HANDLE_EINTR(ftruncate(fd.get(), static_cast<off_t>(size))); -+ if (rv != 0) { -+ CHROMIUM_LOG(WARNING) << "fallocate failed to set shm size: " -+ << strerror(fallocate_errno); - CHROMIUM_LOG(WARNING) -- << "fallocate failed to set shm size: " << strerror(rv); -+ << "ftruncate failed to set shm size: " << strerror(errno); - return false; - } -- } --#else -- int rv = HANDLE_EINTR(ftruncate(fd.get(), static_cast<off_t>(size))); -- if (rv != 0) { -+ } else { - CHROMIUM_LOG(WARNING) -- << "ftruncate failed to set shm size: " << strerror(errno); -+ << "fallocate failed to set shm size: " << strerror(rv); - return false; - } --#endif - } -+# else -+ int rv = HANDLE_EINTR(ftruncate(fd.get(), static_cast<off_t>(size))); -+ if (rv != 0) { -+ CHROMIUM_LOG(WARNING) << "ftruncate failed to set shm size: " -+ << strerror(errno); -+ return false; -+ } -+# endif - - mapped_file_ = std::move(fd); - frozen_file_ = std::move(frozen_fd); - max_size_ = size; - freezeable_ = freezeable; -+ is_memfd_ = is_memfd; - return true; - } - -@@ -212,23 +416,63 @@ bool SharedMemory::ReadOnlyCopy(SharedMemory* ro_out) { - DCHECK(!memory_); - } - -- mozilla::UniqueFileHandle ro_file; --#ifdef ANDROID -- ro_file = std::move(mapped_file_); -- if (mozilla::android::ashmem_setProt(ro_file.get(), PROT_READ) != 0) { -- CHROMIUM_LOG(WARNING) << "failed to set ashmem read-only: " -- << strerror(errno); -- return false; -+# ifdef USE_MEMFD_CREATE -+# ifdef MOZ_VALGRIND -+ // Valgrind allows memfd_create but doesn't understand F_ADD_SEALS. -+ static const bool haveSeals = RUNNING_ON_VALGRIND == 0; -+# else -+ static const bool haveSeals = true; -+# endif -+ static const bool useSeals = !PR_GetEnv("MOZ_SHM_NO_SEALS"); -+ if (is_memfd_ && haveSeals && useSeals) { -+ // Seals are added to the file as defense-in-depth. The primary -+ // method of access control is creating a read-only fd (using -+ // procfs in this case) and requiring that sandboxes processes not -+ // have access to /proc/self/fd to regain write permission; this -+ // is the same as with shm_open. -+ // -+ // Unfortunately, F_SEAL_WRITE is unreliable: if the process -+ // forked while there was a writeable mapping, it will inherit a -+ // copy of the mapping, which causes the seal to fail. -+ // -+ // (Also, in the future we may want to split this into separate -+ // classes for mappings and shared memory handles, which would -+ // complicate identifying the case where `F_SEAL_WRITE` would be -+ // possible even in the absence of races with fork.) -+ // -+ // However, Linux 5.1 added F_SEAL_FUTURE_WRITE, which prevents -+ // write operations afterwards, but existing writeable mappings -+ // are unaffected (similar to ashmem protection semantics). -+ -+ const int seals = F_SEAL_GROW | F_SEAL_SHRINK | F_SEAL_SEAL; -+ int sealError = EINVAL; -+ -+# ifdef F_SEAL_FUTURE_WRITE -+ sealError = -+ fcntl(mapped_file_.get(), F_ADD_SEALS, seals | F_SEAL_FUTURE_WRITE) == 0 -+ ? 0 -+ : errno; -+# endif // F_SEAL_FUTURE_WRITE -+ if (sealError == EINVAL) { -+ sealError = -+ fcntl(mapped_file_.get(), F_ADD_SEALS, seals) == 0 ? 0 : errno; -+ } -+ if (sealError != 0) { -+ CHROMIUM_LOG(WARNING) << "failed to seal memfd: " << strerror(errno); -+ return false; -+ } - } --#else -+# else // !USE_MEMFD_CREATE -+ DCHECK(!is_memfd_); -+# endif -+ - DCHECK(frozen_file_); -+ DCHECK(mapped_file_); - mapped_file_ = nullptr; -- ro_file = std::move(frozen_file_); --#endif -+ mozilla::UniqueFileHandle ro_file = std::move(frozen_file_); - - DCHECK(ro_file); - freezeable_ = false; -- - ro_out->Close(); - ro_out->mapped_file_ = std::move(ro_file); - ro_out->max_size_ = max_size_; -@@ -238,6 +482,8 @@ bool SharedMemory::ReadOnlyCopy(SharedMemory* ro_out) { - return true; - } - -+#endif // not Android -+ - bool SharedMemory::Map(size_t bytes, void* fixed_address) { - if (!mapped_file_) { - return false; -@@ -292,10 +538,12 @@ void SharedMemory::Close(bool unmap_view) { - } - - mapped_file_ = nullptr; -+#ifndef ANDROID - if (frozen_file_) { - CHROMIUM_LOG(WARNING) << "freezeable shared memory was never frozen"; - frozen_file_ = nullptr; - } -+#endif - } - - } // namespace base -diff --git ipc/gtest/TestSharedMemory.cpp ipc/gtest/TestSharedMemory.cpp -index 4c8ab0c77592..1474be0ea0d9 100644 ---- ipc/gtest/TestSharedMemory.cpp -+++ ipc/gtest/TestSharedMemory.cpp -@@ -13,6 +13,15 @@ - #include "mozilla/ipc/SharedMemory.h" - #include "mozilla/ipc/SharedMemoryBasic.h" - -+#ifdef XP_LINUX -+# include <errno.h> -+# include <linux/magic.h> -+# include <stdio.h> -+# include <string.h> -+# include <sys/statfs.h> -+# include <sys/utsname.h> -+#endif -+ - #ifdef XP_WIN - # include <windows.h> - #endif -@@ -293,4 +302,41 @@ TEST(IPCSharedMemory, BasicIsZero) - } - #endif - -+#if defined(XP_LINUX) && !defined(ANDROID) -+// Test that memfd_create is used where expected. -+// -+// More precisely: if memfd_create support is expected, verify that -+// shared memory isn't subject to a filesystem size limit. -+TEST(IPCSharedMemory, IsMemfd) -+{ -+ static constexpr int kMajor = 3; -+ static constexpr int kMinor = 17; -+ -+ struct utsname uts; -+ ASSERT_EQ(uname(&uts), 0) << strerror(errno); -+ ASSERT_STREQ(uts.sysname, "Linux"); -+ int major, minor; -+ ASSERT_EQ(sscanf(uts.release, "%d.%d", &major, &minor), 2); -+ bool expectMemfd = major > kMajor || (major == kMajor && minor >= kMinor); -+ -+ base::SharedMemory shm; -+ ASSERT_TRUE(shm.Create(1)); -+ UniqueFileHandle fd = shm.TakeHandle(); -+ ASSERT_TRUE(fd); -+ -+ struct statfs fs; -+ ASSERT_EQ(fstatfs(fd.get(), &fs), 0) << strerror(errno); -+ EXPECT_EQ(fs.f_type, TMPFS_MAGIC); -+ static constexpr decltype(fs.f_blocks) kNoLimit = 0; -+ if (expectMemfd) { -+ EXPECT_EQ(fs.f_blocks, kNoLimit); -+ } else { -+ // On older kernels, we expect the memfd / no-limit test to fail. -+ // (In theory it could succeed if backported memfd support exists; -+ // if that ever happens, this check can be removed.) -+ EXPECT_NE(fs.f_blocks, kNoLimit); -+ } -+} -+#endif -+ - } // namespace mozilla -diff --git security/sandbox/linux/SandboxFilter.cpp security/sandbox/linux/SandboxFilter.cpp -index b337a513791e..3017cd967766 100644 ---- security/sandbox/linux/SandboxFilter.cpp -+++ security/sandbox/linux/SandboxFilter.cpp -@@ -704,6 +704,10 @@ class SandboxPolicyCommon : public SandboxPolicyBase { - case __NR_munmap: - return Allow(); - -+ // Shared memory -+ case __NR_memfd_create: -+ return Allow(); -+ - // ipc::Shmem; also, glibc when creating threads: - case __NR_mprotect: - return Allow(); -@@ -1395,11 +1399,6 @@ class ContentSandboxPolicy : public SandboxPolicyCommon { - case __NR_eventfd2: - return Allow(); - --# ifdef __NR_memfd_create -- case __NR_memfd_create: -- return Allow(); --# endif -- - # ifdef __NR_rt_tgsigqueueinfo - // Only allow to send signals within the process. - case __NR_rt_tgsigqueueinfo: { |