// Copyright 2024 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifdef UNSAFE_BUFFERS_BUILD // TODO(crbug.com/40284755): Remove this and spanify to fix the errors. #pragma allow_unsafe_buffers #endif // Protected memory is memory holding security-sensitive data intended to be // left read-only for the majority of its lifetime to avoid being overwritten // by attackers. ProtectedMemory is a simple wrapper around platform-specific // APIs to set memory read-write and read-only when required. Protected memory // should be set read-write for the minimum amount of time required. // // Normally mutable variables are held in read-write memory and constant data // is held in read-only memory to ensure it is not accidentally overwritten. // In some cases we want to hold mutable variables in read-only memory, except // when they are being written to, to ensure that they are not tampered with. // // ProtectedMemory is a container class intended to hold a single variable in // read-only memory, except when explicitly set read-write. The variable can be // set read-write by creating a scoped AutoWritableMemory object, the memory // stays writable until the returned object goes out of scope and is destructed. // The wrapped variable can be accessed using operator* and operator->. // // Instances of ProtectedMemory must be defined using DEFINE_PROTECTED_DATA // and as global variables. Global definitions are required to avoid the linker // placing statics in inlinable functions into a comdat section and setting the // protected memory section read-write when they are merged. If a declaration of // a protected variable is required DECLARE_PROTECTED_DATA should be used. // // Instances of `base::ProtectedMemory` use constant initialization. To allow // protection of objects which do not provide constant initialization or would // require a global constructor, `base::ProtectedMemory` provides lazy // initialization through `ProtectedMemoryInitializer`. Additionally, on // platforms where it is not possible to have the protected memory section start // as read-only, the very first call to ProtectedMemoryInitializer will // initialize the memory section to read-only. Explicit initialization through // `ProtectedMemoryInitializer` is mandatory, even for objects that provide // constant initialization. This ensures that in the unlikely event that the // value is modified before the memory is initialized to read-only, it will be // forced back to a known, safe, initial state before it ever used. If data is // accessed without initialization a CHECK triggers. This CHECK is not expected // to provided security guarantees, but to help catch programming errors. // // TODO(crbug.com/356428974): Improve protection offered by Protected Memory. // // `base::ProtectedMemory` requires T to be trivially destructible. T having // a non-trivial constructor indicates that is holds data which can not be // protected by `base::ProtectedMemory`. // // EXAMPLE: // // struct Items { void* item1; }; // static DEFINE_PROTECTED_DATA base::ProtectedMemory<Items> items; // void InitializeItems() { // // Explicitly set items read-write before writing to it. // auto writer = base::AutoWritableMemory(items); // writer->item1 = /* ... */; // assert(items->item1 != nullptr); // // items is set back to read-only on the destruction of writer // } // // using FnPtr = void (*)(void); // DEFINE_PROTECTED_DATA base::ProtectedMemory<FnPtr> fnPtr; // FnPtr ResolveFnPtr(void) { // // `ProtectedMemoryInitializer` is a helper class for creating a static // // initializer for a ProtectedMemory variable. It implicitly sets the // // variable read-write during initialization. // static base::ProtectedMemoryInitializer initializer(&fnPtr, // reinterpret_cast<FnPtr>(dlsym(/* ... */))); // return *fnPtr; // } #ifndef BASE_MEMORY_PROTECTED_MEMORY_H_ #define BASE_MEMORY_PROTECTED_MEMORY_H_ #include <stddef.h> #include <stdint.h> #include <memory> #include <type_traits> #include "base/bits.h" #include "base/check.h" #include "base/check_op.h" #include "base/gtest_prod_util.h" #include "base/memory/page_size.h" #include "base/memory/protected_memory_buildflags.h" #include "base/memory/raw_ref.h" #include "base/no_destructor.h" #include "base/synchronization/lock.h" #include "base/thread_annotations.h" #include "build/build_config.h" #if BUILDFLAG(PROTECTED_MEMORY_ENABLED) #if BUILDFLAG(IS_WIN) // Define a read-write prot section. The $a, $mem, and $z 'sub-sections' are // merged alphabetically so $a and $z are used to define the start and end of // the protected memory section, and $mem holds protected variables. // (Note: Sections in Portable Executables are equivalent to segments in other // executable formats, so this section is mapped into its own pages.) #pragma section("prot$a", read, write) #pragma section("prot$mem", read, write) #pragma section("prot$z", read, write) // We want the protected memory section to be read-only, not read-write so we // instruct the linker to set the section read-only at link time. We do this // at link time instead of compile time, because defining the prot section // read-only would cause mis-compiles due to optimizations assuming that the // section contents are constant. #pragma comment(linker, "/SECTION:prot,R") __declspec(allocate("prot$a")) __declspec(selectany) char __start_protected_memory; __declspec(allocate("prot$z")) __declspec(selectany) char __stop_protected_memory; #define DECLARE_PROTECTED_DATA … #define DEFINE_PROTECTED_DATA … #elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_ANDROID) // This value is used to align the writers variable. That variable needs to be // aligned to ensure that the protected memory section starts on a page // boundary. #if (PA_BUILDFLAG(IS_ANDROID) && PA_BUILDFLAG(PA_ARCH_CPU_64_BITS)) || \ (PA_BUILDFLAG(IS_LINUX) && PA_BUILDFLAG(PA_ARCH_CPU_ARM64)) // arm64 supports 4kb, 16kb, and 64kb pages. Set to the largest of 64kb as that // will guarantee the section is page aligned regardless of the choice. inline constexpr int kProtectedMemoryAlignment = 65536; #elif PA_BUILDFLAG(PA_ARCH_CPU_PPC64) || defined(ARCH_CPU_PPC64) // Modern ppc64 systems support 4kB (shift = 12) and 64kB (shift = 16) page // sizes. Set to the largest of 64kb as that will guarantee the section is page // aligned regardless of the choice. inline constexpr int kProtectedMemoryAlignment = 65536; #elif defined(_MIPS_ARCH_LOONGSON) || PA_BUILDFLAG(PA_ARCH_CPU_LOONGARCH64) || \ defined(ARCH_CPU_LOONGARCH64) // 16kb page size inline constexpr int kProtectedMemoryAlignment = 16384; #else // 4kb page size inline constexpr int kProtectedMemoryAlignment = 4096; #endif __asm__(".section protected_memory, \"a\"\n\t"); __asm__(".section protected_memory_buffer, \"a\"\n\t"); // Explicitly mark these variables hidden so the symbols are local to the // currently built component. Otherwise they are created with global (external) // linkage and component builds would break because a single pair of these // symbols would override the rest. __attribute__((visibility("hidden"))) extern char __start_protected_memory; __attribute__((visibility("hidden"))) extern char __stop_protected_memory; #define DECLARE_PROTECTED_DATA … #define DEFINE_PROTECTED_DATA … #elif BUILDFLAG(IS_MAC) // The segment the section is in is defined with a linker flag in // build/config/mac/BUILD.gn #define DECLARE_PROTECTED_DATA … #define DEFINE_PROTECTED_DATA … extern char __start_protected_memory __asm( "section$start$PROTECTED_MEMORY$protected_memory"); extern char __stop_protected_memory __asm( "section$end$PROTECTED_MEMORY$protected_memory"); #else #error "Protected Memory is not supported on this platform." #endif #else #define DECLARE_PROTECTED_DATA … #define DEFINE_PROTECTED_DATA … #endif // BUILDFLAG(PROTECTED_MEMORY_ENABLED) base // namespace base #endif // BASE_MEMORY_PROTECTED_MEMORY_H_