gecko-dev/ipc/glue/ProcessUtils_common.cpp
Jed Davis 9a41450b28 Bug 1479960 - Get rid of base::SharedMemory::handle. r=froydnj
Despite the comment saying not to use the "handle" except as an opaque
identifier, it is being used to pass the handle to other OS APIs.  Direct
access to the handle needs to be controlled to make sure freezing is
safe, so this patch replaces that with interfaces that are more explicit
about ownership and lifetime.

Depends on D26739

Differential Revision: https://phabricator.services.mozilla.com/D26740

--HG--
extra : moz-landing-system : lando
2019-08-14 22:48:22 +00:00

211 lines
6.7 KiB
C++

/* -*- 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 "ProcessUtils.h"
#include "mozilla/Preferences.h"
#include "mozilla/ipc/GeckoChildProcessHost.h"
#include "mozilla/UniquePtrExtensions.h"
namespace mozilla {
namespace ipc {
SharedPreferenceSerializer::SharedPreferenceSerializer() : mPrefMapSize(0) {
MOZ_COUNT_CTOR(SharedPreferenceSerializer);
}
SharedPreferenceSerializer::~SharedPreferenceSerializer() {
MOZ_COUNT_DTOR(SharedPreferenceSerializer);
}
SharedPreferenceSerializer::SharedPreferenceSerializer(
SharedPreferenceSerializer&& aOther)
: mPrefMapSize(aOther.mPrefMapSize),
mPrefsLength(aOther.mPrefsLength),
mPrefMapHandle(std::move(aOther.mPrefMapHandle)),
mPrefsHandle(std::move(aOther.mPrefsHandle)) {
MOZ_COUNT_CTOR(SharedPreferenceSerializer);
}
bool SharedPreferenceSerializer::SerializeToSharedMemory() {
mPrefMapHandle =
Preferences::EnsureSnapshot(&mPrefMapSize).TakePlatformHandle();
// Serialize the early prefs.
nsAutoCStringN<1024> prefs;
Preferences::SerializePreferences(prefs);
mPrefsLength = prefs.Length();
base::SharedMemory shm;
// Set up the shared memory.
if (!shm.Create(prefs.Length())) {
NS_ERROR("failed to create shared memory in the parent");
return false;
}
if (!shm.Map(prefs.Length())) {
NS_ERROR("failed to map shared memory in the parent");
return false;
}
// Copy the serialized prefs into the shared memory.
memcpy(static_cast<char*>(shm.memory()), prefs.get(), mPrefsLength);
mPrefsHandle = shm.TakeHandle();
return true;
}
void SharedPreferenceSerializer::AddSharedPrefCmdLineArgs(
mozilla::ipc::GeckoChildProcessHost& procHost,
std::vector<std::string>& aExtraOpts) const {
// Formats a pointer or pointer-sized-integer as a string suitable for passing
// in an arguments list.
auto formatPtrArg = [](auto arg) {
return nsPrintfCString("%zu", uintptr_t(arg));
};
#if defined(XP_WIN)
// Record the handle as to-be-shared, and pass it via a command flag. This
// works because Windows handles are system-wide.
procHost.AddHandleToShare(GetPrefsHandle().get());
procHost.AddHandleToShare(GetPrefMapHandle().get());
aExtraOpts.push_back("-prefsHandle");
aExtraOpts.push_back(formatPtrArg(GetPrefsHandle().get()).get());
aExtraOpts.push_back("-prefMapHandle");
aExtraOpts.push_back(formatPtrArg(GetPrefMapHandle().get()).get());
#else
// In contrast, Unix fds are per-process. So remap the fd to a fixed one that
// will be used in the child.
// XXX: bug 1440207 is about improving how fixed fds are used.
//
// Note: on Android, AddFdToRemap() sets up the fd to be passed via a Parcel,
// and the fixed fd isn't used. However, we still need to mark it for
// remapping so it doesn't get closed in the child.
procHost.AddFdToRemap(GetPrefsHandle().get(), kPrefsFileDescriptor);
procHost.AddFdToRemap(GetPrefMapHandle().get(), kPrefMapFileDescriptor);
#endif
// Pass the lengths via command line flags.
aExtraOpts.push_back("-prefsLen");
aExtraOpts.push_back(formatPtrArg(GetPrefsLength()).get());
aExtraOpts.push_back("-prefMapSize");
aExtraOpts.push_back(formatPtrArg(GetPrefMapSize()).get());
}
#ifdef ANDROID
static int gPrefsFd = -1;
static int gPrefMapFd = -1;
void SetPrefsFd(int aFd) { gPrefsFd = aFd; }
void SetPrefMapFd(int aFd) { gPrefMapFd = aFd; }
#endif
SharedPreferenceDeserializer::SharedPreferenceDeserializer() {
MOZ_COUNT_CTOR(SharedPreferenceDeserializer);
}
SharedPreferenceDeserializer::~SharedPreferenceDeserializer() {
MOZ_COUNT_DTOR(SharedPreferenceDeserializer);
}
bool SharedPreferenceDeserializer::DeserializeFromSharedMemory(
char* aPrefsHandleStr, char* aPrefMapHandleStr, char* aPrefsLenStr,
char* aPrefMapSizeStr) {
#ifdef XP_WIN
MOZ_ASSERT(aPrefsHandleStr && aPrefMapHandleStr, "Can't be null");
#endif
MOZ_ASSERT(aPrefsLenStr && aPrefMapSizeStr, "Can't be null");
// Parses an arg containing a pointer-sized-integer.
auto parseUIntPtrArg = [](char*& aArg) {
// ContentParent uses %zu to print a word-sized unsigned integer. So
// even though strtoull() returns a long long int, it will fit in a
// uintptr_t.
return uintptr_t(strtoull(aArg, &aArg, 10));
};
#ifdef XP_WIN
auto parseHandleArg = [&](char*& aArg) {
return HANDLE(parseUIntPtrArg(aArg));
};
mPrefsHandle = Some(parseHandleArg(aPrefsHandleStr));
if (!aPrefsHandleStr || aPrefsHandleStr[0] != '\0') {
return false;
}
FileDescriptor::UniquePlatformHandle handle(
parseHandleArg(aPrefMapHandleStr));
if (!aPrefMapHandleStr || aPrefMapHandleStr[0] != '\0') {
return false;
}
mPrefMapHandle.emplace(std::move(handle));
#endif
mPrefsLen = Some(parseUIntPtrArg(aPrefsLenStr));
if (!aPrefsLenStr || aPrefsLenStr[0] != '\0') {
return false;
}
mPrefMapSize = Some(parseUIntPtrArg(aPrefMapSizeStr));
if (!aPrefMapSizeStr || aPrefMapSizeStr[0] != '\0') {
return false;
}
#ifdef ANDROID
// Android is different; get the FD via gPrefsFd instead of a fixed fd.
MOZ_RELEASE_ASSERT(gPrefsFd != -1);
mPrefsHandle = Some(base::FileDescriptor(gPrefsFd, /* auto_close */ true));
mPrefMapHandle.emplace(UniqueFileHandle(gPrefMapFd));
#elif XP_UNIX
mPrefsHandle = Some(base::FileDescriptor(kPrefsFileDescriptor,
/* auto_close */ true));
mPrefMapHandle.emplace(UniqueFileHandle(kPrefMapFileDescriptor));
#endif
if (mPrefsHandle.isNothing() || mPrefsLen.isNothing() ||
mPrefMapHandle.isNothing() || mPrefMapSize.isNothing()) {
return false;
}
// Init the shared-memory base preference mapping first, so that only changed
// preferences wind up in heap memory.
Preferences::InitSnapshot(mPrefMapHandle.ref(), *mPrefMapSize);
// Set up early prefs from the shared memory.
if (!mShmem.SetHandle(*mPrefsHandle, /* read_only */ true)) {
NS_ERROR("failed to open shared memory in the child");
return false;
}
if (!mShmem.Map(*mPrefsLen)) {
NS_ERROR("failed to map shared memory in the child");
return false;
}
Preferences::DeserializePreferences(static_cast<char*>(mShmem.memory()),
*mPrefsLen);
return true;
}
const base::SharedMemoryHandle& SharedPreferenceDeserializer::GetPrefsHandle()
const {
MOZ_ASSERT(mPrefsHandle.isSome());
return mPrefsHandle.ref();
}
const FileDescriptor& SharedPreferenceDeserializer::GetPrefMapHandle() const {
MOZ_ASSERT(mPrefMapHandle.isSome());
return mPrefMapHandle.ref();
}
} // namespace ipc
} // namespace mozilla