1 : // Copyright 2012 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/asan_heap.h"
16 :
17 : #include "base/logging.h"
18 : #include "base/stringprintf.h"
19 : #include "base/sys_string_conversions.h"
20 : #include "base/debug/stack_trace.h"
21 : #include "syzygy/agent/asan/asan_logger.h"
22 : #include "syzygy/agent/asan/asan_shadow.h"
23 :
24 : namespace agent {
25 : namespace asan {
26 : namespace {
27 :
28 : typedef StackCapture::StackId StackId;
29 :
30 : // Redzone size allocated at the start of every heap block.
31 : const size_t kRedZoneSize = 32U;
32 :
33 : // Utility class which implements an auto lock for a HeapProxy.
34 : class HeapLocker {
35 : public:
36 E : explicit HeapLocker(HeapProxy* const heap) : heap_(heap) {
37 E : DCHECK(heap != NULL);
38 E : if (!heap->Lock()) {
39 i : LOG(ERROR) << "Unable to lock the heap.";
40 : }
41 E : }
42 :
43 E : ~HeapLocker() {
44 E : DCHECK(heap_ != NULL);
45 E : if (!heap_->Unlock()) {
46 i : LOG(ERROR) << "Unable to lock the heap.";
47 : }
48 E : }
49 :
50 : private:
51 : HeapProxy* const heap_;
52 :
53 : DISALLOW_COPY_AND_ASSIGN(HeapLocker);
54 : };
55 :
56 : } // namespace
57 :
58 : // Arbitrarily keep 16 megabytes of quarantine per heap by default.
59 : size_t HeapProxy::default_quarantine_max_size_ = 16 * 1024 * 1024;
60 :
61 E : void ASANDbgCmd(const wchar_t* fmt, ...) {
62 : // The string should start with "ASAN" to be interpreted by the debugger as a
63 : // command.
64 E : std::wstring command_wstring = L"ASAN ";
65 : va_list args;
66 E : va_start(args, fmt);
67 :
68 : // Append the actual command to the wstring.
69 E : base::StringAppendV(&command_wstring, fmt, args);
70 :
71 : // Append "; g" to make sure that the debugger continue its execution after
72 : // executing this command. This is needed because when the .ocommand function
73 : // is used under Windbg the debugger will break on OutputDebugString.
74 E : command_wstring.append(L"; g");
75 :
76 E : OutputDebugString(command_wstring.c_str());
77 E : }
78 :
79 E : void ASANDbgMessage(const wchar_t* fmt, ...) {
80 : // Prepend the message with the .echo command so it'll be printed into the
81 : // debugger's console.
82 E : std::wstring message_wstring = L".echo ";
83 : va_list args;
84 E : va_start(args, fmt);
85 :
86 : // Append the actual message to the wstring.
87 E : base::StringAppendV(&message_wstring, fmt, args);
88 :
89 : // Treat the message as a command to print it.
90 E : ASANDbgCmd(message_wstring.c_str());
91 E : }
92 :
93 : // Switch to the caller's context and print its stack trace in Windbg.
94 E : void ASANDbgPrintContext(const CONTEXT& context) {
95 E : ASANDbgMessage(L"Caller's context (%p) and stack trace:", &context);
96 E : ASANDbgCmd(L".cxr %p; kv", reinterpret_cast<uint32>(&context));
97 E : }
98 :
99 : HeapProxy::HeapProxy(StackCaptureCache* stack_cache, AsanLogger* logger)
100 : : heap_(NULL),
101 : stack_cache_(stack_cache),
102 : logger_(logger),
103 : head_(NULL),
104 : tail_(NULL),
105 : quarantine_size_(0),
106 E : quarantine_max_size_(0) {
107 E : DCHECK(stack_cache != NULL);
108 E : DCHECK(logger != NULL);
109 E : }
110 :
111 E : HeapProxy::~HeapProxy() {
112 E : if (heap_ != NULL)
113 i : Destroy();
114 :
115 E : DCHECK(heap_ == NULL);
116 E : }
117 :
118 E : HANDLE HeapProxy::ToHandle(HeapProxy* proxy) {
119 E : DCHECK(proxy != NULL);
120 E : return proxy;
121 E : }
122 :
123 E : HeapProxy* HeapProxy::FromHandle(HANDLE heap) {
124 E : DCHECK(heap != NULL);
125 E : return reinterpret_cast<HeapProxy*>(heap);
126 E : }
127 :
128 : bool HeapProxy::Create(DWORD options,
129 : size_t initial_size,
130 E : size_t maximum_size) {
131 E : DCHECK(heap_ == NULL);
132 : COMPILE_ASSERT(sizeof(HeapProxy::BlockHeader) <= kRedZoneSize,
133 : asan_block_header_too_big);
134 :
135 E : set_quarantine_max_size(default_quarantine_max_size_);
136 :
137 E : HANDLE heap_new = ::HeapCreate(options, initial_size, maximum_size);
138 E : if (heap_new == NULL)
139 E : return false;
140 :
141 E : heap_ = heap_new;
142 :
143 E : return true;
144 E : }
145 :
146 E : bool HeapProxy::Destroy() {
147 E : DCHECK(heap_ != NULL);
148 :
149 : // Flush the quarantine.
150 E : set_quarantine_max_size(0);
151 :
152 E : if (::HeapDestroy(heap_)) {
153 E : heap_ = NULL;
154 E : return true;
155 : }
156 :
157 i : return false;
158 E : }
159 :
160 E : void* HeapProxy::Alloc(DWORD flags, size_t bytes) {
161 E : DCHECK(heap_ != NULL);
162 :
163 E : size_t alloc_size = GetAllocSize(bytes);
164 : BlockHeader* block =
165 E : reinterpret_cast<BlockHeader*>(::HeapAlloc(heap_, flags, alloc_size));
166 :
167 E : if (block == NULL)
168 i : return NULL;
169 :
170 : // Poison head and tail zones, and un-poison alloc.
171 E : size_t header_size = kRedZoneSize;
172 E : size_t trailer_size = alloc_size - kRedZoneSize - bytes;
173 E : memset(block, 0xCC, header_size);
174 E : Shadow::Poison(block, kRedZoneSize, Shadow::kHeapLeftRedzone);
175 :
176 : // Capture the current stack. InitFromStack is inlined to preserve the
177 : // greatest number of stack frames.
178 E : StackCapture stack;
179 E : stack.InitFromStack();
180 :
181 : // Initialize the block fields.
182 E : block->magic_number = kBlockHeaderSignature;
183 E : block->size = bytes;
184 E : block->state = ALLOCATED;
185 E : block->alloc_stack = stack_cache_->SaveStackTrace(stack);
186 E : block->free_stack = NULL;
187 :
188 E : uint8* block_alloc = ToAlloc(block);
189 E : Shadow::Unpoison(block_alloc, bytes);
190 :
191 E : memset(block_alloc + bytes, 0xCD, trailer_size);
192 E : Shadow::Poison(block_alloc + bytes, trailer_size, Shadow::kHeapRightRedzone);
193 :
194 E : return block_alloc;
195 E : }
196 :
197 E : void* HeapProxy::ReAlloc(DWORD flags, void* mem, size_t bytes) {
198 E : DCHECK(heap_ != NULL);
199 :
200 E : void *new_mem = Alloc(flags, bytes);
201 E : if (new_mem != NULL && mem != NULL)
202 E : memcpy(new_mem, mem, std::min(bytes, Size(0, mem)));
203 :
204 E : if (mem)
205 E : Free(flags, mem);
206 :
207 E : return new_mem;
208 E : }
209 :
210 E : bool HeapProxy::Free(DWORD flags, void* mem) {
211 E : DCHECK(heap_ != NULL);
212 E : BlockHeader* block = ToBlock(mem);
213 : // The standard allows to call free on a null pointer. ToBlock returns null if
214 : // the given pointer is null so we return true here.
215 E : if (block == NULL)
216 i : return true;
217 :
218 : // Capture the current stack.
219 E : StackCapture stack;
220 E : stack.InitFromStack();
221 :
222 E : if (block->state != ALLOCATED) {
223 : // We're not supposed to see another kind of block here, the FREED state
224 : // is only applied to block after invalidating their magic number and freed
225 : // them.
226 E : DCHECK(block->state == QUARANTINED);
227 :
228 : BadAccessKind bad_access_kind =
229 E : GetBadAccessKind(static_cast<const uint8*>(mem), block);
230 E : DCHECK_NE(UNKNOWN_BAD_ACCESS, bad_access_kind);
231 :
232 E : CONTEXT context = {};
233 E : ::RtlCaptureContext(&context);
234 :
235 : ReportAsanError("attempting double-free", static_cast<const uint8*>(mem),
236 : context, stack, bad_access_kind, block,
237 E : ASAN_UNKNOWN_ACCESS, 0);
238 :
239 E : return false;
240 : }
241 :
242 E : block->free_stack = stack_cache_->SaveStackTrace(stack);
243 E : DCHECK(ToAlloc(block) == mem);
244 :
245 : // If the size of the allocation is zero then we shouldn't check the shadow
246 : // memory as it'll only contain the red-zone for the head and tail of this
247 : // block.
248 E : if (block->size != 0 && !Shadow::IsAccessible(ToAlloc(block)))
249 i : return false;
250 E : QuarantineBlock(block);
251 E : return true;
252 E : }
253 :
254 E : size_t HeapProxy::Size(DWORD flags, const void* mem) {
255 E : DCHECK(heap_ != NULL);
256 E : BlockHeader* block = ToBlock(mem);
257 E : if (block == NULL)
258 i : return -1;
259 :
260 E : return block->size;
261 E : }
262 :
263 E : bool HeapProxy::Validate(DWORD flags, const void* mem) {
264 E : DCHECK(heap_ != NULL);
265 E : return ::HeapValidate(heap_, flags, ToBlock(mem)) == TRUE;
266 E : }
267 :
268 E : size_t HeapProxy::Compact(DWORD flags) {
269 E : DCHECK(heap_ != NULL);
270 E : return ::HeapCompact(heap_, flags);
271 E : }
272 :
273 E : bool HeapProxy::Lock() {
274 E : DCHECK(heap_ != NULL);
275 E : return ::HeapLock(heap_) == TRUE;
276 E : }
277 :
278 E : bool HeapProxy::Unlock() {
279 E : DCHECK(heap_ != NULL);
280 E : return ::HeapUnlock(heap_) == TRUE;
281 E : }
282 :
283 E : bool HeapProxy::Walk(PROCESS_HEAP_ENTRY* entry) {
284 E : DCHECK(heap_ != NULL);
285 E : return ::HeapWalk(heap_, entry) == TRUE;
286 E : }
287 :
288 : bool HeapProxy::SetInformation(HEAP_INFORMATION_CLASS info_class,
289 : void* info,
290 E : size_t info_length) {
291 E : DCHECK(heap_ != NULL);
292 E : return ::HeapSetInformation(heap_, info_class, info, info_length) == TRUE;
293 E : }
294 :
295 : bool HeapProxy::QueryInformation(HEAP_INFORMATION_CLASS info_class,
296 : void* info,
297 : size_t info_length,
298 E : unsigned long* return_length) {
299 E : DCHECK(heap_ != NULL);
300 : return ::HeapQueryInformation(heap_,
301 : info_class,
302 : info,
303 : info_length,
304 E : return_length) == TRUE;
305 E : }
306 :
307 E : void HeapProxy::set_quarantine_max_size(size_t quarantine_max_size) {
308 E : base::AutoLock lock(lock_);
309 E : quarantine_max_size_ = quarantine_max_size;
310 :
311 E : while (quarantine_size_ > quarantine_max_size_)
312 E : PopQuarantineUnlocked();
313 E : }
314 :
315 E : void HeapProxy::PopQuarantineUnlocked() {
316 E : DCHECK(head_ != NULL && tail_ != NULL);
317 : // The caller should have locked the quarantine.
318 E : lock_.AssertAcquired();
319 :
320 E : FreeBlockHeader* free_block = head_;
321 E : head_ = free_block->next;
322 E : if (head_ == NULL)
323 E : tail_ = NULL;
324 :
325 E : size_t alloc_size = GetAllocSize(free_block->size);
326 E : Shadow::Unpoison(free_block, alloc_size);
327 E : free_block->state = FREED;
328 E : free_block->magic_number = ~kBlockHeaderSignature;
329 E : free_block->alloc_stack = NULL;
330 E : free_block->free_stack = NULL;
331 :
332 E : DCHECK_NE(kBlockHeaderSignature, free_block->magic_number);
333 E : ::HeapFree(heap_, 0, free_block);
334 :
335 E : DCHECK_GE(quarantine_size_, alloc_size);
336 E : quarantine_size_ -= alloc_size;
337 E : }
338 :
339 E : void HeapProxy::QuarantineBlock(BlockHeader* block) {
340 E : DCHECK(block != NULL);
341 E : base::AutoLock lock(lock_);
342 E : FreeBlockHeader* free_block = static_cast<FreeBlockHeader*>(block);
343 :
344 E : free_block->next = NULL;
345 E : if (tail_ != NULL) {
346 E : tail_->next = free_block;
347 E : } else {
348 E : DCHECK(head_ == NULL);
349 E : head_ = free_block;
350 : }
351 E : tail_ = free_block;
352 :
353 : // Poison the released alloc.
354 E : size_t alloc_size = GetAllocSize(free_block->size);
355 : // Trash the data in the block and poison it.
356 E : uint8* mem = ToAlloc(free_block);
357 E : memset(mem, 0xCC, free_block->size);
358 E : Shadow::MarkAsFreed(mem, free_block->size);
359 E : quarantine_size_ += alloc_size;
360 : // Mark the block as quarantined.
361 E : free_block->state = QUARANTINED;
362 :
363 : // Flush quarantine overage.
364 E : while (quarantine_size_ > quarantine_max_size_)
365 E : PopQuarantineUnlocked();
366 E : }
367 :
368 E : size_t HeapProxy::GetAllocSize(size_t bytes) {
369 E : bytes += kRedZoneSize;
370 E : return (bytes + kRedZoneSize + kRedZoneSize - 1) & ~(kRedZoneSize - 1);
371 E : }
372 :
373 E : HeapProxy::BlockHeader* HeapProxy::ToBlock(const void* alloc) {
374 E : if (alloc == NULL)
375 i : return NULL;
376 :
377 E : const uint8* mem = static_cast<const uint8*>(alloc);
378 : const BlockHeader* header =
379 E : reinterpret_cast<const BlockHeader*>(mem -kRedZoneSize);
380 E : if (header->magic_number != kBlockHeaderSignature) {
381 i : CONTEXT context = {};
382 i : ::RtlCaptureContext(&context);
383 :
384 i : StackCapture stack;
385 i : stack.InitFromStack();
386 :
387 i : if (!OnBadAccess(mem, context, stack, ASAN_UNKNOWN_ACCESS, 0)) {
388 i : ReportUnknownError(mem, context, stack, ASAN_UNKNOWN_ACCESS, 0);
389 i : Shadow::PrintShadowMemoryForAddress(alloc);
390 : }
391 i : return NULL;
392 : }
393 :
394 E : return const_cast<BlockHeader*>(header);
395 E : }
396 :
397 E : uint8* HeapProxy::ToAlloc(BlockHeader* block) {
398 E : DCHECK(block != NULL);
399 E : DCHECK_EQ(kBlockHeaderSignature, block->magic_number);
400 E : DCHECK(block->state == ALLOCATED || block->state == QUARANTINED);
401 :
402 E : uint8* mem = reinterpret_cast<uint8*>(block);
403 :
404 E : return mem + kRedZoneSize;
405 E : }
406 :
407 : void HeapProxy::ReportAddressInformation(const void* addr,
408 : BlockHeader* header,
409 E : BadAccessKind bad_access_kind) {
410 E : DCHECK(addr != NULL);
411 E : DCHECK(header != NULL);
412 E : uint8* block_alloc = ToAlloc(header);
413 E : int offset = 0;
414 E : char* offset_relativity = "";
415 E : switch (bad_access_kind) {
416 : case HEAP_BUFFER_OVERFLOW:
417 E : offset = static_cast<const uint8*>(addr) - block_alloc - header->size;
418 E : offset_relativity = "to the right";
419 E : break;
420 : case HEAP_BUFFER_UNDERFLOW:
421 E : offset = block_alloc - static_cast<const uint8*>(addr);
422 E : offset_relativity = "to the left";
423 E : break;
424 : case USE_AFTER_FREE:
425 E : offset = static_cast<const uint8*>(addr) - block_alloc;
426 E : offset_relativity = "inside";
427 E : break;
428 : default:
429 i : NOTREACHED() << "Error trying to dump address information.";
430 : }
431 :
432 : logger_->Write(base::StringPrintf(
433 : "0x%08X is located %d bytes %s of %d-bytes region [0x%08X,0x%08X)\n",
434 : addr,
435 : offset,
436 : offset_relativity,
437 : header->size,
438 : block_alloc,
439 E : block_alloc + header->size));
440 E : if (header->free_stack != NULL) {
441 : std::string message = base::StringPrintf(
442 E : "freed here (stack_id=0x%08X):\n", header->free_stack->stack_id());
443 : logger_->WriteWithStackTrace(message,
444 : header->free_stack->frames(),
445 E : header->free_stack->num_frames());
446 E : }
447 E : if (header->alloc_stack != NULL) {
448 : std::string message = base::StringPrintf(
449 : "previously allocated here (stack_id=0x%08X):\n",
450 E : header->alloc_stack->stack_id());
451 : logger_->WriteWithStackTrace(message,
452 : header->alloc_stack->frames(),
453 E : header->alloc_stack->num_frames());
454 E : }
455 :
456 E : std::string shadow_text;
457 E : Shadow::AppendShadowMemoryText(addr, &shadow_text);
458 E : logger_->Write(shadow_text);
459 E : }
460 :
461 : HeapProxy::BadAccessKind HeapProxy::GetBadAccessKind(const void* addr,
462 E : BlockHeader* header) {
463 E : DCHECK(addr != NULL);
464 E : DCHECK(header != NULL);
465 :
466 E : BadAccessKind bad_access_kind = UNKNOWN_BAD_ACCESS;
467 :
468 E : if (header->state == QUARANTINED) {
469 : // At this point we can't know if this address belongs to this
470 : // quarantined block... If the block containing this address has been
471 : // moved from the quarantine list its memory space could have been re-used
472 : // and freed again (so having this block in the quarantine list don't
473 : // guarantee that this is the original block).
474 : // TODO(sebmarchand): Find a way to fix this bug.
475 E : bad_access_kind = USE_AFTER_FREE;
476 E : } else {
477 E : if (addr < (ToAlloc(header)))
478 E : bad_access_kind = HEAP_BUFFER_UNDERFLOW;
479 E : else if (addr >= (ToAlloc(header) + header->size))
480 E : bad_access_kind = HEAP_BUFFER_OVERFLOW;
481 : }
482 E : return bad_access_kind;
483 E : }
484 :
485 E : HeapProxy::BlockHeader* HeapProxy::FindAddressBlock(const void* addr) {
486 E : DCHECK(addr != NULL);
487 E : PROCESS_HEAP_ENTRY heap_entry = {};
488 E : memset(&heap_entry, 0, sizeof(heap_entry));
489 E : BlockHeader* header = NULL;
490 :
491 : // Walk through the heap to find the block containing @p addr.
492 E : HeapLocker heap_locker(this);
493 E : while (Walk(&heap_entry)) {
494 : uint8* entry_upper_bound =
495 E : static_cast<uint8*>(heap_entry.lpData) + heap_entry.cbData;
496 :
497 E : if (heap_entry.lpData <= addr && entry_upper_bound > addr) {
498 E : header = reinterpret_cast<BlockHeader*>(heap_entry.lpData);
499 : // Ensures that the block have been allocated by this proxy.
500 E : if (header->magic_number == kBlockHeaderSignature) {
501 E : DCHECK(header->state != FREED);
502 E : break;
503 i : } else {
504 E : header = NULL;
505 : }
506 : }
507 E : }
508 :
509 E : return header;
510 E : }
511 :
512 : bool HeapProxy::OnBadAccess(const void* addr,
513 : const CONTEXT& context,
514 : const StackCapture& stack,
515 : AccessMode access_mode,
516 E : size_t access_size) {
517 E : DCHECK(addr != NULL);
518 E : base::AutoLock lock(lock_);
519 E : BadAccessKind bad_access_kind = UNKNOWN_BAD_ACCESS;
520 E : BlockHeader* header = FindAddressBlock(addr);
521 :
522 E : if (header == NULL)
523 i : return false;
524 :
525 E : bad_access_kind = GetBadAccessKind(addr, header);
526 : // Get the bad access description if we've been able to determine its kind.
527 E : if (bad_access_kind != UNKNOWN_BAD_ACCESS) {
528 E : const char* bug_descr = AccessTypeToStr(bad_access_kind);
529 : ReportAsanError(bug_descr,
530 : addr,
531 : context,
532 : stack,
533 : bad_access_kind,
534 : header,
535 : access_mode,
536 E : access_size);
537 E : return true;
538 : }
539 :
540 i : return false;
541 E : }
542 :
543 : void HeapProxy::ReportUnknownError(const void* addr,
544 : const CONTEXT& context,
545 : const StackCapture& stack,
546 : AccessMode access_mode,
547 i : size_t access_size) {
548 i : DCHECK(addr != NULL);
549 : ReportAsanErrorBase("unknown-crash",
550 : addr,
551 : context,
552 : stack,
553 : UNKNOWN_BAD_ACCESS,
554 : access_mode,
555 i : access_size);
556 :
557 i : ASANDbgPrintContext(context);
558 i : }
559 :
560 : void HeapProxy::ReportAsanError(const char* bug_descr,
561 : const void* addr,
562 : const CONTEXT& context,
563 : const StackCapture& stack,
564 : BadAccessKind bad_access_kind,
565 : BlockHeader* header,
566 : AccessMode access_mode,
567 E : size_t access_size) {
568 E : DCHECK(bug_descr != NULL);
569 E : DCHECK(addr != NULL);
570 E : DCHECK(header != NULL);
571 :
572 : ReportAsanErrorBase(bug_descr,
573 : addr,
574 : context,
575 : stack,
576 : bad_access_kind,
577 : access_mode,
578 E : access_size);
579 :
580 : // Print the Windbg information to display the allocation stack if present.
581 E : if (header->alloc_stack != NULL) {
582 E : ASANDbgMessage(L"Allocation stack trace:");
583 : ASANDbgCmd(L"dps %p l%d",
584 : header->alloc_stack->frames(),
585 E : header->alloc_stack->num_frames());
586 : }
587 :
588 : // Print the Windbg information to display the free stack if present.
589 E : if (header->free_stack != NULL) {
590 E : ASANDbgMessage(L"Free stack trace:");
591 : ASANDbgCmd(L"dps %p l%d",
592 : header->free_stack->frames(),
593 E : header->free_stack->num_frames());
594 : }
595 :
596 E : ReportAddressInformation(addr, header, bad_access_kind);
597 :
598 E : ASANDbgPrintContext(context);
599 E : }
600 :
601 : void HeapProxy::ReportAsanErrorBase(const char* bug_descr,
602 : const void* addr,
603 : const CONTEXT& context,
604 : const StackCapture& stack,
605 : BadAccessKind bad_access_kind,
606 : AccessMode access_mode,
607 E : size_t access_size) {
608 E : DCHECK(bug_descr != NULL);
609 E : DCHECK(addr != NULL);
610 :
611 : // Print the base of the Windbg help message.
612 : ASANDbgMessage(L"An Asan error has been found (%ls), here are the details:",
613 E : base::SysUTF8ToWide(bug_descr).c_str());
614 :
615 : // TODO(sebmarchand): Print PC, BP and SP.
616 : std::string output(base::StringPrintf(
617 : "SyzyASAN error: %s on address 0x%08X (stack_id=0x%08X)\n",
618 E : bug_descr, addr, stack.stack_id()));
619 E : if (access_mode != ASAN_UNKNOWN_ACCESS) {
620 E : const char* access_mode_str = NULL;
621 E : if (access_mode == ASAN_READ_ACCESS)
622 E : access_mode_str = "READ";
623 E : else
624 i : access_mode_str = "WRITE";
625 : base::StringAppendF(&output,
626 : "%s of size %d at 0x%08X\n",
627 : access_mode_str,
628 E : access_size);
629 : }
630 :
631 : // Log the failure and stack.
632 E : logger_->WriteWithContext(output, context);
633 E : }
634 :
635 E : const char* HeapProxy::AccessTypeToStr(BadAccessKind bad_access_kind) {
636 E : switch (bad_access_kind) {
637 : case USE_AFTER_FREE:
638 E : return "heap-use-after-free";
639 : case HEAP_BUFFER_UNDERFLOW:
640 E : return "heap-buffer-underflow";
641 : case HEAP_BUFFER_OVERFLOW:
642 E : return "heap-buffer-overflow";
643 : default:
644 i : NOTREACHED() << "Unexpected bad access kind.";
645 i : return NULL;
646 : }
647 E : }
648 :
649 E : LIST_ENTRY* HeapProxy::ToListEntry(HeapProxy* proxy) {
650 E : DCHECK(proxy != NULL);
651 E : return &proxy->list_entry_;
652 E : }
653 :
654 E : HeapProxy* HeapProxy::FromListEntry(LIST_ENTRY* list_entry) {
655 E : DCHECK(list_entry != NULL);
656 E : return CONTAINING_RECORD(list_entry, HeapProxy, list_entry_);
657 E : }
658 :
659 : } // namespace asan
660 : } // namespace agent
|