chromium/base/memory/protected_memory_unittest.cc

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

#include "base/memory/protected_memory.h"

#include <stddef.h>
#include <stdint.h>

#include <climits>
#include <type_traits>

#include "base/memory/protected_memory_buildflags.h"
#include "base/synchronization/lock.h"
#include "base/test/gtest_util.h"
#include "build/build_config.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace base {
namespace {

struct Data {};

struct DataWithNonTrivialConstructor {};

static_assert;

#if BUILDFLAG(PROTECTED_MEMORY_ENABLED)
void VerifyByteSequenceIsNotWriteable(unsigned char* const byte_pattern,
                                      const size_t number_of_bits,
                                      const size_t bit_increment) {
  const auto check_bit_not_writeable = [=](const size_t bit_index) {
    const size_t byte_index = bit_index / CHAR_BIT;
    const size_t local_bit_index = bit_index % CHAR_BIT;

    EXPECT_CHECK_DEATH_WITH(
        byte_pattern[byte_index] ^= (0x1 << local_bit_index), "")
        << " at bit " << bit_index << " of " << number_of_bits;
  };

  // Check the boundary bits explicitly to ensure we cover these.
  if (number_of_bits >= 1) {
    check_bit_not_writeable(0);
  }

  if (number_of_bits >= 2) {
    check_bit_not_writeable(number_of_bits - 1);
  }

  // Now check the bits in between at the requested increment.
  for (size_t bit_index = bit_increment; bit_index < (number_of_bits - 1);
       bit_index += bit_increment) {
    check_bit_not_writeable(bit_index);
  }
}

template <typename T>
void VerifyInstanceIsNotWriteable(T& instance, const size_t bit_increment = 3) {
  VerifyByteSequenceIsNotWriteable(
      reinterpret_cast<unsigned char*>(std::addressof(instance)),
      sizeof(T) * CHAR_BIT, bit_increment);
}
#endif  // BUILDFLAG(PROTECTED_MEMORY_ENABLED)

DEFINE_PROTECTED_DATA ProtectedMemory<int> g_explicit_initialization;

TEST(ProtectedMemoryTest, ExplicitInitializationWithExplicitValue) {}

DEFINE_PROTECTED_DATA ProtectedMemory<int>
    g_explicit_initialization_with_default_value;

TEST(ProtectedMemoryTest, VerifyExplicitInitializationWithDefaultValue) {}

DEFINE_PROTECTED_DATA
ProtectedMemory<DataWithNonTrivialConstructor>
    g_lazily_initialized_with_explicit_initialization;

TEST(ProtectedMemoryTest, ExplicitLazyInitializationWithExplicitValue) {}

DEFINE_PROTECTED_DATA
ProtectedMemory<DataWithNonTrivialConstructor> g_uninitialized;

TEST(ProtectedMemoryDeathTest, AccessWithoutInitialization) {}

#if BUILDFLAG(PROTECTED_MEMORY_ENABLED)
DEFINE_PROTECTED_DATA ProtectedMemory<Data> g_initialized;

TEST(ProtectedMemoryTest, VerifySetValue) {
  static ProtectedMemoryInitializer initializer_explicit_value(g_initialized);
  ASSERT_NE(g_initialized->foo, 5);
  EXPECT_EQ(g_initialized->bar, -1);
  {
    base::AutoWritableMemory writer(g_initialized);
    writer.GetProtectedDataPtr()->foo = 5;
  }
  EXPECT_EQ(g_initialized->foo, 5);
  EXPECT_EQ(g_initialized->bar, -1);
}

DEFINE_PROTECTED_DATA ProtectedMemory<Data> g_not_writable;

TEST(ProtectedMemoryDeathTest, AccessWithoutWriteAccessCrashes) {
  static ProtectedMemoryInitializer initializer_explicit_value(g_not_writable);
  VerifyInstanceIsNotWriteable(g_not_writable);
}

TEST(ProtectedMemoryDeathTest, FailsIfDefinedOutsideOfProtectMemoryRegion) {
  ProtectedMemory<Data> data;
  EXPECT_CHECK_DEATH({ AutoWritableMemory writer(data); });
}

#endif  // BUILDFLAG(PROTECTED_MEMORY_ENABLED)

}  // namespace
}  // namespace base