1 : // Copyright 2014 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 : #include "syzygy/agent/asan/page_protection_helpers.h"
16 :
17 : #include "gtest/gtest.h"
18 : #include "syzygy/agent/asan/unittest_util.h"
19 :
20 : namespace agent {
21 : namespace asan {
22 :
23 : namespace {
24 :
25 : // Use the unittest fixture with an OnExceptionCallback.
26 : class PageProtectionHelpersTest : public testing::OnExceptionCallbackTest {
27 : public:
28 : using Super = testing::OnExceptionCallbackTest;
29 :
30 : enum Protection {
31 : kProtectNone,
32 : kProtectRedzones,
33 : kProtectAll,
34 : };
35 :
36 E : void SetUp() override {
37 E : Super::SetUp();
38 E : shadow_.SetUp();
39 E : }
40 :
41 E : void TearDown() override {
42 E : shadow_.TearDown();
43 E : Super::TearDown();
44 E : }
45 :
46 : // Wrapper to testing::IsAccessible that also checks the shadow memory page
47 : // protection bits.
48 E : bool IsAccessible(void* addr) {
49 E : if (!testing::IsAccessible(addr))
50 i : return false;
51 E : if (shadow_.PageIsProtected(addr))
52 i : return false;
53 E : return true;
54 E : }
55 :
56 : // Wrapper to testing::IsNotAccessible that also checks the shadow memory page
57 : // protection bits.
58 E : bool IsNotAccessible(void* addr) {
59 E : if (!testing::IsNotAccessible(addr))
60 i : return false;
61 E : if (!shadow_.PageIsProtected(addr))
62 i : return false;
63 E : return true;
64 E : }
65 :
66 : void TestAccessUnderProtection(const BlockInfo& block_info,
67 E : Protection protection) {
68 : // Grab a set of points to sample for access, scattered across the various
69 : // components of the block.
70 E : std::set<void*> samples;
71 E : samples.insert(block_info.RawHeader());
72 E : samples.insert(block_info.RawHeaderPadding() - 1);
73 E : samples.insert(block_info.header_padding);
74 : samples.insert(block_info.RawHeaderPadding() +
75 E : block_info.header_padding_size / 2);
76 : samples.insert(block_info.RawHeaderPadding() +
77 E : block_info.header_padding_size - 1);
78 E : samples.insert(block_info.RawBody());
79 E : samples.insert(block_info.RawBody() + block_info.body_size / 2);
80 E : samples.insert(block_info.RawBody() + block_info.body_size - 1);
81 E : samples.insert(block_info.RawTrailerPadding());
82 : samples.insert(block_info.RawTrailerPadding() +
83 E : block_info.trailer_padding_size / 2);
84 : samples.insert(block_info.RawTrailerPadding() +
85 E : block_info.trailer_padding_size - 1);
86 E : samples.insert(block_info.RawTrailer());
87 E : samples.insert(block_info.RawTrailer());
88 E : samples.insert(block_info.RawBlock() + block_info.block_size - 1);
89 :
90 : // Also sample at points at the edges of the pages in the redzones.
91 E : if (block_info.left_redzone_pages_size > 0) {
92 E : if (block_info.RawBlock() < block_info.left_redzone_pages)
93 i : samples.insert(block_info.left_redzone_pages - 1);
94 E : samples.insert(block_info.left_redzone_pages);
95 : samples.insert(block_info.left_redzone_pages +
96 E : block_info.left_redzone_pages_size - 1);
97 : samples.insert(block_info.left_redzone_pages +
98 E : block_info.left_redzone_pages_size);
99 : }
100 E : if (block_info.right_redzone_pages_size > 0) {
101 E : samples.insert(block_info.right_redzone_pages - 1);
102 E : samples.insert(block_info.right_redzone_pages);
103 : samples.insert(block_info.right_redzone_pages +
104 E : block_info.right_redzone_pages_size - 1);
105 : uint8* past_end =
106 E : block_info.right_redzone_pages + block_info.right_redzone_pages_size;
107 E : if (past_end < block_info.RawBlock() + block_info.block_size)
108 E : samples.insert(past_end);
109 : }
110 :
111 : uint8* left_end =
112 E : block_info.left_redzone_pages + block_info.left_redzone_pages_size;
113 : uint8* right_end =
114 E : block_info.right_redzone_pages + block_info.right_redzone_pages_size;
115 E : uint8* block_end = block_info.block_pages + block_info.block_pages_size;
116 :
117 E : std::set<void*>::const_iterator it = samples.begin();
118 E : for (; it != samples.end(); ++it) {
119 : if ((*it >= block_info.left_redzone_pages && *it < left_end) ||
120 E : (*it >= block_info.right_redzone_pages && *it < right_end)) {
121 : // In the left or right guard pages.
122 E : if (protection == kProtectNone) {
123 E : EXPECT_TRUE(IsAccessible(*it));
124 E : } else {
125 E : EXPECT_TRUE(IsNotAccessible(*it));
126 E : }
127 E : } else if (*it >= block_info.block_pages && *it < block_end) {
128 : // In the block pages, but not a left or right guard page.
129 E : if (protection == kProtectAll) {
130 E : EXPECT_TRUE(IsNotAccessible(*it));
131 E : } else {
132 E : EXPECT_TRUE(IsAccessible(*it));
133 : }
134 E : } else {
135 : // In the block, but in a page that is only partially covered.
136 E : EXPECT_TRUE(IsAccessible(*it));
137 : }
138 E : }
139 E : }
140 :
141 : // Tests that the page protections are as expected after calling
142 : // BlockProtectNone.
143 E : void TestProtectNone(const BlockInfo& block_info) {
144 E : BlockProtectNone(block_info, &shadow_);
145 E : EXPECT_NO_FATAL_FAILURE(
146 : TestAccessUnderProtection(block_info, kProtectNone));
147 E : }
148 :
149 : // Tests that the page protections are as expected after calling
150 : // BlockProtectRedzones.
151 E : void TestProtectRedzones(const BlockInfo& block_info) {
152 E : BlockProtectRedzones(block_info, &shadow_);
153 E : EXPECT_NO_FATAL_FAILURE(
154 : TestAccessUnderProtection(block_info, kProtectRedzones));
155 E : }
156 :
157 : // Tests that the page protections are as expected after calling
158 : // BlockProtectAll.
159 E : void TestProtectAll(const BlockInfo& block_info) {
160 E : BlockProtectAll(block_info, &shadow_);
161 E : EXPECT_NO_FATAL_FAILURE(TestAccessUnderProtection(block_info, kProtectAll));
162 E : }
163 :
164 : // Tests that all page protection transitions work.
165 : void TestAllProtectionTransitions(size_t chunk_size,
166 : size_t alignment,
167 : size_t size,
168 : size_t min_left_redzone_size,
169 E : size_t min_right_redzone_size) {
170 : // Create and initialize the given block.
171 E : BlockLayout layout = {};
172 E : EXPECT_TRUE(BlockPlanLayout(chunk_size, alignment, size,
173 : min_left_redzone_size, min_right_redzone_size,
174 : &layout));
175 : void* alloc =
176 E : ::VirtualAlloc(NULL, layout.block_size, MEM_COMMIT, PAGE_READWRITE);
177 E : ASSERT_TRUE(alloc != NULL);
178 E : BlockInfo block_info = {};
179 E : BlockInitialize(layout, alloc, false, &block_info);
180 :
181 : // By default the protections should be disabled for a fresh allocation.
182 E : EXPECT_NO_FATAL_FAILURE(
183 : TestAccessUnderProtection(block_info, kProtectNone));
184 :
185 : // Try a full cycle of page protections. This tests all possible
186 : // transitions, including self transitions.
187 E : EXPECT_NO_FATAL_FAILURE(TestProtectNone(block_info));
188 E : EXPECT_NO_FATAL_FAILURE(TestProtectNone(block_info));
189 E : EXPECT_NO_FATAL_FAILURE(TestProtectRedzones(block_info));
190 E : EXPECT_NO_FATAL_FAILURE(TestProtectRedzones(block_info));
191 E : EXPECT_NO_FATAL_FAILURE(TestProtectAll(block_info));
192 E : EXPECT_NO_FATAL_FAILURE(TestProtectAll(block_info));
193 E : EXPECT_NO_FATAL_FAILURE(TestProtectNone(block_info));
194 E : EXPECT_NO_FATAL_FAILURE(TestProtectAll(block_info));
195 E : EXPECT_NO_FATAL_FAILURE(TestProtectRedzones(block_info));
196 E : EXPECT_NO_FATAL_FAILURE(TestProtectNone(block_info));
197 :
198 E : ASSERT_EQ(TRUE, ::VirtualFree(alloc, 0, MEM_RELEASE));
199 E : }
200 :
201 : Shadow shadow_;
202 : };
203 :
204 : using testing::_;
205 :
206 : } // namespace
207 :
208 E : TEST_F(PageProtectionHelpersTest, GetBlockInfo) {
209 : // Plan a layout that is subject to page protections.
210 E : BlockLayout layout = {};
211 E : BlockPlanLayout(4096, 4096, 4096, 4096, 4096, &layout);
212 :
213 : void* alloc = ::VirtualAlloc(
214 E : nullptr, layout.block_size, MEM_COMMIT, PAGE_READWRITE);
215 E : ASSERT_TRUE(alloc != nullptr);
216 E : ::memset(alloc, 0, layout.block_size);
217 :
218 : // Initialize the block in both memory and the shadow memory.
219 E : BlockInfo info = {};
220 E : BlockInitialize(layout, alloc, false, &info);
221 E : shadow_.PoisonAllocatedBlock(info);
222 :
223 : // Try recovering in the usual case.
224 E : BlockInfo info_recovered = {};
225 E : EXPECT_TRUE(BlockInfoFromMemory(info.header, &info_recovered));
226 E : EXPECT_TRUE(GetBlockInfo(&shadow_, info.body, &info_recovered));
227 E : EXPECT_EQ(0, ::memcmp(&info, &info_recovered, sizeof(info)));
228 :
229 : // Muck up the header and try again.
230 E : info.header->magic++;
231 E : EXPECT_FALSE(BlockInfoFromMemory(info.header, &info_recovered));
232 E : EXPECT_TRUE(GetBlockInfo(&shadow_, info.body, &info_recovered));
233 E : EXPECT_EQ(0, ::memcmp(&info, &info_recovered, sizeof(info)));
234 E : info.header->magic--;
235 E : EXPECT_TRUE(BlockInfoFromMemory(info.header, &info_recovered));
236 :
237 : // Set page protections and try again.
238 E : BlockProtectRedzones(info, &shadow_);
239 E : EXPECT_CALL(*this, OnExceptionCallback(_));
240 E : EXPECT_FALSE(BlockInfoFromMemory(info.header, &info_recovered));
241 E : testing::Mock::VerifyAndClearExpectations(this);
242 E : EXPECT_TRUE(GetBlockInfo(&shadow_, info.body, &info_recovered));
243 E : EXPECT_EQ(0, ::memcmp(&info, &info_recovered, sizeof(info)));
244 E : BlockProtectNone(info, &shadow_);
245 E : EXPECT_TRUE(BlockInfoFromMemory(info.header, &info_recovered));
246 :
247 : // Clean up.
248 E : shadow_.Unpoison(info.header, info.block_size);
249 E : ::VirtualFree(alloc, layout.block_size, MEM_RELEASE);
250 E : }
251 :
252 E : TEST_F(PageProtectionHelpersTest, ProtectionTransitions) {
253 : // Left and right guard pages, everything page aligned.
254 : EXPECT_NO_FATAL_FAILURE(TestAllProtectionTransitions(
255 E : 4096, 4096, 4096, 4096, 4096));
256 :
257 : // Left and right guard pages will contain entire pages, but
258 : // not be entirely covered by pages.
259 : EXPECT_NO_FATAL_FAILURE(TestAllProtectionTransitions(
260 E : 8, 8, 128, 4100, 8200));
261 :
262 : // An allocation that will contain no pages whatsoever.
263 : EXPECT_NO_FATAL_FAILURE(TestAllProtectionTransitions(
264 E : 8, 8, 67, 0, 0));
265 :
266 : // An allocation with redzones containing no pages, but that covers an
267 : // entire page.
268 : EXPECT_NO_FATAL_FAILURE(TestAllProtectionTransitions(
269 E : 4096, 8, 67, 0, 0));
270 E : }
271 :
272 E : TEST_F(PageProtectionHelpersTest, BlockProtectAuto) {
273 E : BlockLayout layout = {};
274 E : const size_t kPageSize = GetPageSize();
275 : EXPECT_TRUE(BlockPlanLayout(kPageSize, kPageSize, kPageSize, kPageSize,
276 E : kPageSize, &layout));
277 : void* alloc = ::VirtualAlloc(NULL, layout.block_size, MEM_COMMIT,
278 E : PAGE_READWRITE);
279 E : ASSERT_TRUE(alloc != NULL);
280 :
281 E : BlockInfo block_info = {};
282 E : BlockInitialize(layout, alloc, false, &block_info);
283 E : TestAccessUnderProtection(block_info, kProtectNone);
284 :
285 E : block_info.header->state = ALLOCATED_BLOCK;
286 E : BlockProtectAuto(block_info, &shadow_);
287 E : TestAccessUnderProtection(block_info, kProtectRedzones);
288 E : BlockProtectNone(block_info, &shadow_);
289 :
290 E : block_info.header->state = QUARANTINED_BLOCK;
291 E : BlockProtectAuto(block_info, &shadow_);
292 E : TestAccessUnderProtection(block_info, kProtectAll);
293 E : BlockProtectNone(block_info, &shadow_);
294 :
295 E : block_info.header->state = QUARANTINED_FLOODED_BLOCK;
296 E : BlockProtectAuto(block_info, &shadow_);
297 E : TestAccessUnderProtection(block_info, kProtectAll);
298 E : BlockProtectNone(block_info, &shadow_);
299 :
300 E : block_info.header->state = FREED_BLOCK;
301 E : BlockProtectAuto(block_info, &shadow_);
302 E : TestAccessUnderProtection(block_info, kProtectAll);
303 E : BlockProtectNone(block_info, &shadow_);
304 :
305 E : ASSERT_EQ(TRUE, ::VirtualFree(alloc, 0, MEM_RELEASE));
306 E : }
307 :
308 : } // namespace asan
309 : } // namespace agent
|