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 "base/strings/string_util.h"
18 : #include "syzygy/agent/asan/asan_runtime.h"
19 : #include "syzygy/agent/asan/block_utils.h"
20 : #include "syzygy/agent/asan/shadow.h"
21 : #include "syzygy/agent/asan/stack_capture_cache.h"
22 : #include "syzygy/crashdata/crashdata.h"
23 :
24 : namespace agent {
25 : namespace asan {
26 :
27 : namespace {
28 :
29 : // Copy a stack capture object into an array.
30 : // @param stack_capture The stack capture that we want to copy.
31 : // @param dst Will receive the stack frames.
32 : // @param dst_size Will receive the number of frames that has been copied.
33 : void CopyStackCaptureToArray(const common::StackCapture* stack_capture,
34 E : void* dst, uint8* dst_size) {
35 E : DCHECK_NE(reinterpret_cast<common::StackCapture*>(NULL), stack_capture);
36 E : DCHECK_NE(reinterpret_cast<void*>(NULL), dst);
37 E : DCHECK_NE(reinterpret_cast<uint8*>(NULL), dst_size);
38 : ::memcpy(dst,
39 : stack_capture->frames(),
40 E : stack_capture->num_frames() * sizeof(void*));
41 E : *dst_size = stack_capture->num_frames();
42 E : }
43 :
44 : // Get the information about an address relative to a block.
45 : // @param header The header of the block containing this address.
46 : // @param bad_access_info Will receive the information about this address.
47 : void GetAddressInformation(BlockHeader* header,
48 E : AsanErrorInfo* bad_access_info) {
49 E : DCHECK(header != NULL);
50 E : DCHECK(bad_access_info != NULL);
51 :
52 E : DCHECK(header != NULL);
53 E : DCHECK(bad_access_info != NULL);
54 E : DCHECK(bad_access_info->location != NULL);
55 :
56 E : BlockInfo block_info = {};
57 E : Shadow::BlockInfoFromShadow(header, &block_info);
58 E : int offset = 0;
59 E : char* offset_relativity = "";
60 E : switch (bad_access_info->error_type) {
61 : case HEAP_BUFFER_OVERFLOW: {
62 : offset = static_cast<const uint8*>(bad_access_info->location)
63 E : - block_info.body - block_info.body_size;
64 E : offset_relativity = "beyond";
65 E : break;
66 : }
67 : case HEAP_BUFFER_UNDERFLOW: {
68 : offset = block_info.body -
69 E : static_cast<const uint8*>(bad_access_info->location);
70 E : offset_relativity = "before";
71 E : break;
72 : }
73 : case USE_AFTER_FREE: {
74 : offset = static_cast<const uint8*>(bad_access_info->location)
75 E : - block_info.body;
76 E : offset_relativity = "inside";
77 E : break;
78 : }
79 : case WILD_ACCESS:
80 : case DOUBLE_FREE:
81 : case UNKNOWN_BAD_ACCESS:
82 : case CORRUPT_BLOCK:
83 E : return;
84 : default:
85 i : NOTREACHED() << "Error trying to dump address information.";
86 : }
87 :
88 : size_t shadow_info_bytes = base::snprintf(
89 : bad_access_info->shadow_info,
90 : arraysize(bad_access_info->shadow_info) - 1,
91 : "%08X is %d bytes %s %d-byte block [%08X,%08X)\n",
92 : bad_access_info->location,
93 : offset,
94 : offset_relativity,
95 : block_info.body_size,
96 : block_info.body,
97 E : block_info.trailer_padding);
98 :
99 E : std::string shadow_memory;
100 E : Shadow::AppendShadowArrayText(bad_access_info->location, &shadow_memory);
101 : size_t shadow_mem_bytes = base::snprintf(
102 : bad_access_info->shadow_memory,
103 : arraysize(bad_access_info->shadow_memory) - 1,
104 : "%s",
105 E : shadow_memory.c_str());
106 :
107 : // Ensure that we had enough space to store the full shadow information.
108 E : DCHECK_LE(shadow_info_bytes, arraysize(bad_access_info->shadow_info) - 1);
109 E : DCHECK_LE(shadow_mem_bytes, arraysize(bad_access_info->shadow_memory) - 1);
110 E : }
111 :
112 : } // namespace
113 :
114 : const char kHeapUseAfterFree[] = "heap-use-after-free";
115 : const char kHeapBufferUnderFlow[] = "heap-buffer-underflow";
116 : const char kHeapBufferOverFlow[] = "heap-buffer-overflow";
117 : const char kAttemptingDoubleFree[] = "attempting double-free";
118 : const char kInvalidAddress[] = "invalid-address";
119 : const char kWildAccess[] = "wild-access";
120 : const char kHeapUnknownError[] = "heap-unknown-error";
121 : const char kHeapCorruptBlock[] = "corrupt-block";
122 : const char kCorruptHeap[] = "corrupt-heap";
123 :
124 E : const char* ErrorInfoAccessTypeToStr(BadAccessKind bad_access_kind) {
125 E : switch (bad_access_kind) {
126 : case USE_AFTER_FREE:
127 E : return kHeapUseAfterFree;
128 : case HEAP_BUFFER_UNDERFLOW:
129 E : return kHeapBufferUnderFlow;
130 : case HEAP_BUFFER_OVERFLOW:
131 E : return kHeapBufferOverFlow;
132 : case WILD_ACCESS:
133 E : return kWildAccess;
134 : case INVALID_ADDRESS:
135 E : return kInvalidAddress;
136 : case DOUBLE_FREE:
137 E : return kAttemptingDoubleFree;
138 : case UNKNOWN_BAD_ACCESS:
139 E : return kHeapUnknownError;
140 : case CORRUPT_BLOCK:
141 E : return kHeapCorruptBlock;
142 : case CORRUPT_HEAP:
143 E : return kCorruptHeap;
144 : default:
145 i : NOTREACHED() << "Unexpected bad access kind.";
146 i : return NULL;
147 : }
148 E : }
149 :
150 : bool ErrorInfoGetBadAccessInformation(StackCaptureCache* stack_cache,
151 E : AsanErrorInfo* bad_access_info) {
152 E : DCHECK_NE(reinterpret_cast<StackCaptureCache*>(NULL), stack_cache);
153 E : DCHECK_NE(reinterpret_cast<AsanErrorInfo*>(NULL), bad_access_info);
154 E : BlockInfo block_info = {};
155 E : if (!Shadow::BlockInfoFromShadow(bad_access_info->location, &block_info))
156 E : return false;
157 :
158 : // Fill out the information about the primary block.
159 : ErrorInfoGetAsanBlockInfo(
160 E : block_info, stack_cache, &bad_access_info->block_info);
161 :
162 : if (bad_access_info->error_type != DOUBLE_FREE &&
163 E : bad_access_info->error_type != CORRUPT_BLOCK) {
164 : bad_access_info->error_type =
165 E : ErrorInfoGetBadAccessKind(bad_access_info->location, block_info.header);
166 : }
167 :
168 : // Get the bad access description if we've been able to determine its kind.
169 E : if (bad_access_info->error_type != UNKNOWN_BAD_ACCESS) {
170 E : GetAddressInformation(block_info.header, bad_access_info);
171 E : return true;
172 : }
173 :
174 i : return false;
175 E : }
176 :
177 : BadAccessKind ErrorInfoGetBadAccessKind(const void* addr,
178 E : const BlockHeader* header) {
179 E : DCHECK_NE(reinterpret_cast<const void*>(NULL), addr);
180 E : DCHECK_NE(reinterpret_cast<const BlockHeader*>(NULL), header);
181 :
182 E : BadAccessKind bad_access_kind = UNKNOWN_BAD_ACCESS;
183 :
184 E : if (header->state == QUARANTINED_BLOCK) {
185 E : bad_access_kind = USE_AFTER_FREE;
186 E : } else {
187 E : BlockInfo block_info = {};
188 E : Shadow::BlockInfoFromShadow(header, &block_info);
189 E : if (addr < block_info.body) {
190 E : bad_access_kind = HEAP_BUFFER_UNDERFLOW;
191 E : } else if (addr >= (block_info.body + block_info.body_size)) {
192 E : bad_access_kind = HEAP_BUFFER_OVERFLOW;
193 i : } else if (Shadow::GetShadowMarkerForAddress(addr) == kHeapFreedMarker) {
194 : // This is a use after free on a block managed by a nested heap.
195 i : bad_access_kind = USE_AFTER_FREE;
196 : }
197 : }
198 E : return bad_access_kind;
199 E : }
200 :
201 : void ErrorInfoGetAsanBlockInfo(const BlockInfo& block_info,
202 : StackCaptureCache* stack_cache,
203 E : AsanBlockInfo* asan_block_info) {
204 E : DCHECK_NE(reinterpret_cast<StackCaptureCache*>(NULL), stack_cache);
205 E : DCHECK_NE(reinterpret_cast<AsanBlockInfo*>(NULL), asan_block_info);
206 :
207 E : ::memset(asan_block_info, 0, sizeof(*asan_block_info));
208 E : BlockAnalyze(block_info, &asan_block_info->analysis);
209 :
210 E : asan_block_info->header = block_info.header;
211 E : asan_block_info->user_size = block_info.header->body_size;
212 E : asan_block_info->state = block_info.header->state;
213 E : asan_block_info->alloc_tid = block_info.trailer->alloc_tid;
214 E : asan_block_info->free_tid = block_info.trailer->free_tid;
215 :
216 : if (block_info.header->state != ALLOCATED_BLOCK &&
217 E : block_info.trailer->free_ticks != 0) {
218 : asan_block_info->milliseconds_since_free =
219 E : ::GetTickCount() - block_info.trailer->free_ticks;
220 : }
221 :
222 : // TODO(chrisha): Use detailed analysis results to do this more efficiently.
223 E : asan_block_info->heap_type = kUnknownHeapType;
224 E : HeapManagerInterface::HeapId heap_id = block_info.trailer->heap_id;
225 E : if (heap_id != 0) {
226 E : AsanRuntime* runtime = AsanRuntime::runtime();
227 E : DCHECK_NE(static_cast<AsanRuntime*>(nullptr), runtime);
228 E : asan_block_info->heap_type = runtime->GetHeapType(heap_id);
229 : }
230 :
231 : // Copy the alloc and free stack traces if they're valid.
232 : // TODO(chrisha): Use detailed analysis results that have been gathered
233 : // once, rather than recalculating this.
234 : if (stack_cache->StackCapturePointerIsValid(
235 E : block_info.header->alloc_stack)) {
236 : CopyStackCaptureToArray(block_info.header->alloc_stack,
237 : asan_block_info->alloc_stack,
238 E : &asan_block_info->alloc_stack_size);
239 : }
240 : if (block_info.header->state != ALLOCATED_BLOCK &&
241 : stack_cache->StackCapturePointerIsValid(
242 E : block_info.header->free_stack)) {
243 : CopyStackCaptureToArray(block_info.header->free_stack,
244 : asan_block_info->free_stack,
245 E : &asan_block_info->free_stack_size);
246 : }
247 E : }
248 :
249 : namespace {
250 :
251 : // Converts an access mode to a string.
252 E : void AccessModeToString(AccessMode access_mode, std::string* str) {
253 E : DCHECK_NE(static_cast<std::string*>(nullptr), str);
254 E : switch (access_mode) {
255 E : case ASAN_READ_ACCESS: *str = "read"; break;
256 i : case ASAN_WRITE_ACCESS: *str = "write"; break;
257 E : default: *str = "(unknown)"; break;
258 : }
259 E : }
260 :
261 : // Converts a block state to a string.
262 E : void BlockStateToString(BlockState block_state, std::string* str) {
263 E : DCHECK_NE(static_cast<std::string*>(nullptr), str);
264 E : switch (block_state) {
265 E : case ALLOCATED_BLOCK: *str = "allocated"; break;
266 E : case QUARANTINED_BLOCK: *str = "quarantined"; break;
267 i : case FREED_BLOCK: *str = "freed"; break;
268 i : default: *str = "(unknown)"; break;
269 : }
270 E : }
271 :
272 E : uint64 CastAddress(const void* address) {
273 E : return static_cast<uint64>(reinterpret_cast<uint32>(address));
274 E : }
275 :
276 : void PopulateStackTrace(const void* const* frames,
277 : size_t frame_count,
278 E : crashdata::StackTrace* stack_trace) {
279 E : DCHECK_NE(static_cast<void*>(nullptr), frames);
280 E : DCHECK_LT(0u, frame_count);
281 E : DCHECK_NE(static_cast<crashdata::StackTrace*>(nullptr), stack_trace);
282 E : for (size_t i = 0; i < frame_count; ++i)
283 E : stack_trace->add_frames(CastAddress(frames[i]));
284 E : }
285 :
286 E : void DataStateToString(DataState data_state, std::string* str) {
287 E : DCHECK_NE(static_cast<std::string*>(nullptr), str);
288 E : switch (data_state) {
289 : default:
290 E : case kDataStateUnknown: *str = "(unknown)"; break;
291 E : case kDataIsClean: *str = "clean"; break;
292 E : case kDataIsCorrupt: *str = "corrupt"; break;
293 : }
294 E : }
295 :
296 : void PopulateBlockAnalysisResult(const BlockAnalysisResult& analysis,
297 E : crashdata::Dictionary* dict) {
298 E : DCHECK_NE(static_cast<crashdata::Dictionary*>(nullptr), dict);
299 : DataStateToString(
300 : analysis.block_state,
301 E : crashdata::LeafGetString(crashdata::DictAddLeaf("block", dict)));
302 : DataStateToString(
303 : analysis.header_state,
304 E : crashdata::LeafGetString(crashdata::DictAddLeaf("header", dict)));
305 : DataStateToString(
306 : analysis.body_state,
307 E : crashdata::LeafGetString(crashdata::DictAddLeaf("body", dict)));
308 : DataStateToString(
309 : analysis.trailer_state,
310 E : crashdata::LeafGetString(crashdata::DictAddLeaf("trailer", dict)));
311 E : }
312 :
313 : } // namespace
314 :
315 : void PopulateBlockInfo(const AsanBlockInfo& block_info,
316 E : crashdata::Value* value) {
317 E : DCHECK_NE(static_cast<crashdata::Value*>(nullptr), value);
318 :
319 E : crashdata::Dictionary* dict = ValueGetDict(value);
320 E : DCHECK_NE(static_cast<crashdata::Dictionary*>(nullptr), dict);
321 :
322 : // Set block properties.
323 : crashdata::LeafGetAddress(crashdata::DictAddLeaf("header", dict))
324 E : ->set_address(CastAddress(block_info.header));
325 : crashdata::LeafSetUInt(block_info.user_size,
326 E : crashdata::DictAddLeaf("user-size", dict));
327 : BlockStateToString(
328 : static_cast<BlockState>(block_info.state),
329 E : crashdata::LeafGetString(crashdata::DictAddLeaf("state", dict)));
330 : crashdata::LeafGetString(crashdata::DictAddLeaf("heap-type", dict))
331 E : ->assign(kHeapTypes[block_info.heap_type]);
332 :
333 : // Set the block analysis.
334 : PopulateBlockAnalysisResult(
335 : block_info.analysis,
336 E : crashdata::ValueGetDict(crashdata::DictAddValue("analysis", dict)));
337 :
338 : // Set the allocation information.
339 E : if (block_info.alloc_stack_size != 0) {
340 : crashdata::LeafSetUInt(block_info.alloc_tid,
341 E : crashdata::DictAddLeaf("alloc-thread-id", dict));
342 : PopulateStackTrace(block_info.alloc_stack,
343 : block_info.alloc_stack_size,
344 : crashdata::LeafGetStackTrace(
345 E : crashdata::DictAddLeaf("alloc-stack", dict)));
346 : }
347 :
348 : // Set the free information if available.
349 E : if (block_info.free_stack_size != 0) {
350 : crashdata::LeafSetUInt(block_info.free_tid,
351 E : crashdata::DictAddLeaf("free-thread-id", dict));
352 : PopulateStackTrace(block_info.free_stack,
353 : block_info.free_stack_size,
354 : crashdata::LeafGetStackTrace(
355 E : crashdata::DictAddLeaf("free-stack", dict)));
356 : crashdata::LeafSetUInt(
357 : block_info.milliseconds_since_free,
358 E : crashdata::DictAddLeaf("milliseconds-since-free", dict));
359 : }
360 E : }
361 :
362 : void PopulateCorruptBlockRange(const AsanCorruptBlockRange& range,
363 E : crashdata::Value* value) {
364 E : DCHECK_NE(static_cast<crashdata::Value*>(nullptr), value);
365 :
366 E : crashdata::Dictionary* dict = ValueGetDict(value);
367 E : DCHECK_NE(static_cast<crashdata::Dictionary*>(nullptr), dict);
368 :
369 : crashdata::LeafGetAddress(crashdata::DictAddLeaf("address", dict))
370 E : ->set_address(CastAddress(range.address));
371 E : crashdata::LeafSetUInt(range.length, crashdata::DictAddLeaf("length", dict));
372 : crashdata::LeafSetUInt(range.block_count,
373 E : crashdata::DictAddLeaf("block-count", dict));
374 :
375 : // Add the blocks.
376 E : if (range.block_info_count > 0) {
377 : crashdata::List* list = crashdata::ValueGetList(
378 E : crashdata::DictAddValue("blocks", dict));
379 E : for (size_t i = 0; i < range.block_info_count; ++i) {
380 E : if (range.block_info[i].header != nullptr)
381 E : PopulateBlockInfo(range.block_info[i], list->add_values());
382 E : }
383 : }
384 E : }
385 :
386 : namespace {
387 :
388 : void PopulateShadowMemoryBlob(const AsanErrorInfo& error_info,
389 E : crashdata::Dictionary* dict) {
390 E : DCHECK_NE(static_cast<crashdata::Dictionary*>(nullptr), dict);
391 :
392 : // The shadow-info string can be reconstructed from information already in
393 : // the crash (location, block-info, access-mode, access-size), so there's no
394 : // need to send it. This is emitted as a blob.
395 E : uintptr_t index = reinterpret_cast<uintptr_t>(error_info.location);
396 E : index >>= kShadowRatioLog;
397 E : index = (index / Shadow::kShadowBytesPerLine) * Shadow::kShadowBytesPerLine;
398 : uintptr_t index_min = index -
399 E : Shadow::kShadowContextLines * Shadow::kShadowBytesPerLine;
400 E : if (index_min > index)
401 i : index_min = 0;
402 : uintptr_t index_max = index +
403 E : Shadow::kShadowContextLines * Shadow::kShadowBytesPerLine;
404 E : if (index_max < index)
405 i : index_max = 0;
406 E : uintptr_t length = index_max - index_min;
407 : crashdata::LeafSetUInt(
408 E : index, crashdata::DictAddLeaf("shadow-memory-index", dict));
409 : crashdata::Blob* blob = crashdata::LeafGetBlob(
410 E : crashdata::DictAddLeaf("shadow-memory", dict));
411 : blob->mutable_data()->assign(
412 : reinterpret_cast<const char*>(Shadow::shadow() + index_min),
413 E : length);
414 E : }
415 :
416 : void PopulatePageBitsBlob(const AsanErrorInfo& error_info,
417 E : crashdata::Dictionary* dict) {
418 E : DCHECK_NE(static_cast<crashdata::Dictionary*>(nullptr), dict);
419 :
420 : // Emit information about page protections surround the address in question.
421 : static const size_t kPageBitsContext = 2;
422 E : uintptr_t index = reinterpret_cast<uintptr_t>(error_info.location);
423 E : index /= GetPageSize(); // 1 bit per page.
424 E : index /= 8; // 8 bits per byte.
425 E : uintptr_t index_min = index - kPageBitsContext;
426 E : if (index_min > index)
427 E : index_min = 0;
428 E : uintptr_t index_max = index + 1 + kPageBitsContext;
429 E : if (index_max < index)
430 i : index_max = 0;
431 E : uintptr_t length = index_max - index_min;
432 : crashdata::LeafSetUInt(
433 E : index, crashdata::DictAddLeaf("page-bits-index", dict));
434 : crashdata::Blob* blob = crashdata::LeafGetBlob(
435 E : crashdata::DictAddLeaf("page-bits", dict));
436 : blob->mutable_data()->assign(
437 : reinterpret_cast<const char*>(Shadow::page_bits() + index_min),
438 E : length);
439 E : }
440 :
441 : } // namespace
442 :
443 : // TODO(chrisha): Only emit information that makes sense for the given error
444 : // type. For example, wild-access errors have no associated
445 : // block information.
446 : void PopulateErrorInfo(const AsanErrorInfo& error_info,
447 E : crashdata::Value* value) {
448 E : DCHECK_NE(static_cast<crashdata::Value*>(nullptr), value);
449 :
450 : // Create a single outermost dictionary.
451 E : crashdata::Dictionary* dict = ValueGetDict(value);
452 E : DCHECK_NE(static_cast<crashdata::Dictionary*>(nullptr), dict);
453 :
454 : crashdata::LeafGetAddress(crashdata::DictAddLeaf("location", dict))
455 E : ->set_address(CastAddress(error_info.location));
456 : crashdata::LeafSetUInt(error_info.crash_stack_id,
457 E : crashdata::DictAddLeaf("crash-stack-id", dict));
458 E : if (error_info.block_info.header != nullptr) {
459 : PopulateBlockInfo(error_info.block_info,
460 E : crashdata::DictAddValue("block-info", dict));
461 : }
462 : crashdata::LeafGetString(crashdata::DictAddLeaf("error-type", dict))
463 E : ->assign(ErrorInfoAccessTypeToStr(error_info.error_type));
464 : AccessModeToString(
465 : error_info.access_mode,
466 E : crashdata::LeafGetString(crashdata::DictAddLeaf("access-mode", dict)));
467 : crashdata::LeafSetUInt(error_info.access_size,
468 E : crashdata::DictAddLeaf("access-size", dict));
469 :
470 E : PopulateShadowMemoryBlob(error_info, dict);
471 E : PopulatePageBitsBlob(error_info, dict);
472 :
473 : // Send information about corruption.
474 : crashdata::LeafSetUInt(error_info.heap_is_corrupt,
475 E : crashdata::DictAddLeaf("heap-is-corrupt", dict));
476 : crashdata::LeafSetUInt(error_info.corrupt_range_count,
477 E : crashdata::DictAddLeaf("corrupt-range-count", dict));
478 : crashdata::LeafSetUInt(error_info.corrupt_block_count,
479 E : crashdata::DictAddLeaf("corrupt-block-count", dict));
480 E : if (error_info.corrupt_ranges_reported > 0) {
481 : crashdata::List* list = crashdata::ValueGetList(
482 E : crashdata::DictAddValue("corrupt-ranges", dict));
483 E : for (size_t i = 0; i < error_info.corrupt_ranges_reported; ++i) {
484 : PopulateCorruptBlockRange(error_info.corrupt_ranges[i],
485 E : list->add_values());
486 E : }
487 : }
488 E : }
489 :
490 : } // namespace asan
491 : } // namespace agent
|