1
0
Fork 0
firefox/ipc/glue/SharedMemoryMapping.cpp
Daniel Baumann 5e9a113729
Adding upstream version 140.0.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
2025-06-25 09:37:52 +02:00

204 lines
6.1 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/. */
/* 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 RegisterMappingMemoryReporter() {
static Atomic<bool> registered;
if (registered.compareExchange(false, true)) {
RegisterStrongMemoryReporter(new MappingReporter());
}
}
MappingBase::MappingBase() = default;
MappingBase& MappingBase::operator=(MappingBase&& aOther) {
// Swap members with `aOther`, and unmap that mapping.
std::swap(aOther.mMemory, mMemory);
std::swap(aOther.mSize, mSize);
aOther.Unmap();
return *this;
}
void* MappingBase::Address() const {
#ifdef FUZZING
return SharedMemoryFuzzer::MutateSharedMemory(mMemory, mSize);
#else
return mMemory;
#endif
}
bool MappingBase::Map(const HandleBase& aHandle, void* aFixedAddress,
bool aReadOnly) {
// Invalid handles will fail and result in an invalid mapping.
if (!aHandle) {
return false;
}
// 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::Error,
"handle size to map exceeds address space size");
return false;
}
return MapSubregion(aHandle, /* aOffset */ 0, checkedSize.value(),
aFixedAddress, aReadOnly);
}
bool MappingBase::MapSubregion(const HandleBase& aHandle, uint64_t aOffset,
size_t aSize, void* aFixedAddress,
bool aReadOnly) {
CheckedInt<uint64_t> endOffset(aOffset);
endOffset += aSize;
if (!endOffset.isValid() || endOffset.value() > aHandle.Size()) {
MOZ_LOG_FMT(gSharedMemoryLog, LogLevel::Error,
"cannot map region exceeding aHandle.Size()");
return false;
}
RegisterMappingMemoryReporter();
if (auto mem =
Platform::Map(aHandle, aOffset, aSize, aFixedAddress, aReadOnly)) {
mMemory = *mem;
mSize = aSize;
MappingReporter::mapped += mSize;
return true;
}
return false;
}
void MappingBase::Unmap() {
if (IsValid()) {
Platform::Unmap(mMemory, mSize);
MOZ_ASSERT(MappingReporter::mapped >= mSize,
"Can't unmap more than mapped");
MappingReporter::mapped -= mSize;
}
mMemory = nullptr;
mSize = 0;
}
std::tuple<void*, size_t> MappingBase::Release() && {
// NOTE: this doesn't reduce gShmemMapped since it _is_ still mapped memory
// (and will be until the process terminates).
return std::make_tuple(std::exchange(mMemory, nullptr),
std::exchange(mSize, 0));
}
MutableOrReadOnlyMapping::Mapping(const MutableHandle& aHandle,
void* aFixedAddress)
: mReadOnly(false) {
Map(aHandle, aFixedAddress, false);
}
MutableOrReadOnlyMapping::Mapping(const ReadOnlyHandle& aHandle,
void* aFixedAddress)
: mReadOnly(true) {
Map(aHandle, aFixedAddress, true);
}
MutableOrReadOnlyMapping::Mapping(MutableMapping&& aMapping)
: MappingData(std::move(aMapping)), mReadOnly(false) {}
MutableOrReadOnlyMapping::Mapping(ReadOnlyMapping&& aMapping)
: MappingData(std::move(aMapping)), mReadOnly(true) {}
// We still store the handle if `Map` fails: the user may want to get it back
// (for instance, if fixed-address mapping doesn't work they may try mapping
// without one).
FreezableMapping::Mapping(FreezableHandle&& aHandle, void* aFixedAddress)
: mHandle(std::move(aHandle)) {
Map(mHandle, aFixedAddress, false);
}
FreezableMapping::Mapping(FreezableHandle&& aHandle, uint64_t aOffset,
size_t aSize, void* aFixedAddress)
: mHandle(std::move(aHandle)) {
MapSubregion(mHandle, aOffset, aSize, aFixedAddress, false);
}
ReadOnlyHandle FreezableMapping::Freeze() && {
return std::move(*this).Unmap().Freeze();
}
std::tuple<ReadOnlyHandle, MutableMapping>
FreezableMapping::FreezeWithMutableMapping() && {
auto handle = std::move(mHandle);
return std::make_tuple(std::move(handle).Freeze(),
ConvertMappingTo<Type::Mutable>(std::move(*this)));
}
FreezableHandle FreezableMapping::Unmap() && {
auto handle = std::move(mHandle);
*this = nullptr;
return handle;
}
bool LocalProtect(char* aAddr, size_t aSize, Access aAccess) {
return Platform::Protect(aAddr, aSize, aAccess);
}
void* FindFreeAddressSpace(size_t aSize) {
return Platform::FindFreeAddressSpace(aSize);
}
size_t SystemPageSize() { return Platform::PageSize(); }
size_t SystemAllocationGranularity() {
return Platform::AllocationGranularity();
}
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;
}
} // namespace mozilla::ipc::shared_memory