mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-02-12 18:50:08 +00:00
Bug 1463587: Part 1 - Add helper class for creating snapshots of shared memory regions. r=jld,erahm
This class allows us to map a read-write shared memory region, and then safely remap it read-only, so that it can be shared with sandboxed content processes. MozReview-Commit-ID: 2PJMQgOwA4V --HG-- extra : rebase_source : c556cabfa7d379a91dc9ef7171ac0a7d7d8fb32e extra : absorb_source : e78e304ed95891c694050f79a0bb5d40d11ee884
This commit is contained in:
parent
5b6cf04d31
commit
fd093e7cca
129
dom/ipc/MemMapSnapshot.cpp
Normal file
129
dom/ipc/MemMapSnapshot.cpp
Normal file
@ -0,0 +1,129 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* vim: set ts=8 sts=4 et sw=4 tw=99: */
|
||||
/* 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 "MemMapSnapshot.h"
|
||||
|
||||
#include "base/eintr_wrapper.h"
|
||||
#include "base/file_util.h"
|
||||
#include "mozilla/ResultExtensions.h"
|
||||
#include "mozilla/ScopeExit.h"
|
||||
#include "mozilla/ipc/FileDescriptorUtils.h"
|
||||
#include "nsIFile.h"
|
||||
|
||||
#ifdef XP_UNIX
|
||||
# include <sys/stat.h>
|
||||
#endif
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
using loader::AutoMemMap;
|
||||
|
||||
namespace ipc {
|
||||
|
||||
Result<Ok, nsresult>
|
||||
MemMapSnapshot::Init(size_t aSize)
|
||||
{
|
||||
MOZ_ASSERT(!mInitialized);
|
||||
|
||||
MOZ_TRY(Create(aSize));
|
||||
|
||||
mInitialized = true;
|
||||
return Ok();
|
||||
}
|
||||
|
||||
Result<Ok, nsresult>
|
||||
MemMapSnapshot::Finalize(AutoMemMap& aMem)
|
||||
{
|
||||
MOZ_ASSERT(mInitialized);
|
||||
|
||||
MOZ_TRY(Freeze(aMem));
|
||||
|
||||
mInitialized = false;
|
||||
return Ok();
|
||||
}
|
||||
|
||||
#if defined(XP_WIN)
|
||||
|
||||
Result<Ok, nsresult>
|
||||
MemMapSnapshot::Create(size_t aSize)
|
||||
{
|
||||
HANDLE handle = CreateFileMapping(
|
||||
INVALID_HANDLE_VALUE, nullptr, PAGE_READWRITE,
|
||||
0, DWORD(aSize), nullptr);
|
||||
|
||||
if (!handle) {
|
||||
return Err(NS_ERROR_FAILURE);
|
||||
}
|
||||
|
||||
mFile.emplace(handle);
|
||||
return mMem.initWithHandle(mFile.ref(), aSize, PR_PROT_READWRITE);
|
||||
}
|
||||
|
||||
Result<Ok, nsresult>
|
||||
MemMapSnapshot::Freeze(AutoMemMap& aMem)
|
||||
{
|
||||
auto orig = mFile.ref().ClonePlatformHandle();
|
||||
mFile.reset();
|
||||
|
||||
HANDLE handle;
|
||||
if (!::DuplicateHandle(GetCurrentProcess(), orig.release(), GetCurrentProcess(),
|
||||
&handle, GENERIC_READ | FILE_MAP_READ,
|
||||
false, DUPLICATE_CLOSE_SOURCE)) {
|
||||
return Err(NS_ERROR_FAILURE);
|
||||
}
|
||||
|
||||
return aMem.initWithHandle(FileDescriptor(handle), mMem.size());
|
||||
}
|
||||
|
||||
#elif defined(XP_UNIX)
|
||||
|
||||
Result<Ok, nsresult>
|
||||
MemMapSnapshot::Create(size_t aSize)
|
||||
{
|
||||
FilePath path;
|
||||
ScopedCloseFile fd(file_util::CreateAndOpenTemporaryShmemFile(&path));
|
||||
if (!fd) {
|
||||
return Err(NS_ERROR_FAILURE);
|
||||
}
|
||||
|
||||
if (HANDLE_EINTR(ftruncate(fileno(fd), aSize)) != 0) {
|
||||
return Err(NS_ERROR_FAILURE);
|
||||
}
|
||||
|
||||
MOZ_TRY(mMem.init(FILEToFileDescriptor(fd), PR_PROT_READWRITE));
|
||||
|
||||
mPath.Assign(path.value().data(), path.value().length());
|
||||
return Ok();
|
||||
}
|
||||
|
||||
Result<Ok, nsresult>
|
||||
MemMapSnapshot::Freeze(AutoMemMap& aMem)
|
||||
{
|
||||
// Delete the shm file after we're done here, whether we succeed or not. The
|
||||
// open file descriptor will keep it alive until all remaining references
|
||||
// are closed, at which point it will be automatically freed.
|
||||
auto cleanup = MakeScopeExit([&]() {
|
||||
PR_Delete(mPath.get());
|
||||
});
|
||||
|
||||
// Make the shm file readonly. This doesn't make a difference in practice,
|
||||
// since we open and share a read-only file descriptor, and then delete the
|
||||
// file. But it doesn't hurt, either.
|
||||
chmod(mPath.get(), 0400);
|
||||
|
||||
nsCOMPtr<nsIFile> file;
|
||||
MOZ_TRY(NS_NewNativeLocalFile(mPath, /* followLinks = */ false,
|
||||
getter_AddRefs(file)));
|
||||
|
||||
return aMem.init(file);
|
||||
}
|
||||
|
||||
#else
|
||||
# error "Unsupported build configuration"
|
||||
#endif
|
||||
|
||||
} // namespace ipc
|
||||
} // namespace mozilla
|
64
dom/ipc/MemMapSnapshot.h
Normal file
64
dom/ipc/MemMapSnapshot.h
Normal file
@ -0,0 +1,64 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* vim: set ts=8 sts=4 et sw=4 tw=99: */
|
||||
/* 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/. */
|
||||
|
||||
#ifndef dom_ipc_MemMapSnapshot_h
|
||||
#define dom_ipc_MemMapSnapshot_h
|
||||
|
||||
#include "AutoMemMap.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/Maybe.h"
|
||||
#include "mozilla/RangedPtr.h"
|
||||
#include "mozilla/Result.h"
|
||||
#ifdef XP_WIN
|
||||
# include "mozilla/ipc/FileDescriptor.h"
|
||||
#endif
|
||||
|
||||
namespace mozilla {
|
||||
namespace ipc {
|
||||
|
||||
/**
|
||||
* A helper class for creating a read-only snapshot of memory-mapped data.
|
||||
*
|
||||
* The Init() method initializes a read-write memory mapped region of the given
|
||||
* size, which can be initialized with arbitrary data. The Finalize() method
|
||||
* remaps that region as read-only (and backs it with a read-only file
|
||||
* descriptor), and initializes an AutoMemMap with the new contents.
|
||||
*
|
||||
* The file descriptor for the resulting AutoMemMap can be shared among
|
||||
* processes, to safely access a shared, read-only copy of the data snapshot.
|
||||
*/
|
||||
class MOZ_RAII MemMapSnapshot
|
||||
{
|
||||
public:
|
||||
Result<Ok, nsresult> Init(size_t aSize);
|
||||
Result<Ok, nsresult> Finalize(loader::AutoMemMap& aMap);
|
||||
|
||||
template<typename T = void>
|
||||
RangedPtr<T> Get()
|
||||
{
|
||||
MOZ_ASSERT(mInitialized);
|
||||
return mMem.get<T>();
|
||||
}
|
||||
|
||||
private:
|
||||
Result<Ok, nsresult> Create(size_t aSize);
|
||||
Result<Ok, nsresult> Freeze(loader::AutoMemMap& aMem);
|
||||
|
||||
loader::AutoMemMap mMem;
|
||||
|
||||
bool mInitialized = false;
|
||||
|
||||
#ifdef XP_WIN
|
||||
Maybe<FileDescriptor> mFile;
|
||||
#else
|
||||
nsCString mPath;
|
||||
#endif
|
||||
};
|
||||
|
||||
} // ipc
|
||||
} // mozilla
|
||||
|
||||
#endif // dom_ipc_MemMapSnapshot_h
|
@ -61,6 +61,7 @@ UNIFIED_SOURCES += [
|
||||
'ContentProcessHost.cpp',
|
||||
'ContentProcessManager.cpp',
|
||||
'FilePickerParent.cpp',
|
||||
'MemMapSnapshot.cpp',
|
||||
'MemoryReportRequest.cpp',
|
||||
'nsIContentChild.cpp',
|
||||
'nsIContentParent.cpp',
|
||||
@ -132,6 +133,7 @@ LOCAL_INCLUDES += [
|
||||
'/extensions/spellcheck/src',
|
||||
'/gfx/2d',
|
||||
'/hal/sandbox',
|
||||
'/js/xpconnect/loader',
|
||||
'/layout/base',
|
||||
'/media/webrtc',
|
||||
'/netwerk/base',
|
||||
|
@ -19,19 +19,11 @@ using namespace mozilla::ipc;
|
||||
|
||||
AutoMemMap::~AutoMemMap()
|
||||
{
|
||||
if (fileMap) {
|
||||
if (addr) {
|
||||
Unused << NS_WARN_IF(PR_MemUnmap(addr, size()) != PR_SUCCESS);
|
||||
addr = nullptr;
|
||||
}
|
||||
|
||||
Unused << NS_WARN_IF(PR_CloseFileMap(fileMap) != PR_SUCCESS);
|
||||
fileMap = nullptr;
|
||||
}
|
||||
reset();
|
||||
}
|
||||
|
||||
FileDescriptor
|
||||
AutoMemMap::cloneFileDescriptor()
|
||||
AutoMemMap::cloneFileDescriptor() const
|
||||
{
|
||||
if (fd.get()) {
|
||||
auto handle = FileDescriptor::PlatformHandleType(PR_FileDesc2NativeHandle(fd.get()));
|
||||
@ -51,7 +43,8 @@ AutoMemMap::init(nsIFile* file, int flags, int mode, PRFileMapProtect prot)
|
||||
}
|
||||
|
||||
Result<Ok, nsresult>
|
||||
AutoMemMap::init(const FileDescriptor& file)
|
||||
AutoMemMap::init(const FileDescriptor& file, PRFileMapProtect prot,
|
||||
size_t expectedSize)
|
||||
{
|
||||
MOZ_ASSERT(!fd);
|
||||
if (!file.IsValid()) {
|
||||
@ -66,11 +59,11 @@ AutoMemMap::init(const FileDescriptor& file)
|
||||
}
|
||||
Unused << handle.release();
|
||||
|
||||
return initInternal();
|
||||
return initInternal(prot, expectedSize);
|
||||
}
|
||||
|
||||
Result<Ok, nsresult>
|
||||
AutoMemMap::initInternal(PRFileMapProtect prot)
|
||||
AutoMemMap::initInternal(PRFileMapProtect prot, size_t expectedSize)
|
||||
{
|
||||
MOZ_ASSERT(!fileMap);
|
||||
MOZ_ASSERT(!addr);
|
||||
@ -86,6 +79,12 @@ AutoMemMap::initInternal(PRFileMapProtect prot)
|
||||
return Err(NS_ERROR_FAILURE);
|
||||
|
||||
size_ = fileInfo.size;
|
||||
// The memory region size passed in certain IPC messages isn't necessary on
|
||||
// Unix-like systems, since we can always stat the file descriptor to
|
||||
// determine it accurately. But since we have it, anyway, sanity check that
|
||||
// it matches the size returned by the stat.
|
||||
MOZ_ASSERT_IF(expectedSize > 0, size_ == expectedSize);
|
||||
|
||||
addr = PR_MemMap(fileMap, 0, size_);
|
||||
if (!addr)
|
||||
return Err(NS_ERROR_FAILURE);
|
||||
@ -93,5 +92,73 @@ AutoMemMap::initInternal(PRFileMapProtect prot)
|
||||
return Ok();
|
||||
}
|
||||
|
||||
#ifdef XP_WIN
|
||||
|
||||
Result<Ok, nsresult>
|
||||
AutoMemMap::initWithHandle(const FileDescriptor& file, size_t size, PRFileMapProtect prot)
|
||||
{
|
||||
MOZ_ASSERT(!fd);
|
||||
MOZ_ASSERT(!handle_);
|
||||
if (!file.IsValid()) {
|
||||
return Err(NS_ERROR_INVALID_ARG);
|
||||
}
|
||||
|
||||
handle_ = file.ClonePlatformHandle().release();
|
||||
|
||||
MOZ_ASSERT(!addr);
|
||||
|
||||
size_ = size;
|
||||
|
||||
addr = MapViewOfFile(
|
||||
handle_,
|
||||
prot == PR_PROT_READONLY ? FILE_MAP_READ : FILE_MAP_ALL_ACCESS,
|
||||
0, 0, size);
|
||||
|
||||
return Ok();
|
||||
}
|
||||
|
||||
FileDescriptor
|
||||
AutoMemMap::cloneHandle() const
|
||||
{
|
||||
return FileDescriptor(handle_);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
Result<Ok, nsresult>
|
||||
AutoMemMap::initWithHandle(const FileDescriptor& file, size_t size, PRFileMapProtect prot)
|
||||
{
|
||||
return init(file, prot);
|
||||
}
|
||||
|
||||
FileDescriptor
|
||||
AutoMemMap::cloneHandle() const
|
||||
{
|
||||
return cloneFileDescriptor();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void
|
||||
AutoMemMap::reset()
|
||||
{
|
||||
if (fileMap) {
|
||||
if (addr) {
|
||||
Unused << NS_WARN_IF(PR_MemUnmap(addr, size()) != PR_SUCCESS);
|
||||
addr = nullptr;
|
||||
}
|
||||
|
||||
Unused << NS_WARN_IF(PR_CloseFileMap(fileMap) != PR_SUCCESS);
|
||||
fileMap = nullptr;
|
||||
}
|
||||
#ifdef XP_WIN
|
||||
if (handle_) {
|
||||
CloseHandle(handle_);
|
||||
handle_ = nullptr;
|
||||
}
|
||||
#endif
|
||||
fd.dispose();
|
||||
}
|
||||
|
||||
} // namespace loader
|
||||
} // namespace mozilla
|
||||
|
@ -34,14 +34,26 @@ class AutoMemMap
|
||||
PRFileMapProtect prot = PR_PROT_READONLY);
|
||||
|
||||
Result<Ok, nsresult>
|
||||
init(const ipc::FileDescriptor& file);
|
||||
init(const ipc::FileDescriptor& file,
|
||||
PRFileMapProtect prot = PR_PROT_READONLY,
|
||||
size_t expectedSize = 0);
|
||||
|
||||
// Initializes the mapped memory with a shared memory handle. On
|
||||
// Unix-like systems, this is identical to the above init() method. On
|
||||
// Windows, the FileDescriptor must be a handle for a file mapping,
|
||||
// rather than a file descriptor.
|
||||
Result<Ok, nsresult>
|
||||
initWithHandle(const ipc::FileDescriptor& file, size_t size,
|
||||
PRFileMapProtect prot = PR_PROT_READONLY);
|
||||
|
||||
void reset();
|
||||
|
||||
bool initialized() { return addr; }
|
||||
|
||||
uint32_t size() const { MOZ_ASSERT(fd); return size_; }
|
||||
uint32_t size() const { return size_; }
|
||||
|
||||
template<typename T = void>
|
||||
const RangedPtr<T> get()
|
||||
RangedPtr<T> get()
|
||||
{
|
||||
MOZ_ASSERT(addr);
|
||||
return { static_cast<T*>(addr), size_ };
|
||||
@ -56,14 +68,23 @@ class AutoMemMap
|
||||
|
||||
size_t nonHeapSizeOfExcludingThis() { return size_; }
|
||||
|
||||
FileDescriptor cloneFileDescriptor();
|
||||
FileDescriptor cloneFileDescriptor() const;
|
||||
FileDescriptor cloneHandle() const;
|
||||
|
||||
private:
|
||||
Result<Ok, nsresult> initInternal(PRFileMapProtect prot = PR_PROT_READONLY);
|
||||
Result<Ok, nsresult> initInternal(PRFileMapProtect prot = PR_PROT_READONLY,
|
||||
size_t expectedSize = 0);
|
||||
|
||||
AutoFDClose fd;
|
||||
PRFileMap* fileMap = nullptr;
|
||||
|
||||
#ifdef XP_WIN
|
||||
// We can't include windows.h in this header, since it gets included
|
||||
// by some binding headers (which are explicitly incompatible with
|
||||
// windows.h). So we can't use the HANDLE type here.
|
||||
void* handle_ = nullptr;
|
||||
#endif
|
||||
|
||||
uint32_t size_ = 0;
|
||||
void* addr = nullptr;
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user