blob: 93955e5607b1dd306d0f832fa5cfa00feb286aeb [file] [log] [blame]
// 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 {
Data() = default;
constexpr Data(int16_t f, int32_t b) : foo(f), bar(b) {}
int16_t foo = 0;
int32_t bar = -1;
};
struct DataWithNonTrivialConstructor {
explicit DataWithNonTrivialConstructor(int f) : foo(f) {}
int foo;
};
static_assert(
!std::is_trivially_constructible_v<DataWithNonTrivialConstructor>);
#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) {
static ProtectedMemoryInitializer initializer_explicit_value(
g_explicit_initialization, 4);
EXPECT_EQ(*g_explicit_initialization, 4);
}
DEFINE_PROTECTED_DATA ProtectedMemory<int>
g_explicit_initialization_with_default_value;
TEST(ProtectedMemoryTest, VerifyExplicitInitializationWithDefaultValue) {
static ProtectedMemoryInitializer initializer_explicit_value(
g_explicit_initialization_with_default_value);
EXPECT_EQ(*g_explicit_initialization_with_default_value, int());
}
DEFINE_PROTECTED_DATA
ProtectedMemory<DataWithNonTrivialConstructor>
g_lazily_initialized_with_explicit_initialization;
TEST(ProtectedMemoryTest, ExplicitLazyInitializationWithExplicitValue) {
static ProtectedMemoryInitializer initializer_explicit_value(
g_lazily_initialized_with_explicit_initialization, 4);
EXPECT_EQ(g_lazily_initialized_with_explicit_initialization->foo, 4);
}
DEFINE_PROTECTED_DATA
ProtectedMemory<DataWithNonTrivialConstructor> g_uninitialized;
TEST(ProtectedMemoryDeathTest, AccessWithoutInitialization) {
EXPECT_CHECK_DEATH_WITH(g_uninitialized.operator*(), "");
EXPECT_CHECK_DEATH_WITH(g_uninitialized.operator->(), "");
}
#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