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/error_info.h"
16 :
17 : #include <windows.h>
18 :
19 : #include <string>
20 :
21 : #include "base/strings/stringprintf.h"
22 : #include "gtest/gtest.h"
23 : #include "syzygy/agent/asan/unittest_util.h"
24 : #include "syzygy/crashdata/json.h"
25 :
26 : namespace agent {
27 : namespace asan {
28 :
29 : namespace {
30 :
31 : class AsanErrorInfoTest : public testing::TestWithAsanRuntime {
32 : public:
33 : typedef testing::TestWithAsanRuntime Super;
34 E : void SetUp() override {
35 E : Super::SetUp();
36 E : }
37 :
38 E : void TearDown() override {
39 : // Clean up the fake asan block if there is one.
40 : // TODO(chrisha): Migrate this to using a dynamic shadow.
41 E : if (!dummy_block_data_.empty()) {
42 : runtime_->shadow()->Unpoison(dummy_block_data_.data(),
43 E : dummy_block_data_.size());
44 : }
45 :
46 E : Super::TearDown();
47 E : }
48 :
49 E : void InitAsanBlockInfo(AsanBlockInfo* block_info) {
50 E : if (dummy_block_data_.empty()) {
51 : // Create a dummy block to physically back the report.
52 E : BlockLayout layout = {};
53 E : BlockPlanLayout(kShadowRatio, kShadowRatio, 8, 0, 0, &layout);
54 E : dummy_block_data_.resize(layout.block_size);
55 E : BlockInfo info = {};
56 E : BlockInitialize(layout, &dummy_block_data_.at(0), false, &info);
57 E : runtime_->shadow()->PoisonAllocatedBlock(info);
58 :
59 : // Normalize a handful of fields to make the comparison simpler.
60 E : info.trailer->alloc_ticks = 0;
61 E : info.trailer->alloc_tid = 0;
62 : }
63 :
64 E : block_info->header = &dummy_block_data_.at(0);
65 E : block_info->user_size = 8;
66 E : block_info->state = ALLOCATED_BLOCK;
67 E : block_info->alloc_tid = 47;
68 E : block_info->analysis.block_state = kDataIsCorrupt;
69 E : block_info->analysis.header_state = kDataIsCorrupt;
70 E : block_info->analysis.body_state = kDataStateUnknown;
71 E : block_info->analysis.trailer_state = kDataIsClean;
72 E : block_info->alloc_stack[0] = reinterpret_cast<void*>(1);
73 E : block_info->alloc_stack[1] = reinterpret_cast<void*>(2);
74 E : block_info->alloc_stack_size = 2;
75 E : block_info->heap_type = kWinHeap;
76 E : }
77 :
78 E : const void* BlockShadowAddress() {
79 : return runtime_->shadow()->shadow() +
80 E : reinterpret_cast<uintptr_t>(dummy_block_data_.data()) / kShadowRatio;
81 E : }
82 :
83 : private:
84 : std::vector<unsigned char> dummy_block_data_;
85 : };
86 :
87 :
88 : } // namespace
89 :
90 E : TEST_F(AsanErrorInfoTest, ErrorInfoAccessTypeToStr) {
91 E : EXPECT_EQ(kHeapUseAfterFree, ErrorInfoAccessTypeToStr(USE_AFTER_FREE));
92 : EXPECT_EQ(kHeapBufferUnderFlow,
93 E : ErrorInfoAccessTypeToStr(HEAP_BUFFER_UNDERFLOW));
94 : EXPECT_EQ(kHeapBufferOverFlow,
95 E : ErrorInfoAccessTypeToStr(HEAP_BUFFER_OVERFLOW));
96 E : EXPECT_EQ(kAttemptingDoubleFree, ErrorInfoAccessTypeToStr(DOUBLE_FREE));
97 E : EXPECT_EQ(kInvalidAddress, ErrorInfoAccessTypeToStr(INVALID_ADDRESS));
98 E : EXPECT_EQ(kWildAccess, ErrorInfoAccessTypeToStr(WILD_ACCESS));
99 E : EXPECT_EQ(kHeapUnknownError, ErrorInfoAccessTypeToStr(UNKNOWN_BAD_ACCESS));
100 E : EXPECT_EQ(kHeapCorruptBlock, ErrorInfoAccessTypeToStr(CORRUPT_BLOCK));
101 E : EXPECT_EQ(kCorruptHeap, ErrorInfoAccessTypeToStr(CORRUPT_HEAP));
102 E : }
103 :
104 E : TEST_F(AsanErrorInfoTest, ErrorInfoGetBadAccessInformation) {
105 : testing::FakeAsanBlock fake_block(
106 E : runtime_->shadow(), kShadowRatioLog, runtime_->stack_cache());
107 E : const size_t kAllocSize = 100;
108 E : EXPECT_TRUE(fake_block.InitializeBlock(kAllocSize));
109 :
110 E : AsanErrorInfo error_info = {};
111 E : error_info.location = fake_block.block_info.RawBody() + kAllocSize + 1;
112 : EXPECT_TRUE(ErrorInfoGetBadAccessInformation(runtime_->shadow(),
113 : runtime_->stack_cache(),
114 E : &error_info));
115 E : EXPECT_EQ(HEAP_BUFFER_OVERFLOW, error_info.error_type);
116 E : EXPECT_EQ(kUnknownHeapType, error_info.block_info.heap_type);
117 :
118 E : EXPECT_TRUE(fake_block.MarkBlockAsQuarantined());
119 E : error_info.location = fake_block.block_info.body;
120 : EXPECT_TRUE(ErrorInfoGetBadAccessInformation(runtime_->shadow(),
121 : runtime_->stack_cache(),
122 E : &error_info));
123 E : EXPECT_EQ(USE_AFTER_FREE, error_info.error_type);
124 E : EXPECT_EQ(kUnknownHeapType, error_info.block_info.heap_type);
125 :
126 E : error_info.location = fake_block.buffer_align_begin - 1;
127 : EXPECT_FALSE(ErrorInfoGetBadAccessInformation(runtime_->shadow(),
128 : runtime_->stack_cache(),
129 E : &error_info));
130 E : }
131 :
132 E : TEST_F(AsanErrorInfoTest, GetBadAccessInformationNestedBlock) {
133 : // Test a nested use after free. We allocate an outer block and an inner block
134 : // inside it, then we mark the outer block as quarantined and we test a bad
135 : // access inside the inner block.
136 :
137 : testing::FakeAsanBlock fake_block(
138 E : runtime_->shadow(), kShadowRatioLog, runtime_->stack_cache());
139 E : const size_t kInnerBlockAllocSize = 100;
140 :
141 : // Allocates the outer block.
142 E : BlockLayout outer_block_layout = {};
143 : EXPECT_TRUE(BlockPlanLayout(kShadowRatio, kShadowRatio, kInnerBlockAllocSize,
144 E : 0, 0, &outer_block_layout));
145 E : EXPECT_TRUE(fake_block.InitializeBlock(outer_block_layout.block_size));
146 :
147 E : common::StackCapture stack;
148 E : stack.InitFromStack();
149 :
150 : // Initializes the inner block.
151 E : BlockLayout inner_block_layout = {};
152 : EXPECT_TRUE(BlockPlanLayout(kShadowRatio,
153 : kShadowRatio,
154 : kInnerBlockAllocSize,
155 : 0,
156 : 0,
157 E : &inner_block_layout));
158 E : BlockInfo inner_block_info = {};
159 : BlockInitialize(inner_block_layout, fake_block.block_info.body, true,
160 E : &inner_block_info);
161 E : ASSERT_NE(reinterpret_cast<void*>(NULL), inner_block_info.body);
162 E : runtime_->shadow()->PoisonAllocatedBlock(inner_block_info);
163 : inner_block_info.header->alloc_stack =
164 E : runtime_->stack_cache()->SaveStackTrace(stack);
165 E : BlockHeader* inner_header = inner_block_info.header;
166 : BlockHeader* outer_header = reinterpret_cast<BlockHeader*>(
167 E : fake_block.buffer_align_begin);
168 :
169 E : AsanErrorInfo error_info = {};
170 :
171 : // Mark the inner block as quarantined and check that we detect a use after
172 : // free when trying to access its data.
173 : inner_block_info.header->free_stack =
174 E : runtime_->stack_cache()->SaveStackTrace(stack);
175 E : EXPECT_NE(reinterpret_cast<void*>(NULL), inner_header->free_stack);
176 E : inner_header->state = QUARANTINED_BLOCK;
177 :
178 E : error_info.location = fake_block.block_info.body;
179 : EXPECT_TRUE(ErrorInfoGetBadAccessInformation(runtime_->shadow(),
180 : runtime_->stack_cache(),
181 E : &error_info));
182 E : EXPECT_EQ(USE_AFTER_FREE, error_info.error_type);
183 E : EXPECT_NE(reinterpret_cast<void*>(NULL), error_info.block_info.free_stack);
184 E : EXPECT_EQ(kUnknownHeapType, error_info.block_info.heap_type);
185 :
186 : EXPECT_EQ(inner_header->free_stack->num_frames(),
187 E : error_info.block_info.free_stack_size);
188 E : for (size_t i = 0; i < inner_header->free_stack->num_frames(); ++i) {
189 : EXPECT_EQ(inner_header->free_stack->frames()[i],
190 E : error_info.block_info.free_stack[i]);
191 E : }
192 :
193 : // Mark the outer block as quarantined, we should detect a use after free
194 : // when trying to access the data of the inner block, and the free stack
195 : // should be the one of the inner block.
196 E : EXPECT_TRUE(fake_block.MarkBlockAsQuarantined());
197 E : EXPECT_NE(ALLOCATED_BLOCK, static_cast<BlockState>(outer_header->state));
198 E : EXPECT_NE(reinterpret_cast<void*>(NULL), outer_header->free_stack);
199 :
200 : // Tests an access in the inner block.
201 E : error_info.location = inner_block_info.body;
202 : EXPECT_TRUE(ErrorInfoGetBadAccessInformation(runtime_->shadow(),
203 : runtime_->stack_cache(),
204 E : &error_info));
205 E : EXPECT_EQ(USE_AFTER_FREE, error_info.error_type);
206 E : EXPECT_NE(reinterpret_cast<void*>(NULL), error_info.block_info.free_stack);
207 E : EXPECT_EQ(kUnknownHeapType, error_info.block_info.heap_type);
208 :
209 : EXPECT_EQ(inner_header->free_stack->num_frames(),
210 E : error_info.block_info.free_stack_size);
211 E : for (size_t i = 0; i < inner_header->free_stack->num_frames(); ++i) {
212 : EXPECT_EQ(inner_header->free_stack->frames()[i],
213 E : error_info.block_info.free_stack[i]);
214 E : }
215 E : }
216 :
217 E : TEST_F(AsanErrorInfoTest, ErrorInfoGetBadAccessKind) {
218 E : const size_t kAllocSize = 100;
219 : testing::FakeAsanBlock fake_block(
220 E : runtime_->shadow(), kShadowRatioLog, runtime_->stack_cache());
221 E : EXPECT_TRUE(fake_block.InitializeBlock(kAllocSize));
222 E : uint8* heap_underflow_address = fake_block.block_info.RawBody() - 1;
223 : uint8* heap_overflow_address = fake_block.block_info.RawBody() +
224 E : kAllocSize * sizeof(uint8);
225 : EXPECT_EQ(HEAP_BUFFER_UNDERFLOW,
226 : ErrorInfoGetBadAccessKind(runtime_->shadow(),
227 : heap_underflow_address,
228 E : fake_block.block_info.header));
229 : EXPECT_EQ(HEAP_BUFFER_OVERFLOW,
230 : ErrorInfoGetBadAccessKind(runtime_->shadow(),
231 : heap_overflow_address,
232 E : fake_block.block_info.header));
233 E : EXPECT_TRUE(fake_block.MarkBlockAsQuarantined());
234 : EXPECT_EQ(USE_AFTER_FREE, ErrorInfoGetBadAccessKind(runtime_->shadow(),
235 E : fake_block.block_info.body, fake_block.block_info.header));
236 E : }
237 :
238 E : TEST_F(AsanErrorInfoTest, ErrorInfoGetAsanBlockInfo) {
239 E : const size_t kAllocSize = 100;
240 : testing::FakeAsanBlock fake_block(
241 E : runtime_->shadow(), kShadowRatioLog, runtime_->stack_cache());
242 E : EXPECT_TRUE(fake_block.InitializeBlock(kAllocSize));
243 :
244 E : AsanBlockInfo asan_block_info = {};
245 : ErrorInfoGetAsanBlockInfo(runtime_->shadow(), fake_block.block_info,
246 E : runtime_->stack_cache(), &asan_block_info);
247 :
248 : // Test ErrorInfoGetAsanBlockInfo with an allocated block.
249 E : EXPECT_EQ(fake_block.block_info.body_size, asan_block_info.user_size);
250 E : EXPECT_EQ(ALLOCATED_BLOCK, static_cast<BlockState>(asan_block_info.state));
251 : EXPECT_EQ(fake_block.block_info.header->state,
252 E : static_cast<BlockState>(asan_block_info.state));
253 E : EXPECT_EQ(::GetCurrentThreadId(), asan_block_info.alloc_tid);
254 E : EXPECT_EQ(0, asan_block_info.free_tid);
255 E : EXPECT_EQ(kDataIsClean, asan_block_info.analysis.block_state);
256 : EXPECT_EQ(fake_block.block_info.header->alloc_stack->num_frames(),
257 E : asan_block_info.alloc_stack_size);
258 E : EXPECT_EQ(0, asan_block_info.free_stack_size);
259 E : EXPECT_EQ(kUnknownHeapType, asan_block_info.heap_type);
260 :
261 : // Now test it with a quarantined block.
262 E : EXPECT_TRUE(fake_block.MarkBlockAsQuarantined());
263 : ErrorInfoGetAsanBlockInfo(runtime_->shadow(), fake_block.block_info,
264 E : runtime_->stack_cache(), &asan_block_info);
265 E : EXPECT_EQ(QUARANTINED_BLOCK, static_cast<BlockState>(asan_block_info.state));
266 : EXPECT_EQ(fake_block.block_info.header->state,
267 E : static_cast<BlockState>(asan_block_info.state));
268 E : EXPECT_EQ(::GetCurrentThreadId(), asan_block_info.free_tid);
269 : EXPECT_EQ(fake_block.block_info.header->free_stack->num_frames(),
270 E : asan_block_info.free_stack_size);
271 E : EXPECT_EQ(kUnknownHeapType, asan_block_info.heap_type);
272 :
273 : // Ensure that the block is correctly tagged as corrupt if the header is
274 : // invalid.
275 : fake_block.block_info.header->magic =
276 E : static_cast<unsigned>(~kBlockHeaderMagic);
277 : ErrorInfoGetAsanBlockInfo(runtime_->shadow(), fake_block.block_info,
278 E : runtime_->stack_cache(), &asan_block_info);
279 E : EXPECT_EQ(kDataIsCorrupt, asan_block_info.analysis.block_state);
280 : fake_block.block_info.header->magic =
281 E : static_cast<unsigned>(~kBlockHeaderMagic);
282 E : }
283 :
284 E : TEST_F(AsanErrorInfoTest, GetTimeSinceFree) {
285 E : const size_t kAllocSize = 100;
286 E : const size_t kSleepTime = 25;
287 : testing::FakeAsanBlock fake_block(
288 E : runtime_->shadow(), kShadowRatioLog, runtime_->stack_cache());
289 E : EXPECT_TRUE(fake_block.InitializeBlock(kAllocSize));
290 :
291 E : uint32 ticks_before_free = ::GetTickCount();
292 E : EXPECT_TRUE(fake_block.MarkBlockAsQuarantined());
293 E : ::Sleep(kSleepTime);
294 E : AsanErrorInfo error_info = {};
295 E : error_info.error_type = USE_AFTER_FREE;
296 E : error_info.location = fake_block.block_info.body;
297 : EXPECT_TRUE(ErrorInfoGetBadAccessInformation(runtime_->shadow(),
298 : runtime_->stack_cache(),
299 E : &error_info));
300 E : EXPECT_NE(0U, error_info.block_info.milliseconds_since_free);
301 :
302 E : uint32 ticks_delta = ::GetTickCount() - ticks_before_free;
303 E : EXPECT_GT(ticks_delta, 0U);
304 :
305 E : EXPECT_GE(ticks_delta, error_info.block_info.milliseconds_since_free);
306 E : }
307 :
308 E : TEST_F(AsanErrorInfoTest, PopulateBlockInfo) {
309 E : AsanBlockInfo block_info = {};
310 E : InitAsanBlockInfo(&block_info);
311 :
312 : {
313 E : crashdata::Value info;
314 E : PopulateBlockInfo(runtime_->shadow(), block_info, false, &info, nullptr);
315 E : std::string json;
316 E : EXPECT_TRUE(crashdata::ToJson(true, &info, &json));
317 : const char kExpected[] =
318 : "{\n"
319 : " \"header\": \"0x%08X\",\n"
320 : " \"user-size\": 8,\n"
321 : " \"state\": \"allocated\",\n"
322 : " \"heap-type\": \"WinHeap\",\n"
323 : " \"analysis\": {\n"
324 : " \"block\": \"corrupt\",\n"
325 : " \"header\": \"corrupt\",\n"
326 : " \"body\": \"(unknown)\",\n"
327 : " \"trailer\": \"clean\"\n"
328 : " },\n"
329 : " \"alloc-thread-id\": 47,\n"
330 : " \"alloc-stack\": [\n"
331 : " \"0x00000001\", \"0x00000002\"\n"
332 : " ]\n"
333 E : "}";
334 : std::string expected = base::StringPrintf(
335 E : kExpected, block_info.header);
336 E : EXPECT_EQ(expected, json);
337 E : }
338 :
339 : {
340 E : block_info.state = QUARANTINED_FLOODED_BLOCK;
341 E : block_info.free_tid = 32;
342 E : block_info.free_stack[0] = reinterpret_cast<void*>(3);
343 E : block_info.free_stack[1] = reinterpret_cast<void*>(4);
344 E : block_info.free_stack[2] = reinterpret_cast<void*>(5);
345 E : block_info.free_stack_size = 3;
346 E : block_info.heap_type = kWinHeap;
347 E : block_info.milliseconds_since_free = 100;
348 :
349 E : crashdata::Value value;
350 E : PopulateBlockInfo(runtime_->shadow(), block_info, true, &value, nullptr);
351 E : std::string json;
352 E : EXPECT_TRUE(crashdata::ToJson(true, &value, &json));
353 : const char kExpected[] =
354 : "{\n"
355 : " \"header\": \"0x%08X\",\n"
356 : " \"user-size\": 8,\n"
357 : " \"state\": \"quarantined (flooded)\",\n"
358 : " \"heap-type\": \"WinHeap\",\n"
359 : " \"analysis\": {\n"
360 : " \"block\": \"corrupt\",\n"
361 : " \"header\": \"corrupt\",\n"
362 : " \"body\": \"(unknown)\",\n"
363 : " \"trailer\": \"clean\"\n"
364 : " },\n"
365 : " \"alloc-thread-id\": 47,\n"
366 : " \"alloc-stack\": [\n"
367 : " \"0x00000001\", \"0x00000002\"\n"
368 : " ],\n"
369 : " \"free-thread-id\": 32,\n"
370 : " \"free-stack\": [\n"
371 : " \"0x00000003\", \"0x00000004\", \"0x00000005\"\n"
372 : " ],\n"
373 : " \"milliseconds-since-free\": 100,\n"
374 : " \"contents\": {\n"
375 : " \"type\": \"blob\",\n"
376 : " \"address\": \"0x%08X\",\n"
377 : " \"size\": null,\n"
378 : " \"data\": [\n"
379 : " \"0x80\", \"0xCA\", \"0x00\", \"0x00\", \"0x20\", \"0x00\","
380 : " \"0x00\", \"0x00\",\n"
381 : " \"0x00\", \"0x00\", \"0x00\", \"0x00\", \"0x00\", \"0x00\","
382 : " \"0x00\", \"0x00\",\n"
383 : " \"0x00\", \"0x00\", \"0x00\", \"0x00\", \"0x00\", \"0x00\","
384 : " \"0x00\", \"0x00\",\n"
385 : " \"0xC3\", \"0xC3\", \"0xC3\", \"0xC3\", \"0x00\", \"0x00\","
386 : " \"0x00\", \"0x00\",\n"
387 : " \"0x00\", \"0x00\", \"0x00\", \"0x00\", \"0x00\", \"0x00\","
388 : " \"0x00\", \"0x00\",\n"
389 : " \"0x00\", \"0x00\", \"0x00\", \"0x00\", \"0x00\", \"0x00\","
390 : " \"0x00\", \"0x00\"\n"
391 : " ]\n"
392 : " },\n"
393 : " \"shadow\": {\n"
394 : " \"type\": \"blob\",\n"
395 : " \"address\": \"0x%08X\",\n"
396 : " \"size\": null,\n"
397 : " \"data\": [\n"
398 : " \"0xE0\", \"0xFA\", \"0x00\", \"0xFB\", \"0xFB\", \"0xF4\"\n"
399 : " ]\n"
400 : " }\n"
401 E : "}";
402 : std::string expected = base::StringPrintf(
403 : kExpected,
404 : block_info.header,
405 : block_info.header,
406 E : BlockShadowAddress());
407 E : EXPECT_EQ(expected, json);
408 E : }
409 E : }
410 :
411 E : TEST_F(AsanErrorInfoTest, PopulateBlockInfoWithMemoryRanges) {
412 E : AsanBlockInfo block_info = {};
413 E : InitAsanBlockInfo(&block_info);
414 :
415 : {
416 E : crashdata::Value info;
417 E : PopulateBlockInfo(runtime_->shadow(), block_info, false, &info, nullptr);
418 E : std::string json;
419 E : EXPECT_TRUE(crashdata::ToJson(true, &info, &json));
420 : const char kExpected[] =
421 : "{\n"
422 : " \"header\": \"0x%08X\",\n"
423 : " \"user-size\": 8,\n"
424 : " \"state\": \"allocated\",\n"
425 : " \"heap-type\": \"WinHeap\",\n"
426 : " \"analysis\": {\n"
427 : " \"block\": \"corrupt\",\n"
428 : " \"header\": \"corrupt\",\n"
429 : " \"body\": \"(unknown)\",\n"
430 : " \"trailer\": \"clean\"\n"
431 : " },\n"
432 : " \"alloc-thread-id\": 47,\n"
433 : " \"alloc-stack\": [\n"
434 : " \"0x00000001\", \"0x00000002\"\n"
435 : " ]\n"
436 E : "}";
437 E : std::string expected = base::StringPrintf(kExpected, block_info.header);
438 E : EXPECT_EQ(expected, json);
439 E : }
440 :
441 : {
442 E : block_info.state = QUARANTINED_FLOODED_BLOCK;
443 E : block_info.free_tid = 32;
444 E : block_info.free_stack[0] = reinterpret_cast<void*>(3);
445 E : block_info.free_stack[1] = reinterpret_cast<void*>(4);
446 E : block_info.free_stack[2] = reinterpret_cast<void*>(5);
447 E : block_info.free_stack_size = 3;
448 E : block_info.heap_type = kWinHeap;
449 E : block_info.milliseconds_since_free = 100;
450 :
451 E : crashdata::Value value;
452 E : MemoryRanges memory_ranges;
453 : PopulateBlockInfo(runtime_->shadow(), block_info, true, &value,
454 E : &memory_ranges);
455 E : std::string json;
456 E : EXPECT_TRUE(crashdata::ToJson(true, &value, &json));
457 : const char kExpected[] =
458 : "{\n"
459 : " \"header\": \"0x%08X\",\n"
460 : " \"user-size\": 8,\n"
461 : " \"state\": \"quarantined (flooded)\",\n"
462 : " \"heap-type\": \"WinHeap\",\n"
463 : " \"analysis\": {\n"
464 : " \"block\": \"corrupt\",\n"
465 : " \"header\": \"corrupt\",\n"
466 : " \"body\": \"(unknown)\",\n"
467 : " \"trailer\": \"clean\"\n"
468 : " },\n"
469 : " \"alloc-thread-id\": 47,\n"
470 : " \"alloc-stack\": [\n"
471 : " \"0x00000001\", \"0x00000002\"\n"
472 : " ],\n"
473 : " \"free-thread-id\": 32,\n"
474 : " \"free-stack\": [\n"
475 : " \"0x00000003\", \"0x00000004\", \"0x00000005\"\n"
476 : " ],\n"
477 : " \"milliseconds-since-free\": 100,\n"
478 : " \"contents\": {\n"
479 : " \"type\": \"blob\",\n"
480 : " \"address\": \"0x%08X\",\n"
481 : " \"size\": 48,\n"
482 : " \"data\": null\n"
483 : " },\n"
484 : " \"shadow\": {\n"
485 : " \"type\": \"blob\",\n"
486 : " \"address\": \"0x%08X\",\n"
487 : " \"size\": 6,\n"
488 : " \"data\": null\n"
489 : " }\n"
490 E : "}";
491 : std::string expected = base::StringPrintf(
492 E : kExpected, block_info.header, block_info.header, BlockShadowAddress());
493 E : EXPECT_EQ(expected, json);
494 :
495 E : ASSERT_EQ(2, memory_ranges.size());
496 : const char* kExpectedMemoryRangesAddresses[] = {
497 : reinterpret_cast<const char*>(block_info.header),
498 E : reinterpret_cast<const char*>(BlockShadowAddress())};
499 E : size_t kExpectedMemoryRangesSize[] = {48, 6};
500 E : for (int i = 0; i < 2; i++) {
501 E : EXPECT_EQ(kExpectedMemoryRangesAddresses[i], memory_ranges[i].first);
502 E : EXPECT_EQ(kExpectedMemoryRangesSize[i], memory_ranges[i].second);
503 E : }
504 E : }
505 E : }
506 :
507 E : TEST_F(AsanErrorInfoTest, PopulateCorruptBlockRange) {
508 E : AsanBlockInfo block_info = {};
509 E : InitAsanBlockInfo(&block_info);
510 :
511 E : AsanCorruptBlockRange range = {};
512 E : range.address = reinterpret_cast<void*>(0xBAADF00D);
513 E : range.length = 1024 * 1024;
514 E : range.block_count = 100;
515 E : range.block_info_count = 1;
516 E : range.block_info = &block_info;
517 :
518 E : crashdata::Value info;
519 E : PopulateCorruptBlockRange(runtime_->shadow(), range, &info, nullptr);
520 :
521 E : std::string json;
522 E : EXPECT_TRUE(crashdata::ToJson(true, &info, &json));
523 : const char kExpected[] =
524 : "{\n"
525 : " \"address\": \"0xBAADF00D\",\n"
526 : " \"length\": 1048576,\n"
527 : " \"block-count\": 100,\n"
528 : " \"blocks\": [\n"
529 : " {\n"
530 : " \"header\": \"0x%08X\",\n"
531 : " \"user-size\": 8,\n"
532 : " \"state\": \"allocated\",\n"
533 : " \"heap-type\": \"WinHeap\",\n"
534 : " \"analysis\": {\n"
535 : " \"block\": \"corrupt\",\n"
536 : " \"header\": \"corrupt\",\n"
537 : " \"body\": \"(unknown)\",\n"
538 : " \"trailer\": \"clean\"\n"
539 : " },\n"
540 : " \"alloc-thread-id\": 47,\n"
541 : " \"alloc-stack\": [\n"
542 : " \"0x00000001\", \"0x00000002\"\n"
543 : " ]\n"
544 : " }\n"
545 : " ]\n"
546 E : "}";
547 E : std::string expected = base::StringPrintf(kExpected, block_info.header);
548 E : EXPECT_EQ(expected, json);
549 E : }
550 :
551 E : TEST_F(AsanErrorInfoTest, PopulateErrorInfo) {
552 E : AsanBlockInfo block_info = {};
553 E : InitAsanBlockInfo(&block_info);
554 :
555 E : AsanCorruptBlockRange range = {};
556 E : range.address = reinterpret_cast<void*>(0xBAADF00D);
557 E : range.length = 1024 * 1024;
558 E : range.block_count = 100;
559 E : range.block_info_count = 1;
560 E : range.block_info = &block_info;
561 :
562 : // The 'location' address needs to be at a consistent place in system memory
563 : // so that shadow memory contents and page bits don't vary, otherwise the
564 : // test won't be deterministic.
565 E : AsanErrorInfo error_info = {};
566 E : error_info.location = reinterpret_cast<void*>(0x00001000);
567 E : error_info.crash_stack_id = 1234;
568 E : InitAsanBlockInfo(&error_info.block_info);
569 E : error_info.error_type = WILD_ACCESS;
570 E : error_info.access_mode = ASAN_READ_ACCESS;
571 E : error_info.access_size = 4;
572 : ::strncpy(error_info.shadow_info,
573 : "shadow info!",
574 E : sizeof(error_info.shadow_info));
575 : ::strncpy(error_info.shadow_memory,
576 : "shadow memory!",
577 E : sizeof(error_info.shadow_memory));
578 E : error_info.heap_is_corrupt = true;
579 E : error_info.corrupt_range_count = 10;
580 E : error_info.corrupt_block_count = 200;
581 E : error_info.corrupt_ranges_reported = 1;
582 E : error_info.corrupt_ranges = ⦥
583 :
584 E : ::common::SetDefaultAsanParameters(&error_info.asan_parameters);
585 :
586 E : crashdata::Value info;
587 E : PopulateErrorInfo(runtime_->shadow(), error_info, &info, nullptr);
588 :
589 E : std::string json;
590 E : EXPECT_TRUE(crashdata::ToJson(true, &info, &json));
591 : const char kExpected[] =
592 : "{\n"
593 : " \"location\": \"0x00001000\",\n"
594 : " \"crash-stack-id\": 1234,\n"
595 : " \"block-info\": {\n"
596 : " \"header\": \"0x%08X\",\n"
597 : " \"user-size\": 8,\n"
598 : " \"state\": \"allocated\",\n"
599 : " \"heap-type\": \"WinHeap\",\n"
600 : " \"analysis\": {\n"
601 : " \"block\": \"corrupt\",\n"
602 : " \"header\": \"corrupt\",\n"
603 : " \"body\": \"(unknown)\",\n"
604 : " \"trailer\": \"clean\"\n"
605 : " },\n"
606 : " \"alloc-thread-id\": 47,\n"
607 : " \"alloc-stack\": [\n"
608 : " \"0x00000001\", \"0x00000002\"\n"
609 : " ],\n"
610 : " \"contents\": {\n"
611 : " \"type\": \"blob\",\n"
612 : " \"address\": \"0x%08X\",\n"
613 : " \"size\": null,\n"
614 : " \"data\": [\n"
615 : " \"0x80\", \"0xCA\", \"0x00\", \"0x00\", \"0x20\", \"0x00\","
616 : " \"0x00\", \"0x00\",\n"
617 : " \"0x00\", \"0x00\", \"0x00\", \"0x00\", \"0x00\", \"0x00\","
618 : " \"0x00\", \"0x00\",\n"
619 : " \"0x00\", \"0x00\", \"0x00\", \"0x00\", \"0x00\", \"0x00\","
620 : " \"0x00\", \"0x00\",\n"
621 : " \"0xC3\", \"0xC3\", \"0xC3\", \"0xC3\", \"0x00\", \"0x00\","
622 : " \"0x00\", \"0x00\",\n"
623 : " \"0x00\", \"0x00\", \"0x00\", \"0x00\", \"0x00\", \"0x00\","
624 : " \"0x00\", \"0x00\",\n"
625 : " \"0x00\", \"0x00\", \"0x00\", \"0x00\", \"0x00\", \"0x00\","
626 : " \"0x00\", \"0x00\"\n"
627 : " ]\n"
628 : " },\n"
629 : " \"shadow\": {\n"
630 : " \"type\": \"blob\",\n"
631 : " \"address\": \"0x%08X\",\n"
632 : " \"size\": null,\n"
633 : " \"data\": [\n"
634 : " \"0xE0\", \"0xFA\", \"0x00\", \"0xFB\", \"0xFB\", \"0xF4\"\n"
635 : " ]\n"
636 : " }\n"
637 : " },\n"
638 : " \"error-type\": \"wild-access\",\n"
639 : " \"access-mode\": \"read\",\n"
640 : " \"access-size\": 4,\n"
641 : " \"shadow-memory-index\": 512,\n"
642 : " \"shadow-memory\": {\n"
643 : " \"type\": \"blob\",\n"
644 : " \"address\": \"0x%08X\",\n"
645 : " \"size\": null,\n"
646 : " \"data\": [\n"
647 : " \"0xF2\", \"0xF2\", \"0xF2\", \"0xF2\", \"0xF2\", \"0xF2\","
648 : " \"0xF2\", \"0xF2\",\n"
649 : " \"0xF2\", \"0xF2\", \"0xF2\", \"0xF2\", \"0xF2\", \"0xF2\","
650 : " \"0xF2\", \"0xF2\",\n"
651 : " \"0xF2\", \"0xF2\", \"0xF2\", \"0xF2\", \"0xF2\", \"0xF2\","
652 : " \"0xF2\", \"0xF2\",\n"
653 : " \"0xF2\", \"0xF2\", \"0xF2\", \"0xF2\", \"0xF2\", \"0xF2\","
654 : " \"0xF2\", \"0xF2\",\n"
655 : " \"0xF2\", \"0xF2\", \"0xF2\", \"0xF2\", \"0xF2\", \"0xF2\","
656 : " \"0xF2\", \"0xF2\",\n"
657 : " \"0xF2\", \"0xF2\", \"0xF2\", \"0xF2\", \"0xF2\", \"0xF2\","
658 : " \"0xF2\", \"0xF2\",\n"
659 : " \"0xF2\", \"0xF2\", \"0xF2\", \"0xF2\", \"0xF2\", \"0xF2\","
660 : " \"0xF2\", \"0xF2\",\n"
661 : " \"0xF2\", \"0xF2\", \"0xF2\", \"0xF2\", \"0xF2\", \"0xF2\","
662 : " \"0xF2\", \"0xF2\"\n"
663 : " ]\n"
664 : " },\n"
665 : " \"page-bits-index\": 0,\n"
666 : " \"page-bits\": {\n"
667 : " \"type\": \"blob\",\n"
668 : " \"address\": \"0x%08X\",\n"
669 : " \"size\": null,\n"
670 : " \"data\": [\n"
671 : " \"0x00\", \"0x00\", \"0x00\"\n"
672 : " ]\n"
673 : " },\n"
674 : " \"heap-is-corrupt\": 1,\n"
675 : " \"corrupt-range-count\": 10,\n"
676 : " \"corrupt-block-count\": 200,\n"
677 : " \"corrupt-ranges\": [\n"
678 : " {\n"
679 : " \"address\": \"0xBAADF00D\",\n"
680 : " \"length\": 1048576,\n"
681 : " \"block-count\": 100,\n"
682 : " \"blocks\": [\n"
683 : " {\n"
684 : " \"header\": \"0x%08X\",\n"
685 : " \"user-size\": 8,\n"
686 : " \"state\": \"allocated\",\n"
687 : " \"heap-type\": \"WinHeap\",\n"
688 : " \"analysis\": {\n"
689 : " \"block\": \"corrupt\",\n"
690 : " \"header\": \"corrupt\",\n"
691 : " \"body\": \"(unknown)\",\n"
692 : " \"trailer\": \"clean\"\n"
693 : " },\n"
694 : " \"alloc-thread-id\": 47,\n"
695 : " \"alloc-stack\": [\n"
696 : " \"0x00000001\", \"0x00000002\"\n"
697 : " ]\n"
698 : " }\n"
699 : " ]\n"
700 : " }\n"
701 : " ],\n"
702 : " \"asan-parameters\": {\n"
703 : " \"quarantine-size\": 16777216,\n"
704 : " \"trailer-padding-size\": 0,\n"
705 : " \"quarantine-block-size\": 4194304,\n"
706 : " \"check-heap-on-failure\": 1,\n"
707 : " \"enable-zebra-block-heap\": 0,\n"
708 : " \"enable-large-block-heap\": 1,\n"
709 : " \"enable-allocation-filter\": 0,\n"
710 : " \"allocation-guard-rate\": 1.0000000000000000E+000,\n"
711 : " \"zebra-block-heap-size\": 16777216,\n"
712 : " \"zebra-block-heap-quarantine-ratio\": 2.5000000000000000E-001,\n"
713 : " \"large-allocation-threshold\": 20480,\n"
714 : " \"quarantine-flood-fill-rate\": 5.0000000000000000E-001\n"
715 : " }\n"
716 E : "}";
717 E : AsanErrorShadowMemory shadow_memory = {};
718 : GetAsanErrorShadowMemory(runtime_->shadow(), error_info.location,
719 E : &shadow_memory);
720 : std::string expected =
721 : base::StringPrintf(kExpected, block_info.header, block_info.header,
722 : BlockShadowAddress(), shadow_memory.address,
723 E : runtime_->shadow()->page_bits(), block_info.header);
724 E : EXPECT_EQ(expected, json);
725 E : }
726 :
727 E : TEST_F(AsanErrorInfoTest, PopulateErrorInfoWithMemoryRanges) {
728 E : AsanBlockInfo block_info = {};
729 E : InitAsanBlockInfo(&block_info);
730 :
731 E : AsanCorruptBlockRange range = {};
732 E : range.address = reinterpret_cast<void*>(0xBAADF00D);
733 E : range.length = 1024 * 1024;
734 E : range.block_count = 100;
735 E : range.block_info_count = 1;
736 E : range.block_info = &block_info;
737 :
738 : // The 'location' address needs to be at a consistent place in system memory
739 : // so that shadow memory contents and page bits don't vary, otherwise the
740 : // test won't be deterministic.
741 E : AsanErrorInfo error_info = {};
742 E : error_info.location = reinterpret_cast<void*>(0x00001000);
743 E : error_info.crash_stack_id = 1234;
744 E : InitAsanBlockInfo(&error_info.block_info);
745 E : error_info.error_type = WILD_ACCESS;
746 E : error_info.access_mode = ASAN_READ_ACCESS;
747 E : error_info.access_size = 4;
748 : ::strncpy(error_info.shadow_info, "shadow info!",
749 E : sizeof(error_info.shadow_info));
750 : ::strncpy(error_info.shadow_memory, "shadow memory!",
751 E : sizeof(error_info.shadow_memory));
752 E : error_info.heap_is_corrupt = true;
753 E : error_info.corrupt_range_count = 10;
754 E : error_info.corrupt_block_count = 200;
755 E : error_info.corrupt_ranges_reported = 1;
756 E : error_info.corrupt_ranges = ⦥
757 :
758 E : ::common::SetDefaultAsanParameters(&error_info.asan_parameters);
759 :
760 E : crashdata::Value info;
761 E : MemoryRanges memory_ranges;
762 E : PopulateErrorInfo(runtime_->shadow(), error_info, &info, &memory_ranges);
763 :
764 E : std::string json;
765 E : EXPECT_TRUE(crashdata::ToJson(true, &info, &json));
766 : const char kExpected[] =
767 : "{\n"
768 : " \"location\": \"0x00001000\",\n"
769 : " \"crash-stack-id\": 1234,\n"
770 : " \"block-info\": {\n"
771 : " \"header\": \"0x%08X\",\n"
772 : " \"user-size\": 8,\n"
773 : " \"state\": \"allocated\",\n"
774 : " \"heap-type\": \"WinHeap\",\n"
775 : " \"analysis\": {\n"
776 : " \"block\": \"corrupt\",\n"
777 : " \"header\": \"corrupt\",\n"
778 : " \"body\": \"(unknown)\",\n"
779 : " \"trailer\": \"clean\"\n"
780 : " },\n"
781 : " \"alloc-thread-id\": 47,\n"
782 : " \"alloc-stack\": [\n"
783 : " \"0x00000001\", \"0x00000002\"\n"
784 : " ],\n"
785 : " \"contents\": {\n"
786 : " \"type\": \"blob\",\n"
787 : " \"address\": \"0x%08X\",\n"
788 : " \"size\": 48,\n"
789 : " \"data\": null\n"
790 : " },\n"
791 : " \"shadow\": {\n"
792 : " \"type\": \"blob\",\n"
793 : " \"address\": \"0x%08X\",\n"
794 : " \"size\": 6,\n"
795 : " \"data\": null\n"
796 : " }\n"
797 : " },\n"
798 : " \"error-type\": \"wild-access\",\n"
799 : " \"access-mode\": \"read\",\n"
800 : " \"access-size\": 4,\n"
801 : " \"shadow-memory-index\": 512,\n"
802 : " \"shadow-memory\": {\n"
803 : " \"type\": \"blob\",\n"
804 : " \"address\": \"0x%08X\",\n"
805 : " \"size\": 64,\n"
806 : " \"data\": null\n"
807 : " },\n"
808 : " \"page-bits-index\": 0,\n"
809 : " \"page-bits\": {\n"
810 : " \"type\": \"blob\",\n"
811 : " \"address\": \"0x%08X\",\n"
812 : " \"size\": 3,\n"
813 : " \"data\": null\n"
814 : " },\n"
815 : " \"heap-is-corrupt\": 1,\n"
816 : " \"corrupt-range-count\": 10,\n"
817 : " \"corrupt-block-count\": 200,\n"
818 : " \"corrupt-ranges\": [\n"
819 : " {\n"
820 : " \"address\": \"0xBAADF00D\",\n"
821 : " \"length\": 1048576,\n"
822 : " \"block-count\": 100,\n"
823 : " \"blocks\": [\n"
824 : " {\n"
825 : " \"header\": \"0x%08X\",\n"
826 : " \"user-size\": 8,\n"
827 : " \"state\": \"allocated\",\n"
828 : " \"heap-type\": \"WinHeap\",\n"
829 : " \"analysis\": {\n"
830 : " \"block\": \"corrupt\",\n"
831 : " \"header\": \"corrupt\",\n"
832 : " \"body\": \"(unknown)\",\n"
833 : " \"trailer\": \"clean\"\n"
834 : " },\n"
835 : " \"alloc-thread-id\": 47,\n"
836 : " \"alloc-stack\": [\n"
837 : " \"0x00000001\", \"0x00000002\"\n"
838 : " ]\n"
839 : " }\n"
840 : " ]\n"
841 : " }\n"
842 : " ],\n"
843 : " \"asan-parameters\": {\n"
844 : " \"quarantine-size\": 16777216,\n"
845 : " \"trailer-padding-size\": 0,\n"
846 : " \"quarantine-block-size\": 4194304,\n"
847 : " \"check-heap-on-failure\": 1,\n"
848 : " \"enable-zebra-block-heap\": 0,\n"
849 : " \"enable-large-block-heap\": 1,\n"
850 : " \"enable-allocation-filter\": 0,\n"
851 : " \"allocation-guard-rate\": 1.0000000000000000E+000,\n"
852 : " \"zebra-block-heap-size\": 16777216,\n"
853 : " \"zebra-block-heap-quarantine-ratio\": 2.5000000000000000E-001,\n"
854 : " \"large-allocation-threshold\": 20480,\n"
855 : " \"quarantine-flood-fill-rate\": 5.0000000000000000E-001\n"
856 : " }\n"
857 E : "}";
858 E : AsanErrorShadowMemory shadow_memory = {};
859 : GetAsanErrorShadowMemory(runtime_->shadow(), error_info.location,
860 E : &shadow_memory);
861 : std::string expected =
862 : base::StringPrintf(kExpected, block_info.header, block_info.header,
863 : BlockShadowAddress(), shadow_memory.address,
864 E : runtime_->shadow()->page_bits(), block_info.header);
865 E : EXPECT_EQ(expected, json);
866 :
867 : // Check memory ranges.
868 E : ASSERT_EQ(4, memory_ranges.size());
869 : const char* kExpectedMemoryRangesAddresses[] = {
870 : reinterpret_cast<const char*>(block_info.header),
871 : reinterpret_cast<const char*>(BlockShadowAddress()),
872 : reinterpret_cast<const char*>(shadow_memory.address),
873 E : reinterpret_cast<const char*>(runtime_->shadow()->page_bits())};
874 E : size_t kExpectedMemoryRangesSize[] = {48, 6, 64, 3};
875 E : for (int i = 0; i < memory_ranges.size(); i++) {
876 E : EXPECT_EQ(kExpectedMemoryRangesAddresses[i], memory_ranges[i].first)
877 : << " Where i = " << i;
878 E : EXPECT_EQ(kExpectedMemoryRangesSize[i], memory_ranges[i].second)
879 : << " Where i = " << i;
880 E : }
881 E : }
882 :
883 : } // namespace asan
884 : } // namespace agent
|