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 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->. 21 // 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`. 40 // 41 // EXAMPLE: 42 // 43 // struct Items { void* item1; }; 44 // static DEFINE_PROTECTED_DATA base::ProtectedMemory<Items, false> items; 45 // void InitializeItems() { 46 // // Explicitly set items read-write before writing to it. 47 // auto writer = base::AutoWritableMemory(items); 48 // writer->item1 = /* ... */; 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); 54 // DEFINE_PROTECTED_DATA base::ProtectedMemory<FnPtr, true> fnPtr; 55 // FnPtr ResolveFnPtr(void) { 56 // // `ProtectedMemoryInitializer` is a helper class for creating a static 57 // // initializer for a ProtectedMemory variable. It implicitly sets the 58 // // variable read-write during initialization. 59 // static base::ProtectedMemoryInitializer initializer(&fnPtr, 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 67 #include <stddef.h> 68 #include <stdint.h> 69 70 #include <memory> 71 #include <type_traits> 72 73 #include "base/check.h" 74 #include "base/check_op.h" 75 #include "base/gtest_prod_util.h" 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" 80 #include "base/thread_annotations.h" 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 106 #define DECLARE_PROTECTED_DATA constinit 107 #define DEFINE_PROTECTED_DATA constinit __declspec(allocate("prot$mem")) 108 #else 109 #error "Protected Memory is currently only supported on Windows." 110 #endif // BUILDFLAG(IS_WIN) 111 112 #else 113 #define DECLARE_PROTECTED_DATA constinit 114 #define DEFINE_PROTECTED_DATA DECLARE_PROTECTED_DATA 115 #endif // BUILDFLAG(PROTECTED_MEMORY_ENABLED) 116 117 namespace base { 118 119 template <typename T, bool ConstructLazily> 120 class AutoWritableMemory; 121 122 FORWARD_DECLARE_TEST(ProtectedMemoryDeathTest, VerifyTerminationOnAccess); 123 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> ProtectedDataHolder(U &&...args)136 consteval explicit ProtectedDataHolder(U&&... args) 137 : data_(std::forward<U>(args)...) {} 138 GetReference()139 T& GetReference() { return data_; } GetReference()140 const T& GetReference() const { return data_; } 141 GetPointer()142 T* GetPointer() { return &data_; } GetPointer()143 const T* GetPointer() const { return &data_; } 144 145 template <typename... U> emplace(U &&...data)146 void emplace(U&&... data) { 147 data_ = T(std::forward<U>(data)...); 148 } 149 150 private: 151 T data_ = T(); 152 }; 153 154 template <typename T> 155 class ProtectedDataHolder<T, true /*ConstructLazily*/> { 156 public: 157 consteval ProtectedDataHolder() = default; 158 GetReference()159 T& GetReference() { return *GetPointer(); } GetReference()160 const T& GetReference() const { return *GetPointer(); } 161 GetPointer()162 T* GetPointer() { 163 CHECK(constructed_); 164 return reinterpret_cast<T*>(&data_); 165 } GetPointer()166 const T* GetPointer() const { 167 CHECK(constructed_); 168 return reinterpret_cast<const T*>(&data_); 169 } 170 171 template <typename... U> emplace(U &&...args)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> 201 class ProtectedMemory { 202 public: 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. This check complements the verification in the constructor since 206 // `ProtectedMemory` with `ConstructLazily` set to `true` is always trivially 207 // destructible. 208 static_assert(std::is_trivially_destructible_v<T>); 209 210 // For lazily constructed data we enable this constructor only if there are 211 // no arguments. For lazily constructed data no arguments are accepted as T is 212 // not initialized when `ProtectedMemory<T>` is created but through 213 // `ProtectedMemoryInitializer` instead. 214 template < 215 typename... U, 216 bool ConstructLazilyP = ConstructLazily, 217 std::enable_if_t<!ConstructLazilyP || sizeof...(U) == 0, bool> = true> ProtectedMemory(U &&...args)218 consteval explicit ProtectedMemory(U&&... args) 219 : data_(std::forward<U>(args)...) { 220 static_assert(std::is_trivially_destructible_v<ProtectedMemory>); 221 } 222 223 ProtectedMemory(const ProtectedMemory&) = delete; 224 ProtectedMemory& operator=(const ProtectedMemory&) = delete; 225 226 // Expose direct access to the encapsulated variable 227 const T& operator*() const { return data_.GetReference(); } 228 const T* operator->() const { return data_.GetPointer(); } 229 230 private: 231 friend class AutoWritableMemory<T, ConstructLazily>; 232 FRIEND_TEST_ALL_PREFIXES(ProtectedMemoryDeathTest, VerifyTerminationOnAccess); 233 234 internal::ProtectedDataHolder<T, ConstructLazily> data_; 235 }; 236 237 #if BUILDFLAG(PROTECTED_MEMORY_ENABLED) 238 namespace internal { 239 // Checks that the byte at `ptr` is read-only. 240 BASE_EXPORT bool IsMemoryReadOnly(const void* ptr); 241 242 // Abstract out platform-specific methods to get the beginning and end of the 243 // PROTECTED_MEMORY_SECTION. ProtectedMemoryEnd returns a pointer to the byte 244 // past the end of the PROTECTED_MEMORY_SECTION. 245 inline constexpr void* kProtectedMemoryStart = &__start_protected_memory; 246 inline constexpr void* kProtectedMemoryEnd = &__stop_protected_memory; 247 } // namespace internal 248 #endif // BUILDFLAG(PROTECTED_MEMORY_ENABLED) 249 250 // Provide some common functionality for `AutoWritableMemory<T>`. 251 class BASE_EXPORT AutoWritableMemoryBase { 252 protected: 253 #if BUILDFLAG(PROTECTED_MEMORY_ENABLED) 254 // Checks that `object` is located within the interval 255 // (internal::kProtectedMemoryStart, internal::kProtectedMemoryEnd). 256 template <typename T> IsObjectInProtectedSection(const T & object)257 static bool IsObjectInProtectedSection(const T& object) { 258 const T* const ptr = std::addressof(object); 259 const T* const ptr_end = ptr + 1; 260 return (ptr > internal::kProtectedMemoryStart) && 261 (ptr_end <= internal::kProtectedMemoryEnd); 262 } 263 264 template <typename T> IsObjectReadOnly(const T & object)265 static bool IsObjectReadOnly(const T& object) { 266 return internal::IsMemoryReadOnly(std::addressof(object)); 267 } 268 269 template <typename T> SetObjectReadWrite(T & object)270 static bool SetObjectReadWrite(T& object) { 271 T* const ptr = std::addressof(object); 272 T* const ptr_end = ptr + 1; 273 return SetMemoryReadWrite(ptr, ptr_end); 274 } 275 SetProtectedSectionReadOnly()276 static bool SetProtectedSectionReadOnly() { 277 return SetMemoryReadOnly(internal::kProtectedMemoryStart, 278 internal::kProtectedMemoryEnd); 279 } 280 281 // When linking, each DSO will have its own protected section. We can't keep 282 // track of each section, yet we have to ensure to always unlock and re-lock 283 // the correct section. 284 // 285 // We solve this by defining a separate global writers variable (explained 286 // below) in every dynamic shared object (DSO) that includes this header. To 287 // do that we use this structure to define global writer data without 288 // duplicate symbol errors. 289 // 290 // Storing the data in a substructure is required to store `writers` within 291 // the protected subsection. If `writers` and `writers_lock()` are located 292 // directly in `AutoWritableMemoryBase`, for unknown reasons `writers` is not 293 // placed into the protected section. 294 struct WriterData { 295 // `writers` is a global holding the number of ProtectedMemory instances set 296 // writable, used to avoid races setting protected memory readable/writable. 297 // When this reaches zero the protected memory region is set read only. 298 // Access is controlled by writers_lock. 299 // 300 // Declare writers in the protected memory section to avoid the scenario 301 // where an attacker could overwrite it with a large value and invoke code 302 // that constructs and destructs an AutoWritableMemory. After such a call 303 // protected memory would still be set writable because writers > 0. 304 DEFINE_PROTECTED_DATA 305 static inline size_t writers GUARDED_BY(writers_lock()) = 0; 306 307 // Synchronizes access to the writers variable and the simultaneous actions 308 // that need to happen alongside writers changes, e.g. setting the protected 309 // memory region readable when writers is decremented to 0. writers_lockWriterData310 static Lock& writers_lock() { 311 static NoDestructor<Lock> writers_lock; 312 return *writers_lock; 313 } 314 }; 315 316 private: 317 // Abstract out platform-specific memory APIs. |end| points to the byte 318 // past the end of the region of memory having its memory protections 319 // changed. 320 static bool SetMemoryReadWrite(void* start, void* end); 321 static bool SetMemoryReadOnly(void* start, void* end); 322 #endif // BUILDFLAG(PROTECTED_MEMORY_ENABLED) 323 }; 324 325 // A class that sets a given ProtectedMemory variable writable while the 326 // AutoWritableMemory is in scope. This class implements the logic for setting 327 // the protected memory region read-only/read-write in a thread-safe manner. 328 // 329 // |AutoWritableMemory| affects the write-permissions of _all_ protected data 330 // for a DSO, not just of the instance that it's being passed! All protected 331 // data is stored within the same binary section. At the same time, the OS-level 332 // support enforcing write protection can only be changed at page level. To 333 // allow a more fine grained control a dedicated page per instance of protected 334 // data would be required. 335 template <typename T, bool ConstructLazily> 336 class AutoWritableMemory : public AutoWritableMemoryBase { 337 public: AutoWritableMemory(ProtectedMemory<T,ConstructLazily> & protected_memory)338 explicit AutoWritableMemory( 339 ProtectedMemory<T, ConstructLazily>& protected_memory) 340 #if BUILDFLAG(PROTECTED_MEMORY_ENABLED) 341 LOCKS_EXCLUDED(WriterData::writers_lock()) 342 #endif 343 : protected_memory_(protected_memory) { 344 #if BUILDFLAG(PROTECTED_MEMORY_ENABLED) 345 346 // Check that the data is located in the protected section to 347 // ensure consistency of data. 348 CHECK(IsObjectInProtectedSection(protected_memory_->data_)); 349 CHECK(IsObjectInProtectedSection(WriterData::writers)); 350 351 { 352 base::AutoLock auto_lock(WriterData::writers_lock()); 353 354 if (WriterData::writers == 0) { 355 CHECK(IsObjectReadOnly(protected_memory_->data_)); 356 CHECK(IsObjectReadOnly(WriterData::writers)); 357 CHECK(SetObjectReadWrite(WriterData::writers)); 358 } 359 360 ++WriterData::writers; 361 } 362 363 CHECK(SetObjectReadWrite(protected_memory_->data_)); 364 #endif // BUILDFLAG(PROTECTED_MEMORY_ENABLED) 365 } 366 367 ~AutoWritableMemory() 368 #if BUILDFLAG(PROTECTED_MEMORY_ENABLED) LOCKS_EXCLUDED(WriterData::writers_lock ())369 LOCKS_EXCLUDED(WriterData::writers_lock()) 370 #endif 371 { 372 #if BUILDFLAG(PROTECTED_MEMORY_ENABLED) 373 base::AutoLock auto_lock(WriterData::writers_lock()); 374 CHECK_GT(WriterData::writers, 0u); 375 --WriterData::writers; 376 377 if (WriterData::writers == 0) { 378 // Lock the whole section of protected memory and set _all_ instances of 379 // base::ProtectedMemory to non-writeable. 380 CHECK(SetProtectedSectionReadOnly()); 381 CHECK(IsObjectReadOnly( 382 *static_cast<const char*>(internal::kProtectedMemoryStart))); 383 CHECK(IsObjectReadOnly(WriterData::writers)); 384 } 385 #endif // BUILDFLAG(PROTECTED_MEMORY_ENABLED) 386 } 387 388 AutoWritableMemory(AutoWritableMemory& original) = delete; 389 AutoWritableMemory& operator=(AutoWritableMemory& original) = delete; 390 AutoWritableMemory(AutoWritableMemory&& original) = delete; 391 AutoWritableMemory& operator=(AutoWritableMemory&& original) = delete; 392 GetProtectedData()393 T& GetProtectedData() { return protected_memory_->data_.GetReference(); } GetProtectedDataPtr()394 T* GetProtectedDataPtr() { return protected_memory_->data_.GetPointer(); } 395 396 template <typename... U> emplace(U &&...args)397 void emplace(U&&... args) { 398 protected_memory_->data_.emplace(std::forward<U>(args)...); 399 } 400 401 private: 402 const raw_ref<ProtectedMemory<T, ConstructLazily>> protected_memory_; 403 }; 404 405 // Helper class for creating simple ProtectedMemory static initializers. 406 class ProtectedMemoryInitializer { 407 public: 408 template <typename T, bool ConstructLazily, typename... U> ProtectedMemoryInitializer(ProtectedMemory<T,ConstructLazily> & protected_memory,U &&...args)409 explicit ProtectedMemoryInitializer( 410 ProtectedMemory<T, ConstructLazily>& protected_memory, 411 U&&... args) { 412 AutoWritableMemory writer(protected_memory); 413 writer.emplace(std::forward<U>(args)...); 414 } 415 416 ProtectedMemoryInitializer() = delete; 417 ProtectedMemoryInitializer(const ProtectedMemoryInitializer&) = delete; 418 ProtectedMemoryInitializer& operator=(const ProtectedMemoryInitializer&) = 419 delete; 420 }; 421 422 } // namespace base 423 424 #endif // BASE_MEMORY_PROTECTED_MEMORY_H_ 425