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 <string>
18 :
19 : #include "base/strings/string_util.h"
20 : #include "syzygy/agent/asan/block_utils.h"
21 : #include "syzygy/agent/asan/runtime.h"
22 : #include "syzygy/agent/asan/shadow.h"
23 : #include "syzygy/agent/asan/stack_capture_cache.h"
24 : #include "syzygy/crashdata/crashdata.h"
25 :
26 : namespace agent {
27 : namespace asan {
28 :
29 : namespace {
30 :
31 : // Copy a stack capture object into an array.
32 : // @param stack_capture The stack capture that we want to copy.
33 : // @param dst Will receive the stack frames.
34 : // @param dst_size Will receive the number of frames that has been copied.
35 : void CopyStackCaptureToArray(const common::StackCapture* stack_capture,
36 : void* dst,
37 E : uint8_t* dst_size) {
38 E : DCHECK_NE(static_cast<common::StackCapture*>(nullptr), stack_capture);
39 E : DCHECK_NE(static_cast<void*>(nullptr), dst);
40 E : DCHECK_NE(static_cast<uint8_t*>(nullptr), dst_size);
41 E : ::memcpy(dst,
42 : stack_capture->frames(),
43 : stack_capture->num_frames() * sizeof(void*));
44 E : *dst_size = static_cast<uint8_t>(stack_capture->num_frames());
45 E : }
46 :
47 : // Get the information about an address relative to a block.
48 : // @param shadow The shadow memory to query.
49 : // @param header The header of the block containing this address.
50 : // @param bad_access_info Will receive the information about this address.
51 : void GetAddressInformation(const Shadow* shadow,
52 : BlockHeader* header,
53 E : AsanErrorInfo* bad_access_info) {
54 E : DCHECK_NE(static_cast<Shadow*>(nullptr), shadow);
55 E : DCHECK_NE(static_cast<BlockHeader*>(nullptr), header);
56 E : DCHECK_NE(static_cast<AsanErrorInfo*>(nullptr), bad_access_info);
57 E : DCHECK_NE(static_cast<void*>(nullptr), bad_access_info->location);
58 :
59 E : BlockInfo block_info = {};
60 E : shadow->BlockInfoFromShadow(header, &block_info);
61 E : SSIZE_T offset = 0;
62 E : char* offset_relativity = "";
63 E : switch (bad_access_info->error_type) {
64 : case HEAP_BUFFER_OVERFLOW: {
65 E : offset = static_cast<const uint8_t*>(bad_access_info->location) -
66 : block_info.RawBody() - block_info.body_size;
67 E : offset_relativity = "beyond";
68 E : break;
69 : }
70 : case HEAP_BUFFER_UNDERFLOW: {
71 E : offset = block_info.RawBody() -
72 : static_cast<const uint8_t*>(bad_access_info->location);
73 E : offset_relativity = "before";
74 E : break;
75 : }
76 : case USE_AFTER_FREE: {
77 E : offset = static_cast<const uint8_t*>(bad_access_info->location) -
78 : block_info.RawBody();
79 E : offset_relativity = "inside";
80 E : break;
81 : }
82 : case WILD_ACCESS:
83 : case DOUBLE_FREE:
84 : case UNKNOWN_BAD_ACCESS:
85 : case CORRUPT_BLOCK:
86 E : return;
87 : default:
88 i : NOTREACHED() << "Error trying to dump address information.";
89 : }
90 :
91 E : size_t shadow_info_bytes = base::snprintf(
92 : bad_access_info->shadow_info,
93 : arraysize(bad_access_info->shadow_info) - 1,
94 : "%08X is %d bytes %s %d-byte block [%08X,%08X)\n",
95 : bad_access_info->location,
96 : offset,
97 : offset_relativity,
98 : block_info.body_size,
99 : block_info.body,
100 : block_info.trailer_padding);
101 :
102 E : std::string shadow_memory;
103 E : shadow->AppendShadowArrayText(
104 : bad_access_info->location, &shadow_memory);
105 E : size_t shadow_mem_bytes = base::snprintf(
106 : bad_access_info->shadow_memory,
107 : arraysize(bad_access_info->shadow_memory) - 1,
108 : "%s",
109 : shadow_memory.c_str());
110 :
111 : // Ensure that we had enough space to store the full shadow information.
112 E : DCHECK_LE(shadow_info_bytes, arraysize(bad_access_info->shadow_info) - 1);
113 E : DCHECK_LE(shadow_mem_bytes, arraysize(bad_access_info->shadow_memory) - 1);
114 E : }
115 :
116 : } // namespace
117 :
118 : const char kHeapUseAfterFree[] = "heap-use-after-free";
119 : const char kHeapBufferUnderFlow[] = "heap-buffer-underflow";
120 : const char kHeapBufferOverFlow[] = "heap-buffer-overflow";
121 : const char kAttemptingDoubleFree[] = "attempting double-free";
122 : const char kInvalidAddress[] = "invalid-address";
123 : const char kWildAccess[] = "wild-access";
124 : const char kHeapUnknownError[] = "heap-unknown-error";
125 : const char kHeapCorruptBlock[] = "corrupt-block";
126 : const char kCorruptHeap[] = "corrupt-heap";
127 :
128 E : const char* ErrorInfoAccessTypeToStr(BadAccessKind bad_access_kind) {
129 E : switch (bad_access_kind) {
130 : case USE_AFTER_FREE:
131 E : return kHeapUseAfterFree;
132 : case HEAP_BUFFER_UNDERFLOW:
133 E : return kHeapBufferUnderFlow;
134 : case HEAP_BUFFER_OVERFLOW:
135 E : return kHeapBufferOverFlow;
136 : case WILD_ACCESS:
137 E : return kWildAccess;
138 : case INVALID_ADDRESS:
139 E : return kInvalidAddress;
140 : case DOUBLE_FREE:
141 E : return kAttemptingDoubleFree;
142 : case UNKNOWN_BAD_ACCESS:
143 E : return kHeapUnknownError;
144 : case CORRUPT_BLOCK:
145 E : return kHeapCorruptBlock;
146 : case CORRUPT_HEAP:
147 E : return kCorruptHeap;
148 : default:
149 i : NOTREACHED() << "Unexpected bad access kind.";
150 i : return nullptr;
151 : }
152 E : }
153 :
154 : namespace {
155 :
156 : // Converts an access kind string to its corresponding enum value.
157 : BadAccessKind ErrorInfoAccessTypeStrToEnum(const std::string& access_kind) {
158 : if (::strcmp(kHeapUseAfterFree, access_kind.c_str())) {
159 : return USE_AFTER_FREE;
160 : } else if (::strcmp(kHeapBufferUnderFlow, access_kind.c_str()) == 0) {
161 : return HEAP_BUFFER_UNDERFLOW;
162 : } else if (::strcmp(kHeapBufferOverFlow, access_kind.c_str()) == 0) {
163 : return HEAP_BUFFER_OVERFLOW;
164 : } else if (::strcmp(kWildAccess, access_kind.c_str()) == 0) {
165 : return WILD_ACCESS;
166 : } else if (::strcmp(kInvalidAddress, access_kind.c_str()) == 0) {
167 : return INVALID_ADDRESS;
168 : } else if (::strcmp(kAttemptingDoubleFree, access_kind.c_str()) == 0) {
169 : return DOUBLE_FREE;
170 : } else if (::strcmp(kHeapUnknownError, access_kind.c_str()) == 0) {
171 : return UNKNOWN_BAD_ACCESS;
172 : } else if (::strcmp(kHeapCorruptBlock, access_kind.c_str()) == 0) {
173 : return CORRUPT_BLOCK;
174 : } else if (::strcmp(kCorruptHeap, access_kind.c_str()) == 0) {
175 : return CORRUPT_HEAP;
176 : } else {
177 : NOTREACHED() << "Unexpected bad access kind.";
178 : return BAD_ACCESS_KIND_MAX;
179 : }
180 : }
181 :
182 : } // namespace
183 :
184 : bool ErrorInfoGetBadAccessInformation(const Shadow* shadow,
185 : StackCaptureCache* stack_cache,
186 E : AsanErrorInfo* bad_access_info) {
187 E : DCHECK_NE(static_cast<Shadow*>(nullptr), shadow);
188 E : DCHECK_NE(static_cast<StackCaptureCache*>(nullptr), stack_cache);
189 E : DCHECK_NE(static_cast<AsanErrorInfo*>(nullptr), bad_access_info);
190 E : BlockInfo block_info = {};
191 E : if (!shadow->BlockInfoFromShadow(
192 : bad_access_info->location, &block_info)) {
193 E : return false;
194 : }
195 :
196 : // Fill out the information about the primary block.
197 E : ErrorInfoGetAsanBlockInfo(shadow, block_info, stack_cache,
198 : &bad_access_info->block_info);
199 :
200 E : if (bad_access_info->error_type != DOUBLE_FREE &&
201 : bad_access_info->error_type != CORRUPT_BLOCK) {
202 E : bad_access_info->error_type = ErrorInfoGetBadAccessKind(
203 : shadow, bad_access_info->location, block_info.header);
204 : }
205 :
206 : // Get the bad access description if we've been able to determine its kind.
207 E : if (bad_access_info->error_type != UNKNOWN_BAD_ACCESS) {
208 E : GetAddressInformation(shadow, block_info.header, bad_access_info);
209 E : return true;
210 : }
211 :
212 i : return false;
213 E : }
214 :
215 : BadAccessKind ErrorInfoGetBadAccessKind(const Shadow* shadow,
216 : const void* addr,
217 E : const BlockHeader* header) {
218 E : DCHECK_NE(static_cast<Shadow*>(nullptr), shadow);
219 E : DCHECK_NE(static_cast<const void*>(nullptr), addr);
220 E : DCHECK_NE(static_cast<const BlockHeader*>(nullptr), header);
221 :
222 E : switch (static_cast<BlockState>(header->state)) {
223 : case ALLOCATED_BLOCK: {
224 E : BlockInfo block_info = {};
225 E : shadow->BlockInfoFromShadow(header, &block_info);
226 E : if (addr < block_info.body) {
227 E : return HEAP_BUFFER_UNDERFLOW;
228 E : } else if (addr >= (block_info.RawBody() + block_info.body_size)) {
229 E : return HEAP_BUFFER_OVERFLOW;
230 i : } else if (shadow->GetShadowMarkerForAddress(addr) ==
231 : kHeapFreedMarker) {
232 : // This is a use after free on a block managed by a nested heap.
233 i : return USE_AFTER_FREE;
234 : }
235 i : break;
236 : }
237 :
238 : case QUARANTINED_BLOCK:
239 : case QUARANTINED_FLOODED_BLOCK:
240 : case FREED_BLOCK: {
241 E : return USE_AFTER_FREE;
242 : break;
243 : }
244 : }
245 :
246 i : return UNKNOWN_BAD_ACCESS;
247 E : }
248 :
249 : void ErrorInfoGetAsanBlockInfo(const Shadow* shadow,
250 : const BlockInfo& block_info,
251 : StackCaptureCache* stack_cache,
252 E : AsanBlockInfo* asan_block_info) {
253 E : DCHECK_NE(static_cast<StackCaptureCache*>(nullptr), stack_cache);
254 E : DCHECK_NE(static_cast<AsanBlockInfo*>(nullptr), asan_block_info);
255 :
256 E : ::memset(asan_block_info, 0, sizeof(*asan_block_info));
257 E : BlockState block_state = BlockDetermineMostLikelyState(shadow, block_info);
258 E : BlockAnalyze(block_state, block_info, &asan_block_info->analysis);
259 :
260 E : asan_block_info->header = block_info.header;
261 E : asan_block_info->user_size = block_info.header->body_size;
262 E : asan_block_info->state = block_info.header->state;
263 E : asan_block_info->alloc_tid = block_info.trailer->alloc_tid;
264 E : asan_block_info->free_tid = block_info.trailer->free_tid;
265 :
266 E : if (block_info.header->state != ALLOCATED_BLOCK &&
267 : block_info.trailer->free_ticks != 0) {
268 E : asan_block_info->milliseconds_since_free =
269 : ::GetTickCount() - block_info.trailer->free_ticks;
270 : }
271 :
272 : // TODO(chrisha): Use detailed analysis results to do this more efficiently.
273 E : asan_block_info->heap_type = kUnknownHeapType;
274 E : HeapManagerInterface::HeapId heap_id = block_info.trailer->heap_id;
275 E : if (heap_id != 0) {
276 E : AsanRuntime* runtime = AsanRuntime::runtime();
277 E : DCHECK_NE(static_cast<AsanRuntime*>(nullptr), runtime);
278 E : asan_block_info->heap_type = runtime->GetHeapType(heap_id);
279 : }
280 :
281 : // Copy the alloc and free stack traces if they're valid.
282 : // TODO(chrisha): Use detailed analysis results that have been gathered
283 : // once, rather than recalculating this.
284 E : if (stack_cache->StackCapturePointerIsValid(
285 : block_info.header->alloc_stack)) {
286 E : CopyStackCaptureToArray(block_info.header->alloc_stack,
287 : asan_block_info->alloc_stack,
288 : &asan_block_info->alloc_stack_size);
289 : }
290 E : if (block_info.header->state != ALLOCATED_BLOCK &&
291 : stack_cache->StackCapturePointerIsValid(
292 : block_info.header->free_stack)) {
293 E : CopyStackCaptureToArray(block_info.header->free_stack,
294 : asan_block_info->free_stack,
295 : &asan_block_info->free_stack_size);
296 : }
297 E : }
298 :
299 : void GetAsanErrorShadowMemory(const Shadow* shadow,
300 : const void* error_location,
301 E : AsanErrorShadowMemory* shadow_memory) {
302 E : DCHECK_NE(static_cast<AsanErrorShadowMemory*>(nullptr), shadow_memory);
303 :
304 E : shadow_memory->index = reinterpret_cast<uintptr_t>(error_location);
305 E : shadow_memory->index >>= kShadowRatioLog;
306 E : shadow_memory->index = (shadow_memory->index / shadow->kShadowBytesPerLine) *
307 : Shadow::kShadowBytesPerLine;
308 :
309 : uintptr_t index_min =
310 E : shadow_memory->index -
311 : Shadow::kShadowContextLines * Shadow::kShadowBytesPerLine;
312 E : if (index_min > shadow_memory->index)
313 i : index_min = 0;
314 : uintptr_t index_max =
315 E : shadow_memory->index +
316 : Shadow::kShadowContextLines * Shadow::kShadowBytesPerLine;
317 E : if (index_max < shadow_memory->index)
318 i : index_max = 0;
319 :
320 E : shadow_memory->address =
321 : reinterpret_cast<uintptr_t>(shadow->shadow() + index_min);
322 E : shadow_memory->length = index_max - index_min;
323 E : }
324 :
325 : namespace {
326 :
327 : const char kAllocatedBlock[] = "allocated";
328 : const char kQuarantinedBlock[] = "quarantined";
329 : const char kQuarantinedFloodedBlock[] = "quarantined (flooded)";
330 : const char kFreedBlock[] = "freed";
331 : const char kAccessModeRead[] = "read";
332 : const char kAccessModeWrite[] = "write";
333 : const char kAccessModeUnknown[] = "(unknown)";
334 :
335 : // Converts an access mode to a string.
336 E : void AccessModeToString(AccessMode access_mode, std::string* str) {
337 E : DCHECK_NE(static_cast<std::string*>(nullptr), str);
338 E : switch (access_mode) {
339 : case ASAN_READ_ACCESS:
340 E : *str = kAccessModeRead;
341 E : break;
342 : case ASAN_WRITE_ACCESS:
343 E : *str = kAccessModeWrite;
344 E : break;
345 : default:
346 E : *str = kAccessModeUnknown;
347 : break;
348 : }
349 E : }
350 :
351 : // Converts an access mode string to its corresponding enum value.
352 : AccessMode AccessModeStringToEnum(const std::string& access_mode) {
353 : if (::strcmp(kAccessModeRead, access_mode.c_str()) == 0) {
354 : return ASAN_READ_ACCESS;
355 : } else if (::strcmp(kAccessModeWrite, access_mode.c_str()) == 0) {
356 : return ASAN_WRITE_ACCESS;
357 : } else if (::strcmp(kAccessModeUnknown, access_mode.c_str()) == 0) {
358 : return ASAN_UNKNOWN_ACCESS;
359 : } else {
360 : NOTREACHED() << "Unexpected access mode.";
361 : return ASAN_ACCESS_MODE_MAX;
362 : }
363 : }
364 :
365 : // Converts a block state to a string.
366 E : void BlockStateToString(BlockState block_state, std::string* str) {
367 E : DCHECK_NE(static_cast<std::string*>(nullptr), str);
368 E : switch (block_state) {
369 : case ALLOCATED_BLOCK:
370 E : *str = kAllocatedBlock;
371 E : break;
372 : case QUARANTINED_BLOCK:
373 E : *str = kQuarantinedBlock;
374 E : break;
375 : case QUARANTINED_FLOODED_BLOCK:
376 E : *str = kQuarantinedFloodedBlock;
377 E : break;
378 i : case FREED_BLOCK: *str = "freed"; break;
379 : }
380 E : }
381 :
382 : // Converts a block state string to its corresponding enum value.
383 E : BlockState BlockStateStringToEnum(const std::string& block_state) {
384 E : if (::strcmp(kAllocatedBlock, block_state.c_str()) == 0) {
385 i : return ALLOCATED_BLOCK;
386 E : } else if (::strcmp(kQuarantinedBlock, block_state.c_str()) == 0) {
387 i : return QUARANTINED_BLOCK;
388 E : } else if (::strcmp(kQuarantinedFloodedBlock, block_state.c_str()) == 0) {
389 E : return QUARANTINED_FLOODED_BLOCK;
390 i : } else if (::strcmp(kFreedBlock, block_state.c_str()) == 0) {
391 i : return FREED_BLOCK;
392 i : } else {
393 i : NOTREACHED() << "Unexpected block state.";
394 i : return BLOCK_STATE_MAX;
395 : }
396 E : }
397 :
398 : // Converts a heap type string to its corresponding enum value.
399 E : HeapType HeapTypeStrToEnum(const std::string& heap_type) {
400 E : for (size_t i = 0; i < kHeapTypeMax; ++i) {
401 E : if (::strcmp(kHeapTypes[i], heap_type.c_str()) == 0) {
402 E : return static_cast<HeapType>(i);
403 : }
404 E : }
405 i : NOTREACHED() << "Unexpected heap type.";
406 i : return kHeapTypeMax;
407 E : }
408 :
409 E : uint64_t CastAddress(const void* address) {
410 E : return static_cast<uint64_t>(reinterpret_cast<uint32_t>(address));
411 E : }
412 :
413 : void PopulateStackTrace(const void* const* frames,
414 : size_t frame_count,
415 E : crashdata::StackTrace* stack_trace) {
416 E : DCHECK_NE(static_cast<void*>(nullptr), frames);
417 E : DCHECK_LT(0u, frame_count);
418 E : DCHECK_NE(static_cast<crashdata::StackTrace*>(nullptr), stack_trace);
419 E : for (size_t i = 0; i < frame_count; ++i)
420 E : stack_trace->add_frames(CastAddress(frames[i]));
421 E : }
422 :
423 E : void DataStateToString(DataState data_state, std::string* str) {
424 E : DCHECK_NE(static_cast<std::string*>(nullptr), str);
425 E : switch (data_state) {
426 : default:
427 E : case kDataStateUnknown: *str = "(unknown)"; break;
428 E : case kDataIsClean: *str = "clean"; break;
429 E : case kDataIsCorrupt: *str = "corrupt"; break;
430 : }
431 E : }
432 :
433 : void PopulateBlockAnalysisResult(const BlockAnalysisResult& analysis,
434 E : crashdata::Dictionary* dict) {
435 E : DCHECK_NE(static_cast<crashdata::Dictionary*>(nullptr), dict);
436 E : DataStateToString(
437 : analysis.block_state,
438 : crashdata::LeafGetString(crashdata::DictAddLeaf("block", dict)));
439 E : DataStateToString(
440 : analysis.header_state,
441 : crashdata::LeafGetString(crashdata::DictAddLeaf("header", dict)));
442 E : DataStateToString(
443 : analysis.body_state,
444 : crashdata::LeafGetString(crashdata::DictAddLeaf("body", dict)));
445 E : DataStateToString(
446 : analysis.trailer_state,
447 : crashdata::LeafGetString(crashdata::DictAddLeaf("trailer", dict)));
448 E : }
449 :
450 : } // namespace
451 :
452 : void PopulateBlockInfo(const Shadow* shadow,
453 : const AsanBlockInfo& block_info,
454 : bool include_block_contents,
455 : crashdata::Value* value,
456 E : MemoryRanges* memory_ranges) {
457 E : DCHECK_NE(static_cast<Shadow*>(nullptr), shadow);
458 E : DCHECK_NE(static_cast<crashdata::Value*>(nullptr), value);
459 :
460 E : crashdata::Dictionary* dict = ValueGetDict(value);
461 E : DCHECK_NE(static_cast<crashdata::Dictionary*>(nullptr), dict);
462 :
463 : // Set block properties.
464 : crashdata::LeafGetAddress(crashdata::DictAddLeaf("header", dict))
465 E : ->set_address(CastAddress(block_info.header));
466 E : crashdata::LeafSetUInt(block_info.user_size,
467 : crashdata::DictAddLeaf("user-size", dict));
468 E : BlockStateToString(
469 : static_cast<BlockState>(block_info.state),
470 : crashdata::LeafGetString(crashdata::DictAddLeaf("state", dict)));
471 : crashdata::LeafGetString(crashdata::DictAddLeaf("heap-type", dict))
472 E : ->assign(kHeapTypes[block_info.heap_type]);
473 :
474 : // Set the block analysis.
475 E : PopulateBlockAnalysisResult(
476 : block_info.analysis,
477 : crashdata::ValueGetDict(crashdata::DictAddValue("analysis", dict)));
478 :
479 : // Set the allocation information.
480 E : if (block_info.alloc_stack_size != 0) {
481 E : crashdata::LeafSetUInt(block_info.alloc_tid,
482 : crashdata::DictAddLeaf("alloc-thread-id", dict));
483 E : PopulateStackTrace(block_info.alloc_stack,
484 : block_info.alloc_stack_size,
485 : crashdata::LeafGetStackTrace(
486 : crashdata::DictAddLeaf("alloc-stack", dict)));
487 : }
488 :
489 : // Set the free information if available.
490 E : if (block_info.free_stack_size != 0) {
491 E : crashdata::LeafSetUInt(block_info.free_tid,
492 : crashdata::DictAddLeaf("free-thread-id", dict));
493 E : PopulateStackTrace(block_info.free_stack,
494 : block_info.free_stack_size,
495 : crashdata::LeafGetStackTrace(
496 : crashdata::DictAddLeaf("free-stack", dict)));
497 E : crashdata::LeafSetUInt(
498 : block_info.milliseconds_since_free,
499 : crashdata::DictAddLeaf("milliseconds-since-free", dict));
500 : }
501 :
502 E : if (include_block_contents) {
503 : // Get the full block information from the shadow memory.
504 E : BlockInfo full_block_info = {};
505 E : shadow->BlockInfoFromShadow(block_info.header, &full_block_info);
506 :
507 : // Copy the entire block contents.
508 E : crashdata::Blob* blob = crashdata::LeafGetBlob(
509 : crashdata::DictAddLeaf("contents", dict));
510 E : blob->mutable_address()->set_address(CastAddress(block_info.header));
511 :
512 : // Use memory range feature if available. Fallback on blob data.
513 E : if (memory_ranges) {
514 : // protobuf accepts uint32 as size, so it must fit.
515 E : DCHECK_LE(full_block_info.block_size,
516 E : static_cast<size_t>(std::numeric_limits<uint32_t>::max()));
517 E : blob->set_size(static_cast<uint32_t>(full_block_info.block_size));
518 :
519 E : memory_ranges->push_back(std::pair<const char*, size_t>(
520 : static_cast<const char*>(block_info.header),
521 : full_block_info.block_size));
522 E : } else {
523 E : blob->mutable_data()->assign(
524 : reinterpret_cast<const char*>(block_info.header),
525 : full_block_info.block_size);
526 : }
527 :
528 : // Copy the associated shadow memory.
529 : size_t shadow_index =
530 E : reinterpret_cast<size_t>(block_info.header) / kShadowRatio;
531 E : size_t shadow_length = full_block_info.block_size / kShadowRatio;
532 : const char* shadow_data =
533 E : reinterpret_cast<const char*>(shadow->shadow()) +
534 : shadow_index;
535 E : blob = crashdata::LeafGetBlob(
536 : crashdata::DictAddLeaf("shadow", dict));
537 E : blob->mutable_address()->set_address(CastAddress(shadow_data));
538 :
539 : // Use memory range feature if available. Fallback on blob data.
540 E : if (memory_ranges) {
541 : // protobuf accepts uint32 as size, so it must fit.
542 E : DCHECK_LE(shadow_length,
543 E : static_cast<size_t>(std::numeric_limits<uint32_t>::max()));
544 E : blob->set_size(static_cast<uint32_t>(shadow_length));
545 :
546 E : memory_ranges->push_back(
547 : std::pair<const char*, size_t>(shadow_data, shadow_length));
548 E : } else {
549 E : blob->mutable_data()->assign(shadow_data, shadow_length);
550 : }
551 : }
552 E : }
553 :
554 : void PopulateCorruptBlockRange(const Shadow* shadow,
555 : const AsanCorruptBlockRange& range,
556 : crashdata::Value* value,
557 E : MemoryRanges* memory_ranges) {
558 E : DCHECK_NE(static_cast<Shadow*>(nullptr), shadow);
559 E : DCHECK_NE(static_cast<crashdata::Value*>(nullptr), value);
560 :
561 E : crashdata::Dictionary* dict = ValueGetDict(value);
562 E : DCHECK_NE(static_cast<crashdata::Dictionary*>(nullptr), dict);
563 :
564 : crashdata::LeafGetAddress(crashdata::DictAddLeaf("address", dict))
565 E : ->set_address(CastAddress(range.address));
566 E : crashdata::LeafSetUInt(range.length, crashdata::DictAddLeaf("length", dict));
567 E : crashdata::LeafSetUInt(range.block_count,
568 : crashdata::DictAddLeaf("block-count", dict));
569 :
570 : // Add the blocks.
571 E : if (range.block_info_count > 0) {
572 : crashdata::ValueList* list =
573 E : crashdata::ValueGetValueList(crashdata::DictAddValue("blocks", dict));
574 E : for (size_t i = 0; i < range.block_info_count; ++i) {
575 E : if (range.block_info[i].header != nullptr)
576 : // Emit the block info but don't explicitly include the contents.
577 E : PopulateBlockInfo(shadow, range.block_info[i], false,
578 : list->add_values(), memory_ranges);
579 E : }
580 : }
581 E : }
582 :
583 : namespace {
584 :
585 : void PopulateShadowMemoryBlob(const Shadow* shadow,
586 : const AsanErrorInfo& error_info,
587 : crashdata::Dictionary* dict,
588 E : MemoryRanges* memory_ranges) {
589 E : DCHECK_NE(static_cast<Shadow*>(nullptr), shadow);
590 E : DCHECK_NE(static_cast<crashdata::Dictionary*>(nullptr), dict);
591 :
592 : // The shadow-info string can be reconstructed from information already in
593 : // the crash (location, block-info, access-mode, access-size), so there's no
594 : // need to send it. This is emitted as a blob.
595 E : AsanErrorShadowMemory shadow_memory = {};
596 E : GetAsanErrorShadowMemory(shadow, error_info.location, &shadow_memory);
597 :
598 E : crashdata::LeafSetUInt(shadow_memory.index,
599 : crashdata::DictAddLeaf("shadow-memory-index", dict));
600 E : crashdata::Blob* blob = crashdata::LeafGetBlob(
601 : crashdata::DictAddLeaf("shadow-memory", dict));
602 E : blob->mutable_address()->set_address(shadow_memory.address);
603 :
604 : // Use memory range feature if available. Fallback on blob data.
605 E : if (memory_ranges) {
606 E : blob->set_size(shadow_memory.length);
607 :
608 E : const char* data = reinterpret_cast<const char*>(shadow_memory.address);
609 E : memory_ranges->push_back(
610 : std::pair<const char*, size_t>(data, shadow_memory.length));
611 E : } else {
612 E : blob->mutable_data()->assign(
613 : reinterpret_cast<const char*>(shadow_memory.address),
614 : shadow_memory.length);
615 : }
616 E : }
617 :
618 : void PopulatePageBitsBlob(const Shadow* shadow,
619 : const AsanErrorInfo& error_info,
620 : crashdata::Dictionary* dict,
621 E : MemoryRanges* memory_ranges) {
622 E : DCHECK_NE(static_cast<crashdata::Dictionary*>(nullptr), dict);
623 :
624 : // Emit information about page protections surround the address in question.
625 : static const size_t kPageBitsContext = 2;
626 E : uintptr_t index = reinterpret_cast<uintptr_t>(error_info.location);
627 E : index /= GetPageSize(); // 1 bit per page.
628 E : index /= 8; // 8 bits per byte.
629 E : uintptr_t index_min = index - kPageBitsContext;
630 E : if (index_min > index)
631 E : index_min = 0;
632 E : uintptr_t index_max = index + 1 + kPageBitsContext;
633 E : if (index_max < index)
634 i : index_max = 0;
635 E : uintptr_t length = index_max - index_min;
636 :
637 E : crashdata::LeafSetUInt(
638 : index, crashdata::DictAddLeaf("page-bits-index", dict));
639 : crashdata::Blob* blob =
640 E : crashdata::LeafGetBlob(crashdata::DictAddLeaf("page-bits", dict));
641 E : blob->mutable_address()->set_address(
642 : CastAddress(shadow->page_bits() + index_min));
643 :
644 : // Use memory range feature if available. Fallback on blob data.
645 E : if (memory_ranges) {
646 E : blob->set_size(length);
647 :
648 : const char* data =
649 E : reinterpret_cast<const char*>(shadow->page_bits() + index_min);
650 E : memory_ranges->push_back(std::pair<const char*, size_t>(data, length));
651 E : } else {
652 E : blob->mutable_data()->assign(
653 : reinterpret_cast<const char*>(shadow->page_bits() + index_min), length);
654 : }
655 E : }
656 :
657 : void PopulateAsanParameters(const AsanErrorInfo& error_info,
658 E : crashdata::Dictionary* dict) {
659 E : DCHECK_NE(static_cast<crashdata::Dictionary*>(nullptr), dict);
660 :
661 : // Any new parameter added to the parameters structure should also be added
662 : // here.
663 : static_assert(14 == ::common::kAsanParametersVersion,
664 : "Pointers in the params must be linked up here.");
665 E : crashdata::Dictionary* param_dict = crashdata::DictAddDict("asan-parameters",
666 : dict);
667 E : DCHECK_NE(static_cast<crashdata::Dictionary*>(nullptr), param_dict);
668 E : crashdata::LeafSetUInt(error_info.asan_parameters.quarantine_size,
669 : crashdata::DictAddLeaf("quarantine-size", param_dict));
670 E : crashdata::LeafSetUInt(error_info.asan_parameters.trailer_padding_size,
671 : crashdata::DictAddLeaf("trailer-padding-size",
672 : param_dict));
673 E : crashdata::LeafSetUInt(error_info.asan_parameters.quarantine_block_size,
674 : crashdata::DictAddLeaf("quarantine-block-size",
675 : param_dict));
676 E : crashdata::LeafSetUInt(error_info.asan_parameters.check_heap_on_failure,
677 : crashdata::DictAddLeaf("check-heap-on-failure",
678 : param_dict));
679 E : crashdata::LeafSetUInt(error_info.asan_parameters.enable_zebra_block_heap,
680 : crashdata::DictAddLeaf("enable-zebra-block-heap",
681 : param_dict));
682 E : crashdata::LeafSetUInt(error_info.asan_parameters.enable_large_block_heap,
683 : crashdata::DictAddLeaf("enable-large-block-heap",
684 : param_dict));
685 E : crashdata::LeafSetUInt(error_info.asan_parameters.enable_allocation_filter,
686 : crashdata::DictAddLeaf("enable-allocation-filter",
687 : param_dict));
688 E : crashdata::LeafSetReal(error_info.asan_parameters.allocation_guard_rate,
689 : crashdata::DictAddLeaf("allocation-guard-rate",
690 : param_dict));
691 E : crashdata::LeafSetUInt(error_info.asan_parameters.zebra_block_heap_size,
692 : crashdata::DictAddLeaf("zebra-block-heap-size",
693 : param_dict));
694 E : crashdata::LeafSetReal(
695 : error_info.asan_parameters.zebra_block_heap_quarantine_ratio,
696 : crashdata::DictAddLeaf("zebra-block-heap-quarantine-ratio", param_dict));
697 E : crashdata::LeafSetUInt(error_info.asan_parameters.large_allocation_threshold,
698 : crashdata::DictAddLeaf("large-allocation-threshold",
699 : param_dict));
700 E : crashdata::LeafSetReal(
701 : error_info.asan_parameters.quarantine_flood_fill_rate,
702 : crashdata::DictAddLeaf("quarantine-flood-fill-rate", param_dict));
703 E : }
704 :
705 : } // namespace
706 :
707 : // TODO(chrisha): Only emit information that makes sense for the given error
708 : // type. For example, wild-access errors have no associated
709 : // block information.
710 : void PopulateErrorInfo(const Shadow* shadow,
711 : const AsanErrorInfo& error_info,
712 : crashdata::Value* value,
713 E : MemoryRanges* memory_ranges) {
714 E : DCHECK_NE(static_cast<Shadow*>(nullptr), shadow);
715 E : DCHECK_NE(static_cast<crashdata::Value*>(nullptr), value);
716 :
717 : // Create a single outermost dictionary.
718 E : crashdata::Dictionary* dict = ValueGetDict(value);
719 E : DCHECK_NE(static_cast<crashdata::Dictionary*>(nullptr), dict);
720 :
721 : crashdata::LeafGetAddress(crashdata::DictAddLeaf("location", dict))
722 E : ->set_address(CastAddress(error_info.location));
723 E : crashdata::LeafSetUInt(error_info.crash_stack_id,
724 : crashdata::DictAddLeaf("crash-stack-id", dict));
725 E : if (error_info.block_info.header != nullptr) {
726 : // Include the block contents only if the block isn't too large. This tries
727 : // to reflect the cap on crash server minidump sizes.
728 : // TODO(chrisha): This decision should be made higher up the stack, and not
729 : // here.
730 E : bool include_block_info = error_info.block_info.user_size < 100 * 1024;
731 E : PopulateBlockInfo(shadow, error_info.block_info, include_block_info,
732 : crashdata::DictAddValue("block-info", dict),
733 : memory_ranges);
734 : }
735 : crashdata::LeafGetString(crashdata::DictAddLeaf("error-type", dict))
736 E : ->assign(ErrorInfoAccessTypeToStr(error_info.error_type));
737 E : AccessModeToString(
738 : error_info.access_mode,
739 : crashdata::LeafGetString(crashdata::DictAddLeaf("access-mode", dict)));
740 E : crashdata::LeafSetUInt(error_info.access_size,
741 : crashdata::DictAddLeaf("access-size", dict));
742 :
743 E : PopulateShadowMemoryBlob(shadow, error_info, dict, memory_ranges);
744 E : PopulatePageBitsBlob(shadow, error_info, dict, memory_ranges);
745 :
746 : // Send information about corruption.
747 E : crashdata::LeafSetUInt(error_info.heap_is_corrupt,
748 : crashdata::DictAddLeaf("heap-is-corrupt", dict));
749 E : crashdata::LeafSetUInt(error_info.corrupt_range_count,
750 : crashdata::DictAddLeaf("corrupt-range-count", dict));
751 E : crashdata::LeafSetUInt(error_info.corrupt_block_count,
752 : crashdata::DictAddLeaf("corrupt-block-count", dict));
753 E : if (error_info.corrupt_ranges_reported > 0) {
754 E : crashdata::ValueList* list = crashdata::ValueGetValueList(
755 : crashdata::DictAddValue("corrupt-ranges", dict));
756 E : for (size_t i = 0; i < error_info.corrupt_ranges_reported; ++i) {
757 E : PopulateCorruptBlockRange(shadow, error_info.corrupt_ranges[i],
758 : list->add_values(), memory_ranges);
759 E : }
760 : }
761 E : PopulateAsanParameters(error_info, dict);
762 E : }
763 :
764 : void CrashdataProtobufToErrorInfo(const crashdata::Value& protobuf,
765 E : AsanErrorInfo* error_info) {
766 E : DCHECK_NE(static_cast<AsanErrorInfo*>(nullptr), error_info);
767 :
768 E : DCHECK_EQ(crashdata::Value_Type_DICTIONARY, protobuf.type());
769 :
770 E : const crashdata::Dictionary outer_dict = protobuf.dictionary();
771 E : DCHECK(outer_dict.IsInitialized());
772 E : for (const auto& iter : outer_dict.values()) {
773 E : if (iter.value().has_leaf()) {
774 E : const crashdata::Leaf& leaf = iter.value().leaf();
775 E : if (base::LowerCaseEqualsASCII(iter.key(), "header")) {
776 E : DCHECK(leaf.has_address());
777 E : error_info->block_info.header =
778 : reinterpret_cast<const void*>(leaf.address().address());
779 E : } else if (base::LowerCaseEqualsASCII(iter.key(), "user-size")) {
780 E : DCHECK(leaf.has_unsigned_integer());
781 E : error_info->block_info.user_size = leaf.unsigned_integer();
782 E : } else if (base::LowerCaseEqualsASCII(iter.key(), "state")) {
783 E : DCHECK(leaf.has_string());
784 E : error_info->block_info.state = BlockStateStringToEnum(leaf.string());
785 E : } else if (base::LowerCaseEqualsASCII(iter.key(), "heap-type")) {
786 E : DCHECK(leaf.has_string());
787 E : error_info->block_info.heap_type = HeapTypeStrToEnum(leaf.string());
788 E : } else if (base::LowerCaseEqualsASCII(iter.key(), "alloc-thread-id")) {
789 E : DCHECK(leaf.has_unsigned_integer());
790 E : error_info->block_info.alloc_tid =
791 : static_cast<DWORD>(leaf.unsigned_integer());
792 E : } else if (base::LowerCaseEqualsASCII(iter.key(), "alloc-stack")) {
793 E : DCHECK(leaf.has_stack_trace());
794 : // TODO(sebmarchand): Parse this field.
795 E : } else if (base::LowerCaseEqualsASCII(iter.key(), "free-thread-id")) {
796 E : DCHECK(leaf.has_unsigned_integer());
797 E : error_info->block_info.free_tid =
798 : static_cast<DWORD>(leaf.unsigned_integer());
799 E : } else if (base::LowerCaseEqualsASCII(iter.key(), "free-stack")) {
800 E : DCHECK(leaf.has_stack_trace());
801 : // TODO(sebmarchand): Parse this field.
802 E : } else if (base::LowerCaseEqualsASCII(iter.key(),
803 : "milliseconds-since-free")) {
804 E : DCHECK(leaf.has_unsigned_integer());
805 E : error_info->block_info.milliseconds_since_free =
806 : leaf.unsigned_integer();
807 E : } else if (base::LowerCaseEqualsASCII(iter.key(), "contents")) {
808 E : DCHECK(leaf.has_blob());
809 : // TODO(sebmarchand): Parse this field.
810 E : } else if (base::LowerCaseEqualsASCII(iter.key(), "shadow")) {
811 E : DCHECK(leaf.has_blob());
812 : // TODO(sebmarchand): Parse this field.
813 E : } else {
814 i : NOTREACHED() << "Unexpected leaf entry.";
815 E : }
816 E : } else if (iter.value().has_dictionary()) {
817 E : if (base::LowerCaseEqualsASCII(iter.key(), "analysis")) {
818 : // TODO(sebmarchand): Parse this field.
819 E : } else {
820 i : NOTREACHED() << "Unexpected dictionary entry.";
821 : }
822 E : } else {
823 i : NOTREACHED() << "Unexpected entry.";
824 : }
825 E : }
826 E : }
827 :
828 : } // namespace asan
829 : } // namespace agent
|