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 "gtest/gtest.h"
20 : #include "syzygy/agent/asan/unittest_util.h"
21 : #include "syzygy/crashdata/json.h"
22 :
23 : namespace agent {
24 : namespace asan {
25 :
26 : namespace {
27 :
28 : typedef testing::TestWithAsanRuntime AsanErrorInfoTest;
29 :
30 : } // namespace
31 :
32 E : TEST_F(AsanErrorInfoTest, ErrorInfoAccessTypeToStr) {
33 E : EXPECT_EQ(kHeapUseAfterFree, ErrorInfoAccessTypeToStr(USE_AFTER_FREE));
34 : EXPECT_EQ(kHeapBufferUnderFlow,
35 E : ErrorInfoAccessTypeToStr(HEAP_BUFFER_UNDERFLOW));
36 : EXPECT_EQ(kHeapBufferOverFlow,
37 E : ErrorInfoAccessTypeToStr(HEAP_BUFFER_OVERFLOW));
38 E : EXPECT_EQ(kAttemptingDoubleFree, ErrorInfoAccessTypeToStr(DOUBLE_FREE));
39 E : EXPECT_EQ(kInvalidAddress, ErrorInfoAccessTypeToStr(INVALID_ADDRESS));
40 E : EXPECT_EQ(kWildAccess, ErrorInfoAccessTypeToStr(WILD_ACCESS));
41 E : EXPECT_EQ(kHeapUnknownError, ErrorInfoAccessTypeToStr(UNKNOWN_BAD_ACCESS));
42 E : EXPECT_EQ(kHeapCorruptBlock, ErrorInfoAccessTypeToStr(CORRUPT_BLOCK));
43 E : EXPECT_EQ(kCorruptHeap, ErrorInfoAccessTypeToStr(CORRUPT_HEAP));
44 E : }
45 :
46 E : TEST_F(AsanErrorInfoTest, ErrorInfoGetBadAccessInformation) {
47 E : testing::FakeAsanBlock fake_block(kShadowRatioLog, runtime_->stack_cache());
48 E : const size_t kAllocSize = 100;
49 E : EXPECT_TRUE(fake_block.InitializeBlock(kAllocSize));
50 :
51 E : AsanErrorInfo error_info = {};
52 : error_info.location = fake_block.block_info.body +
53 E : kAllocSize + 1;
54 : EXPECT_TRUE(ErrorInfoGetBadAccessInformation(runtime_->stack_cache(),
55 E : &error_info));
56 E : EXPECT_EQ(HEAP_BUFFER_OVERFLOW, error_info.error_type);
57 E : EXPECT_EQ(kUnknownHeapType, error_info.block_info.heap_type);
58 :
59 E : EXPECT_TRUE(fake_block.MarkBlockAsQuarantined());
60 E : error_info.location = fake_block.block_info.body;
61 : EXPECT_TRUE(ErrorInfoGetBadAccessInformation(runtime_->stack_cache(),
62 E : &error_info));
63 E : EXPECT_EQ(USE_AFTER_FREE, error_info.error_type);
64 E : EXPECT_EQ(kUnknownHeapType, error_info.block_info.heap_type);
65 :
66 E : error_info.location = fake_block.buffer_align_begin - 1;
67 : EXPECT_FALSE(ErrorInfoGetBadAccessInformation(runtime_->stack_cache(),
68 E : &error_info));
69 E : }
70 :
71 E : TEST_F(AsanErrorInfoTest, GetBadAccessInformationNestedBlock) {
72 : // Test a nested use after free. We allocate an outer block and an inner block
73 : // inside it, then we mark the outer block as quarantined and we test a bad
74 : // access inside the inner block.
75 :
76 E : testing::FakeAsanBlock fake_block(kShadowRatioLog, runtime_->stack_cache());
77 E : const size_t kInnerBlockAllocSize = 100;
78 :
79 : // Allocates the outer block.
80 E : BlockLayout outer_block_layout = {};
81 : EXPECT_TRUE(BlockPlanLayout(kShadowRatio, kShadowRatio, kInnerBlockAllocSize,
82 E : 0, 0, &outer_block_layout));
83 E : EXPECT_TRUE(fake_block.InitializeBlock(outer_block_layout.block_size));
84 :
85 E : common::StackCapture stack;
86 E : stack.InitFromStack();
87 :
88 : // Initializes the inner block.
89 E : BlockLayout inner_block_layout = {};
90 : EXPECT_TRUE(BlockPlanLayout(kShadowRatio,
91 : kShadowRatio,
92 : kInnerBlockAllocSize,
93 : 0,
94 : 0,
95 E : &inner_block_layout));
96 E : BlockInfo inner_block_info = {};
97 : BlockInitialize(inner_block_layout, fake_block.block_info.body, true,
98 E : &inner_block_info);
99 E : ASSERT_NE(reinterpret_cast<void*>(NULL), inner_block_info.body);
100 E : Shadow::PoisonAllocatedBlock(inner_block_info);
101 : inner_block_info.header->alloc_stack =
102 E : runtime_->stack_cache()->SaveStackTrace(stack);
103 E : BlockHeader* inner_header = inner_block_info.header;
104 : BlockHeader* outer_header = reinterpret_cast<BlockHeader*>(
105 E : fake_block.buffer_align_begin);
106 :
107 E : AsanErrorInfo error_info = {};
108 :
109 : // Mark the inner block as quarantined and check that we detect a use after
110 : // free when trying to access its data.
111 : inner_block_info.header->free_stack =
112 E : runtime_->stack_cache()->SaveStackTrace(stack);
113 E : EXPECT_NE(reinterpret_cast<void*>(NULL), inner_header->free_stack);
114 E : inner_header->state = QUARANTINED_BLOCK;
115 :
116 E : error_info.location = fake_block.block_info.body;
117 : EXPECT_TRUE(ErrorInfoGetBadAccessInformation(runtime_->stack_cache(),
118 E : &error_info));
119 E : EXPECT_EQ(USE_AFTER_FREE, error_info.error_type);
120 E : EXPECT_NE(reinterpret_cast<void*>(NULL), error_info.block_info.free_stack);
121 E : EXPECT_EQ(kUnknownHeapType, error_info.block_info.heap_type);
122 :
123 : EXPECT_EQ(inner_header->free_stack->num_frames(),
124 E : error_info.block_info.free_stack_size);
125 E : for (size_t i = 0; i < inner_header->free_stack->num_frames(); ++i) {
126 : EXPECT_EQ(inner_header->free_stack->frames()[i],
127 E : error_info.block_info.free_stack[i]);
128 E : }
129 :
130 : // Mark the outer block as quarantined, we should detect a use after free
131 : // when trying to access the data of the inner block, and the free stack
132 : // should be the one of the inner block.
133 E : EXPECT_TRUE(fake_block.MarkBlockAsQuarantined());
134 E : EXPECT_NE(ALLOCATED_BLOCK, static_cast<BlockState>(outer_header->state));
135 E : EXPECT_NE(reinterpret_cast<void*>(NULL), outer_header->free_stack);
136 :
137 : // Tests an access in the inner block.
138 E : error_info.location = inner_block_info.body;
139 : EXPECT_TRUE(ErrorInfoGetBadAccessInformation(runtime_->stack_cache(),
140 E : &error_info));
141 E : EXPECT_EQ(USE_AFTER_FREE, error_info.error_type);
142 E : EXPECT_NE(reinterpret_cast<void*>(NULL), error_info.block_info.free_stack);
143 E : EXPECT_EQ(kUnknownHeapType, error_info.block_info.heap_type);
144 :
145 : EXPECT_EQ(inner_header->free_stack->num_frames(),
146 E : error_info.block_info.free_stack_size);
147 E : for (size_t i = 0; i < inner_header->free_stack->num_frames(); ++i) {
148 : EXPECT_EQ(inner_header->free_stack->frames()[i],
149 E : error_info.block_info.free_stack[i]);
150 E : }
151 E : }
152 :
153 E : TEST_F(AsanErrorInfoTest, ErrorInfoGetBadAccessKind) {
154 E : const size_t kAllocSize = 100;
155 E : testing::FakeAsanBlock fake_block(kShadowRatioLog, runtime_->stack_cache());
156 E : EXPECT_TRUE(fake_block.InitializeBlock(kAllocSize));
157 E : uint8* heap_underflow_address = fake_block.block_info.body - 1;
158 : uint8* heap_overflow_address = fake_block.block_info.body +
159 E : kAllocSize * sizeof(uint8);
160 : EXPECT_EQ(HEAP_BUFFER_UNDERFLOW,
161 : ErrorInfoGetBadAccessKind(heap_underflow_address,
162 E : fake_block.block_info.header));
163 : EXPECT_EQ(HEAP_BUFFER_OVERFLOW,
164 : ErrorInfoGetBadAccessKind(heap_overflow_address,
165 E : fake_block.block_info.header));
166 E : EXPECT_TRUE(fake_block.MarkBlockAsQuarantined());
167 : EXPECT_EQ(USE_AFTER_FREE, ErrorInfoGetBadAccessKind(
168 E : fake_block.block_info.body, fake_block.block_info.header));
169 E : }
170 :
171 E : TEST_F(AsanErrorInfoTest, ErrorInfoGetAsanBlockInfo) {
172 E : const size_t kAllocSize = 100;
173 E : testing::FakeAsanBlock fake_block(kShadowRatioLog, runtime_->stack_cache());
174 E : EXPECT_TRUE(fake_block.InitializeBlock(kAllocSize));
175 :
176 E : AsanBlockInfo asan_block_info = {};
177 : ErrorInfoGetAsanBlockInfo(fake_block.block_info,
178 : runtime_->stack_cache(),
179 E : &asan_block_info);
180 :
181 : // Test ErrorInfoGetAsanBlockInfo with an allocated block.
182 E : EXPECT_EQ(fake_block.block_info.body_size, asan_block_info.user_size);
183 E : EXPECT_EQ(ALLOCATED_BLOCK, static_cast<BlockState>(asan_block_info.state));
184 : EXPECT_EQ(fake_block.block_info.header->state,
185 E : static_cast<BlockState>(asan_block_info.state));
186 E : EXPECT_EQ(::GetCurrentThreadId(), asan_block_info.alloc_tid);
187 E : EXPECT_EQ(0, asan_block_info.free_tid);
188 E : EXPECT_EQ(kDataIsClean, asan_block_info.analysis.block_state);
189 : EXPECT_EQ(fake_block.block_info.header->alloc_stack->num_frames(),
190 E : asan_block_info.alloc_stack_size);
191 E : EXPECT_EQ(0, asan_block_info.free_stack_size);
192 E : EXPECT_EQ(kUnknownHeapType, asan_block_info.heap_type);
193 :
194 : // Now test it with a quarantined block.
195 E : EXPECT_TRUE(fake_block.MarkBlockAsQuarantined());
196 : ErrorInfoGetAsanBlockInfo(fake_block.block_info,
197 : runtime_->stack_cache(),
198 E : &asan_block_info);
199 E : EXPECT_EQ(QUARANTINED_BLOCK, static_cast<BlockState>(asan_block_info.state));
200 : EXPECT_EQ(fake_block.block_info.header->state,
201 E : static_cast<BlockState>(asan_block_info.state));
202 E : EXPECT_EQ(::GetCurrentThreadId(), asan_block_info.free_tid);
203 : EXPECT_EQ(fake_block.block_info.header->free_stack->num_frames(),
204 E : asan_block_info.free_stack_size);
205 E : EXPECT_EQ(kUnknownHeapType, asan_block_info.heap_type);
206 :
207 : // Ensure that the block is correctly tagged as corrupt if the header is
208 : // invalid.
209 E : fake_block.block_info.header->magic = ~kBlockHeaderMagic;
210 : ErrorInfoGetAsanBlockInfo(fake_block.block_info,
211 : runtime_->stack_cache(),
212 E : &asan_block_info);
213 E : EXPECT_EQ(kDataIsCorrupt, asan_block_info.analysis.block_state);
214 E : fake_block.block_info.header->magic = kBlockHeaderMagic;
215 E : }
216 :
217 E : TEST_F(AsanErrorInfoTest, GetTimeSinceFree) {
218 E : const size_t kAllocSize = 100;
219 E : const size_t kSleepTime = 25;
220 E : testing::FakeAsanBlock fake_block(kShadowRatioLog, runtime_->stack_cache());
221 E : EXPECT_TRUE(fake_block.InitializeBlock(kAllocSize));
222 :
223 E : uint32 ticks_before_free = ::GetTickCount();
224 E : EXPECT_TRUE(fake_block.MarkBlockAsQuarantined());
225 E : ::Sleep(kSleepTime);
226 E : AsanErrorInfo error_info = {};
227 E : error_info.error_type = USE_AFTER_FREE;
228 E : error_info.location = fake_block.block_info.body;
229 : EXPECT_TRUE(ErrorInfoGetBadAccessInformation(runtime_->stack_cache(),
230 E : &error_info));
231 E : EXPECT_NE(0U, error_info.block_info.milliseconds_since_free);
232 :
233 E : uint32 ticks_delta = ::GetTickCount() - ticks_before_free;
234 E : EXPECT_GT(ticks_delta, 0U);
235 :
236 E : EXPECT_GE(ticks_delta, error_info.block_info.milliseconds_since_free);
237 E : }
238 :
239 : namespace {
240 :
241 E : void InitAsanBlockInfo(AsanBlockInfo* block_info) {
242 E : block_info->header = reinterpret_cast<void*>(0xDEADBEEF);
243 E : block_info->user_size = 1024;
244 E : block_info->state = ALLOCATED_BLOCK;
245 E : block_info->alloc_tid = 47;
246 E : block_info->analysis.block_state = kDataIsCorrupt;
247 E : block_info->analysis.header_state = kDataIsCorrupt;
248 E : block_info->analysis.body_state = kDataStateUnknown;
249 E : block_info->analysis.trailer_state = kDataIsClean;
250 E : block_info->alloc_stack[0] = reinterpret_cast<void*>(1);
251 E : block_info->alloc_stack[1] = reinterpret_cast<void*>(2);
252 E : block_info->alloc_stack_size = 2;
253 E : block_info->heap_type = kWinHeap;
254 E : }
255 :
256 : } // namespace
257 :
258 E : TEST_F(AsanErrorInfoTest, PopulateBlockInfo) {
259 E : AsanBlockInfo block_info = {};
260 E : InitAsanBlockInfo(&block_info);
261 :
262 : {
263 E : crashdata::Value info;
264 E : PopulateBlockInfo(block_info, &info);
265 E : std::string json;
266 E : EXPECT_TRUE(crashdata::ToJson(true, &info, &json));
267 : const char kExpected[] =
268 : "{\n"
269 : " \"header\": 0xDEADBEEF,\n"
270 : " \"user-size\": 1024,\n"
271 : " \"state\": \"allocated\",\n"
272 : " \"heap-type\": \"WinHeap\",\n"
273 : " \"analysis\": {\n"
274 : " \"block\": \"corrupt\",\n"
275 : " \"header\": \"corrupt\",\n"
276 : " \"body\": \"(unknown)\",\n"
277 : " \"trailer\": \"clean\"\n"
278 : " },\n"
279 : " \"alloc-thread-id\": 47,\n"
280 : " \"alloc-stack\": [\n"
281 : " 0x00000001, 0x00000002\n"
282 : " ]\n"
283 E : "}";
284 E : EXPECT_EQ(kExpected, json);
285 E : }
286 :
287 E : block_info.state = QUARANTINED_BLOCK;
288 E : block_info.free_tid = 32;
289 E : block_info.free_stack[0] = reinterpret_cast<void*>(3);
290 E : block_info.free_stack[1] = reinterpret_cast<void*>(4);
291 E : block_info.free_stack[2] = reinterpret_cast<void*>(5);
292 E : block_info.free_stack_size = 3;
293 E : block_info.heap_type = kCtMallocHeap;
294 E : block_info.milliseconds_since_free = 100;
295 :
296 : {
297 E : crashdata::Value info;
298 E : PopulateBlockInfo(block_info, &info);
299 E : std::string json;
300 E : EXPECT_TRUE(crashdata::ToJson(true, &info, &json));
301 : const char kExpected[] =
302 : "{\n"
303 : " \"header\": 0xDEADBEEF,\n"
304 : " \"user-size\": 1024,\n"
305 : " \"state\": \"quarantined\",\n"
306 : " \"heap-type\": \"CtMallocHeap\",\n"
307 : " \"analysis\": {\n"
308 : " \"block\": \"corrupt\",\n"
309 : " \"header\": \"corrupt\",\n"
310 : " \"body\": \"(unknown)\",\n"
311 : " \"trailer\": \"clean\"\n"
312 : " },\n"
313 : " \"alloc-thread-id\": 47,\n"
314 : " \"alloc-stack\": [\n"
315 : " 0x00000001, 0x00000002\n"
316 : " ],\n"
317 : " \"free-thread-id\": 32,\n"
318 : " \"free-stack\": [\n"
319 : " 0x00000003, 0x00000004, 0x00000005\n"
320 : " ],\n"
321 : " \"milliseconds-since-free\": 100\n"
322 E : "}";
323 E : EXPECT_EQ(kExpected, json);
324 E : }
325 E : }
326 :
327 E : TEST_F(AsanErrorInfoTest, PopulateCorruptBlockRange) {
328 E : AsanBlockInfo block_info = {};
329 E : InitAsanBlockInfo(&block_info);
330 :
331 E : AsanCorruptBlockRange range = {};
332 E : range.address = reinterpret_cast<void*>(0xBAADF00D);
333 E : range.length = 1024 * 1024;
334 E : range.block_count = 100;
335 E : range.block_info_count = 1;
336 E : range.block_info = &block_info;
337 :
338 E : crashdata::Value info;
339 E : PopulateCorruptBlockRange(range, &info);
340 :
341 E : std::string json;
342 E : EXPECT_TRUE(crashdata::ToJson(true, &info, &json));
343 : const char kExpected[] =
344 : "{\n"
345 : " \"address\": 0xBAADF00D,\n"
346 : " \"length\": 1048576,\n"
347 : " \"block-count\": 100,\n"
348 : " \"blocks\": [\n"
349 : " {\n"
350 : " \"header\": 0xDEADBEEF,\n"
351 : " \"user-size\": 1024,\n"
352 : " \"state\": \"allocated\",\n"
353 : " \"heap-type\": \"WinHeap\",\n"
354 : " \"analysis\": {\n"
355 : " \"block\": \"corrupt\",\n"
356 : " \"header\": \"corrupt\",\n"
357 : " \"body\": \"(unknown)\",\n"
358 : " \"trailer\": \"clean\"\n"
359 : " },\n"
360 : " \"alloc-thread-id\": 47,\n"
361 : " \"alloc-stack\": [\n"
362 : " 0x00000001, 0x00000002\n"
363 : " ]\n"
364 : " }\n"
365 : " ]\n"
366 E : "}";
367 E : EXPECT_EQ(kExpected, json);
368 E : }
369 :
370 E : TEST_F(AsanErrorInfoTest, PopulateErrorInfo) {
371 E : AsanBlockInfo block_info = {};
372 E : InitAsanBlockInfo(&block_info);
373 :
374 E : AsanCorruptBlockRange range = {};
375 E : range.address = reinterpret_cast<void*>(0xBAADF00D);
376 E : range.length = 1024 * 1024;
377 E : range.block_count = 100;
378 E : range.block_info_count = 1;
379 E : range.block_info = &block_info;
380 :
381 : // The 'location' address needs to be at a consistent place in system memory
382 : // so that shadow memory contents and page bits don't vary, otherwise the
383 : // test won't be deterministic.
384 E : AsanErrorInfo error_info = {};
385 E : error_info.location = reinterpret_cast<void*>(0x00001000);
386 E : error_info.crash_stack_id = 1234;
387 E : InitAsanBlockInfo(&error_info.block_info);
388 E : error_info.error_type = WILD_ACCESS;
389 E : error_info.access_mode = ASAN_READ_ACCESS;
390 E : error_info.access_size = 4;
391 : ::strncpy(error_info.shadow_info,
392 : "shadow info!",
393 E : sizeof(error_info.shadow_info));
394 : ::strncpy(error_info.shadow_memory,
395 : "shadow memory!",
396 E : sizeof(error_info.shadow_memory));
397 E : error_info.heap_is_corrupt = true;
398 E : error_info.corrupt_range_count = 10;
399 E : error_info.corrupt_block_count = 200;
400 E : error_info.corrupt_ranges_reported = 1;
401 E : error_info.corrupt_ranges = ⦥
402 :
403 E : crashdata::Value info;
404 E : PopulateErrorInfo(error_info, &info);
405 :
406 E : std::string json;
407 E : EXPECT_TRUE(crashdata::ToJson(true, &info, &json));
408 : const char kExpected[] =
409 : "{\n"
410 : " \"location\": 0x00001000,\n"
411 : " \"crash-stack-id\": 1234,\n"
412 : " \"block-info\": {\n"
413 : " \"header\": 0xDEADBEEF,\n"
414 : " \"user-size\": 1024,\n"
415 : " \"state\": \"allocated\",\n"
416 : " \"heap-type\": \"WinHeap\",\n"
417 : " \"analysis\": {\n"
418 : " \"block\": \"corrupt\",\n"
419 : " \"header\": \"corrupt\",\n"
420 : " \"body\": \"(unknown)\",\n"
421 : " \"trailer\": \"clean\"\n"
422 : " },\n"
423 : " \"alloc-thread-id\": 47,\n"
424 : " \"alloc-stack\": [\n"
425 : " 0x00000001, 0x00000002\n"
426 : " ]\n"
427 : " },\n"
428 : " \"error-type\": \"wild-access\",\n"
429 : " \"access-mode\": \"read\",\n"
430 : " \"access-size\": 4,\n"
431 : " \"shadow-memory-index\": 512,\n"
432 : " \"shadow-memory\": {\n"
433 : " \"type\": \"blob\",\n"
434 : " \"address\": null,\n"
435 : " \"size\": null,\n"
436 : " \"data\": [\n"
437 : " 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2,\n"
438 : " 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2,\n"
439 : " 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2,\n"
440 : " 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2,\n"
441 : " 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2,\n"
442 : " 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2,\n"
443 : " 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2,\n"
444 : " 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2\n"
445 : " ]\n"
446 : " },\n"
447 : " \"page-bits-index\": 0,\n"
448 : " \"page-bits\": {\n"
449 : " \"type\": \"blob\",\n"
450 : " \"address\": null,\n"
451 : " \"size\": null,\n"
452 : " \"data\": [\n"
453 : " 0x00, 0x00, 0x00\n"
454 : " ]\n"
455 : " },\n"
456 : " \"heap-is-corrupt\": 1,\n"
457 : " \"corrupt-range-count\": 10,\n"
458 : " \"corrupt-block-count\": 200,\n"
459 : " \"corrupt-ranges\": [\n"
460 : " {\n"
461 : " \"address\": 0xBAADF00D,\n"
462 : " \"length\": 1048576,\n"
463 : " \"block-count\": 100,\n"
464 : " \"blocks\": [\n"
465 : " {\n"
466 : " \"header\": 0xDEADBEEF,\n"
467 : " \"user-size\": 1024,\n"
468 : " \"state\": \"allocated\",\n"
469 : " \"heap-type\": \"WinHeap\",\n"
470 : " \"analysis\": {\n"
471 : " \"block\": \"corrupt\",\n"
472 : " \"header\": \"corrupt\",\n"
473 : " \"body\": \"(unknown)\",\n"
474 : " \"trailer\": \"clean\"\n"
475 : " },\n"
476 : " \"alloc-thread-id\": 47,\n"
477 : " \"alloc-stack\": [\n"
478 : " 0x00000001, 0x00000002\n"
479 : " ]\n"
480 : " }\n"
481 : " ]\n"
482 : " }\n"
483 : " ]\n"
484 E : "}";
485 E : EXPECT_EQ(kExpected, json);
486 E : }
487 :
488 : } // namespace asan
489 : } // namespace agent
|