1 : // Copyright 2015 Google Inc. All Rights Reserved.
2 : //
3 : // Licensed under the Apache License, Version 2.0 (the "License");
4 : // you may not use this file except in compliance with the License.
5 : // You may obtain a copy of the License at
6 : //
7 : // http://www.apache.org/licenses/LICENSE-2.0
8 : //
9 : // Unless required by applicable law or agreed to in writing, software
10 : // distributed under the License is distributed on an "AS IS" BASIS,
11 : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 : // See the License for the specific language governing permissions and
13 : // limitations under the License.
14 : //
15 : // Declares a helper class for use in performing hot-patching operations.
16 : // The class takes care of modifying page protections as the patcher works.
17 :
18 : #include "syzygy/agent/asan/scoped_page_protections.h"
19 :
20 : #include <cstdint>
21 : #include "base/logging.h"
22 : #include "gtest/gtest.h"
23 : #include "syzygy/agent/asan/constants.h"
24 :
25 : namespace agent {
26 : namespace asan {
27 :
28 : namespace {
29 :
30 : class ScopedPageProtectionsTest : public testing::Test {
31 : public:
32 : static const size_t kPageCount = 3;
33 :
34 E : ScopedPageProtectionsTest() : page_start_(nullptr), page_end_(nullptr) {}
35 :
36 E : void SetUp() override {
37 : page_start_ = reinterpret_cast<uint8_t*>(::VirtualAlloc(
38 E : nullptr, GetPageSize() * kPageCount, MEM_COMMIT, PAGE_READONLY));
39 E : ASSERT_TRUE(page_start_);
40 E : page_end_ = page_start_ + kPageCount * GetPageSize();
41 E : }
42 :
43 E : void TearDown() override {
44 E : ASSERT_TRUE(::VirtualFree(page_start_, 0, MEM_DECOMMIT));
45 E : page_start_ = nullptr;
46 E : page_end_ = nullptr;
47 E : }
48 :
49 : // Returns the base address of the page with a given index.
50 E : uint8_t* BaseOfPage(size_t index) {
51 E : CHECK_LE(index, kPageCount);
52 E : return page_start_ + index * GetPageSize();
53 E : }
54 :
55 : // Sets the given protection on the given page.
56 E : void SetProtection(size_t index, DWORD protection) {
57 E : CHECK_LT(index, kPageCount);
58 E : DWORD old_protection_unused = 0;
59 : ::VirtualProtect(BaseOfPage(index), GetPageSize(), protection,
60 E : &old_protection_unused);
61 E : }
62 :
63 : // Returns the protection associated with the given page.
64 E : DWORD GetProtection(size_t index) {
65 E : CHECK_LT(index, kPageCount);
66 E : MEMORY_BASIC_INFORMATION mem_info = {};
67 E : CHECK_EQ(sizeof(mem_info),
68 : ::VirtualQuery(BaseOfPage(index), &mem_info, sizeof(mem_info)));
69 E : return mem_info.Protect;
70 E : }
71 :
72 : // An allocation with multiple pages. This is initially allocated read-only.
73 : uint8_t* page_start_;
74 : uint8_t* page_end_;
75 : };
76 :
77 : } // namespace
78 :
79 E : TEST_F(ScopedPageProtectionsTest, ReadOnlyBecomesReadWrite) {
80 E : ScopedPageProtections spp;
81 :
82 : // The fixture should guarantee this.
83 E : ASSERT_EQ(PAGE_READONLY, GetProtection(0));
84 :
85 E : EXPECT_TRUE(spp.EnsureContainingPagesWritable(BaseOfPage(0), 1));
86 E : EXPECT_EQ(PAGE_READWRITE, GetProtection(0));
87 E : spp.RestorePageProtections();
88 E : EXPECT_EQ(PAGE_READONLY, GetProtection(0));
89 E : }
90 :
91 E : TEST_F(ScopedPageProtectionsTest, ExecReadOnlyBecomesExecReadWrite) {
92 E : ScopedPageProtections spp;
93 :
94 : // The fixture should guarantee these assertions.
95 E : ASSERT_EQ(PAGE_READONLY, GetProtection(0));
96 E : SetProtection(0, PAGE_EXECUTE_READ);
97 E : ASSERT_EQ(PAGE_EXECUTE_READ, GetProtection(0));
98 :
99 E : EXPECT_TRUE(spp.EnsureContainingPagesWritable(BaseOfPage(0), 1));
100 E : EXPECT_EQ(PAGE_EXECUTE_READWRITE, GetProtection(0));
101 E : spp.RestorePageProtections();
102 E : EXPECT_EQ(PAGE_EXECUTE_READ, GetProtection(0));
103 E : }
104 :
105 E : TEST_F(ScopedPageProtectionsTest, SpanMultiplePages) {
106 E : ScopedPageProtections spp;
107 :
108 : // The fixture should guarantee these assertions.
109 E : ASSERT_EQ(PAGE_READONLY, GetProtection(0));
110 E : ASSERT_EQ(PAGE_READONLY, GetProtection(1));
111 E : ASSERT_EQ(PAGE_READONLY, GetProtection(2));
112 E : SetProtection(1, PAGE_EXECUTE_READ);
113 E : ASSERT_EQ(PAGE_READONLY, GetProtection(0));
114 E : ASSERT_EQ(PAGE_EXECUTE_READ, GetProtection(1));
115 E : ASSERT_EQ(PAGE_READONLY, GetProtection(2));
116 :
117 E : uint8_t* begin = BaseOfPage(0) + 13;
118 E : uint8_t* end = BaseOfPage(3) - 100;
119 E : EXPECT_TRUE(spp.EnsureContainingPagesWritable(begin, end - begin));
120 E : EXPECT_EQ(PAGE_READWRITE, GetProtection(0));
121 E : EXPECT_EQ(PAGE_EXECUTE_READWRITE, GetProtection(1));
122 E : EXPECT_EQ(PAGE_READWRITE, GetProtection(2));
123 :
124 E : spp.RestorePageProtections();
125 E : EXPECT_EQ(PAGE_READONLY, GetProtection(0));
126 E : EXPECT_EQ(PAGE_EXECUTE_READ, GetProtection(1));
127 E : EXPECT_EQ(PAGE_READONLY, GetProtection(2));
128 E : }
129 :
130 E : TEST_F(ScopedPageProtectionsTest, RestoresProtectionsInDestructor) {
131 : // The fixture should guarantee this.
132 E : ASSERT_EQ(PAGE_READONLY, GetProtection(0));
133 :
134 : {
135 E : ScopedPageProtections spp;
136 E : EXPECT_TRUE(spp.EnsureContainingPagesWritable(BaseOfPage(0), 1));
137 E : EXPECT_EQ(PAGE_READWRITE, GetProtection(0));
138 E : }
139 :
140 E : EXPECT_EQ(PAGE_READONLY, GetProtection(0));
141 E : }
142 :
143 : } // namespace asan
144 : } // namespace agent
|