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/block.h"
16 :
17 : #include <set>
18 :
19 : #include "windows.h"
20 :
21 : #include "base/memory/scoped_ptr.h"
22 : #include "gtest/gtest.h"
23 : #include "syzygy/agent/asan/asan_runtime.h"
24 : #include "syzygy/agent/asan/page_protection_helpers.h"
25 :
26 : namespace agent {
27 : namespace asan {
28 :
29 : namespace {
30 :
31 : BlockLayout BuildBlockLayout(size_t block_alignment,
32 : size_t block_size,
33 : size_t header_size,
34 : size_t header_padding_size,
35 : size_t body_size,
36 : size_t trailer_padding_size,
37 E : size_t trailer_size) {
38 : BlockLayout layout = { block_alignment, block_size, header_size,
39 E : header_padding_size, body_size, trailer_padding_size, trailer_size };
40 E : return layout;
41 E : }
42 :
43 : // Checks that the given block is valid, and initialized as expected.
44 E : void IsValidBlockImpl(const BlockInfo& block, bool just_initialized) {
45 E : EXPECT_EQ(0u, block.block_size % kShadowRatio);
46 :
47 : // Validate the layout of the block.
48 E : EXPECT_TRUE(block.block != NULL);
49 E : EXPECT_EQ(0u, block.block_size % kShadowRatio);
50 E : EXPECT_EQ(block.block, reinterpret_cast<void*>(block.header));
51 E : EXPECT_EQ(0u, block.header_padding_size % kShadowRatio);
52 : EXPECT_EQ(reinterpret_cast<uint8*>(block.header + 1),
53 E : block.header_padding);
54 : EXPECT_EQ(block.header_padding + block.header_padding_size,
55 E : block.body);
56 : EXPECT_EQ(reinterpret_cast<uint8*>(block.body + block.body_size),
57 E : block.trailer_padding);
58 : EXPECT_EQ(block.trailer_padding + block.trailer_padding_size,
59 E : reinterpret_cast<uint8*>(block.trailer));
60 : EXPECT_EQ(block.block + block.block_size,
61 E : reinterpret_cast<uint8*>(block.trailer + 1));
62 :
63 : // Validate the actual contents of the various parts of the block.
64 :
65 : // Check the header.
66 E : EXPECT_EQ(kBlockHeaderMagic, block.header->magic);
67 E : EXPECT_FALSE(block.header->is_nested);
68 E : EXPECT_LT(0u, block.header->body_size);
69 E : EXPECT_EQ(block.header->body_size, block.body_size);
70 E : if (just_initialized) {
71 E : EXPECT_EQ(0u, block.header->checksum);
72 E : EXPECT_EQ(NULL, block.header->alloc_stack);
73 E : EXPECT_EQ(NULL, block.header->free_stack);
74 E : EXPECT_EQ(ALLOCATED_BLOCK, block.header->state);
75 : }
76 :
77 : // By default we assume the blocks to not be nested.
78 E : EXPECT_FALSE(block.header->is_nested);
79 E : EXPECT_EQ(static_cast<bool>(block.header->is_nested), block.is_nested);
80 :
81 : // Check the header padding.
82 E : if (block.header->has_header_padding) {
83 E : EXPECT_LE(kShadowRatio, block.header_padding_size);
84 : EXPECT_EQ(block.header_padding_size,
85 E : *reinterpret_cast<const uint32*>(block.header_padding));
86 : EXPECT_EQ(block.header_padding_size,
87 : *reinterpret_cast<const uint32*>(block.header_padding +
88 E : block.header_padding_size - sizeof(uint32)));
89 E : for (size_t i = sizeof(uint32);
90 E : i < block.header_padding_size - sizeof(uint32);
91 E : ++i) {
92 E : EXPECT_EQ(kBlockHeaderPaddingByte, block.header_padding[i]);
93 E : }
94 : }
95 :
96 : // Check the trailer padding.
97 E : size_t start_of_trailer_iteration = 0;
98 E : if (block.header->has_excess_trailer_padding) {
99 E : start_of_trailer_iteration = 4;
100 : EXPECT_EQ(block.trailer_padding_size,
101 E : *reinterpret_cast<const uint32*>(block.trailer_padding));
102 : }
103 E : for (size_t i = start_of_trailer_iteration; i < block.trailer_padding_size;
104 E : ++i) {
105 E : EXPECT_EQ(kBlockTrailerPaddingByte, block.trailer_padding[i]);
106 E : }
107 :
108 : // Check the trailer.
109 E : EXPECT_NE(0u, block.trailer->alloc_tid);
110 E : EXPECT_GE(::GetTickCount(), block.trailer->alloc_ticks);
111 E : if (just_initialized) {
112 E : EXPECT_EQ(0u, block.trailer->free_tid);
113 E : EXPECT_EQ(0u, block.trailer->free_ticks);
114 : }
115 E : }
116 :
117 E : void IsValidInitializedBlock(const BlockInfo& block) {
118 E : IsValidBlockImpl(block, true);
119 E : }
120 :
121 : void IsValidBlock(const BlockInfo& block) {
122 : IsValidBlockImpl(block, false);
123 : }
124 :
125 : } // namespace
126 :
127 E : bool operator==(const BlockLayout& bl1, const BlockLayout& bl2) {
128 E : return ::memcmp(&bl1, &bl2, sizeof(BlockLayout)) == 0;
129 E : }
130 :
131 E : bool operator==(const BlockInfo& bi1, const BlockInfo& bi2) {
132 E : return ::memcmp(&bi1, &bi2, sizeof(BlockInfo)) == 0;
133 E : }
134 :
135 E : TEST(BlockTest, BlockPlanLayout) {
136 E : BlockLayout layout = {};
137 :
138 : // Zero sized allocations should work fine.
139 E : EXPECT_TRUE(BlockPlanLayout(8, 8, 0, 0, 0, &layout));
140 E : EXPECT_EQ(BuildBlockLayout(8, 40, 16, 0, 0, 4, 20), layout);
141 :
142 E : EXPECT_TRUE(BlockPlanLayout(8, 8, 60, 32, 32, &layout));
143 E : EXPECT_EQ(BuildBlockLayout(8, 128, 16, 16, 60, 16, 20), layout);
144 :
145 E : EXPECT_TRUE(BlockPlanLayout(8, 8, 60, 0, 0, &layout));
146 E : EXPECT_EQ(BuildBlockLayout(8, 96, 16, 0, 60, 0, 20), layout);
147 :
148 E : EXPECT_TRUE(BlockPlanLayout(8, 8, 64, 0, 0, &layout));
149 E : EXPECT_EQ(BuildBlockLayout(8, 104, 16, 0, 64, 4, 20), layout);
150 :
151 E : EXPECT_TRUE(BlockPlanLayout(8, 8, 61, 0, 0, &layout));
152 E : EXPECT_EQ(BuildBlockLayout(8, 104, 16, 0, 61, 7, 20), layout);
153 :
154 : // Plan a layout that would use guard pages.
155 E : EXPECT_TRUE(BlockPlanLayout(4096, 8, 100, 4096, 4096, &layout));
156 E : EXPECT_EQ(BuildBlockLayout(4096, 3 * 4096, 16, 8072, 100, 4080, 20), layout);
157 :
158 : // Plan a layout with an invalid size, this should fail.
159 E : EXPECT_FALSE(BlockPlanLayout(8, 8, 0xffffffff, 0, 0, &layout));
160 E : }
161 :
162 E : TEST(BlockTest, EndToEnd) {
163 E : BlockLayout layout = {};
164 E : BlockInfo block_info = {};
165 :
166 E : EXPECT_TRUE(BlockPlanLayout(8, 8, 4, 0, 0, &layout));
167 E : scoped_ptr<uint8> block_data(new uint8[layout.block_size]);
168 E : ::memset(block_data.get(), 0, layout.block_size);
169 E : ASSERT_TRUE(block_data != NULL);
170 E : BlockInitialize(layout, block_data.get(), false, &block_info);
171 E : EXPECT_NO_FATAL_FAILURE(IsValidInitializedBlock(block_info));
172 E : block_data.reset(NULL);
173 :
174 E : EXPECT_TRUE(BlockPlanLayout(8, 8, 61, 0, 0, &layout));
175 E : block_data.reset(new uint8[layout.block_size]);
176 E : ::memset(block_data.get(), 0, layout.block_size);
177 E : ASSERT_TRUE(block_data != NULL);
178 E : BlockInitialize(layout, block_data.get(), false, &block_info);
179 E : EXPECT_NO_FATAL_FAILURE(IsValidInitializedBlock(block_info));
180 E : block_data.reset(NULL);
181 :
182 E : EXPECT_TRUE(BlockPlanLayout(8, 8, 60, 32, 32, &layout));
183 E : block_data.reset(new uint8[layout.block_size]);
184 E : ::memset(block_data.get(), 0, layout.block_size);
185 E : ASSERT_TRUE(block_data != NULL);
186 E : BlockInitialize(layout, block_data.get(), false, &block_info);
187 E : EXPECT_NO_FATAL_FAILURE(IsValidInitializedBlock(block_info));
188 E : block_data.reset(NULL);
189 :
190 : // Do an allocation that uses entire pages.
191 E : EXPECT_TRUE(BlockPlanLayout(4096, 8, 100, 4096, 4096, &layout));
192 : void* data = ::VirtualAlloc(NULL, layout.block_size, MEM_COMMIT,
193 E : PAGE_READWRITE);
194 E : ::memset(data, 0, layout.block_size);
195 E : ASSERT_TRUE(data != NULL);
196 E : BlockInitialize(layout, data, false, &block_info);
197 E : EXPECT_NO_FATAL_FAILURE(IsValidInitializedBlock(block_info));
198 E : ASSERT_EQ(TRUE, ::VirtualFree(data, 0, MEM_RELEASE));
199 E : }
200 :
201 E : TEST(BlockTest, GetHeaderFromBody) {
202 : // Plan two layouts, one with header padding and another without.
203 E : BlockLayout layout1 = {};
204 E : BlockLayout layout2 = {};
205 E : EXPECT_TRUE(BlockPlanLayout(kShadowRatio, kShadowRatio, 10, 0, 0, &layout1));
206 E : EXPECT_TRUE(BlockPlanLayout(kShadowRatio, kShadowRatio, 10, 32, 0, &layout2));
207 :
208 E : scoped_ptr<uint8> data(new uint8[layout2.block_size]);
209 E : ::memset(data.get(), 0, layout2.block_size);
210 :
211 : // First try navigating a block without header padding.
212 E : BlockInfo info = {};
213 E : BlockInitialize(layout1, data.get(), false, &info);
214 : // This should succeed as expected.
215 E : EXPECT_EQ(info.header, BlockGetHeaderFromBody(info.body));
216 : // This fails because of invalid alignment.
217 E : EXPECT_TRUE(BlockGetHeaderFromBody(info.body + 1) == NULL);
218 : // This fails because the pointer is not at the beginning of the
219 : // body.
220 E : EXPECT_TRUE(BlockGetHeaderFromBody(info.body + 8) == NULL);
221 : // This fails because of invalid header magic.
222 E : ++info.header->magic;
223 E : EXPECT_TRUE(BlockGetHeaderFromBody(info.body) == NULL);
224 : // This fails because the header indicates there's padding.
225 E : --info.header->magic;
226 E : info.header->has_header_padding = 1;
227 E : EXPECT_TRUE(BlockGetHeaderFromBody(info.body) == NULL);
228 :
229 : // Now navigate a block with header padding.
230 E : BlockInitialize(layout2, data.get(), false, &info);
231 : // This should succeed as expected.
232 E : EXPECT_EQ(info.header, BlockGetHeaderFromBody(info.body));
233 : // This fails because of invalid alignment.
234 E : EXPECT_TRUE(BlockGetHeaderFromBody(info.body + 1) == NULL);
235 : // This fails because the pointer is not at the beginning of the
236 : // body.
237 E : EXPECT_TRUE(BlockGetHeaderFromBody(info.body + 8) == NULL);
238 : // This fails because of invalid header magic.
239 E : ++info.header->magic;
240 E : EXPECT_TRUE(BlockGetHeaderFromBody(info.body) == NULL);
241 : // This fails because the header indicates there's no padding.
242 E : --info.header->magic;
243 E : info.header->has_header_padding = 0;
244 E : EXPECT_TRUE(BlockGetHeaderFromBody(info.body) == NULL);
245 : // This fails because the padding length is invalid.
246 E : info.header->has_header_padding = 1;
247 E : uint32* head = reinterpret_cast<uint32*>(info.header_padding);
248 E : uint32* tail = head + (info.header_padding_size / sizeof(uint32)) - 1;
249 E : ++(*tail);
250 E : EXPECT_TRUE(BlockGetHeaderFromBody(info.body) == NULL);
251 : // This fails because the padding lengths don't agree.
252 E : --(*tail);
253 E : ++(*head);
254 E : EXPECT_TRUE(BlockGetHeaderFromBody(info.body) == NULL);
255 E : }
256 :
257 E : TEST(BlockTest, GetHeaderFromBodyProtectedMemory) {
258 E : BlockLayout layout = {};
259 E : EXPECT_TRUE(BlockPlanLayout(4096, 4096, 4096, 4096, 4096, &layout));
260 : void* alloc = ::VirtualAlloc(NULL, layout.block_size, MEM_COMMIT,
261 E : PAGE_READWRITE);
262 E : ASSERT_TRUE(alloc != NULL);
263 E : BlockInfo block_info = {};
264 E : BlockInitialize(layout, alloc, false, &block_info);
265 :
266 E : BlockProtectRedzones(block_info);
267 E : EXPECT_TRUE(BlockGetHeaderFromBody(block_info.body) == NULL);
268 E : BlockProtectNone(block_info);
269 :
270 E : ASSERT_EQ(TRUE, ::VirtualFree(alloc, 0, MEM_RELEASE));
271 E : }
272 :
273 E : TEST(BlockTest, ConvertBlockInfo) {
274 E : BlockLayout layout = {};
275 E : EXPECT_TRUE(BlockPlanLayout(kShadowRatio, kShadowRatio, 10, 0, 0, &layout));
276 :
277 E : scoped_ptr<uint8> data(new uint8[layout.block_size]);
278 E : ::memset(data.get(), 0, layout.block_size);
279 :
280 E : BlockInfo expanded = {};
281 E : BlockInitialize(layout, data.get(), false, &expanded);
282 :
283 E : CompactBlockInfo compact = {};
284 E : ConvertBlockInfo(expanded, &compact);
285 E : EXPECT_EQ(layout.block_size, compact.block_size);
286 : EXPECT_EQ(layout.header_size + layout.header_padding_size,
287 E : compact.header_size);
288 : EXPECT_EQ(layout.trailer_size + layout.trailer_padding_size,
289 E : compact.trailer_size);
290 E : EXPECT_FALSE(compact.is_nested);
291 :
292 E : BlockInfo expanded2 = {};
293 E : ConvertBlockInfo(compact, &expanded2);
294 E : EXPECT_EQ(0, ::memcmp(&expanded, &expanded2, sizeof(expanded)));
295 E : }
296 :
297 E : TEST(BlockTest, BlockInfoFromMemory) {
298 : // Plan two layouts, one with header padding and another without.
299 E : BlockLayout layout1 = {};
300 E : BlockLayout layout2 = {};
301 E : EXPECT_TRUE(BlockPlanLayout(kShadowRatio, kShadowRatio, 10, 0, 0, &layout1));
302 E : EXPECT_TRUE(BlockPlanLayout(kShadowRatio, kShadowRatio, 10, 32, 0, &layout2));
303 :
304 E : scoped_ptr<uint8> data(new uint8[layout2.block_size]);
305 E : ::memset(data.get(), 0, layout2.block_size);
306 :
307 : // First recover a block without header padding.
308 E : BlockInfo info = {};
309 E : BlockInitialize(layout1, data.get(), false, &info);
310 E : BlockInfo info_recovered = {};
311 E : EXPECT_TRUE(BlockInfoFromMemory(info.block, &info_recovered));
312 E : EXPECT_EQ(info, info_recovered);
313 : // Failed because its not aligned.
314 E : EXPECT_FALSE(BlockInfoFromMemory(info.block + 1, &info_recovered));
315 : // Failed because the magic is invalid.
316 E : ++info.header->magic;
317 E : EXPECT_FALSE(BlockInfoFromMemory(info.block, &info_recovered));
318 E : --info.header->magic;
319 : // This fails because the header indicates there's padding yet there is
320 : // none.
321 E : info.header->has_header_padding = 1;
322 E : EXPECT_FALSE(BlockInfoFromMemory(info.block, &info_recovered));
323 :
324 : // Now recover a block with header padding.
325 E : BlockInitialize(layout2, data.get(), false, &info);
326 E : EXPECT_TRUE(BlockInfoFromMemory(info.block, &info_recovered));
327 E : EXPECT_EQ(info, info_recovered);
328 : // Failed because the magic is invalid.
329 E : ++info.header->magic;
330 E : EXPECT_FALSE(BlockInfoFromMemory(info.block, &info_recovered));
331 E : --info.header->magic;
332 : // Failed because the header padding lengths don't match.
333 E : uint32* head = reinterpret_cast<uint32*>(info.header_padding);
334 E : uint32* tail = head + (info.header_padding_size / sizeof(uint32)) - 1;
335 E : ++(*tail);
336 E : EXPECT_FALSE(BlockInfoFromMemory(info.block, &info_recovered));
337 E : --(*tail);
338 :
339 : // Finally ensure that we can recover information about blocks of various
340 : // sizes.
341 E : const size_t kAllocSize = 3 * GetPageSize();
342 E : void* alloc = ::VirtualAlloc(NULL, kAllocSize, MEM_COMMIT, PAGE_READWRITE);
343 E : for (size_t block_size = 0; block_size < kShadowRatio * 2; ++block_size) {
344 E : BlockLayout layout = {};
345 : EXPECT_TRUE(BlockPlanLayout(kShadowRatio, kShadowRatio, block_size, 0, 0,
346 E : &layout));
347 E : ASSERT_LE(layout.block_size, kAllocSize);
348 E : BlockInitialize(layout, alloc, false, &info);
349 E : EXPECT_TRUE(BlockInfoFromMemory(info.block, &info_recovered));
350 E : EXPECT_EQ(info.body_size, info_recovered.body_size);
351 E : EXPECT_EQ(info, info_recovered);
352 :
353 : EXPECT_TRUE(BlockPlanLayout(4096, 4096, block_size, 4096, 4096,
354 E : &layout));
355 E : ASSERT_LE(layout.block_size, kAllocSize);
356 E : BlockInitialize(layout, alloc, false, &info);
357 E : EXPECT_TRUE(BlockInfoFromMemory(info.block, &info_recovered));
358 E : EXPECT_EQ(info.body_size, info_recovered.body_size);
359 E : EXPECT_EQ(info, info_recovered);
360 E : }
361 E : ::VirtualFree(alloc, 0, MEM_RELEASE);
362 E : }
363 :
364 E : TEST(BlockTest, BlockInfoFromMemoryInvalidPadding) {
365 E : BlockLayout layout = {};
366 : EXPECT_TRUE(BlockPlanLayout(kShadowRatio, kShadowRatio, 10,
367 E : 4 * sizeof(BlockHeader), 0, &layout));
368 :
369 E : scoped_ptr<uint8> data(new uint8[layout.block_size]);
370 E : ::memset(data.get(), 0, layout.block_size);
371 :
372 E : BlockInfo info = {};
373 E : BlockInitialize(layout, data.get(), false, &info);
374 E : EXPECT_TRUE(info.header->has_header_padding = 1);
375 E : BlockInfo info_recovered = {};
376 E : EXPECT_TRUE(BlockInfoFromMemory(info.block, &info_recovered));
377 E : EXPECT_EQ(info, info_recovered);
378 :
379 : // Invalidates the padding size and make sure that we can't retrieve the block
380 : // information.
381 E : size_t* padding_size = reinterpret_cast<size_t*>(info.header + 1);
382 E : EXPECT_GE(*padding_size, 2 * sizeof(uint32));
383 E : for (*padding_size = 0;
384 E : *padding_size < 2 * sizeof(uint32);
385 E : ++(*padding_size)) {
386 E : EXPECT_FALSE(BlockInfoFromMemory(info.block, &info_recovered));
387 E : }
388 E : }
389 :
390 E : TEST(BlockTest, BlockInfoFromMemoryProtectedMemory) {
391 E : BlockLayout layout = {};
392 E : EXPECT_TRUE(BlockPlanLayout(4096, 4096, 4096, 4096, 4096, &layout));
393 : void* alloc = ::VirtualAlloc(NULL, layout.block_size, MEM_COMMIT,
394 E : PAGE_READWRITE);
395 E : ASSERT_TRUE(alloc != NULL);
396 E : BlockInfo block_info = {};
397 E : BlockInitialize(layout, alloc, false, &block_info);
398 :
399 E : BlockProtectRedzones(block_info);
400 E : BlockInfo recovered_info = {};
401 E : EXPECT_FALSE(BlockInfoFromMemory(block_info.block, &recovered_info));
402 E : BlockProtectNone(block_info);
403 :
404 E : ASSERT_EQ(TRUE, ::VirtualFree(alloc, 0, MEM_RELEASE));
405 E : }
406 :
407 E : TEST(BlockTest, BlockInfoFromMemoryForNestedBlock) {
408 E : BlockLayout layout = {};
409 E : EXPECT_TRUE(BlockPlanLayout(kShadowRatio, kShadowRatio, 10, 0, 0, &layout));
410 :
411 E : scoped_ptr<uint8> data(new uint8[layout.block_size]);
412 E : BlockInfo block_info = {};
413 E : BlockInitialize(layout, data.get(), true, &block_info);
414 :
415 E : BlockInfo recovered_info = {};
416 E : EXPECT_TRUE(BlockInfoFromMemory(block_info.block, &recovered_info));
417 :
418 E : EXPECT_TRUE(recovered_info.is_nested);
419 E : EXPECT_TRUE(recovered_info.header->is_nested);
420 E : }
421 :
422 E : TEST(BlockTest, ChecksumWorksForAllStates) {
423 E : BlockLayout layout = {};
424 E : EXPECT_TRUE(BlockPlanLayout(kShadowRatio, kShadowRatio, 10, 0, 0, &layout));
425 E : scoped_ptr<uint8> data(new uint8[layout.block_size]);
426 E : ::memset(data.get(), 0, layout.block_size);
427 E : BlockInfo info = {};
428 E : BlockInitialize(layout, data.get(), false, &info);
429 E : while (true) {
430 E : BlockCalculateChecksum(info);
431 E : ++info.header->state;
432 E : if (info.header->state == 0)
433 E : break;
434 E : }
435 E : }
436 :
437 : namespace {
438 :
439 : // Given two arrays of data, compares them byte-by-byte to find the first
440 : // byte with altered data. Within that byte determines the mask of bits that
441 : // have been altered. Returns the results via |offset| and |mask|.
442 : void FindModifiedBits(size_t length,
443 : const uint8* buffer1,
444 : const uint8* buffer2,
445 : size_t* offset,
446 E : uint8* mask) {
447 E : ASSERT_TRUE(buffer1 != NULL);
448 E : ASSERT_TRUE(buffer2 != NULL);
449 E : ASSERT_TRUE(offset != NULL);
450 E : ASSERT_TRUE(mask != NULL);
451 :
452 E : for (size_t i = 0; i < length; ++i) {
453 E : if (buffer1[i] != buffer2[i]) {
454 E : *offset = i;
455 E : *mask = buffer1[i] ^ buffer2[i];
456 E : return;
457 : }
458 E : }
459 :
460 i : *offset = 0;
461 i : *mask = 0;
462 E : }
463 :
464 : // This is initialized by TestChecksumDetectsTampering, but referred to by
465 : // ChecksumDetectsTamperingWithMask as well, hence not in a function.
466 : size_t state_offset = -1;
467 : uint8 state_mask = 0;
468 :
469 : bool ChecksumDetectsTamperingWithMask(const BlockInfo& block_info,
470 : void* address_to_modify,
471 E : uint8 mask_to_modify) {
472 E : uint8* byte_to_modify = reinterpret_cast<uint8*>(address_to_modify);
473 :
474 : // Remember the original contents.
475 E : uint8 original_value = *byte_to_modify;
476 E : uint8 original_bits = original_value & ~mask_to_modify;
477 :
478 : // Since the checksum can collide we check a handful of times to build up
479 : // some confidence. Since we sometimes expect this to return false the number
480 : // of iterations needs to be kept reasonably low to keep the unittest fast.
481 E : bool detected = false;
482 E : BlockSetChecksum(block_info);
483 E : uint32 checksum = block_info.header->checksum;
484 E : for (size_t i = 0; i < 4; ++i) {
485 : // Modify the value, altering only bits in |mask_to_modify|.
486 E : while (true) {
487 E : ++(*byte_to_modify);
488 E : if (((*byte_to_modify) & ~mask_to_modify) == original_bits)
489 E : break;
490 i : }
491 E : BlockSetChecksum(block_info);
492 E : if (block_info.header->checksum != checksum) {
493 : // Success, the checksum detected the change!
494 : // Restore the original checksum so the block analysis can continue.
495 E : block_info.header->checksum = checksum;
496 E : detected = true;
497 E : break;
498 : }
499 E : }
500 :
501 : // Run a detailed analysis on the block. We expect the results of this to
502 : // agree with where the block was modified.
503 E : BlockAnalysisResult result = {};
504 E : BlockAnalyze(block_info, &result);
505 E : if (address_to_modify < block_info.body) {
506 E : EXPECT_EQ(kDataIsCorrupt, result.block_state);
507 : // If the thing being modified is the block state, then this is so
508 : // localized that the analysis will sometimes mess up. Seeing this in
509 : // the wild is quite unlikely.
510 : // TODO(chrisha): If we ever have individual checksums for the header
511 : // the body and the trailer, then revisit this.
512 : if (address_to_modify != block_info.block + state_offset ||
513 E : mask_to_modify != state_mask) {
514 E : EXPECT_EQ(kDataIsCorrupt, result.header_state);
515 E : EXPECT_EQ(kDataStateUnknown, result.body_state);
516 E : EXPECT_EQ(kDataIsClean, result.trailer_state);
517 E : }
518 E : } else if (address_to_modify >= block_info.trailer_padding) {
519 E : EXPECT_EQ(kDataIsCorrupt, result.block_state);
520 E : EXPECT_EQ(kDataIsClean, result.header_state);
521 E : EXPECT_EQ(kDataStateUnknown, result.body_state);
522 E : EXPECT_EQ(kDataIsCorrupt, result.trailer_state);
523 E : } else {
524 : // The byte being modified is in the body. Only expect to find
525 : // tampering if the block is quarantined or freed.
526 E : if (block_info.header->state != ALLOCATED_BLOCK) {
527 E : EXPECT_EQ(kDataIsCorrupt, result.block_state);
528 E : EXPECT_EQ(kDataIsClean, result.header_state);
529 E : EXPECT_EQ(kDataIsCorrupt, result.body_state);
530 E : EXPECT_EQ(kDataIsClean, result.trailer_state);
531 E : } else {
532 E : EXPECT_EQ(kDataIsClean, result.block_state);
533 E : EXPECT_EQ(kDataIsClean, result.header_state);
534 E : EXPECT_EQ(kDataIsClean, result.body_state);
535 E : EXPECT_EQ(kDataIsClean, result.trailer_state);
536 : }
537 : }
538 :
539 : // Restore the original value before returning.
540 E : *byte_to_modify = original_value;
541 E : return detected;
542 E : }
543 :
544 : bool ChecksumDetectsTampering(const BlockInfo& block_info,
545 E : void* address_to_modify) {
546 E : if (!ChecksumDetectsTamperingWithMask(block_info, address_to_modify, 0xFF))
547 E : return false;
548 E : return true;
549 E : }
550 :
551 E : void TestChecksumDetectsTampering(const BlockInfo& block_info) {
552 E : uint32 checksum = BlockCalculateChecksum(block_info);
553 E : block_info.header->checksum = checksum;
554 E : EXPECT_TRUE(BlockChecksumIsValid(block_info));
555 E : ++block_info.header->checksum;
556 E : EXPECT_FALSE(BlockChecksumIsValid(block_info));
557 E : BlockSetChecksum(block_info);
558 E : EXPECT_EQ(checksum, block_info.header->checksum);
559 :
560 : // A detailed block analysis should find nothing awry.
561 E : BlockAnalysisResult result = {};
562 E : BlockAnalyze(block_info, &result);
563 E : EXPECT_EQ(kDataIsClean, result.block_state);
564 E : EXPECT_EQ(kDataIsClean, result.header_state);
565 E : EXPECT_EQ(kDataIsClean, result.body_state);
566 E : EXPECT_EQ(kDataIsClean, result.trailer_state);
567 :
568 : // Get the offset of the byte and the mask of the bits containing the
569 : // block state. This is resilient to changes in the BlockHeader layout.
570 E : if (state_offset == -1) {
571 E : BlockHeader header1 = {};
572 E : BlockHeader header2 = {};
573 E : header2.state = -1;
574 : FindModifiedBits(sizeof(BlockHeader),
575 : reinterpret_cast<const uint8*>(&header1),
576 : reinterpret_cast<const uint8*>(&header2),
577 : &state_offset,
578 E : &state_mask);
579 : }
580 :
581 : // Header bytes should be tamper proof.
582 E : EXPECT_TRUE(ChecksumDetectsTampering(block_info, block_info.header));
583 : EXPECT_TRUE(ChecksumDetectsTampering(block_info,
584 E : &block_info.header->alloc_stack));
585 : EXPECT_TRUE(ChecksumDetectsTamperingWithMask(
586 : block_info,
587 : block_info.block + state_offset,
588 E : state_mask));
589 :
590 : // Header padding should be tamper proof.
591 E : if (block_info.header_padding_size > 0) {
592 : EXPECT_TRUE(ChecksumDetectsTampering(block_info,
593 E : block_info.header_padding + block_info.header_padding_size / 2));
594 : }
595 :
596 : // Trailer padding should be tamper proof.
597 E : if (block_info.trailer_padding_size > 0) {
598 : EXPECT_TRUE(ChecksumDetectsTampering(block_info,
599 E : block_info.trailer_padding + block_info.trailer_padding_size / 2));
600 : }
601 :
602 : // Trailer bytes should be tamper proof.
603 E : EXPECT_TRUE(ChecksumDetectsTampering(block_info, block_info.trailer));
604 : EXPECT_TRUE(ChecksumDetectsTampering(block_info,
605 E : &block_info.trailer->heap_id));
606 :
607 : // Expect the checksum to detect body tampering in quarantined and freed
608 : // states, but not in the allocated state.
609 E : bool expected = (block_info.header->state != ALLOCATED_BLOCK);
610 E : EXPECT_EQ(expected, ChecksumDetectsTampering(block_info, block_info.body));
611 : EXPECT_EQ(expected, ChecksumDetectsTampering(block_info,
612 E : block_info.body + block_info.body_size / 2));
613 : EXPECT_EQ(expected, ChecksumDetectsTampering(block_info,
614 E : block_info.body + block_info.body_size - 1));
615 E : }
616 :
617 : } // namespace
618 :
619 E : TEST(BlockTest, ChecksumDetectsTampering) {
620 : // This test requires a runtime because it makes use of BlockAnalyze.
621 : // Initialize it with valid values.
622 E : AsanRuntime runtime;
623 E : ASSERT_NO_FATAL_FAILURE(runtime.SetUp(L""));
624 E : HeapManagerInterface::HeapId valid_heap_id = runtime.GetProcessHeap();
625 E : runtime.AddThreadId(::GetCurrentThreadId());
626 E : common::StackCapture capture;
627 E : capture.InitFromStack();
628 : const common::StackCapture* valid_stack =
629 E : runtime.stack_cache()->SaveStackTrace(capture);
630 :
631 E : size_t kSizes[] = { 1, 4, 7, 16, 23, 32, 117, 1000, 4096 };
632 :
633 : // Doing a single allocation makes this test a bit faster.
634 E : size_t kAllocSize = 4 * 4096;
635 E : void* alloc = ::VirtualAlloc(NULL, kAllocSize, MEM_COMMIT, PAGE_READWRITE);
636 E : ASSERT_TRUE(alloc != NULL);
637 :
638 : // We test 9 different sizes, 9 different chunk sizes, 1 to 9 different
639 : // alignments, and 2 different redzone sizes. This is 810 different
640 : // combinations. We test each of these block allocations in all 3 possible
641 : // states. The probe itself tests the block at 7 to 9 different points, and
642 : // the tests require multiple iterations. Be careful playing with these
643 : // constants or the unittest time can easily spiral out of control! This
644 : // currently requires less than half a second, and is strictly CPU bound.
645 E : for (size_t chunk_size = kShadowRatio; chunk_size <= GetPageSize();
646 E : chunk_size *= 2) {
647 E : for (size_t align = kShadowRatio; align <= chunk_size; align *= 2) {
648 E : for (size_t redzone = 0; redzone <= chunk_size; redzone += chunk_size) {
649 E : for (size_t i = 0; i < arraysize(kSizes); ++i) {
650 E : BlockLayout layout = {};
651 : EXPECT_TRUE(BlockPlanLayout(chunk_size, align, kSizes[i], redzone,
652 E : redzone, &layout));
653 E : ASSERT_GT(kAllocSize, layout.block_size);
654 :
655 E : BlockInfo block_info = {};
656 E : BlockInitialize(layout, alloc, false, &block_info);
657 E : block_info.header->alloc_stack = valid_stack;
658 E : block_info.trailer->heap_id = valid_heap_id;
659 :
660 : // Test that the checksum detects tampering as expected in each block
661 : // state.
662 E : block_info.header->state = ALLOCATED_BLOCK;
663 E : ASSERT_NO_FATAL_FAILURE(TestChecksumDetectsTampering(block_info));
664 :
665 E : block_info.header->state = QUARANTINED_BLOCK;
666 E : block_info.header->free_stack = valid_stack;
667 E : block_info.trailer->free_tid = ::GetCurrentThreadId();
668 E : block_info.trailer->free_ticks = ::GetTickCount();
669 E : ASSERT_NO_FATAL_FAILURE(TestChecksumDetectsTampering(block_info));
670 :
671 E : block_info.header->state = FREED_BLOCK;
672 E : ASSERT_NO_FATAL_FAILURE(TestChecksumDetectsTampering(block_info));
673 E : } // kSizes[i]
674 E : } // redzone
675 E : } // align
676 E : } // chunk_size
677 :
678 E : ASSERT_EQ(TRUE, ::VirtualFree(alloc, 0, MEM_RELEASE));
679 E : ASSERT_NO_FATAL_FAILURE(runtime.TearDown());
680 E : }
681 :
682 : } // namespace asan
683 : } // namespace agent
|