chromium/base/memory/protected_memory.h

// 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_