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 #include "base/memory/protected_memory.h"
6
7 #include <stddef.h>
8 #include <stdint.h>
9
10 #include <climits>
11 #include <type_traits>
12
13 #include "base/memory/protected_memory_buildflags.h"
14 #include "base/synchronization/lock.h"
15 #include "base/test/gtest_util.h"
16 #include "build/build_config.h"
17 #include "testing/gtest/include/gtest/gtest.h"
18
19 namespace base {
20 namespace {
21
22 struct Data {
23 Data() = default;
Database::__anon5f447b480111::Data24 constexpr Data(int16_t f, int32_t b) : foo(f), bar(b) {}
25 int16_t foo = 0;
26 int32_t bar = -1;
27 };
28
29 struct DataWithNonTrivialConstructor {
DataWithNonTrivialConstructorbase::__anon5f447b480111::DataWithNonTrivialConstructor30 explicit DataWithNonTrivialConstructor(int f) : foo(f) {}
31 int foo;
32 };
33
34 static_assert(
35 !std::is_trivially_constructible_v<DataWithNonTrivialConstructor>);
36
37 #if BUILDFLAG(PROTECTED_MEMORY_ENABLED)
VerifyByteSequenceIsNotWriteable(unsigned char * const byte_pattern,const size_t number_of_bits,const size_t bit_increment)38 void VerifyByteSequenceIsNotWriteable(unsigned char* const byte_pattern,
39 const size_t number_of_bits,
40 const size_t bit_increment) {
41 const auto check_bit_not_writeable = [=](const size_t bit_index) {
42 const size_t byte_index = bit_index / CHAR_BIT;
43 const size_t local_bit_index = bit_index % CHAR_BIT;
44
45 EXPECT_CHECK_DEATH_WITH(
46 byte_pattern[byte_index] ^= (0x1 << local_bit_index), "")
47 << " at bit " << bit_index << " of " << number_of_bits;
48 };
49
50 // Check the boundary bits explicitly to ensure we cover these.
51 if (number_of_bits >= 1) {
52 check_bit_not_writeable(0);
53 }
54
55 if (number_of_bits >= 2) {
56 check_bit_not_writeable(number_of_bits - 1);
57 }
58
59 // Now check the bits in between at the requested increment.
60 for (size_t bit_index = bit_increment; bit_index < (number_of_bits - 1);
61 bit_index += bit_increment) {
62 check_bit_not_writeable(bit_index);
63 }
64 }
65
66 template <typename T>
VerifyInstanceIsNotWriteable(T & instance,const size_t bit_increment=3)67 void VerifyInstanceIsNotWriteable(T& instance, const size_t bit_increment = 3) {
68 VerifyByteSequenceIsNotWriteable(
69 reinterpret_cast<unsigned char*>(std::addressof(instance)),
70 sizeof(T) * CHAR_BIT, bit_increment);
71 }
72 #endif // BUILDFLAG(PROTECTED_MEMORY_ENABLED)
73
74 DEFINE_PROTECTED_DATA ProtectedMemory<int, false /*ConstructLazily*/>
75 g_default_initialization;
76
TEST(ProtectedMemoryTest,DefaultInitialization)77 TEST(ProtectedMemoryTest, DefaultInitialization) {
78 EXPECT_EQ(*g_default_initialization, int());
79 }
80
81 DEFINE_PROTECTED_DATA ProtectedMemory<Data, false /*ConstructLazily*/>
82 g_with_initialization_declaration(4, 3);
83
TEST(ProtectedMemoryTest,InitializationDeclaration)84 TEST(ProtectedMemoryTest, InitializationDeclaration) {
85 EXPECT_EQ(g_with_initialization_declaration->foo, 4);
86 EXPECT_EQ(g_with_initialization_declaration->bar, 3);
87 }
88
89 DEFINE_PROTECTED_DATA ProtectedMemory<int, false /*ConstructLazily*/>
90 g_explicit_initialization;
91
TEST(ProtectedMemoryTest,ExplicitInitializationWithExplicitValue)92 TEST(ProtectedMemoryTest, ExplicitInitializationWithExplicitValue) {
93 static ProtectedMemoryInitializer initializer_explicit_value(
94 g_explicit_initialization, 4);
95
96 EXPECT_EQ(*g_explicit_initialization, 4);
97 }
98
99 DEFINE_PROTECTED_DATA ProtectedMemory<int, false /*ConstructLazily*/>
100 g_explicit_initialization_with_default_value;
101
TEST(ProtectedMemoryTest,VerifyExplicitInitializationWithDefaultValue)102 TEST(ProtectedMemoryTest, VerifyExplicitInitializationWithDefaultValue) {
103 static ProtectedMemoryInitializer initializer_explicit_value(
104 g_explicit_initialization_with_default_value);
105
106 EXPECT_EQ(*g_explicit_initialization_with_default_value, int());
107 }
108
109 DEFINE_PROTECTED_DATA
110 ProtectedMemory<DataWithNonTrivialConstructor, true /*ConstructLazily*/>
111 g_lazily_initialized_with_explicit_initialization;
112
TEST(ProtectedMemoryTest,ExplicitLazyInitializationWithExplicitValue)113 TEST(ProtectedMemoryTest, ExplicitLazyInitializationWithExplicitValue) {
114 static ProtectedMemoryInitializer initializer_explicit_value(
115 g_lazily_initialized_with_explicit_initialization, 4);
116
117 EXPECT_EQ(g_lazily_initialized_with_explicit_initialization->foo, 4);
118 }
119
120 DEFINE_PROTECTED_DATA
121 ProtectedMemory<DataWithNonTrivialConstructor, true /*ConstructLazily*/>
122 g_uninitialized;
123
TEST(ProtectedMemoryDeathTest,AccessWithoutInitialization)124 TEST(ProtectedMemoryDeathTest, AccessWithoutInitialization) {
125 EXPECT_CHECK_DEATH_WITH(g_uninitialized.operator*(), "");
126 EXPECT_CHECK_DEATH_WITH(g_uninitialized.operator->(), "");
127 }
128
129 #if BUILDFLAG(PROTECTED_MEMORY_ENABLED)
130 DEFINE_PROTECTED_DATA ProtectedMemory<Data, false /*ConstructLazily*/>
131 g_eagerly_initialized;
132
TEST(ProtectedMemoryTest,VerifySetValue)133 TEST(ProtectedMemoryTest, VerifySetValue) {
134 ASSERT_NE(g_eagerly_initialized->foo, 5);
135 EXPECT_EQ(g_eagerly_initialized->bar, -1);
136 {
137 base::AutoWritableMemory writer(g_eagerly_initialized);
138 writer.GetProtectedDataPtr()->foo = 5;
139 }
140 EXPECT_EQ(g_eagerly_initialized->foo, 5);
141 EXPECT_EQ(g_eagerly_initialized->bar, -1);
142 }
143
TEST(ProtectedMemoryDeathTest,AccessWithoutWriteAccessCrashes)144 TEST(ProtectedMemoryDeathTest, AccessWithoutWriteAccessCrashes) {
145 VerifyInstanceIsNotWriteable(g_with_initialization_declaration);
146 }
147
TEST(ProtectedMemoryDeathTest,FailsIfDefinedOutsideOfProtectMemoryRegion)148 TEST(ProtectedMemoryDeathTest, FailsIfDefinedOutsideOfProtectMemoryRegion) {
149 ProtectedMemory<Data> data;
150 EXPECT_CHECK_DEATH({ AutoWritableMemory writer(data); });
151 }
152
153 #endif // BUILDFLAG(PROTECTED_MEMORY_ENABLED)
154
155 } // namespace
156 } // namespace base
157