Jeff Gour | a3e4ebd | 2024-01-23 21:04:49 | [diff] [blame] | 1 | // Copyright 2024 The Chromium Authors |
| 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
| 5 | // Protected memory is memory holding security-sensitive data intended to be |
| 6 | // left read-only for the majority of its lifetime to avoid being overwritten |
| 7 | // by attackers. ProtectedMemory is a simple wrapper around platform-specific |
| 8 | // APIs to set memory read-write and read-only when required. Protected memory |
| 9 | // should be set read-write for the minimum amount of time required. |
| 10 | // |
| 11 | // Normally mutable variables are held in read-write memory and constant data |
| 12 | // is held in read-only memory to ensure it is not accidentally overwritten. |
| 13 | // In some cases we want to hold mutable variables in read-only memory, except |
| 14 | // when they are being written to, to ensure that they are not tampered with. |
| 15 | // |
| 16 | // ProtectedMemory is a container class intended to hold a single variable in |
| 17 | // read-only memory, except when explicitly set read-write. The variable can be |
André Kempe | 0f7da88a | 2024-03-22 19:12:47 | [diff] [blame^] | 18 | // set read-write by creating a scoped AutoWritableMemory object, the memory |
| 19 | // stays writable until the returned object goes out of scope and is destructed. |
| 20 | // The wrapped variable can be accessed using operator* and operator->. |
Jeff Gour | a3e4ebd | 2024-01-23 21:04:49 | [diff] [blame] | 21 | // |
André Kempe | 0f7da88a | 2024-03-22 19:12:47 | [diff] [blame^] | 22 | // Instances of ProtectedMemory must be defined using DEFINE_PROTECTED_DATA |
| 23 | // and as global variables. Global definitions are required to avoid the linker |
| 24 | // placing statics in inlinable functions into a comdat section and setting the |
| 25 | // protected memory section read-write when they are merged. If a declaration of |
| 26 | // a protected variable is required DECLARE_PROTECTED_DATA should be used. |
| 27 | // |
| 28 | // Instances of `base::ProtectedMemory` use constant initialization. To allow |
| 29 | // protection of objects which do not provide constant initialization or would |
| 30 | // require a global constructor, `base::ProtectedMemory` provides lazy |
| 31 | // initialization. With template parameter `ConstructLazily` set to `true`, the |
| 32 | // value is constructed lazily when initialized through |
| 33 | // `ProtectedMemoryInitializer`. In this case, explicit initialization through |
| 34 | // `ProtectedMemoryInitializer` is mandatory to prevent accessing uninitialized |
| 35 | // memory. If data is accessed without initialization a CHECK triggers. |
| 36 | // |
| 37 | // `base::ProtectedMemory` requires T to be trivially destructible. T having |
| 38 | // a non-trivial constructor indicates that is holds data which can not be |
| 39 | // protected by `base::ProtectedMemory`. |
Jeff Gour | a3e4ebd | 2024-01-23 21:04:49 | [diff] [blame] | 40 | // |
| 41 | // EXAMPLE: |
| 42 | // |
| 43 | // struct Items { void* item1; }; |
André Kempe | 0f7da88a | 2024-03-22 19:12:47 | [diff] [blame^] | 44 | // static DEFINE_PROTECTED_DATA base::ProtectedMemory<Items, false> items; |
Jeff Gour | a3e4ebd | 2024-01-23 21:04:49 | [diff] [blame] | 45 | // void InitializeItems() { |
| 46 | // // Explicitly set items read-write before writing to it. |
André Kempe | 0f7da88a | 2024-03-22 19:12:47 | [diff] [blame^] | 47 | // auto writer = base::AutoWritableMemory(items); |
| 48 | // writer->item1 = /* ... */; |
Jeff Gour | a3e4ebd | 2024-01-23 21:04:49 | [diff] [blame] | 49 | // assert(items->item1 != nullptr); |
| 50 | // // items is set back to read-only on the destruction of writer |
| 51 | // } |
| 52 | // |
| 53 | // using FnPtr = void (*)(void); |
André Kempe | 0f7da88a | 2024-03-22 19:12:47 | [diff] [blame^] | 54 | // DEFINE_PROTECTED_DATA base::ProtectedMemory<FnPtr, true> fnPtr; |
Jeff Gour | a3e4ebd | 2024-01-23 21:04:49 | [diff] [blame] | 55 | // FnPtr ResolveFnPtr(void) { |
André Kempe | 0f7da88a | 2024-03-22 19:12:47 | [diff] [blame^] | 56 | // // `ProtectedMemoryInitializer` is a helper class for creating a static |
Jeff Gour | a3e4ebd | 2024-01-23 21:04:49 | [diff] [blame] | 57 | // // initializer for a ProtectedMemory variable. It implicitly sets the |
| 58 | // // variable read-write during initialization. |
André Kempe | 0f7da88a | 2024-03-22 19:12:47 | [diff] [blame^] | 59 | // static base::ProtectedMemoryInitializer initializer(&fnPtr, |
Jeff Gour | a3e4ebd | 2024-01-23 21:04:49 | [diff] [blame] | 60 | // reinterpret_cast<FnPtr>(dlsym(/* ... */))); |
| 61 | // return *fnPtr; |
| 62 | // } |
| 63 | |
| 64 | #ifndef BASE_MEMORY_PROTECTED_MEMORY_H_ |
| 65 | #define BASE_MEMORY_PROTECTED_MEMORY_H_ |
| 66 | |
André Kempe | 5adc4d5a | 2024-03-15 12:23:33 | [diff] [blame] | 67 | #include <stddef.h> |
| 68 | #include <stdint.h> |
| 69 | |
| 70 | #include <memory> |
André Kempe | 0f7da88a | 2024-03-22 19:12:47 | [diff] [blame^] | 71 | #include <type_traits> |
André Kempe | 5adc4d5a | 2024-03-15 12:23:33 | [diff] [blame] | 72 | |
| 73 | #include "base/check.h" |
Jeff Gour | a3e4ebd | 2024-01-23 21:04:49 | [diff] [blame] | 74 | #include "base/check_op.h" |
André Kempe | 5adc4d5a | 2024-03-15 12:23:33 | [diff] [blame] | 75 | #include "base/gtest_prod_util.h" |
Jeff Gour | a3e4ebd | 2024-01-23 21:04:49 | [diff] [blame] | 76 | #include "base/memory/protected_memory_buildflags.h" |
| 77 | #include "base/memory/raw_ref.h" |
| 78 | #include "base/no_destructor.h" |
| 79 | #include "base/synchronization/lock.h" |
André Kempe | 5adc4d5a | 2024-03-15 12:23:33 | [diff] [blame] | 80 | #include "base/thread_annotations.h" |
Jeff Gour | a3e4ebd | 2024-01-23 21:04:49 | [diff] [blame] | 81 | #include "build/build_config.h" |
| 82 | |
| 83 | #if BUILDFLAG(PROTECTED_MEMORY_ENABLED) |
| 84 | #if BUILDFLAG(IS_WIN) |
| 85 | // Define a read-write prot section. The $a, $mem, and $z 'sub-sections' are |
| 86 | // merged alphabetically so $a and $z are used to define the start and end of |
| 87 | // the protected memory section, and $mem holds protected variables. |
| 88 | // (Note: Sections in Portable Executables are equivalent to segments in other |
| 89 | // executable formats, so this section is mapped into its own pages.) |
| 90 | #pragma section("prot$a", read, write) |
| 91 | #pragma section("prot$mem", read, write) |
| 92 | #pragma section("prot$z", read, write) |
| 93 | |
| 94 | // We want the protected memory section to be read-only, not read-write so we |
| 95 | // instruct the linker to set the section read-only at link time. We do this |
| 96 | // at link time instead of compile time, because defining the prot section |
| 97 | // read-only would cause mis-compiles due to optimizations assuming that the |
| 98 | // section contents are constant. |
| 99 | #pragma comment(linker, "/SECTION:prot,R") |
| 100 | |
| 101 | __declspec(allocate("prot$a")) |
| 102 | __declspec(selectany) char __start_protected_memory; |
| 103 | __declspec(allocate("prot$z")) |
| 104 | __declspec(selectany) char __stop_protected_memory; |
| 105 | |
André Kempe | 0f7da88a | 2024-03-22 19:12:47 | [diff] [blame^] | 106 | #define DECLARE_PROTECTED_DATA constinit |
| 107 | #define DEFINE_PROTECTED_DATA constinit __declspec(allocate("prot$mem")) |
Jeff Gour | a3e4ebd | 2024-01-23 21:04:49 | [diff] [blame] | 108 | #else |
| 109 | #error "Protected Memory is currently only supported on Windows." |
| 110 | #endif // BUILDFLAG(IS_WIN) |
| 111 | |
| 112 | #else |
André Kempe | 0f7da88a | 2024-03-22 19:12:47 | [diff] [blame^] | 113 | #define DECLARE_PROTECTED_DATA constinit |
| 114 | #define DEFINE_PROTECTED_DATA DECLARE_PROTECTED_DATA |
Jeff Gour | a3e4ebd | 2024-01-23 21:04:49 | [diff] [blame] | 115 | #endif // BUILDFLAG(PROTECTED_MEMORY_ENABLED) |
| 116 | |
| 117 | namespace base { |
| 118 | |
André Kempe | 0f7da88a | 2024-03-22 19:12:47 | [diff] [blame^] | 119 | template <typename T, bool ConstructLazily> |
Jeff Gour | a3e4ebd | 2024-01-23 21:04:49 | [diff] [blame] | 120 | class AutoWritableMemory; |
| 121 | |
André Kempe | 5adc4d5a | 2024-03-15 12:23:33 | [diff] [blame] | 122 | FORWARD_DECLARE_TEST(ProtectedMemoryDeathTest, VerifyTerminationOnAccess); |
| 123 | |
André Kempe | 0f7da88a | 2024-03-22 19:12:47 | [diff] [blame^] | 124 | namespace internal { |
| 125 | // Helper classes which store the data and implement and initialization |
| 126 | // according to `ConstructLazily`. With `ConstructLazily` set to false, the |
| 127 | // instance of T is created upon construction time, whereas with |
| 128 | // `ConstructLazily` set to true, the instance of T is only constructed when |
| 129 | // emplace is called. |
| 130 | template <typename T, bool ConstructLazily> |
| 131 | class ProtectedDataHolder { |
| 132 | public: |
| 133 | consteval ProtectedDataHolder() = default; |
| 134 | |
| 135 | template <typename... U> |
| 136 | consteval explicit ProtectedDataHolder(U&&... args) |
| 137 | : data_(std::forward<U>(args)...) {} |
| 138 | |
| 139 | T& GetReference() { return data_; } |
| 140 | const T& GetReference() const { return data_; } |
| 141 | |
| 142 | T* GetPointer() { return &data_; } |
| 143 | const T* GetPointer() const { return &data_; } |
| 144 | |
| 145 | template <typename... U> |
| 146 | void emplace(U&&... data) { |
| 147 | data_ = T(std::forward<U>(data)...); |
| 148 | } |
| 149 | |
| 150 | private: |
| 151 | T data_ = T(); |
| 152 | }; |
| 153 | |
Jeff Gour | a3e4ebd | 2024-01-23 21:04:49 | [diff] [blame] | 154 | template <typename T> |
André Kempe | 0f7da88a | 2024-03-22 19:12:47 | [diff] [blame^] | 155 | class ProtectedDataHolder<T, true /*ConstructLazily*/> { |
| 156 | public: |
| 157 | consteval ProtectedDataHolder() = default; |
| 158 | |
| 159 | T& GetReference() { return *GetPointer(); } |
| 160 | const T& GetReference() const { return *GetPointer(); } |
| 161 | |
| 162 | T* GetPointer() { |
| 163 | CHECK(constructed_); |
| 164 | return reinterpret_cast<T*>(&data_); |
| 165 | } |
| 166 | const T* GetPointer() const { |
| 167 | CHECK(constructed_); |
| 168 | return reinterpret_cast<const T*>(&data_); |
| 169 | } |
| 170 | |
| 171 | template <typename... U> |
| 172 | void emplace(U&&... args) { |
| 173 | if (constructed_) { |
| 174 | std::destroy_at(reinterpret_cast<T*>(&data_)); |
| 175 | constructed_ = false; |
| 176 | } |
| 177 | |
| 178 | std::construct_at(reinterpret_cast<T*>(&data_), std::forward<U>(args)...); |
| 179 | constructed_ = true; |
| 180 | } |
| 181 | |
| 182 | private: |
| 183 | // Initializing with a constant/zero value ensures no global constructor is |
| 184 | // required when instantiating `ProtectedDataHolder` and `ProtectedMemory`. |
| 185 | alignas(T) uint8_t data_[sizeof(T)] = {0}; |
| 186 | bool constructed_ = false; |
| 187 | }; |
| 188 | |
| 189 | } // namespace internal |
| 190 | |
| 191 | // The wrapper class for data of type `T` which is to be stored in protected |
| 192 | // memory. `ProtectedMemory` provides improved type safety in conjunction with |
| 193 | // the other classes, although the basic mechanisms like unlocking and |
| 194 | // re-locking of the memory would also work without it. |
| 195 | // |
| 196 | // To allow using `T`s which do not have constant initialization, the template |
| 197 | // parameter `ConstructLazily` enables a lazy initialization. In this case, an |
| 198 | // initialization before first access is mandatory (see |
| 199 | // `ProtectedMemoryInitializer`). |
| 200 | template <typename T, bool ConstructLazily = false> |
Jeff Gour | a3e4ebd | 2024-01-23 21:04:49 | [diff] [blame] | 201 | class ProtectedMemory { |
| 202 | public: |
André Kempe | 0f7da88a | 2024-03-22 19:12:47 | [diff] [blame^] | 203 | // T must be trivially destructible. Otherwise it indicates that T holds data |
| 204 | // which would not be covered by this write protection, i.e. data allocated on |
| 205 | // heap. |
| 206 | static_assert(std::is_trivially_destructible_v<T>); |
| 207 | |
| 208 | // For lazily constructed data we enable this constructor only if there are |
| 209 | // no arguments. For lazily constructed data no arguments are accepted as T is |
| 210 | // not initialized when `ProtectedMemory<T>` is created but through |
| 211 | // `ProtectedMemoryInitializer` instead. |
| 212 | template < |
| 213 | typename... U, |
| 214 | bool ConstructLazilyP = ConstructLazily, |
| 215 | std::enable_if_t<!ConstructLazilyP || sizeof...(U) == 0, bool> = true> |
| 216 | consteval explicit ProtectedMemory(U&&... args) |
| 217 | : data_(std::forward<U>(args)...) { |
| 218 | static_assert(std::is_trivially_destructible_v<ProtectedMemory>); |
| 219 | } |
| 220 | |
Jeff Gour | a3e4ebd | 2024-01-23 21:04:49 | [diff] [blame] | 221 | ProtectedMemory(const ProtectedMemory&) = delete; |
| 222 | ProtectedMemory& operator=(const ProtectedMemory&) = delete; |
| 223 | |
| 224 | // Expose direct access to the encapsulated variable |
André Kempe | 0f7da88a | 2024-03-22 19:12:47 | [diff] [blame^] | 225 | const T& operator*() const { return data_.GetReference(); } |
| 226 | const T* operator->() const { return data_.GetPointer(); } |
Jeff Gour | a3e4ebd | 2024-01-23 21:04:49 | [diff] [blame] | 227 | |
| 228 | private: |
André Kempe | 0f7da88a | 2024-03-22 19:12:47 | [diff] [blame^] | 229 | friend class AutoWritableMemory<T, ConstructLazily>; |
André Kempe | 5adc4d5a | 2024-03-15 12:23:33 | [diff] [blame] | 230 | FRIEND_TEST_ALL_PREFIXES(ProtectedMemoryDeathTest, VerifyTerminationOnAccess); |
| 231 | |
André Kempe | 0f7da88a | 2024-03-22 19:12:47 | [diff] [blame^] | 232 | internal::ProtectedDataHolder<T, ConstructLazily> data_; |
Jeff Gour | a3e4ebd | 2024-01-23 21:04:49 | [diff] [blame] | 233 | }; |
| 234 | |
Jeff Gour | a3e4ebd | 2024-01-23 21:04:49 | [diff] [blame] | 235 | #if BUILDFLAG(PROTECTED_MEMORY_ENABLED) |
André Kempe | 5adc4d5a | 2024-03-15 12:23:33 | [diff] [blame] | 236 | namespace internal { |
| 237 | // Checks that the byte at `ptr` is read-only. |
| 238 | BASE_EXPORT bool IsMemoryReadOnly(const void* ptr); |
Jeff Gour | a3e4ebd | 2024-01-23 21:04:49 | [diff] [blame] | 239 | |
| 240 | // Abstract out platform-specific methods to get the beginning and end of the |
| 241 | // PROTECTED_MEMORY_SECTION. ProtectedMemoryEnd returns a pointer to the byte |
| 242 | // past the end of the PROTECTED_MEMORY_SECTION. |
André Kempe | 5adc4d5a | 2024-03-15 12:23:33 | [diff] [blame] | 243 | inline constexpr void* kProtectedMemoryStart = &__start_protected_memory; |
| 244 | inline constexpr void* kProtectedMemoryEnd = &__stop_protected_memory; |
| 245 | } // namespace internal |
Jeff Gour | a3e4ebd | 2024-01-23 21:04:49 | [diff] [blame] | 246 | #endif // BUILDFLAG(PROTECTED_MEMORY_ENABLED) |
| 247 | |
André Kempe | 5adc4d5a | 2024-03-15 12:23:33 | [diff] [blame] | 248 | // Provide some common functionality for `AutoWritableMemory<T>`. |
| 249 | class BASE_EXPORT AutoWritableMemoryBase { |
| 250 | protected: |
| 251 | #if BUILDFLAG(PROTECTED_MEMORY_ENABLED) |
| 252 | // Checks that `object` is located within the interval |
| 253 | // (internal::kProtectedMemoryStart, internal::kProtectedMemoryEnd). |
| 254 | template <typename T> |
| 255 | static bool IsObjectInProtectedSection(const T& object) { |
| 256 | const T* const ptr = std::addressof(object); |
| 257 | const T* const ptr_end = ptr + 1; |
| 258 | return (ptr > internal::kProtectedMemoryStart) && |
| 259 | (ptr_end <= internal::kProtectedMemoryEnd); |
| 260 | } |
| 261 | |
| 262 | template <typename T> |
| 263 | static bool IsObjectReadOnly(const T& object) { |
| 264 | return internal::IsMemoryReadOnly(std::addressof(object)); |
| 265 | } |
| 266 | |
| 267 | template <typename T> |
| 268 | static bool SetObjectReadWrite(T& object) { |
| 269 | T* const ptr = std::addressof(object); |
| 270 | T* const ptr_end = ptr + 1; |
| 271 | return SetMemoryReadWrite(ptr, ptr_end); |
| 272 | } |
| 273 | |
| 274 | static bool SetProtectedSectionReadOnly() { |
| 275 | return SetMemoryReadOnly(internal::kProtectedMemoryStart, |
| 276 | internal::kProtectedMemoryEnd); |
| 277 | } |
| 278 | |
| 279 | // When linking, each DSO will have its own protected section. We can't keep |
| 280 | // track of each section, yet we have to ensure to always unlock and re-lock |
| 281 | // the correct section. |
| 282 | // |
| 283 | // We solve this by defining a separate global writers variable (explained |
| 284 | // below) in every dynamic shared object (DSO) that includes this header. To |
| 285 | // do that we use this structure to define global writer data without |
| 286 | // duplicate symbol errors. |
| 287 | // |
| 288 | // Storing the data in a substructure is required to store `writers` within |
| 289 | // the protected subsection. If `writers` and `writers_lock()` are located |
| 290 | // directly in `AutoWritableMemoryBase`, for unknown reasons `writers` is not |
| 291 | // placed into the protected section. |
| 292 | struct WriterData { |
| 293 | // `writers` is a global holding the number of ProtectedMemory instances set |
| 294 | // writable, used to avoid races setting protected memory readable/writable. |
| 295 | // When this reaches zero the protected memory region is set read only. |
| 296 | // Access is controlled by writers_lock. |
| 297 | // |
| 298 | // Declare writers in the protected memory section to avoid the scenario |
| 299 | // where an attacker could overwrite it with a large value and invoke code |
| 300 | // that constructs and destructs an AutoWritableMemory. After such a call |
| 301 | // protected memory would still be set writable because writers > 0. |
André Kempe | 0f7da88a | 2024-03-22 19:12:47 | [diff] [blame^] | 302 | DEFINE_PROTECTED_DATA |
André Kempe | 5adc4d5a | 2024-03-15 12:23:33 | [diff] [blame] | 303 | static inline size_t writers GUARDED_BY(writers_lock()) = 0; |
| 304 | |
| 305 | // Synchronizes access to the writers variable and the simultaneous actions |
| 306 | // that need to happen alongside writers changes, e.g. setting the protected |
| 307 | // memory region readable when writers is decremented to 0. |
| 308 | static Lock& writers_lock() { |
| 309 | static NoDestructor<Lock> writers_lock; |
| 310 | return *writers_lock; |
| 311 | } |
| 312 | }; |
| 313 | |
| 314 | private: |
| 315 | // Abstract out platform-specific memory APIs. |end| points to the byte |
| 316 | // past the end of the region of memory having its memory protections |
| 317 | // changed. |
| 318 | static bool SetMemoryReadWrite(void* start, void* end); |
| 319 | static bool SetMemoryReadOnly(void* start, void* end); |
| 320 | #endif // BUILDFLAG(PROTECTED_MEMORY_ENABLED) |
Jeff Gour | a3e4ebd | 2024-01-23 21:04:49 | [diff] [blame] | 321 | }; |
Jeff Gour | a3e4ebd | 2024-01-23 21:04:49 | [diff] [blame] | 322 | |
| 323 | // A class that sets a given ProtectedMemory variable writable while the |
| 324 | // AutoWritableMemory is in scope. This class implements the logic for setting |
| 325 | // the protected memory region read-only/read-write in a thread-safe manner. |
André Kempe | 5adc4d5a | 2024-03-15 12:23:33 | [diff] [blame] | 326 | // |
| 327 | // |AutoWritableMemory| affects the write-permissions of _all_ protected data |
| 328 | // for a DSO, not just of the instance that it's being passed! All protected |
| 329 | // data is stored within the same binary section. At the same time, the OS-level |
| 330 | // support enforcing write protection can only be changed at page level. To |
| 331 | // allow a more fine grained control a dedicated page per instance of protected |
| 332 | // data would be required. |
André Kempe | 0f7da88a | 2024-03-22 19:12:47 | [diff] [blame^] | 333 | template <typename T, bool ConstructLazily> |
André Kempe | 5adc4d5a | 2024-03-15 12:23:33 | [diff] [blame] | 334 | class AutoWritableMemory : public AutoWritableMemoryBase { |
Jeff Gour | a3e4ebd | 2024-01-23 21:04:49 | [diff] [blame] | 335 | public: |
André Kempe | 0f7da88a | 2024-03-22 19:12:47 | [diff] [blame^] | 336 | explicit AutoWritableMemory( |
| 337 | ProtectedMemory<T, ConstructLazily>& protected_memory) |
André Kempe | 5adc4d5a | 2024-03-15 12:23:33 | [diff] [blame] | 338 | #if BUILDFLAG(PROTECTED_MEMORY_ENABLED) |
| 339 | LOCKS_EXCLUDED(WriterData::writers_lock()) |
| 340 | #endif |
Jeff Gour | a3e4ebd | 2024-01-23 21:04:49 | [diff] [blame] | 341 | : protected_memory_(protected_memory) { |
| 342 | #if BUILDFLAG(PROTECTED_MEMORY_ENABLED) |
André Kempe | 5adc4d5a | 2024-03-15 12:23:33 | [diff] [blame] | 343 | |
| 344 | // Check that the data is located in the protected section to |
| 345 | // ensure consistency of data. |
| 346 | CHECK(IsObjectInProtectedSection(protected_memory_->data_)); |
| 347 | CHECK(IsObjectInProtectedSection(WriterData::writers)); |
Jeff Gour | a3e4ebd | 2024-01-23 21:04:49 | [diff] [blame] | 348 | |
| 349 | { |
André Kempe | 5adc4d5a | 2024-03-15 12:23:33 | [diff] [blame] | 350 | base::AutoLock auto_lock(WriterData::writers_lock()); |
| 351 | |
| 352 | if (WriterData::writers == 0) { |
| 353 | CHECK(IsObjectReadOnly(protected_memory_->data_)); |
| 354 | CHECK(IsObjectReadOnly(WriterData::writers)); |
| 355 | CHECK(SetObjectReadWrite(WriterData::writers)); |
Jeff Gour | a3e4ebd | 2024-01-23 21:04:49 | [diff] [blame] | 356 | } |
| 357 | |
André Kempe | 5adc4d5a | 2024-03-15 12:23:33 | [diff] [blame] | 358 | ++WriterData::writers; |
Jeff Gour | a3e4ebd | 2024-01-23 21:04:49 | [diff] [blame] | 359 | } |
| 360 | |
André Kempe | 5adc4d5a | 2024-03-15 12:23:33 | [diff] [blame] | 361 | CHECK(SetObjectReadWrite(protected_memory_->data_)); |
Jeff Gour | a3e4ebd | 2024-01-23 21:04:49 | [diff] [blame] | 362 | #endif // BUILDFLAG(PROTECTED_MEMORY_ENABLED) |
| 363 | } |
| 364 | |
André Kempe | 5adc4d5a | 2024-03-15 12:23:33 | [diff] [blame] | 365 | ~AutoWritableMemory() |
Jeff Gour | a3e4ebd | 2024-01-23 21:04:49 | [diff] [blame] | 366 | #if BUILDFLAG(PROTECTED_MEMORY_ENABLED) |
André Kempe | 5adc4d5a | 2024-03-15 12:23:33 | [diff] [blame] | 367 | LOCKS_EXCLUDED(WriterData::writers_lock()) |
| 368 | #endif |
| 369 | { |
| 370 | #if BUILDFLAG(PROTECTED_MEMORY_ENABLED) |
| 371 | base::AutoLock auto_lock(WriterData::writers_lock()); |
| 372 | CHECK_GT(WriterData::writers, 0u); |
| 373 | --WriterData::writers; |
Jeff Gour | a3e4ebd | 2024-01-23 21:04:49 | [diff] [blame] | 374 | |
André Kempe | 5adc4d5a | 2024-03-15 12:23:33 | [diff] [blame] | 375 | if (WriterData::writers == 0) { |
| 376 | // Lock the whole section of protected memory and set _all_ instances of |
| 377 | // base::ProtectedMemory to non-writeable. |
| 378 | CHECK(SetProtectedSectionReadOnly()); |
| 379 | CHECK(IsObjectReadOnly( |
| 380 | *static_cast<const char*>(internal::kProtectedMemoryStart))); |
| 381 | CHECK(IsObjectReadOnly(WriterData::writers)); |
Jeff Gour | a3e4ebd | 2024-01-23 21:04:49 | [diff] [blame] | 382 | } |
| 383 | #endif // BUILDFLAG(PROTECTED_MEMORY_ENABLED) |
| 384 | } |
| 385 | |
| 386 | AutoWritableMemory(AutoWritableMemory& original) = delete; |
| 387 | AutoWritableMemory& operator=(AutoWritableMemory& original) = delete; |
| 388 | AutoWritableMemory(AutoWritableMemory&& original) = delete; |
| 389 | AutoWritableMemory& operator=(AutoWritableMemory&& original) = delete; |
| 390 | |
André Kempe | 0f7da88a | 2024-03-22 19:12:47 | [diff] [blame^] | 391 | T& GetProtectedData() { return protected_memory_->data_.GetReference(); } |
| 392 | T* GetProtectedDataPtr() { return protected_memory_->data_.GetPointer(); } |
| 393 | |
| 394 | template <typename... U> |
| 395 | void emplace(U&&... args) { |
| 396 | protected_memory_->data_.emplace(std::forward<U>(args)...); |
| 397 | } |
Jeff Gour | a3e4ebd | 2024-01-23 21:04:49 | [diff] [blame] | 398 | |
| 399 | private: |
André Kempe | 0f7da88a | 2024-03-22 19:12:47 | [diff] [blame^] | 400 | const raw_ref<ProtectedMemory<T, ConstructLazily>> protected_memory_; |
Jeff Gour | a3e4ebd | 2024-01-23 21:04:49 | [diff] [blame] | 401 | }; |
| 402 | |
André Kempe | 0f7da88a | 2024-03-22 19:12:47 | [diff] [blame^] | 403 | // Helper class for creating simple ProtectedMemory static initializers. |
| 404 | class ProtectedMemoryInitializer { |
| 405 | public: |
| 406 | template <typename T, bool ConstructLazily, typename... U> |
| 407 | explicit ProtectedMemoryInitializer( |
| 408 | ProtectedMemory<T, ConstructLazily>& protected_memory, |
| 409 | U&&... args) { |
| 410 | AutoWritableMemory writer(protected_memory); |
| 411 | writer.emplace(std::forward<U>(args)...); |
| 412 | } |
| 413 | |
| 414 | ProtectedMemoryInitializer() = delete; |
| 415 | ProtectedMemoryInitializer(const ProtectedMemoryInitializer&) = delete; |
| 416 | ProtectedMemoryInitializer& operator=(const ProtectedMemoryInitializer&) = |
| 417 | delete; |
| 418 | }; |
Jeff Gour | a3e4ebd | 2024-01-23 21:04:49 | [diff] [blame] | 419 | |
| 420 | } // namespace base |
| 421 | |
| 422 | #endif // BASE_MEMORY_PROTECTED_MEMORY_H_ |