SharedMemoryMapping.cpp (6243B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 /* This source code was derived from Chromium code, and as such is also subject 8 * to the [Chromium license](ipc/chromium/src/LICENSE). */ 9 10 #include "mozilla/ipc/SharedMemoryHandle.h" 11 #include "mozilla/ipc/SharedMemoryMapping.h" 12 #include "SharedMemoryPlatform.h" 13 14 #include "mozilla/Atomics.h" 15 #include "mozilla/CheckedInt.h" 16 #include "nsIMemoryReporter.h" 17 18 #ifdef FUZZING 19 # include "mozilla/ipc/SharedMemoryFuzzer.h" 20 #endif 21 22 namespace mozilla::ipc::shared_memory { 23 24 class MappingReporter final : public nsIMemoryReporter { 25 ~MappingReporter() = default; 26 27 public: 28 static Atomic<size_t> mapped; 29 30 NS_DECL_THREADSAFE_ISUPPORTS 31 32 NS_IMETHOD 33 CollectReports(nsIHandleReportCallback* aHandleReport, nsISupports* aData, 34 bool aAnonymize) override { 35 MOZ_COLLECT_REPORT( 36 "shmem-mapped", KIND_OTHER, UNITS_BYTES, mapped, 37 "Memory shared with other processes that is mapped into the address " 38 "space."); 39 40 return NS_OK; 41 } 42 }; 43 44 Atomic<size_t> MappingReporter::mapped; 45 46 NS_IMPL_ISUPPORTS(MappingReporter, nsIMemoryReporter) 47 48 static void RegisterMappingMemoryReporter() { 49 static Atomic<bool> registered; 50 if (registered.compareExchange(false, true)) { 51 RegisterStrongMemoryReporter(new MappingReporter()); 52 } 53 } 54 55 MappingBase::MappingBase() = default; 56 57 MappingBase& MappingBase::operator=(MappingBase&& aOther) { 58 // Swap members with `aOther`, and unmap that mapping. 59 std::swap(aOther.mMemory, mMemory); 60 std::swap(aOther.mSize, mSize); 61 aOther.Unmap(); 62 return *this; 63 } 64 65 void* MappingBase::Address() const { 66 #ifdef FUZZING 67 return SharedMemoryFuzzer::MutateSharedMemory(mMemory, mSize); 68 #else 69 return mMemory; 70 #endif 71 } 72 73 bool MappingBase::Map(const HandleBase& aHandle, void* aFixedAddress, 74 bool aReadOnly) { 75 // Invalid handles will fail and result in an invalid mapping. 76 if (!aHandle) { 77 return false; 78 } 79 // Verify that the handle size can be stored as a mapping size first 80 // (otherwise it won't be possible to map in the address space and the Map 81 // call will fail). 82 CheckedInt<size_t> checkedSize(aHandle.Size()); 83 if (!checkedSize.isValid()) { 84 MOZ_LOG_FMT(gSharedMemoryLog, LogLevel::Error, 85 "handle size to map exceeds address space size"); 86 return false; 87 } 88 89 return MapSubregion(aHandle, /* aOffset */ 0, checkedSize.value(), 90 aFixedAddress, aReadOnly); 91 } 92 93 bool MappingBase::MapSubregion(const HandleBase& aHandle, uint64_t aOffset, 94 size_t aSize, void* aFixedAddress, 95 bool aReadOnly) { 96 CheckedInt<uint64_t> endOffset(aOffset); 97 endOffset += aSize; 98 if (!endOffset.isValid() || endOffset.value() > aHandle.Size()) { 99 MOZ_LOG_FMT(gSharedMemoryLog, LogLevel::Error, 100 "cannot map region exceeding aHandle.Size()"); 101 return false; 102 } 103 104 RegisterMappingMemoryReporter(); 105 106 if (auto mem = 107 Platform::Map(aHandle, aOffset, aSize, aFixedAddress, aReadOnly)) { 108 mMemory = *mem; 109 mSize = aSize; 110 MappingReporter::mapped += mSize; 111 return true; 112 } 113 return false; 114 } 115 116 void MappingBase::Unmap() { 117 if (IsValid()) { 118 Platform::Unmap(mMemory, mSize); 119 120 MOZ_ASSERT(MappingReporter::mapped >= mSize, 121 "Can't unmap more than mapped"); 122 MappingReporter::mapped -= mSize; 123 } 124 mMemory = nullptr; 125 mSize = 0; 126 } 127 128 std::tuple<void*, size_t> MappingBase::Release() && { 129 // NOTE: this doesn't reduce gShmemMapped since it _is_ still mapped memory 130 // (and will be until the process terminates). 131 return std::make_tuple(std::exchange(mMemory, nullptr), 132 std::exchange(mSize, 0)); 133 } 134 135 MutableOrReadOnlyMapping::Mapping(const MutableHandle& aHandle, 136 void* aFixedAddress) 137 : mReadOnly(false) { 138 Map(aHandle, aFixedAddress, false); 139 } 140 141 MutableOrReadOnlyMapping::Mapping(const ReadOnlyHandle& aHandle, 142 void* aFixedAddress) 143 : mReadOnly(true) { 144 Map(aHandle, aFixedAddress, true); 145 } 146 147 MutableOrReadOnlyMapping::Mapping(MutableMapping&& aMapping) 148 : MappingData(std::move(aMapping)), mReadOnly(false) {} 149 150 MutableOrReadOnlyMapping::Mapping(ReadOnlyMapping&& aMapping) 151 : MappingData(std::move(aMapping)), mReadOnly(true) {} 152 153 // We still store the handle if `Map` fails: the user may want to get it back 154 // (for instance, if fixed-address mapping doesn't work they may try mapping 155 // without one). 156 FreezableMapping::Mapping(FreezableHandle&& aHandle, void* aFixedAddress) 157 : mHandle(std::move(aHandle)) { 158 Map(mHandle, aFixedAddress, false); 159 } 160 161 FreezableMapping::Mapping(FreezableHandle&& aHandle, uint64_t aOffset, 162 size_t aSize, void* aFixedAddress) 163 : mHandle(std::move(aHandle)) { 164 MapSubregion(mHandle, aOffset, aSize, aFixedAddress, false); 165 } 166 167 ReadOnlyHandle FreezableMapping::Freeze() && { 168 return std::move(*this).Unmap().Freeze(); 169 } 170 171 std::tuple<ReadOnlyHandle, MutableMapping> 172 FreezableMapping::FreezeWithMutableMapping() && { 173 auto handle = std::move(mHandle); 174 return std::make_tuple(std::move(handle).Freeze(), 175 ConvertMappingTo<Type::Mutable>(std::move(*this))); 176 } 177 178 FreezableHandle FreezableMapping::Unmap() && { 179 auto handle = std::move(mHandle); 180 *this = nullptr; 181 return handle; 182 } 183 184 bool LocalProtect(char* aAddr, size_t aSize, Access aAccess) { 185 return Platform::Protect(aAddr, aSize, aAccess); 186 } 187 188 void* FindFreeAddressSpace(size_t aSize) { 189 return Platform::FindFreeAddressSpace(aSize); 190 } 191 192 size_t SystemPageSize() { return Platform::PageSize(); } 193 194 size_t SystemAllocationGranularity() { 195 return Platform::AllocationGranularity(); 196 } 197 198 size_t PageAlignedSize(size_t aMinimum) { 199 const size_t pageSize = Platform::PageSize(); 200 size_t nPagesNeeded = size_t(ceil(double(aMinimum) / double(pageSize))); 201 return pageSize * nPagesNeeded; 202 } 203 204 } // namespace mozilla::ipc::shared_memory