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