SharedMemoryMapping.h (9253B)
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 #ifndef mozilla_ipc_SharedMemoryMapping_h 8 #define mozilla_ipc_SharedMemoryMapping_h 9 10 #include <tuple> 11 #include <type_traits> 12 #include <utility> 13 #include "mozilla/Assertions.h" 14 #include "mozilla/Span.h" 15 #include "SharedMemoryHandle.h" 16 17 namespace mozilla::ipc { 18 19 namespace shared_memory { 20 21 /** 22 * A leaked memory mapping. 23 * 24 * This memory will never be unmapped. 25 */ 26 template <Type T> 27 struct LeakedMapping : Span<uint8_t> { 28 using Span::Span; 29 }; 30 31 template <> 32 struct LeakedMapping<Type::ReadOnly> : Span<const uint8_t> { 33 using Span::Span; 34 }; 35 36 using LeakedMutableMapping = LeakedMapping<Type::Mutable>; 37 using LeakedReadOnlyMapping = LeakedMapping<Type::ReadOnly>; 38 39 class MappingBase { 40 public: 41 /** 42 * The size of the mapping. 43 */ 44 size_t Size() const { return mSize; } 45 46 /** 47 * The pointer to the mapping in memory. 48 */ 49 void* Address() const; 50 51 /** 52 * Whether this shared memory mapping is valid. 53 */ 54 bool IsValid() const { return (bool)*this; } 55 56 /** 57 * Whether this shared memory mapping is valid. 58 */ 59 explicit operator bool() const { return (bool)mMemory; } 60 61 protected: 62 /** 63 * Create an empty Mapping. 64 */ 65 MappingBase(); 66 MOZ_IMPLICIT MappingBase(std::nullptr_t) {} 67 ~MappingBase() { Unmap(); } 68 69 /** 70 * Mappings are movable (but not copyable). 71 */ 72 MappingBase(MappingBase&& aOther) 73 : mMemory(std::exchange(aOther.mMemory, nullptr)), 74 mSize(std::exchange(aOther.mSize, 0)) {} 75 76 MappingBase& operator=(MappingBase&& aOther); 77 78 MappingBase(const MappingBase&) = delete; 79 MappingBase& operator=(const MappingBase&) = delete; 80 81 bool Map(const HandleBase& aHandle, void* aFixedAddress, bool aReadOnly); 82 bool MapSubregion(const HandleBase& aHandle, uint64_t aOffset, size_t aSize, 83 void* aFixedAddress, bool aReadOnly); 84 void Unmap(); 85 86 template <Type T, Type S> 87 static Mapping<T> ConvertMappingTo(Mapping<S>&& from) { 88 Mapping<T> to; 89 static_cast<MappingBase&>(to) = std::move(from); 90 return to; 91 } 92 93 std::tuple<void*, size_t> Release() &&; 94 95 private: 96 void* mMemory = nullptr; 97 size_t mSize = 0; 98 }; 99 100 template <bool CONST_MEMORY> 101 struct MappingData : MappingBase { 102 private: 103 template <typename T> 104 using DataType = 105 std::conditional_t<CONST_MEMORY, std::add_const_t<std::remove_const_t<T>>, 106 T>; 107 108 protected: 109 MappingData() = default; 110 explicit MappingData(MappingBase&& aOther) : MappingBase(std::move(aOther)) {} 111 112 public: 113 /** 114 * Get a pointer to the data in the mapping as a type T. 115 * 116 * The mapping data must meet the alignment requirements of @p T. 117 * 118 * @tparam T The type of data in the mapping. 119 * 120 * @return A pointer of type @p T*. 121 */ 122 template <typename T> 123 DataType<T>* DataAs() const { 124 MOZ_ASSERT((reinterpret_cast<uintptr_t>(Address()) % alignof(T)) == 0, 125 "memory map does not meet alignment requirements of type"); 126 return static_cast<DataType<T>*>(Address()); 127 } 128 129 /** 130 * Get a `Span<T>` over the mapping. 131 * 132 * The mapping data must meet the alignment requirements of @p T. 133 * 134 * @tparam T The type of data in the mapping. 135 * 136 * @return A span of type @p T covering as much of the mapping as possible. 137 */ 138 template <typename T> 139 Span<DataType<T>> DataAsSpan() const { 140 return {DataAs<T>(), Size() / sizeof(T)}; 141 } 142 }; 143 144 /** 145 * A shared memory mapping. 146 */ 147 template <Type T> 148 struct Mapping<T> : MappingData<T == Type::ReadOnly> { 149 /** 150 * Create an empty Mapping. 151 */ 152 Mapping() = default; 153 MOZ_IMPLICIT Mapping(std::nullptr_t) {} 154 155 explicit Mapping(const Handle<T>& aHandle, void* aFixedAddress = nullptr) { 156 MappingBase::Map(aHandle, aFixedAddress, T == Type::ReadOnly); 157 } 158 Mapping(const Handle<T>& aHandle, uint64_t aOffset, size_t aSize, 159 void* aFixedAddress = nullptr) { 160 MappingBase::MapSubregion(aHandle, aOffset, aSize, aFixedAddress, 161 T == Type::ReadOnly); 162 } 163 164 /** 165 * Leak this mapping's memory. 166 * 167 * This will cause the memory to be mapped until the process exits. 168 */ 169 LeakedMapping<T> Release() && { 170 auto [ptr, size] = std::move(*this).MappingBase::Release(); 171 return LeakedMapping<T>{ 172 static_cast<typename LeakedMapping<T>::pointer>(ptr), size}; 173 } 174 }; 175 176 /** 177 * A shared memory mapping which has runtime-stored mutability. 178 */ 179 template <> 180 struct Mapping<Type::MutableOrReadOnly> : MappingData<true> { 181 /** 182 * Create an empty MutableOrReadOnlyMapping. 183 */ 184 Mapping() = default; 185 MOZ_IMPLICIT Mapping(std::nullptr_t) {} 186 187 explicit Mapping(const ReadOnlyHandle& aHandle, 188 void* aFixedAddress = nullptr); 189 explicit Mapping(const MutableHandle& aHandle, void* aFixedAddress = nullptr); 190 MOZ_IMPLICIT Mapping(ReadOnlyMapping&& aMapping); 191 MOZ_IMPLICIT Mapping(MutableMapping&& aMapping); 192 193 /** 194 * Return whether the mapping is read-only. 195 */ 196 bool IsReadOnly() const { return mReadOnly; } 197 198 private: 199 bool mReadOnly = false; 200 }; 201 202 /** 203 * A freezable shared memory mapping. 204 */ 205 template <> 206 struct Mapping<Type::Freezable> : MappingData<false> { 207 /** 208 * Create an empty FreezableMapping. 209 */ 210 Mapping() = default; 211 MOZ_IMPLICIT Mapping(std::nullptr_t) {} 212 213 /** 214 * Freezable mappings take ownership of a handle to ensure there is only one 215 * writeable mapping at a time. 216 * 217 * Call `Unmap()` to get the handle back. 218 */ 219 explicit Mapping(FreezableHandle&& aHandle, void* aFixedAddress = nullptr); 220 Mapping(FreezableHandle&& aHandle, uint64_t aOffset, size_t aSize, 221 void* aFixedAddress = nullptr); 222 223 /** 224 * Freeze the shared memory region. 225 */ 226 ReadOnlyHandle Freeze() &&; 227 228 /** 229 * Freeze the shared memory region. 230 * 231 * The returned Mapping will still be valid and writable until it is deleted, 232 * however no new writable mappings can be created. 233 */ 234 std::tuple<ReadOnlyHandle, MutableMapping> FreezeWithMutableMapping() &&; 235 236 /** 237 * Unmap the shared memory, returning the freezable handle. 238 * 239 * It is only necessary to call this if you need to get the FreezableHandle 240 * back. 241 */ 242 FreezableHandle Unmap() &&; 243 244 protected: 245 FreezableHandle mHandle; 246 }; 247 248 template <Type T> 249 struct Mapping<T, true> : public Mapping<T> { 250 Mapping() {} 251 MOZ_IMPLICIT Mapping(std::nullptr_t) : Mapping<T>(nullptr) {} 252 253 explicit Mapping(shared_memory::Handle<T>&& aHandle, 254 void* aFixedAddress = nullptr) 255 : Mapping<T>(aHandle, aFixedAddress), mHandle(std::move(aHandle)) {} 256 257 const shared_memory::Handle<T>& Handle() const { return mHandle; }; 258 259 std::tuple<shared_memory::Handle<T>, Mapping<T>> Split() && { 260 auto handle = std::move(mHandle); 261 return std::make_tuple(std::move(handle), std::move(*this)); 262 } 263 264 private: 265 shared_memory::Handle<T> mHandle; 266 }; 267 268 // To uphold the guarantees of freezable mappings, we do not allow access to the 269 // handle (and since this should never be used in this way, we make it a useless 270 // type). 271 template <> 272 struct Mapping<Type::Freezable, true>; 273 274 // The access level permitted for memory protection. 275 enum Access { 276 AccessNone = 0, 277 AccessRead = 1 << 0, 278 AccessWrite = 1 << 1, 279 AccessReadWrite = AccessRead | AccessWrite, 280 }; 281 282 /** 283 * Protect the given memory region. 284 * 285 * This protection extends only to the local memory mapping. It doesn't change 286 * the permissions of other mappings nor the associated handle. 287 * 288 * @param aAddr The address at the beginning of the memory region. 289 * @param aSize The size of the region to protect. 290 * @param aAccess The access level to allow. 291 * 292 * @returns Whether protection was successful. 293 */ 294 bool LocalProtect(char* aAddr, size_t aSize, Access aAccess); 295 296 /** 297 * Find a region of free memory. 298 * 299 * @param aSize The size of the region to locate. 300 * 301 * @returns The start of the memory region, or nullptr on error. 302 */ 303 void* FindFreeAddressSpace(size_t aSize); 304 305 /** 306 * Get the system page size. 307 */ 308 size_t SystemPageSize(); 309 310 /** 311 * Get the system allocation granularity. 312 * 313 * This may be distinct from the page size, and controls the required 314 * alignment for fixed mapping addresses and shared memory offsets. 315 */ 316 size_t SystemAllocationGranularity(); 317 318 /** 319 * Return a size which is page-aligned and can fit at least `minimum` bytes. 320 * 321 * @param aMinimum The minimum number of bytes required. 322 * 323 * @returns The page-aligned size that can hold `minimum` bytes. 324 */ 325 size_t PageAlignedSize(size_t aMinimum); 326 327 } // namespace shared_memory 328 329 using SharedMemoryMapping = shared_memory::MutableMapping; 330 using ReadOnlySharedMemoryMapping = shared_memory::ReadOnlyMapping; 331 using MutableOrReadOnlySharedMemoryMapping = 332 shared_memory::MutableOrReadOnlyMapping; 333 using FreezableSharedMemoryMapping = shared_memory::FreezableMapping; 334 335 using SharedMemoryMappingWithHandle = shared_memory::MutableMappingWithHandle; 336 using ReadOnlySharedMemoryMappingWithHandle = 337 shared_memory::ReadOnlyMappingWithHandle; 338 339 } // namespace mozilla::ipc 340 341 #endif