blob: e86a7c8778222b7003757f1dbb03b743fb70200a [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.
#include "base/memory/protected_memory.h"
#include <stddef.h>
#include <stdint.h>
#include <climits>
#include "base/memory/protected_memory_buildflags.h"
#include "base/synchronization/lock.h"
#include "base/test/gtest_util.h"
#include "base/thread_annotations.h"
#include "build/build_config.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace base {
namespace {
struct Data {
Data() = default;
int foo = 0;
};
#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)
PROTECTED_MEMORY_SECTION ProtectedMemory<int> g_int_data;
PROTECTED_MEMORY_SECTION ProtectedMemory<Data> g_structured_data;
} // namespace
TEST(ProtectedMemoryTest, Initializer) {
ProtectedMemory<int>::Initializer initializer(&g_int_data, 4);
EXPECT_EQ(*g_int_data, 4);
}
TEST(ProtectedMemoryTest, Basic) {
AutoWritableMemory writer(g_structured_data);
writer.GetProtectedDataPtr()->foo = 5;
EXPECT_EQ(g_structured_data->foo, 5);
}
PROTECTED_MEMORY_SECTION ProtectedMemory<double> g_double_data;
// Verify that we can successfully create AutoWritableMemory for independent
// data.
TEST(ProtectedMemoryTest, VerifySimultaneousLocksOnDifferentData) {
AutoWritableMemory writer_for_structured_data(g_structured_data);
AutoWritableMemory writer_for_int_data(g_int_data);
AutoWritableMemory writer_for_double_data(g_double_data);
writer_for_structured_data.GetProtectedData().foo += 1;
writer_for_int_data.GetProtectedData() += 1;
writer_for_double_data.GetProtectedData() += 1;
}
#if BUILDFLAG(PROTECTED_MEMORY_ENABLED)
TEST(ProtectedMemoryTest, AssertMemoryIsReadOnly) {
ASSERT_TRUE(internal::IsMemoryReadOnly(&g_structured_data->foo));
{ AutoWritableMemory writer(g_structured_data); }
ASSERT_TRUE(internal::IsMemoryReadOnly(&g_structured_data->foo));
}
TEST(ProtectedMemoryDeathTest, VerifyTerminationOnAccess) {
VerifyInstanceIsNotWriteable(g_structured_data.data_);
}
TEST(ProtectedMemoryTest, FailsIfDefinedOutsideOfProtectMemoryRegion) {
ProtectedMemory<Data> data;
EXPECT_CHECK_DEATH({ AutoWritableMemory writer(data); });
}
#endif // BUILDFLAG(PROTECTED_MEMORY_ENABLED)
} // namespace base