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