| // Copyright 2017 The Chromium Authors. All rights reserved. |
| // 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 "base/cfi_buildflags.h" |
| #include "base/memory/protected_memory_cfi.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; |
| Data(int foo_) : foo(foo_) {} |
| int foo; |
| }; |
| |
| } // namespace |
| |
| class ProtectedMemoryTest : public ::testing::Test { |
| protected: |
| // Run tests one at a time. Some of the negative tests can not be made thread |
| // safe. |
| void SetUp() final { lock.Acquire(); } |
| void TearDown() final { lock.Release(); } |
| |
| Lock lock; |
| }; |
| |
| PROTECTED_MEMORY_SECTION ProtectedMemory<int> init; |
| |
| TEST_F(ProtectedMemoryTest, Initializer) { |
| static ProtectedMemory<int>::Initializer I(&init, 4); |
| EXPECT_EQ(*init, 4); |
| } |
| |
| PROTECTED_MEMORY_SECTION ProtectedMemory<Data> data; |
| |
| TEST_F(ProtectedMemoryTest, Basic) { |
| AutoWritableMemory writer = AutoWritableMemory::Create(data); |
| data->foo = 5; |
| EXPECT_EQ(data->foo, 5); |
| } |
| |
| #if defined(GTEST_HAS_DEATH_TEST) && !defined(OS_ANDROID) |
| |
| #if PROTECTED_MEMORY_ENABLED |
| TEST_F(ProtectedMemoryTest, ReadOnlyOnStart) { |
| EXPECT_DEATH({ data->foo = 6; AutoWritableMemory::Create(data); }, ""); |
| } |
| |
| TEST_F(ProtectedMemoryTest, ReadOnlyAfterSetWritable) { |
| { AutoWritableMemory writer = AutoWritableMemory::Create(data); } |
| EXPECT_DEATH({ data->foo = 7; }, ""); |
| } |
| |
| TEST_F(ProtectedMemoryTest, AssertMemoryIsReadOnly) { |
| AssertMemoryIsReadOnly(&data->foo); |
| { AutoWritableMemory::Create(data); } |
| AssertMemoryIsReadOnly(&data->foo); |
| |
| ProtectedMemory<Data> writable_data; |
| EXPECT_DCHECK_DEATH({ AssertMemoryIsReadOnly(&writable_data->foo); }); |
| } |
| |
| TEST_F(ProtectedMemoryTest, FailsIfDefinedOutsideOfProtectMemoryRegion) { |
| ProtectedMemory<Data> data; |
| EXPECT_DCHECK_DEATH({ AutoWritableMemory::Create(data); }); |
| } |
| |
| TEST_F(ProtectedMemoryTest, UnsanitizedCfiCallOutsideOfProtectedMemoryRegion) { |
| ProtectedMemory<void (*)(void)> data; |
| EXPECT_DCHECK_DEATH({ UnsanitizedCfiCall(data)(); }); |
| } |
| #endif // PROTECTED_MEMORY_ENABLED |
| |
| namespace { |
| |
| struct BadIcall { |
| BadIcall() = default; |
| BadIcall(int (*fp_)(int)) : fp(fp_) {} |
| int (*fp)(int); |
| }; |
| |
| unsigned int bad_icall(int i) { |
| return 4 + i; |
| } |
| |
| } // namespace |
| |
| PROTECTED_MEMORY_SECTION ProtectedMemory<BadIcall> icall_pm1; |
| |
| TEST_F(ProtectedMemoryTest, BadMemberCall) { |
| static ProtectedMemory<BadIcall>::Initializer I( |
| &icall_pm1, BadIcall(reinterpret_cast<int (*)(int)>(&bad_icall))); |
| |
| EXPECT_EQ(UnsanitizedCfiCall(icall_pm1, &BadIcall::fp)(1), 5); |
| #if !BUILDFLAG(CFI_ICALL_CHECK) |
| EXPECT_EQ(icall_pm1->fp(1), 5); |
| #elif BUILDFLAG(CFI_ENFORCEMENT_TRAP) || BUILDFLAG(CFI_ENFORCEMENT_DIAGNOSTIC) |
| EXPECT_DEATH({ icall_pm1->fp(1); }, ""); |
| #endif |
| } |
| |
| PROTECTED_MEMORY_SECTION ProtectedMemory<int (*)(int)> icall_pm2; |
| |
| TEST_F(ProtectedMemoryTest, BadFnPtrCall) { |
| static ProtectedMemory<int (*)(int)>::Initializer I( |
| &icall_pm2, reinterpret_cast<int (*)(int)>(&bad_icall)); |
| |
| EXPECT_EQ(UnsanitizedCfiCall(icall_pm2)(1), 5); |
| #if !BUILDFLAG(CFI_ICALL_CHECK) |
| EXPECT_EQ((*icall_pm2)(1), 5); |
| #elif BUILDFLAG(CFI_ENFORCEMENT_TRAP) || BUILDFLAG(CFI_ENFORCEMENT_DIAGNOSTIC) |
| EXPECT_DEATH({ (*icall_pm2)(1); }, ""); |
| #endif |
| } |
| |
| #endif // defined(GTEST_HAS_DEATH_TEST) && !defined(OS_ANDROID) |
| |
| } // namespace base |