зеркало из https://github.com/mozilla/gecko-dev.git
Backed out 5 changesets (bug 1662564, bug 1664922, bug 1440203) for Valgrind bustages. CLOSED TREE
Backed out changeset 9366b15ee97c (bug 1440203) Backed out changeset bb512f5fdeda (bug 1440203) Backed out changeset be90d6aec690 (bug 1664922) Backed out changeset f6527a1d0f14 (bug 1662564) Backed out changeset 3a2941fa7d4b (bug 1662564)
This commit is contained in:
Родитель
2d509cd2f4
Коммит
75a5750a87
|
@ -809,14 +809,6 @@ add_task(async function() {
|
|||
continue;
|
||||
}
|
||||
|
||||
// "Files" from memfd_create() are similar to tmpfs but never
|
||||
// exist in the filesystem; however, they have names which are
|
||||
// exposed in procfs, and the I/O interposer observes when
|
||||
// they're close()d.
|
||||
if (LINUX && filename.startsWith("/memfd:")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Shared memory uses temporary files on MacOS <= 10.11 to avoid
|
||||
// a kernel security bug that will never be patched (see
|
||||
// https://crbug.com/project-zero/1671 for details). This can
|
||||
|
|
|
@ -65,14 +65,6 @@ 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)
|
||||
|
|
|
@ -1349,10 +1349,5 @@ 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")
|
||||
|
|
|
@ -1,69 +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 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
|
|
@ -36,7 +36,7 @@ typedef FileDescriptor SharedMemoryHandle;
|
|||
class SharedMemory {
|
||||
public:
|
||||
// Create a new SharedMemory object.
|
||||
SharedMemory() = default;
|
||||
SharedMemory();
|
||||
|
||||
// Create a new SharedMemory object from an existing, open
|
||||
// shared memory file.
|
||||
|
@ -46,7 +46,7 @@ class SharedMemory {
|
|||
}
|
||||
|
||||
// Move constructor; transfers ownership.
|
||||
SharedMemory(SharedMemory&& other) = default;
|
||||
SharedMemory(SharedMemory&& other);
|
||||
|
||||
// Destructor. Will close any open files.
|
||||
~SharedMemory();
|
||||
|
@ -60,7 +60,7 @@ class SharedMemory {
|
|||
static bool IsHandleValid(const SharedMemoryHandle& handle);
|
||||
|
||||
// IsHandleValid applied to this object's handle.
|
||||
bool IsValid() const { return static_cast<bool>(mapped_file_); }
|
||||
bool IsValid() const;
|
||||
|
||||
// Return invalid handle (see comment above for exact definition).
|
||||
static SharedMemoryHandle NULLHandle();
|
||||
|
@ -85,7 +85,9 @@ class SharedMemory {
|
|||
bool Map(size_t bytes, void* fixed_address = nullptr);
|
||||
|
||||
// Unmaps the shared memory from the caller's address space.
|
||||
void Unmap() { memory_ = nullptr; }
|
||||
// Returns true if successful; returns false on error or if the
|
||||
// memory is not mapped.
|
||||
bool Unmap();
|
||||
|
||||
// Get the size of the opened shared memory backing file.
|
||||
// Note: This size is only available to the creator of the
|
||||
|
@ -96,24 +98,19 @@ class SharedMemory {
|
|||
|
||||
// Gets a pointer to the opened memory space if it has been
|
||||
// Mapped via Map(). Returns NULL if it is not mapped.
|
||||
void* memory() const { return memory_.get(); }
|
||||
void* memory() const { return memory_; }
|
||||
|
||||
// Extracts the underlying file handle; similar to
|
||||
// GiveToProcess(GetCurrentProcId(), ...) but returns a RAII type.
|
||||
// Like GiveToProcess, this unmaps the memory as a side-effect (and
|
||||
// cleans up any OS-specific resources).
|
||||
mozilla::UniqueFileHandle TakeHandle() {
|
||||
mozilla::UniqueFileHandle handle = std::move(mapped_file_);
|
||||
Close();
|
||||
return handle;
|
||||
}
|
||||
// Like GiveToProcess, this unmaps the memory as a side-effect.
|
||||
mozilla::UniqueFileHandle TakeHandle();
|
||||
|
||||
#ifdef OS_WIN
|
||||
// Used only in gfx/ipc/SharedDIBWin.cpp; should be removable once
|
||||
// NPAPI goes away.
|
||||
HANDLE GetHandle() {
|
||||
freezeable_ = false;
|
||||
return mapped_file_.get();
|
||||
return mapped_file_;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -194,34 +191,20 @@ class SharedMemory {
|
|||
|
||||
bool CreateInternal(size_t size, bool freezeable);
|
||||
|
||||
// Unmapping shared memory requires the mapped size on Unix but not
|
||||
// Windows; this encapsulates that difference.
|
||||
struct MappingDeleter {
|
||||
#ifdef OS_POSIX
|
||||
// A default-constructed deleter must be used only with nullptr
|
||||
// (to allow default-constructing UniqueMapping). A deleter with
|
||||
// a size must be used at most once.
|
||||
size_t mapped_size_ = 0;
|
||||
explicit MappingDeleter(size_t size) : mapped_size_(size) {}
|
||||
#endif
|
||||
MappingDeleter() = default;
|
||||
void operator()(void* ptr);
|
||||
};
|
||||
using UniqueMapping = mozilla::UniquePtr<void, MappingDeleter>;
|
||||
|
||||
UniqueMapping memory_;
|
||||
size_t max_size_ = 0;
|
||||
mozilla::UniqueFileHandle mapped_file_;
|
||||
#if defined(OS_WIN)
|
||||
// If true indicates this came from an external source so needs extra checks
|
||||
// before being mapped.
|
||||
bool external_section_ = false;
|
||||
bool external_section_;
|
||||
HANDLE mapped_file_;
|
||||
#elif defined(OS_POSIX)
|
||||
mozilla::UniqueFileHandle frozen_file_;
|
||||
bool is_memfd_ = false;
|
||||
int mapped_file_;
|
||||
int frozen_file_;
|
||||
size_t mapped_size_;
|
||||
#endif
|
||||
bool read_only_ = false;
|
||||
bool freezeable_ = false;
|
||||
void* memory_;
|
||||
bool read_only_;
|
||||
bool freezeable_;
|
||||
size_t max_size_;
|
||||
|
||||
DISALLOW_EVIL_CONSTRUCTORS(SharedMemory);
|
||||
};
|
||||
|
|
|
@ -16,14 +16,6 @@
|
|||
# include "mozilla/Ashmem.h"
|
||||
#endif
|
||||
|
||||
#ifdef OS_LINUX
|
||||
# include "linux_memfd_defs.h"
|
||||
#endif
|
||||
|
||||
#ifdef __FreeBSD__
|
||||
# include <sys/capsicum.h>
|
||||
#endif
|
||||
|
||||
#include "base/eintr_wrapper.h"
|
||||
#include "base/logging.h"
|
||||
#include "base/string_util.h"
|
||||
|
@ -34,32 +26,43 @@
|
|||
|
||||
namespace base {
|
||||
|
||||
void SharedMemory::MappingDeleter::operator()(void* ptr) {
|
||||
// Check that this isn't a default-constructed deleter. (`munmap`
|
||||
// is specified to fail with `EINVAL` if the length is 0, so this
|
||||
// assertion isn't load-bearing.)
|
||||
DCHECK(mapped_size_ != 0);
|
||||
munmap(ptr, mapped_size_);
|
||||
// Guard against multiple calls of the same deleter, which shouldn't
|
||||
// happen (but could, if `UniquePtr::reset` were used). Calling
|
||||
// `munmap` with an incorrect non-zero length would be bad.
|
||||
mapped_size_ = 0;
|
||||
SharedMemory::SharedMemory()
|
||||
: mapped_file_(-1),
|
||||
frozen_file_(-1),
|
||||
mapped_size_(0),
|
||||
memory_(nullptr),
|
||||
read_only_(false),
|
||||
freezeable_(false),
|
||||
max_size_(0) {}
|
||||
|
||||
SharedMemory::SharedMemory(SharedMemory&& other) {
|
||||
if (this == &other) {
|
||||
return;
|
||||
}
|
||||
|
||||
mapped_file_ = other.mapped_file_;
|
||||
mapped_size_ = other.mapped_size_;
|
||||
frozen_file_ = other.frozen_file_;
|
||||
memory_ = other.memory_;
|
||||
read_only_ = other.read_only_;
|
||||
freezeable_ = other.freezeable_;
|
||||
max_size_ = other.max_size_;
|
||||
|
||||
other.mapped_file_ = -1;
|
||||
other.mapped_size_ = 0;
|
||||
other.frozen_file_ = -1;
|
||||
other.memory_ = nullptr;
|
||||
}
|
||||
|
||||
SharedMemory::~SharedMemory() {
|
||||
// This is almost equal to the default destructor, except for the
|
||||
// warning message about unfrozen freezable memory.
|
||||
Close();
|
||||
}
|
||||
SharedMemory::~SharedMemory() { Close(); }
|
||||
|
||||
bool SharedMemory::SetHandle(SharedMemoryHandle handle, bool read_only) {
|
||||
DCHECK(!mapped_file_);
|
||||
DCHECK(!frozen_file_);
|
||||
DCHECK(mapped_file_ == -1);
|
||||
DCHECK(frozen_file_ == -1);
|
||||
|
||||
freezeable_ = false;
|
||||
mapped_file_.reset(handle.fd);
|
||||
mapped_file_ = handle.fd;
|
||||
read_only_ = read_only;
|
||||
// is_memfd_ only matters for freezing, which isn't possible
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -68,123 +71,16 @@ bool SharedMemory::IsHandleValid(const SharedMemoryHandle& handle) {
|
|||
return handle.fd >= 0;
|
||||
}
|
||||
|
||||
bool SharedMemory::IsValid() const { return mapped_file_ >= 0; }
|
||||
|
||||
// static
|
||||
SharedMemoryHandle SharedMemory::NULLHandle() { return SharedMemoryHandle(); }
|
||||
|
||||
// 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
|
||||
# define USE_MEMFD_CREATE 1
|
||||
# ifdef OS_LINUX
|
||||
|
||||
// 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.
|
||||
|
||||
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__)
|
||||
|
||||
// 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"
|
||||
# undef USE_MEMFD_CREATE
|
||||
# endif // OS selection
|
||||
#endif // HAVE_MEMFD_CREATE
|
||||
|
||||
static bool HaveMemfd() {
|
||||
#ifdef USE_MEMFD_CREATE
|
||||
static const bool kHave = [] {
|
||||
# ifdef OS_LINUX
|
||||
// The Tor Browser project was, at one point, attempting to run
|
||||
// Firefox in an environment without /proc mounted, to reduce
|
||||
// possibilities for fingerprinting. If that's the case, we can't
|
||||
// use memfd, because ReadOnlyCopy requires access to procfs to
|
||||
// remove write permissions.
|
||||
//
|
||||
// Complicating this further, in a sandboxed child process, the
|
||||
// first call to this function may happen after sandboxing is
|
||||
// started; in that case, it's expected that procfs isn't
|
||||
// reachable, but it's also expected that ReadOnlyCopy may not be
|
||||
// possible.
|
||||
if (!PR_GetEnv("MOZ_SANDBOXED") &&
|
||||
access("/proc/self/fd", R_OK | X_OK) < 0) {
|
||||
CHROMIUM_LOG(WARNING) << "can't use memfd without procfs";
|
||||
return false;
|
||||
}
|
||||
# endif
|
||||
int fd = memfd_create("mozilla-ipc-test", MFD_CLOEXEC | MFD_ALLOW_SEALING);
|
||||
if (fd < 0) {
|
||||
DCHECK_EQ(errno, ENOSYS);
|
||||
return false;
|
||||
}
|
||||
close(fd);
|
||||
return true;
|
||||
}();
|
||||
return kHave;
|
||||
#else
|
||||
return false;
|
||||
#endif // USE_MEMFD_CREATE
|
||||
}
|
||||
|
||||
// static
|
||||
bool SharedMemory::AppendPosixShmPrefix(std::string* str, pid_t pid) {
|
||||
#if defined(ANDROID)
|
||||
return false;
|
||||
#else
|
||||
if (HaveMemfd()) {
|
||||
return false;
|
||||
}
|
||||
*str += '/';
|
||||
# ifdef OS_LINUX
|
||||
// The Snap package environment doesn't provide a private /dev/shm
|
||||
|
@ -215,79 +111,53 @@ bool SharedMemory::CreateInternal(size_t size, bool freezeable) {
|
|||
read_only_ = false;
|
||||
|
||||
DCHECK(size > 0);
|
||||
DCHECK(!mapped_file_);
|
||||
DCHECK(!frozen_file_);
|
||||
DCHECK(mapped_file_ == -1);
|
||||
DCHECK(frozen_file_ == -1);
|
||||
|
||||
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
|
||||
|
||||
if (!fd) {
|
||||
#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;
|
||||
// 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);
|
||||
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);
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
} while (!fd && errno == EEXIST);
|
||||
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
|
||||
}
|
||||
|
||||
if (!fd) {
|
||||
CHROMIUM_LOG(WARNING) << "failed to open shm: " << strerror(errno);
|
||||
|
@ -340,16 +210,14 @@ bool SharedMemory::CreateInternal(size_t size, bool freezeable) {
|
|||
#endif
|
||||
}
|
||||
|
||||
mapped_file_ = std::move(fd);
|
||||
frozen_file_ = std::move(frozen_fd);
|
||||
mapped_file_ = fd.release();
|
||||
frozen_file_ = frozen_fd.release();
|
||||
max_size_ = size;
|
||||
freezeable_ = freezeable;
|
||||
is_memfd_ = is_memfd;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SharedMemory::ReadOnlyCopy(SharedMemory* ro_out) {
|
||||
DCHECK(mapped_file_);
|
||||
DCHECK(!read_only_);
|
||||
CHECK(freezeable_);
|
||||
|
||||
|
@ -357,77 +225,28 @@ bool SharedMemory::ReadOnlyCopy(SharedMemory* ro_out) {
|
|||
DCHECK(!memory_);
|
||||
}
|
||||
|
||||
mozilla::UniqueFileHandle ro_file;
|
||||
bool is_ashmem = false;
|
||||
|
||||
int ro_file = -1;
|
||||
#ifdef ANDROID
|
||||
if (!is_memfd_) {
|
||||
is_ashmem = true;
|
||||
DCHECK(!frozen_file_);
|
||||
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;
|
||||
}
|
||||
if (mozilla::android::ashmem_setProt(mapped_file_, PROT_READ) != 0) {
|
||||
CHROMIUM_LOG(WARNING) << "failed to set ashmem read-only: "
|
||||
<< strerror(errno);
|
||||
return false;
|
||||
}
|
||||
ro_file = mapped_file_;
|
||||
#else
|
||||
DCHECK(frozen_file_ >= 0);
|
||||
DCHECK(mapped_file_ >= 0);
|
||||
close(mapped_file_);
|
||||
ro_file = frozen_file_;
|
||||
frozen_file_ = -1;
|
||||
#endif
|
||||
|
||||
#ifdef USE_MEMFD_CREATE
|
||||
static const bool useSeals = !PR_GetEnv("MOZ_SHM_NO_SEALS");
|
||||
if (is_memfd_ && 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 // !USE_MEMFD_CREATE
|
||||
DCHECK(!is_memfd_);
|
||||
#endif
|
||||
|
||||
if (!is_ashmem) {
|
||||
DCHECK(frozen_file_);
|
||||
DCHECK(mapped_file_);
|
||||
mapped_file_ = nullptr;
|
||||
ro_file = std::move(frozen_file_);
|
||||
}
|
||||
|
||||
DCHECK(ro_file);
|
||||
DCHECK(ro_file >= 0);
|
||||
mapped_file_ = -1;
|
||||
freezeable_ = false;
|
||||
|
||||
ro_out->Close();
|
||||
ro_out->mapped_file_ = std::move(ro_file);
|
||||
ro_out->mapped_file_ = ro_file;
|
||||
ro_out->max_size_ = max_size_;
|
||||
ro_out->read_only_ = true;
|
||||
ro_out->freezeable_ = false;
|
||||
|
@ -436,16 +255,14 @@ bool SharedMemory::ReadOnlyCopy(SharedMemory* ro_out) {
|
|||
}
|
||||
|
||||
bool SharedMemory::Map(size_t bytes, void* fixed_address) {
|
||||
if (!mapped_file_) {
|
||||
return false;
|
||||
}
|
||||
if (mapped_file_ == -1) return false;
|
||||
DCHECK(!memory_);
|
||||
|
||||
// Don't use MAP_FIXED when a fixed_address was specified, since that can
|
||||
// replace pages that are alread mapped at that address.
|
||||
void* mem =
|
||||
mmap(fixed_address, bytes, PROT_READ | (read_only_ ? 0 : PROT_WRITE),
|
||||
MAP_SHARED, mapped_file_.get(), 0);
|
||||
MAP_SHARED, mapped_file_, 0);
|
||||
|
||||
if (mem == MAP_FAILED) {
|
||||
CHROMIUM_LOG(WARNING) << "Call to mmap failed: " << strerror(errno);
|
||||
|
@ -458,7 +275,17 @@ bool SharedMemory::Map(size_t bytes, void* fixed_address) {
|
|||
return false;
|
||||
}
|
||||
|
||||
memory_ = UniqueMapping(mem, MappingDeleter(bytes));
|
||||
memory_ = mem;
|
||||
mapped_size_ = bytes;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SharedMemory::Unmap() {
|
||||
if (memory_ == NULL) return false;
|
||||
|
||||
munmap(memory_, mapped_size_);
|
||||
memory_ = NULL;
|
||||
mapped_size_ = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -473,7 +300,7 @@ bool SharedMemory::ShareToProcessCommon(ProcessId processId,
|
|||
SharedMemoryHandle* new_handle,
|
||||
bool close_self) {
|
||||
freezeable_ = false;
|
||||
const int new_fd = dup(mapped_file_.get());
|
||||
const int new_fd = dup(mapped_file_);
|
||||
DCHECK(new_fd >= -1);
|
||||
new_handle->fd = new_fd;
|
||||
new_handle->auto_close = true;
|
||||
|
@ -488,11 +315,24 @@ void SharedMemory::Close(bool unmap_view) {
|
|||
Unmap();
|
||||
}
|
||||
|
||||
mapped_file_ = nullptr;
|
||||
if (frozen_file_) {
|
||||
if (mapped_file_ >= 0) {
|
||||
close(mapped_file_);
|
||||
mapped_file_ = -1;
|
||||
}
|
||||
if (frozen_file_ >= 0) {
|
||||
CHROMIUM_LOG(WARNING) << "freezeable shared memory was never frozen";
|
||||
frozen_file_ = nullptr;
|
||||
close(frozen_file_);
|
||||
frozen_file_ = -1;
|
||||
}
|
||||
}
|
||||
|
||||
mozilla::UniqueFileHandle SharedMemory::TakeHandle() {
|
||||
mozilla::UniqueFileHandle fh(mapped_file_);
|
||||
mapped_file_ = -1;
|
||||
// Now that the main fd is removed, reset everything else: close the
|
||||
// frozen fd if present and unmap the memory if mapped.
|
||||
Close();
|
||||
return fh;
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
|
|
|
@ -58,32 +58,57 @@ bool IsSectionSafeToMap(HANDLE handle) {
|
|||
|
||||
namespace base {
|
||||
|
||||
void SharedMemory::MappingDeleter::operator()(void* ptr) {
|
||||
UnmapViewOfFile(ptr);
|
||||
SharedMemory::SharedMemory()
|
||||
: external_section_(false),
|
||||
mapped_file_(NULL),
|
||||
memory_(NULL),
|
||||
read_only_(false),
|
||||
freezeable_(false),
|
||||
max_size_(0) {}
|
||||
|
||||
SharedMemory::SharedMemory(SharedMemory&& other) {
|
||||
if (this == &other) {
|
||||
return;
|
||||
}
|
||||
|
||||
mapped_file_ = other.mapped_file_;
|
||||
memory_ = other.memory_;
|
||||
read_only_ = other.read_only_;
|
||||
max_size_ = other.max_size_;
|
||||
freezeable_ = other.freezeable_;
|
||||
external_section_ = other.external_section_;
|
||||
|
||||
other.mapped_file_ = nullptr;
|
||||
other.memory_ = nullptr;
|
||||
}
|
||||
|
||||
SharedMemory::~SharedMemory() = default;
|
||||
SharedMemory::~SharedMemory() {
|
||||
external_section_ = true;
|
||||
Close();
|
||||
}
|
||||
|
||||
bool SharedMemory::SetHandle(SharedMemoryHandle handle, bool read_only) {
|
||||
DCHECK(!mapped_file_);
|
||||
DCHECK(mapped_file_ == NULL);
|
||||
|
||||
external_section_ = true;
|
||||
freezeable_ = false; // just in case
|
||||
mapped_file_.reset(handle);
|
||||
mapped_file_ = handle;
|
||||
read_only_ = read_only;
|
||||
return true;
|
||||
}
|
||||
|
||||
// static
|
||||
bool SharedMemory::IsHandleValid(const SharedMemoryHandle& handle) {
|
||||
return handle != nullptr;
|
||||
return handle != NULL;
|
||||
}
|
||||
|
||||
bool SharedMemory::IsValid() const { return mapped_file_ != NULL; }
|
||||
|
||||
// static
|
||||
SharedMemoryHandle SharedMemory::NULLHandle() { return nullptr; }
|
||||
SharedMemoryHandle SharedMemory::NULLHandle() { return NULL; }
|
||||
|
||||
bool SharedMemory::CreateInternal(size_t size, bool freezeable) {
|
||||
DCHECK(!mapped_file_);
|
||||
DCHECK(mapped_file_ == NULL);
|
||||
read_only_ = false;
|
||||
|
||||
// If the shared memory object has no DACL, any process can
|
||||
|
@ -122,9 +147,9 @@ bool SharedMemory::CreateInternal(size_t size, bool freezeable) {
|
|||
}
|
||||
}
|
||||
|
||||
mapped_file_.reset(CreateFileMapping(
|
||||
INVALID_HANDLE_VALUE, psa, PAGE_READWRITE, 0, static_cast<DWORD>(size),
|
||||
name.IsEmpty() ? nullptr : name.get()));
|
||||
mapped_file_ = CreateFileMapping(INVALID_HANDLE_VALUE, psa, PAGE_READWRITE, 0,
|
||||
static_cast<DWORD>(size),
|
||||
name.IsEmpty() ? nullptr : name.get());
|
||||
if (!mapped_file_) return false;
|
||||
|
||||
max_size_ = size;
|
||||
|
@ -141,18 +166,19 @@ bool SharedMemory::ReadOnlyCopy(SharedMemory* ro_out) {
|
|||
}
|
||||
|
||||
HANDLE ro_handle;
|
||||
if (!::DuplicateHandle(GetCurrentProcess(), mapped_file_.release(),
|
||||
GetCurrentProcess(), &ro_handle,
|
||||
GENERIC_READ | FILE_MAP_READ, false,
|
||||
if (!::DuplicateHandle(GetCurrentProcess(), mapped_file_, GetCurrentProcess(),
|
||||
&ro_handle, GENERIC_READ | FILE_MAP_READ, false,
|
||||
DUPLICATE_CLOSE_SOURCE)) {
|
||||
// DUPLICATE_CLOSE_SOURCE applies even if there is an error.
|
||||
mapped_file_ = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
mapped_file_ = nullptr;
|
||||
freezeable_ = false;
|
||||
|
||||
ro_out->Close();
|
||||
ro_out->mapped_file_.reset(ro_handle);
|
||||
ro_out->mapped_file_ = ro_handle;
|
||||
ro_out->max_size_ = max_size_;
|
||||
ro_out->read_only_ = true;
|
||||
ro_out->freezeable_ = false;
|
||||
|
@ -162,27 +188,31 @@ bool SharedMemory::ReadOnlyCopy(SharedMemory* ro_out) {
|
|||
}
|
||||
|
||||
bool SharedMemory::Map(size_t bytes, void* fixed_address) {
|
||||
if (!mapped_file_) {
|
||||
if (mapped_file_ == NULL) return false;
|
||||
|
||||
if (external_section_ && !IsSectionSafeToMap(mapped_file_)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (external_section_ && !IsSectionSafeToMap(mapped_file_.get())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void* mem = MapViewOfFileEx(
|
||||
mapped_file_.get(),
|
||||
read_only_ ? FILE_MAP_READ : FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, bytes,
|
||||
fixed_address);
|
||||
if (mem) {
|
||||
MOZ_ASSERT(!fixed_address || mem == fixed_address,
|
||||
memory_ = MapViewOfFileEx(
|
||||
mapped_file_, read_only_ ? FILE_MAP_READ : FILE_MAP_READ | FILE_MAP_WRITE,
|
||||
0, 0, bytes, fixed_address);
|
||||
if (memory_ != NULL) {
|
||||
MOZ_ASSERT(!fixed_address || memory_ == fixed_address,
|
||||
"MapViewOfFileEx returned an expected address");
|
||||
memory_.reset(mem);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SharedMemory::Unmap() {
|
||||
if (memory_ == NULL) return false;
|
||||
|
||||
UnmapViewOfFile(memory_);
|
||||
memory_ = NULL;
|
||||
return true;
|
||||
}
|
||||
|
||||
void* SharedMemory::FindFreeAddressSpace(size_t size) {
|
||||
void* memory = VirtualAlloc(NULL, size, MEM_RESERVE, PAGE_NOACCESS);
|
||||
if (memory) {
|
||||
|
@ -198,18 +228,14 @@ bool SharedMemory::ShareToProcessCommon(ProcessId processId,
|
|||
*new_handle = 0;
|
||||
DWORD access = FILE_MAP_READ | SECTION_QUERY;
|
||||
DWORD options = 0;
|
||||
HANDLE mapped_file;
|
||||
HANDLE mapped_file = mapped_file_;
|
||||
HANDLE result;
|
||||
if (!read_only_) {
|
||||
access |= FILE_MAP_WRITE;
|
||||
}
|
||||
if (!read_only_) access |= FILE_MAP_WRITE;
|
||||
if (close_self) {
|
||||
// DUPLICATE_CLOSE_SOURCE causes DuplicateHandle to close mapped_file.
|
||||
mapped_file = mapped_file_.release();
|
||||
options = DUPLICATE_CLOSE_SOURCE;
|
||||
mapped_file_ = NULL;
|
||||
Unmap();
|
||||
} else {
|
||||
mapped_file = mapped_file_.get();
|
||||
}
|
||||
|
||||
if (processId == GetCurrentProcId() && close_self) {
|
||||
|
@ -231,7 +257,17 @@ void SharedMemory::Close(bool unmap_view) {
|
|||
Unmap();
|
||||
}
|
||||
|
||||
mapped_file_ = nullptr;
|
||||
if (mapped_file_ != NULL) {
|
||||
CloseHandle(mapped_file_);
|
||||
mapped_file_ = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
mozilla::UniqueFileHandle SharedMemory::TakeHandle() {
|
||||
mozilla::UniqueFileHandle fh(mapped_file_);
|
||||
mapped_file_ = NULL;
|
||||
Unmap();
|
||||
return fh;
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
|
|
|
@ -704,10 +704,6 @@ 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();
|
||||
|
@ -1399,6 +1395,11 @@ 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: {
|
||||
|
|
|
@ -683,15 +683,6 @@ SandboxBrokerPolicyFactory::GetUtilityPolicy(int aPid) {
|
|||
|
||||
AddSharedMemoryPaths(policy.get(), aPid);
|
||||
|
||||
// FIXME (bug 1662321): we should fix nsSystemInfo so that every
|
||||
// child process doesn't need to re-read these files to get the info
|
||||
// the parent process already has.
|
||||
policy->AddPath(rdonly, "/proc/cpuinfo");
|
||||
policy->AddPath(rdonly,
|
||||
"/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq");
|
||||
policy->AddPath(rdonly, "/sys/devices/system/cpu/cpu0/cache/index2/size");
|
||||
policy->AddPath(rdonly, "/sys/devices/system/cpu/cpu0/cache/index3/size");
|
||||
|
||||
if (policy->IsEmpty()) {
|
||||
policy = nullptr;
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче