File: SharedMemoryMapping.h

package info (click to toggle)
firefox 147.0-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 4,683,324 kB
  • sloc: cpp: 7,607,156; javascript: 6,532,492; ansic: 3,775,158; python: 1,415,368; xml: 634,556; asm: 438,949; java: 186,241; sh: 62,751; makefile: 18,079; objc: 13,092; perl: 12,808; yacc: 4,583; cs: 3,846; pascal: 3,448; lex: 1,720; ruby: 1,003; php: 436; lisp: 258; awk: 247; sql: 66; sed: 54; csh: 10; exp: 6
file content (341 lines) | stat: -rw-r--r-- 9,253 bytes parent folder | download | duplicates (13)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
/* -*- 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/. */

#ifndef mozilla_ipc_SharedMemoryMapping_h
#define mozilla_ipc_SharedMemoryMapping_h

#include <tuple>
#include <type_traits>
#include <utility>
#include "mozilla/Assertions.h"
#include "mozilla/Span.h"
#include "SharedMemoryHandle.h"

namespace mozilla::ipc {

namespace shared_memory {

/**
 * A leaked memory mapping.
 *
 * This memory will never be unmapped.
 */
template <Type T>
struct LeakedMapping : Span<uint8_t> {
  using Span::Span;
};

template <>
struct LeakedMapping<Type::ReadOnly> : Span<const uint8_t> {
  using Span::Span;
};

using LeakedMutableMapping = LeakedMapping<Type::Mutable>;
using LeakedReadOnlyMapping = LeakedMapping<Type::ReadOnly>;

class MappingBase {
 public:
  /**
   * The size of the mapping.
   */
  size_t Size() const { return mSize; }

  /**
   * The pointer to the mapping in memory.
   */
  void* Address() const;

  /**
   * Whether this shared memory mapping is valid.
   */
  bool IsValid() const { return (bool)*this; }

  /**
   * Whether this shared memory mapping is valid.
   */
  explicit operator bool() const { return (bool)mMemory; }

 protected:
  /**
   * Create an empty Mapping.
   */
  MappingBase();
  MOZ_IMPLICIT MappingBase(std::nullptr_t) {}
  ~MappingBase() { Unmap(); }

  /**
   * Mappings are movable (but not copyable).
   */
  MappingBase(MappingBase&& aOther)
      : mMemory(std::exchange(aOther.mMemory, nullptr)),
        mSize(std::exchange(aOther.mSize, 0)) {}

  MappingBase& operator=(MappingBase&& aOther);

  MappingBase(const MappingBase&) = delete;
  MappingBase& operator=(const MappingBase&) = delete;

  bool Map(const HandleBase& aHandle, void* aFixedAddress, bool aReadOnly);
  bool MapSubregion(const HandleBase& aHandle, uint64_t aOffset, size_t aSize,
                    void* aFixedAddress, bool aReadOnly);
  void Unmap();

  template <Type T, Type S>
  static Mapping<T> ConvertMappingTo(Mapping<S>&& from) {
    Mapping<T> to;
    static_cast<MappingBase&>(to) = std::move(from);
    return to;
  }

  std::tuple<void*, size_t> Release() &&;

 private:
  void* mMemory = nullptr;
  size_t mSize = 0;
};

template <bool CONST_MEMORY>
struct MappingData : MappingBase {
 private:
  template <typename T>
  using DataType =
      std::conditional_t<CONST_MEMORY, std::add_const_t<std::remove_const_t<T>>,
                         T>;

 protected:
  MappingData() = default;
  explicit MappingData(MappingBase&& aOther) : MappingBase(std::move(aOther)) {}

 public:
  /**
   * Get a pointer to the data in the mapping as a type T.
   *
   * The mapping data must meet the alignment requirements of @p T.
   *
   * @tparam T The type of data in the mapping.
   *
   * @return A pointer of type @p T*.
   */
  template <typename T>
  DataType<T>* DataAs() const {
    MOZ_ASSERT((reinterpret_cast<uintptr_t>(Address()) % alignof(T)) == 0,
               "memory map does not meet alignment requirements of type");
    return static_cast<DataType<T>*>(Address());
  }

  /**
   * Get a `Span<T>` over the mapping.
   *
   * The mapping data must meet the alignment requirements of @p T.
   *
   * @tparam T The type of data in the mapping.
   *
   * @return A span of type @p T covering as much of the mapping as possible.
   */
  template <typename T>
  Span<DataType<T>> DataAsSpan() const {
    return {DataAs<T>(), Size() / sizeof(T)};
  }
};

/**
 * A shared memory mapping.
 */
template <Type T>
struct Mapping<T> : MappingData<T == Type::ReadOnly> {
  /**
   * Create an empty Mapping.
   */
  Mapping() = default;
  MOZ_IMPLICIT Mapping(std::nullptr_t) {}

  explicit Mapping(const Handle<T>& aHandle, void* aFixedAddress = nullptr) {
    MappingBase::Map(aHandle, aFixedAddress, T == Type::ReadOnly);
  }
  Mapping(const Handle<T>& aHandle, uint64_t aOffset, size_t aSize,
          void* aFixedAddress = nullptr) {
    MappingBase::MapSubregion(aHandle, aOffset, aSize, aFixedAddress,
                              T == Type::ReadOnly);
  }

  /**
   * Leak this mapping's memory.
   *
   * This will cause the memory to be mapped until the process exits.
   */
  LeakedMapping<T> Release() && {
    auto [ptr, size] = std::move(*this).MappingBase::Release();
    return LeakedMapping<T>{
        static_cast<typename LeakedMapping<T>::pointer>(ptr), size};
  }
};

/**
 * A shared memory mapping which has runtime-stored mutability.
 */
template <>
struct Mapping<Type::MutableOrReadOnly> : MappingData<true> {
  /**
   * Create an empty MutableOrReadOnlyMapping.
   */
  Mapping() = default;
  MOZ_IMPLICIT Mapping(std::nullptr_t) {}

  explicit Mapping(const ReadOnlyHandle& aHandle,
                   void* aFixedAddress = nullptr);
  explicit Mapping(const MutableHandle& aHandle, void* aFixedAddress = nullptr);
  MOZ_IMPLICIT Mapping(ReadOnlyMapping&& aMapping);
  MOZ_IMPLICIT Mapping(MutableMapping&& aMapping);

  /**
   * Return whether the mapping is read-only.
   */
  bool IsReadOnly() const { return mReadOnly; }

 private:
  bool mReadOnly = false;
};

/**
 * A freezable shared memory mapping.
 */
template <>
struct Mapping<Type::Freezable> : MappingData<false> {
  /**
   * Create an empty FreezableMapping.
   */
  Mapping() = default;
  MOZ_IMPLICIT Mapping(std::nullptr_t) {}

  /**
   * Freezable mappings take ownership of a handle to ensure there is only one
   * writeable mapping at a time.
   *
   * Call `Unmap()` to get the handle back.
   */
  explicit Mapping(FreezableHandle&& aHandle, void* aFixedAddress = nullptr);
  Mapping(FreezableHandle&& aHandle, uint64_t aOffset, size_t aSize,
          void* aFixedAddress = nullptr);

  /**
   * Freeze the shared memory region.
   */
  ReadOnlyHandle Freeze() &&;

  /**
   * Freeze the shared memory region.
   *
   * The returned Mapping will still be valid and writable until it is deleted,
   * however no new writable mappings can be created.
   */
  std::tuple<ReadOnlyHandle, MutableMapping> FreezeWithMutableMapping() &&;

  /**
   * Unmap the shared memory, returning the freezable handle.
   *
   * It is only necessary to call this if you need to get the FreezableHandle
   * back.
   */
  FreezableHandle Unmap() &&;

 protected:
  FreezableHandle mHandle;
};

template <Type T>
struct Mapping<T, true> : public Mapping<T> {
  Mapping() {}
  MOZ_IMPLICIT Mapping(std::nullptr_t) : Mapping<T>(nullptr) {}

  explicit Mapping(shared_memory::Handle<T>&& aHandle,
                   void* aFixedAddress = nullptr)
      : Mapping<T>(aHandle, aFixedAddress), mHandle(std::move(aHandle)) {}

  const shared_memory::Handle<T>& Handle() const { return mHandle; };

  std::tuple<shared_memory::Handle<T>, Mapping<T>> Split() && {
    auto handle = std::move(mHandle);
    return std::make_tuple(std::move(handle), std::move(*this));
  }

 private:
  shared_memory::Handle<T> mHandle;
};

// To uphold the guarantees of freezable mappings, we do not allow access to the
// handle (and since this should never be used in this way, we make it a useless
// type).
template <>
struct Mapping<Type::Freezable, true>;

// The access level permitted for memory protection.
enum Access {
  AccessNone = 0,
  AccessRead = 1 << 0,
  AccessWrite = 1 << 1,
  AccessReadWrite = AccessRead | AccessWrite,
};

/**
 * Protect the given memory region.
 *
 * This protection extends only to the local memory mapping. It doesn't change
 * the permissions of other mappings nor the associated handle.
 *
 * @param aAddr The address at the beginning of the memory region.
 * @param aSize The size of the region to protect.
 * @param aAccess The access level to allow.
 *
 * @returns Whether protection was successful.
 */
bool LocalProtect(char* aAddr, size_t aSize, Access aAccess);

/**
 * Find a region of free memory.
 *
 * @param aSize The size of the region to locate.
 *
 * @returns The start of the memory region, or nullptr on error.
 */
void* FindFreeAddressSpace(size_t aSize);

/**
 * Get the system page size.
 */
size_t SystemPageSize();

/**
 * Get the system allocation granularity.
 *
 * This may be distinct from the page size, and controls the required
 * alignment for fixed mapping addresses and shared memory offsets.
 */
size_t SystemAllocationGranularity();

/**
 * Return a size which is page-aligned and can fit at least `minimum` bytes.
 *
 * @param aMinimum The minimum number of bytes required.
 *
 * @returns The page-aligned size that can hold `minimum` bytes.
 */
size_t PageAlignedSize(size_t aMinimum);

}  // namespace shared_memory

using SharedMemoryMapping = shared_memory::MutableMapping;
using ReadOnlySharedMemoryMapping = shared_memory::ReadOnlyMapping;
using MutableOrReadOnlySharedMemoryMapping =
    shared_memory::MutableOrReadOnlyMapping;
using FreezableSharedMemoryMapping = shared_memory::FreezableMapping;

using SharedMemoryMappingWithHandle = shared_memory::MutableMappingWithHandle;
using ReadOnlySharedMemoryMappingWithHandle =
    shared_memory::ReadOnlyMappingWithHandle;

}  // namespace mozilla::ipc

#endif