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