Source code

Revision control

Copy as Markdown

Other Tools

/* -*- 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/. */
/* This source code was derived from Chromium code, and as such is also subject
* to the [Chromium license](ipc/chromium/src/LICENSE). */
#include "mozilla/ipc/SharedMemoryHandle.h"
#include "mozilla/ipc/SharedMemoryMapping.h"
#include "SharedMemoryPlatform.h"
#include "mozilla/Atomics.h"
#include "mozilla/CheckedInt.h"
#include "nsIMemoryReporter.h"
#ifdef FUZZING
# include "mozilla/ipc/SharedMemoryFuzzer.h"
#endif
namespace mozilla::ipc::shared_memory {
class MappingReporter final : public nsIMemoryReporter {
~MappingReporter() = default;
public:
static Atomic<size_t> mapped;
NS_DECL_THREADSAFE_ISUPPORTS
NS_IMETHOD
CollectReports(nsIHandleReportCallback* aHandleReport, nsISupports* aData,
bool aAnonymize) override {
MOZ_COLLECT_REPORT(
"shmem-mapped", KIND_OTHER, UNITS_BYTES, mapped,
"Memory shared with other processes that is mapped into the address "
"space.");
return NS_OK;
}
};
Atomic<size_t> MappingReporter::mapped;
NS_IMPL_ISUPPORTS(MappingReporter, nsIMemoryReporter)
static void RegisterMemoryReporter() {
static Atomic<bool> registered;
if (registered.compareExchange(false, true)) {
RegisterStrongMemoryReporter(new MappingReporter());
}
}
MappingBase::MappingBase() { RegisterMemoryReporter(); }
MappingBase::~MappingBase() {
if (IsValid()) {
Platform::Unmap(mMemory, mSize);
MOZ_ASSERT(MappingReporter::mapped >= mSize,
"Can't unmap more than mapped");
MappingReporter::mapped -= mSize;
mMemory = nullptr;
mSize = 0;
}
}
void* MappingBase::Data() const {
#ifdef FUZZING
return SharedMemoryFuzzer::MutateSharedMemory(mMemory, mSize);
#else
return mMemory;
#endif
}
LeakedMapping MappingBase::release() && {
// NOTE: this doesn't reduce gShmemMapped since it _is_ still mapped memory
// (and will be until the process terminates).
return LeakedMapping{static_cast<uint8_t*>(std::exchange(mMemory, nullptr)),
std::exchange(mSize, 0)};
}
bool MappingBase::Map(const HandleBase& aHandle, void* aFixedAddress,
bool aReadOnly) {
// Verify that the handle size can be stored as a mapping size first
// (otherwise it won't be possible to map in the address space and the Map
// call will fail).
CheckedInt<size_t> checkedSize(aHandle.Size());
if (!checkedSize.isValid()) {
MOZ_LOG_FMT(gSharedMemoryLog, LogLevel::Warning,
"handle size to map exceeds address space size");
return false;
}
if (auto mem = Platform::Map(aHandle, aFixedAddress, aReadOnly)) {
mMemory = *mem;
mSize = checkedSize.value();
MappingReporter::mapped += mSize;
return true;
}
return false;
}
Mapping::Mapping(const Handle& aHandle, void* aFixedAddress) {
Map(aHandle, aFixedAddress, false);
}
ReadOnlyMapping::ReadOnlyMapping(const ReadOnlyHandle& aHandle,
void* aFixedAddress) {
Map(aHandle, aFixedAddress, true);
}
FreezableMapping::FreezableMapping(FreezableHandle&& aHandle,
void* aFixedAddress) {
if (Map(aHandle, aFixedAddress, false)) {
mHandle = std::move(aHandle);
}
}
std::tuple<Mapping, ReadOnlyHandle> FreezableMapping::Freeze() && {
auto handle = std::move(mHandle);
return std::make_tuple(std::move(*this).ConvertTo<Mapping>(),
std::move(handle).Freeze());
}
FreezableHandle FreezableMapping::Unmap() && {
auto handle = std::move(mHandle);
*this = nullptr;
return handle;
}
void* FindFreeAddressSpace(size_t aSize) {
return Platform::FindFreeAddressSpace(aSize);
}
size_t SystemPageSize() { return Platform::PageSize(); }
size_t PageAlignedSize(size_t aMinimum) {
const size_t pageSize = Platform::PageSize();
size_t nPagesNeeded = size_t(ceil(double(aMinimum) / double(pageSize)));
return pageSize * nPagesNeeded;
}
bool Protect(char* aAddr, size_t aSize, Access aAccess) {
return Platform::Protect(aAddr, aSize, aAccess);
}
} // namespace mozilla::ipc::shared_memory