diff options
author | Kostya Serebryany <kcc@google.com> | 2012-12-21 08:53:59 +0000 |
---|---|---|
committer | Kostya Serebryany <kcc@google.com> | 2012-12-21 08:53:59 +0000 |
commit | fe6d91684bcda766593800f6307233f1a33d31f6 (patch) | |
tree | 309fa82b7a4e10b4f11f37992eff2937ad3f74b7 /lib/asan | |
parent | d37c272621befc2f3b17c9e581ed970fbfbfae64 (diff) | |
download | compiler-rt-fe6d91684bcda766593800f6307233f1a33d31f6.tar.gz |
[asan] add a flag alloc_dealloc_mismatch (off by default for now) which finds malloc/delete, new/free, new/delete[], etc mismatches
git-svn-id: https://llvm.org/svn/llvm-project/compiler-rt/trunk@170869 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'lib/asan')
-rw-r--r-- | lib/asan/asan_allocator.cc | 39 | ||||
-rw-r--r-- | lib/asan/asan_allocator.h | 11 | ||||
-rw-r--r-- | lib/asan/asan_allocator2.cc | 40 | ||||
-rw-r--r-- | lib/asan/asan_flags.h | 2 | ||||
-rw-r--r-- | lib/asan/asan_mac.cc | 4 | ||||
-rw-r--r-- | lib/asan/asan_malloc_linux.cc | 6 | ||||
-rw-r--r-- | lib/asan/asan_malloc_mac.cc | 10 | ||||
-rw-r--r-- | lib/asan/asan_malloc_win.cc | 2 | ||||
-rw-r--r-- | lib/asan/asan_new_delete.cc | 26 | ||||
-rw-r--r-- | lib/asan/asan_report.cc | 20 | ||||
-rw-r--r-- | lib/asan/asan_report.h | 4 | ||||
-rw-r--r-- | lib/asan/asan_rtl.cc | 2 | ||||
-rw-r--r-- | lib/asan/lit_tests/deep_stack_uaf.cc | 2 | ||||
-rw-r--r-- | lib/asan/lit_tests/malloc_delete_mismatch.cc | 26 | ||||
-rw-r--r-- | lib/asan/tests/asan_noinst_test.cc | 15 | ||||
-rw-r--r-- | lib/asan/tests/asan_test.cc | 31 |
16 files changed, 171 insertions, 69 deletions
diff --git a/lib/asan/asan_allocator.cc b/lib/asan/asan_allocator.cc index 9ef1e28d4..4cb8ac1d9 100644 --- a/lib/asan/asan_allocator.cc +++ b/lib/asan/asan_allocator.cc @@ -128,7 +128,8 @@ struct ChunkBase { // Second 8 bytes. uptr alignment_log : 8; - uptr used_size : FIRST_32_SECOND_64(32, 56); // Size requested by the user. + uptr alloc_type : 2; + uptr used_size : FIRST_32_SECOND_64(32, 54); // Size requested by the user. // This field may overlap with the user area and thus should not // be used while the chunk is in CHUNK_ALLOCATED state. @@ -497,7 +498,8 @@ AsanChunkView FindHeapChunkByAddress(uptr address) { return AsanChunkView(malloc_info.FindChunkByAddr(address)); } -static u8 *Allocate(uptr alignment, uptr size, StackTrace *stack) { +static u8 *Allocate(uptr alignment, uptr size, StackTrace *stack, + AllocType alloc_type) { __asan_init(); CHECK(stack); if (size == 0) { @@ -554,6 +556,7 @@ static u8 *Allocate(uptr alignment, uptr size, StackTrace *stack) { CHECK(m); CHECK(m->chunk_state == CHUNK_AVAILABLE); m->chunk_state = CHUNK_ALLOCATED; + m->alloc_type = alloc_type; m->next = 0; CHECK(m->Size() == size_to_allocate); uptr addr = (uptr)m + REDZONE; @@ -588,7 +591,7 @@ static u8 *Allocate(uptr alignment, uptr size, StackTrace *stack) { return (u8*)addr; } -static void Deallocate(u8 *ptr, StackTrace *stack) { +static void Deallocate(u8 *ptr, StackTrace *stack, AllocType alloc_type) { if (!ptr) return; CHECK(stack); @@ -609,6 +612,9 @@ static void Deallocate(u8 *ptr, StackTrace *stack) { ReportFreeNotMalloced((uptr)ptr, stack); } CHECK(old_chunk_state == CHUNK_ALLOCATED); + if (m->alloc_type != alloc_type && flags()->alloc_dealloc_mismatch) + ReportAllocTypeMismatch((uptr)ptr, stack, + (AllocType)m->alloc_type, (AllocType)alloc_type); // With REDZONE==16 m->next is in the user area, otherwise it should be 0. CHECK(REDZONE <= 16 || !m->next); CHECK(m->free_tid == kInvalidTid); @@ -653,11 +659,11 @@ static u8 *Reallocate(u8 *old_ptr, uptr new_size, CHECK(m->chunk_state == CHUNK_ALLOCATED); uptr old_size = m->used_size; uptr memcpy_size = Min(new_size, old_size); - u8 *new_ptr = Allocate(0, new_size, stack); + u8 *new_ptr = Allocate(0, new_size, stack, FROM_MALLOC); if (new_ptr) { CHECK(REAL(memcpy) != 0); REAL(memcpy)(new_ptr, old_ptr, memcpy_size); - Deallocate(old_ptr, stack); + Deallocate(old_ptr, stack, FROM_MALLOC); } return new_ptr; } @@ -682,27 +688,28 @@ void __asan_free_hook(void *ptr) { namespace __asan { SANITIZER_INTERFACE_ATTRIBUTE -void *asan_memalign(uptr alignment, uptr size, StackTrace *stack) { - void *ptr = (void*)Allocate(alignment, size, stack); +void *asan_memalign(uptr alignment, uptr size, StackTrace *stack, + AllocType alloc_type) { + void *ptr = (void*)Allocate(alignment, size, stack, alloc_type); ASAN_MALLOC_HOOK(ptr, size); return ptr; } SANITIZER_INTERFACE_ATTRIBUTE -void asan_free(void *ptr, StackTrace *stack) { +void asan_free(void *ptr, StackTrace *stack, AllocType alloc_type) { ASAN_FREE_HOOK(ptr); - Deallocate((u8*)ptr, stack); + Deallocate((u8*)ptr, stack, alloc_type); } SANITIZER_INTERFACE_ATTRIBUTE void *asan_malloc(uptr size, StackTrace *stack) { - void *ptr = (void*)Allocate(0, size, stack); + void *ptr = (void*)Allocate(0, size, stack, FROM_MALLOC); ASAN_MALLOC_HOOK(ptr, size); return ptr; } void *asan_calloc(uptr nmemb, uptr size, StackTrace *stack) { - void *ptr = (void*)Allocate(0, nmemb * size, stack); + void *ptr = (void*)Allocate(0, nmemb * size, stack, FROM_MALLOC); if (ptr) REAL(memset)(ptr, 0, nmemb * size); ASAN_MALLOC_HOOK(ptr, size); @@ -711,19 +718,19 @@ void *asan_calloc(uptr nmemb, uptr size, StackTrace *stack) { void *asan_realloc(void *p, uptr size, StackTrace *stack) { if (p == 0) { - void *ptr = (void*)Allocate(0, size, stack); + void *ptr = (void*)Allocate(0, size, stack, FROM_MALLOC); ASAN_MALLOC_HOOK(ptr, size); return ptr; } else if (size == 0) { ASAN_FREE_HOOK(p); - Deallocate((u8*)p, stack); + Deallocate((u8*)p, stack, FROM_MALLOC); return 0; } return Reallocate((u8*)p, size, stack); } void *asan_valloc(uptr size, StackTrace *stack) { - void *ptr = (void*)Allocate(GetPageSizeCached(), size, stack); + void *ptr = (void*)Allocate(GetPageSizeCached(), size, stack, FROM_MALLOC); ASAN_MALLOC_HOOK(ptr, size); return ptr; } @@ -735,14 +742,14 @@ void *asan_pvalloc(uptr size, StackTrace *stack) { // pvalloc(0) should allocate one page. size = PageSize; } - void *ptr = (void*)Allocate(PageSize, size, stack); + void *ptr = (void*)Allocate(PageSize, size, stack, FROM_MALLOC); ASAN_MALLOC_HOOK(ptr, size); return ptr; } int asan_posix_memalign(void **memptr, uptr alignment, uptr size, StackTrace *stack) { - void *ptr = Allocate(alignment, size, stack); + void *ptr = Allocate(alignment, size, stack, FROM_MALLOC); CHECK(IsAligned((uptr)ptr, alignment)); ASAN_MALLOC_HOOK(ptr, size); *memptr = ptr; diff --git a/lib/asan/asan_allocator.h b/lib/asan/asan_allocator.h index c80c906e6..f117f6925 100644 --- a/lib/asan/asan_allocator.h +++ b/lib/asan/asan_allocator.h @@ -27,6 +27,12 @@ namespace __asan { +enum AllocType { + FROM_MALLOC = 1, // Memory block came from malloc, calloc, realloc, etc. + FROM_NEW = 2, // Memory block came from operator new. + FROM_NEW_BR = 3 // Memory block came from operator new [ ] +}; + static const uptr kNumberOfSizeClasses = 255; struct AsanChunk; @@ -190,8 +196,9 @@ class FakeStack { FakeFrameLifo call_stack_; }; -void *asan_memalign(uptr alignment, uptr size, StackTrace *stack); -void asan_free(void *ptr, StackTrace *stack); +void *asan_memalign(uptr alignment, uptr size, StackTrace *stack, + AllocType alloc_type); +void asan_free(void *ptr, StackTrace *stack, AllocType alloc_type); void *asan_malloc(uptr size, StackTrace *stack); void *asan_calloc(uptr nmemb, uptr size, StackTrace *stack); diff --git a/lib/asan/asan_allocator2.cc b/lib/asan/asan_allocator2.cc index 3a4587ffe..3036179ed 100644 --- a/lib/asan/asan_allocator2.cc +++ b/lib/asan/asan_allocator2.cc @@ -124,8 +124,10 @@ struct ChunkBase { // 1-st 8 bytes. uptr chunk_state : 8; // Must be first. uptr alloc_tid : 24; + uptr free_tid : 24; uptr from_memalign : 1; + uptr alloc_type : 2; // 2-nd 8 bytes uptr user_requested_size; // Header2 (intersects with user memory). @@ -141,7 +143,9 @@ struct ChunkBase { // 1-st 8 bytes. uptr chunk_state : 8; // Must be first. uptr alloc_tid : 24; + uptr from_memalign : 1; + uptr alloc_type : 2; uptr free_tid : 24; // 2-nd 8 bytes uptr user_requested_size; @@ -271,7 +275,8 @@ AsanChunk *AsanChunkFifoList::Pop() { return res; } -static void *Allocate(uptr size, uptr alignment, StackTrace *stack) { +static void *Allocate(uptr size, uptr alignment, StackTrace *stack, + AllocType alloc_type) { Init(); CHECK(stack); if (alignment < 8) alignment = 8; @@ -306,6 +311,7 @@ static void *Allocate(uptr size, uptr alignment, StackTrace *stack) { uptr chunk_beg = user_beg - kChunkHeaderSize; AsanChunk *m = reinterpret_cast<AsanChunk *>(chunk_beg); m->chunk_state = CHUNK_ALLOCATED; + m->alloc_type = alloc_type; u32 alloc_tid = t ? t->tid() : 0; m->alloc_tid = alloc_tid; CHECK_EQ(alloc_tid, m->alloc_tid); // Does alloc_tid fit into the bitfield? @@ -339,7 +345,7 @@ static void *Allocate(uptr size, uptr alignment, StackTrace *stack) { return res; } -static void Deallocate(void *ptr, StackTrace *stack) { +static void Deallocate(void *ptr, StackTrace *stack, AllocType alloc_type) { uptr p = reinterpret_cast<uptr>(ptr); if (p == 0 || p == kReturnOnZeroMalloc) return; uptr chunk_beg = p - kChunkHeaderSize; @@ -354,6 +360,9 @@ static void Deallocate(void *ptr, StackTrace *stack) { else if (old_chunk_state != CHUNK_ALLOCATED) ReportFreeNotMalloced((uptr)ptr, stack); CHECK(old_chunk_state == CHUNK_ALLOCATED); + if (m->alloc_type != alloc_type && flags()->alloc_dealloc_mismatch) + ReportAllocTypeMismatch((uptr)ptr, stack, + (AllocType)m->alloc_type, (AllocType)alloc_type); CHECK_GE(m->alloc_tid, 0); if (SANITIZER_WORDSIZE == 64) // On 32-bits this resides in user area. @@ -394,11 +403,11 @@ static void *Reallocate(void *old_ptr, uptr new_size, StackTrace *stack) { CHECK(m->chunk_state == CHUNK_ALLOCATED); uptr old_size = m->UsedSize(); uptr memcpy_size = Min(new_size, old_size); - void *new_ptr = Allocate(new_size, 8, stack); + void *new_ptr = Allocate(new_size, 8, stack, FROM_MALLOC); if (new_ptr) { CHECK(REAL(memcpy) != 0); REAL(memcpy)(new_ptr, old_ptr, memcpy_size); - Deallocate(old_ptr, stack); + Deallocate(old_ptr, stack, FROM_MALLOC); } return new_ptr; } @@ -471,22 +480,23 @@ void AsanThreadLocalMallocStorage::CommitBack() { } SANITIZER_INTERFACE_ATTRIBUTE -void *asan_memalign(uptr alignment, uptr size, StackTrace *stack) { - return Allocate(size, alignment, stack); +void *asan_memalign(uptr alignment, uptr size, StackTrace *stack, + AllocType alloc_type) { + return Allocate(size, alignment, stack, alloc_type); } SANITIZER_INTERFACE_ATTRIBUTE -void asan_free(void *ptr, StackTrace *stack) { - Deallocate(ptr, stack); +void asan_free(void *ptr, StackTrace *stack, AllocType alloc_type) { + Deallocate(ptr, stack, alloc_type); } SANITIZER_INTERFACE_ATTRIBUTE void *asan_malloc(uptr size, StackTrace *stack) { - return Allocate(size, 8, stack); + return Allocate(size, 8, stack, FROM_MALLOC); } void *asan_calloc(uptr nmemb, uptr size, StackTrace *stack) { - void *ptr = Allocate(nmemb * size, 8, stack); + void *ptr = Allocate(nmemb * size, 8, stack, FROM_MALLOC); if (ptr) REAL(memset)(ptr, 0, nmemb * size); return ptr; @@ -494,16 +504,16 @@ void *asan_calloc(uptr nmemb, uptr size, StackTrace *stack) { void *asan_realloc(void *p, uptr size, StackTrace *stack) { if (p == 0) - return Allocate(size, 8, stack); + return Allocate(size, 8, stack, FROM_MALLOC); if (size == 0) { - Deallocate(p, stack); + Deallocate(p, stack, FROM_MALLOC); return 0; } return Reallocate(p, size, stack); } void *asan_valloc(uptr size, StackTrace *stack) { - return Allocate(size, GetPageSizeCached(), stack); + return Allocate(size, GetPageSizeCached(), stack, FROM_MALLOC); } void *asan_pvalloc(uptr size, StackTrace *stack) { @@ -513,12 +523,12 @@ void *asan_pvalloc(uptr size, StackTrace *stack) { // pvalloc(0) should allocate one page. size = PageSize; } - return Allocate(size, PageSize, stack); + return Allocate(size, PageSize, stack, FROM_MALLOC); } int asan_posix_memalign(void **memptr, uptr alignment, uptr size, StackTrace *stack) { - void *ptr = Allocate(size, alignment, stack); + void *ptr = Allocate(size, alignment, stack, FROM_MALLOC); CHECK(IsAligned((uptr)ptr, alignment)); *memptr = ptr; return 0; diff --git a/lib/asan/asan_flags.h b/lib/asan/asan_flags.h index 8306170fa..296a5bbf7 100644 --- a/lib/asan/asan_flags.h +++ b/lib/asan/asan_flags.h @@ -102,6 +102,8 @@ struct Flags { // Poison (or not) the heap memory on [de]allocation. Zero value is useful // for benchmarking the allocator or instrumentator. bool poison_heap; + // Report errors on malloc/delete, new/free, new/delete[], etc. + bool alloc_dealloc_mismatch; }; Flags *flags(); diff --git a/lib/asan/asan_mac.cc b/lib/asan/asan_mac.cc index df261870f..12d4f5b8b 100644 --- a/lib/asan/asan_mac.cc +++ b/lib/asan/asan_mac.cc @@ -319,7 +319,7 @@ void asan_dispatch_call_block_and_release(void *block) { asan_register_worker_thread(context->parent_tid, &stack); // Call the original dispatcher for the block. context->func(context->block); - asan_free(context, &stack); + asan_free(context, &stack, FROM_MALLOC); } } // namespace __asan @@ -461,7 +461,7 @@ void *wrap_workitem_func(void *arg) { worker_t fn = (worker_t)(ctxt->func); void *result = fn(ctxt->block); GET_STACK_TRACE_THREAD; - asan_free(arg, &stack); + asan_free(arg, &stack, FROM_MALLOC); return result; } diff --git a/lib/asan/asan_malloc_linux.cc b/lib/asan/asan_malloc_linux.cc index df94513a6..dabd9513f 100644 --- a/lib/asan/asan_malloc_linux.cc +++ b/lib/asan/asan_malloc_linux.cc @@ -61,12 +61,12 @@ using namespace __asan; // NOLINT INTERCEPTOR(void, free, void *ptr) { GET_STACK_TRACE_FREE; - asan_free(ptr, &stack); + asan_free(ptr, &stack, FROM_MALLOC); } INTERCEPTOR(void, cfree, void *ptr) { GET_STACK_TRACE_FREE; - asan_free(ptr, &stack); + asan_free(ptr, &stack, FROM_MALLOC); } INTERCEPTOR(void*, malloc, uptr size) { @@ -97,7 +97,7 @@ INTERCEPTOR(void*, realloc, void *ptr, uptr size) { INTERCEPTOR(void*, memalign, uptr boundary, uptr size) { GET_STACK_TRACE_MALLOC; - return asan_memalign(boundary, size, &stack); + return asan_memalign(boundary, size, &stack, FROM_MALLOC); } INTERCEPTOR(void*, __libc_memalign, uptr align, uptr s) diff --git a/lib/asan/asan_malloc_mac.cc b/lib/asan/asan_malloc_mac.cc index 9ed7b40be..545ede2de 100644 --- a/lib/asan/asan_malloc_mac.cc +++ b/lib/asan/asan_malloc_mac.cc @@ -93,7 +93,7 @@ INTERCEPTOR(void, free, void *ptr) { } else { if (!asan_mz_size(ptr)) ptr = get_saved_cfallocator_ref(ptr); GET_STACK_TRACE_FREE; - asan_free(ptr, &stack); + asan_free(ptr, &stack, FROM_MALLOC); } } @@ -165,7 +165,7 @@ void *mz_valloc(malloc_zone_t *zone, size_t size) { return malloc_zone_valloc(system_malloc_zone, size); } GET_STACK_TRACE_MALLOC; - return asan_memalign(GetPageSizeCached(), size, &stack); + return asan_memalign(GetPageSizeCached(), size, &stack, FROM_MALLOC); } #define GET_ZONE_FOR_PTR(ptr) \ @@ -176,7 +176,7 @@ void ALWAYS_INLINE free_common(void *context, void *ptr) { if (!ptr) return; if (asan_mz_size(ptr)) { GET_STACK_TRACE_FREE; - asan_free(ptr, &stack); + asan_free(ptr, &stack, FROM_MALLOC); } else { // If the pointer does not belong to any of the zones, use one of the // fallback methods to free memory. @@ -192,7 +192,7 @@ void ALWAYS_INLINE free_common(void *context, void *ptr) { ptr = get_saved_cfallocator_ref(ptr); GET_STACK_TRACE_FREE; if (!flags()->mac_ignore_invalid_free) { - asan_free(ptr, &stack); + asan_free(ptr, &stack, FROM_MALLOC); } else { GET_ZONE_FOR_PTR(ptr); WarnMacFreeUnallocated((uptr)ptr, (uptr)zone_ptr, zone_name, &stack); @@ -262,7 +262,7 @@ void *mz_memalign(malloc_zone_t *zone, size_t align, size_t size) { return malloc_zone_memalign(system_malloc_zone, align, size); } GET_STACK_TRACE_MALLOC; - return asan_memalign(align, size, &stack); + return asan_memalign(align, size, &stack, FROM_MALLOC); } // This function is currently unused, and we build with -Werror. diff --git a/lib/asan/asan_malloc_win.cc b/lib/asan/asan_malloc_win.cc index 0ddc3ca44..9fcfea563 100644 --- a/lib/asan/asan_malloc_win.cc +++ b/lib/asan/asan_malloc_win.cc @@ -32,7 +32,7 @@ using namespace __asan; // NOLINT extern "C" { void free(void *ptr) { GET_STACK_TRACE_FREE; - return asan_free(ptr, &stack); + return asan_free(ptr, &stack, FROM_MALLOC); } void _free_dbg(void* ptr, int) { diff --git a/lib/asan/asan_new_delete.cc b/lib/asan/asan_new_delete.cc index e21aa4522..5d1f23c54 100644 --- a/lib/asan/asan_new_delete.cc +++ b/lib/asan/asan_new_delete.cc @@ -35,32 +35,34 @@ namespace std { struct nothrow_t {}; } // namespace std -#define OPERATOR_NEW_BODY \ +#define OPERATOR_NEW_BODY(type) \ GET_STACK_TRACE_MALLOC;\ - return asan_memalign(0, size, &stack); + return asan_memalign(0, size, &stack, type); INTERCEPTOR_ATTRIBUTE -void *operator new(size_t size) { OPERATOR_NEW_BODY; } +void *operator new(size_t size) { OPERATOR_NEW_BODY(FROM_NEW); } INTERCEPTOR_ATTRIBUTE -void *operator new[](size_t size) { OPERATOR_NEW_BODY; } +void *operator new[](size_t size) { OPERATOR_NEW_BODY(FROM_NEW_BR); } INTERCEPTOR_ATTRIBUTE -void *operator new(size_t size, std::nothrow_t const&) { OPERATOR_NEW_BODY; } +void *operator new(size_t size, std::nothrow_t const&) +{ OPERATOR_NEW_BODY(FROM_NEW); } INTERCEPTOR_ATTRIBUTE -void *operator new[](size_t size, std::nothrow_t const&) { OPERATOR_NEW_BODY; } +void *operator new[](size_t size, std::nothrow_t const&) +{ OPERATOR_NEW_BODY(FROM_NEW_BR); } -#define OPERATOR_DELETE_BODY \ +#define OPERATOR_DELETE_BODY(type) \ GET_STACK_TRACE_FREE;\ - asan_free(ptr, &stack); + asan_free(ptr, &stack, type); INTERCEPTOR_ATTRIBUTE -void operator delete(void *ptr) { OPERATOR_DELETE_BODY; } +void operator delete(void *ptr) { OPERATOR_DELETE_BODY(FROM_NEW); } INTERCEPTOR_ATTRIBUTE -void operator delete[](void *ptr) { OPERATOR_DELETE_BODY; } +void operator delete[](void *ptr) { OPERATOR_DELETE_BODY(FROM_NEW_BR); } INTERCEPTOR_ATTRIBUTE void operator delete(void *ptr, std::nothrow_t const&) -{ OPERATOR_DELETE_BODY; } +{ OPERATOR_DELETE_BODY(FROM_NEW); } INTERCEPTOR_ATTRIBUTE void operator delete[](void *ptr, std::nothrow_t const&) -{ OPERATOR_DELETE_BODY; } +{ OPERATOR_DELETE_BODY(FROM_NEW_BR); } #endif diff --git a/lib/asan/asan_report.cc b/lib/asan/asan_report.cc index f403ef4dc..7a2a55d75 100644 --- a/lib/asan/asan_report.cc +++ b/lib/asan/asan_report.cc @@ -488,6 +488,26 @@ void ReportFreeNotMalloced(uptr addr, StackTrace *stack) { DescribeHeapAddress(addr, 1); } +void ReportAllocTypeMismatch(uptr addr, StackTrace *stack, + AllocType alloc_type, + AllocType dealloc_type) { + static const char *alloc_names[] = + {"INVALID", "malloc", "operator new", "operator new []"}; + static const char *dealloc_names[] = + {"INVALID", "free", "operator delete", "operator delete []"}; + CHECK_NE(alloc_type, dealloc_type); + ScopedInErrorReport in_report; + Decorator d; + Printf("%s", d.Warning()); + Report("ERROR: AddressSanitizer: alloc-dealloc-mismatch (%s vs %s) on %p\n", + alloc_names[alloc_type], dealloc_names[dealloc_type], addr); + Printf("%s", d.EndWarning()); + PrintStack(stack); + DescribeHeapAddress(addr, 1); + Report("HINT: if you don't care about these warnings you may set " + "ASAN_OPTIONS=alloc_dealloc_mismatch=0\n"); +} + void ReportMallocUsableSizeNotOwned(uptr addr, StackTrace *stack) { ScopedInErrorReport in_report; Decorator d; diff --git a/lib/asan/asan_report.h b/lib/asan/asan_report.h index dc3bf9b01..f0617f919 100644 --- a/lib/asan/asan_report.h +++ b/lib/asan/asan_report.h @@ -12,6 +12,7 @@ // ASan-private header for error reporting functions. //===----------------------------------------------------------------------===// +#include "asan_allocator.h" #include "asan_internal.h" #include "asan_thread.h" #include "sanitizer/asan_interface.h" @@ -34,6 +35,9 @@ void DescribeThread(AsanThreadSummary *summary); void NORETURN ReportSIGSEGV(uptr pc, uptr sp, uptr bp, uptr addr); void NORETURN ReportDoubleFree(uptr addr, StackTrace *stack); void NORETURN ReportFreeNotMalloced(uptr addr, StackTrace *stack); +void NORETURN ReportAllocTypeMismatch(uptr addr, StackTrace *stack, + AllocType alloc_type, + AllocType dealloc_type); void NORETURN ReportMallocUsableSizeNotOwned(uptr addr, StackTrace *stack); void NORETURN ReportAsanGetAllocatedSizeNotOwned(uptr addr, diff --git a/lib/asan/asan_rtl.cc b/lib/asan/asan_rtl.cc index b4f0d1ab9..80008f641 100644 --- a/lib/asan/asan_rtl.cc +++ b/lib/asan/asan_rtl.cc @@ -107,6 +107,7 @@ static void ParseFlagsFromString(Flags *f, const char *str) { ParseFlag(str, &f->fast_unwind_on_fatal, "fast_unwind_on_fatal"); ParseFlag(str, &f->fast_unwind_on_malloc, "fast_unwind_on_malloc"); ParseFlag(str, &f->poison_heap, "poison_heap"); + ParseFlag(str, &f->alloc_dealloc_mismatch, "alloc_dealloc_mismatch"); } void InitializeFlags(Flags *f, const char *env) { @@ -143,6 +144,7 @@ void InitializeFlags(Flags *f, const char *env) { f->fast_unwind_on_fatal = true; f->fast_unwind_on_malloc = true; f->poison_heap = true; + f->alloc_dealloc_mismatch = false; // Override from user-specified string. ParseFlagsFromString(f, MaybeCallAsanDefaultOptions()); diff --git a/lib/asan/lit_tests/deep_stack_uaf.cc b/lib/asan/lit_tests/deep_stack_uaf.cc index e4481be03..7b32798fe 100644 --- a/lib/asan/lit_tests/deep_stack_uaf.cc +++ b/lib/asan/lit_tests/deep_stack_uaf.cc @@ -25,7 +25,7 @@ struct DeepFree<0> { }; int main() { - char *x = new char[10]; + char *x = (char*)malloc(10); // deep_free(x); DeepFree<200>::free(x); return x[5]; diff --git a/lib/asan/lit_tests/malloc_delete_mismatch.cc b/lib/asan/lit_tests/malloc_delete_mismatch.cc new file mode 100644 index 000000000..070c5ca0a --- /dev/null +++ b/lib/asan/lit_tests/malloc_delete_mismatch.cc @@ -0,0 +1,26 @@ +// Check that we detect malloc/delete mismatch only if the approptiate flag +// is set. + +// RUN: %clangxx_asan -g %s -o %t 2>&1 +// RUN: ASAN_OPTIONS=alloc_dealloc_mismatch=1 %t 2>&1 | \ +// RUN: %symbolize | FileCheck %s + +// No error here. +// RUN: ASAN_OPTIONS=alloc_dealloc_mismatch=0 %t +#include <stdlib.h> + +static volatile char *x; + +int main() { + x = (char*)malloc(10); + x[0] = 0; + delete x; +} +// CHECK: ERROR: AddressSanitizer: alloc-dealloc-mismatch (malloc vs operator delete) on 0x +// CHECK-NEXT: #0{{.*}}operator delete +// CHECK-NEXT: #1{{.*}}main +// CHECK: is located 0 bytes inside of 10-byte region +// CHECK-NEXT: allocated by thread T0 here: +// CHECK-NEXT: #0{{.*}}malloc +// CHECK-NEXT: #1{{.*}}main +// CHECK: HINT: if you don't care about these warnings you may set ASAN_OPTIONS=alloc_dealloc_mismatch=0 diff --git a/lib/asan/tests/asan_noinst_test.cc b/lib/asan/tests/asan_noinst_test.cc index bd2bca066..13ac7a94c 100644 --- a/lib/asan/tests/asan_noinst_test.cc +++ b/lib/asan/tests/asan_noinst_test.cc @@ -64,7 +64,7 @@ static void MallocStress(size_t n) { void *ptr = vec[idx]; vec[idx] = vec.back(); vec.pop_back(); - __asan::asan_free(ptr, &stack1); + __asan::asan_free(ptr, &stack1, __asan::FROM_MALLOC); } else { size_t size = my_rand(&seed) % 1000 + 1; switch ((my_rand(&seed) % 128)) { @@ -73,7 +73,8 @@ static void MallocStress(size_t n) { case 2: size += 4096; break; } size_t alignment = 1 << (my_rand(&seed) % 10 + 1); - char *ptr = (char*)__asan::asan_memalign(alignment, size, &stack2); + char *ptr = (char*)__asan::asan_memalign(alignment, size, + &stack2, __asan::FROM_MALLOC); vec.push_back(ptr); ptr[0] = 0; ptr[size-1] = 0; @@ -81,7 +82,7 @@ static void MallocStress(size_t n) { } } for (size_t i = 0; i < vec.size(); i++) - __asan::asan_free(vec[i], &stack3); + __asan::asan_free(vec[i], &stack3, __asan::FROM_MALLOC); } @@ -262,12 +263,12 @@ TEST(AddressSanitizer, QuarantineTest) { const int size = 32; void *p = __asan::asan_malloc(size, &stack); - __asan::asan_free(p, &stack); + __asan::asan_free(p, &stack, __asan::FROM_MALLOC); size_t i; size_t max_i = 1 << 30; for (i = 0; i < max_i; i++) { void *p1 = __asan::asan_malloc(size, &stack); - __asan::asan_free(p1, &stack); + __asan::asan_free(p1, &stack, __asan::FROM_MALLOC); if (p1 == p) break; } // fprintf(stderr, "i=%ld\n", i); @@ -284,7 +285,7 @@ void *ThreadedQuarantineTestWorker(void *unused) { for (size_t i = 0; i < 1000; i++) { void *p = __asan::asan_malloc(1 + (my_rand(&seed) % 4000), &stack); - __asan::asan_free(p, &stack); + __asan::asan_free(p, &stack, __asan::FROM_MALLOC); } return NULL; } @@ -315,7 +316,7 @@ void *ThreadedOneSizeMallocStress(void *unused) { p[i] = __asan::asan_malloc(32, &stack); } for (size_t i = 0; i < kNumMallocs; i++) { - __asan::asan_free(p[i], &stack); + __asan::asan_free(p[i], &stack, __asan::FROM_MALLOC); } } return NULL; diff --git a/lib/asan/tests/asan_test.cc b/lib/asan/tests/asan_test.cc index db44b0cae..1d2b3ac27 100644 --- a/lib/asan/tests/asan_test.cc +++ b/lib/asan/tests/asan_test.cc @@ -1520,7 +1520,7 @@ void RunAtoiOOBTest(PointerToCallAtoi Atoi) { EXPECT_DEATH(Atoi(array + 9), RightOOBReadMessage(0)); array[8] = '-'; Atoi(array); - delete array; + free(array); } TEST(AddressSanitizer, AtoiAndFriendsOOBTest) { @@ -1574,7 +1574,7 @@ void RunStrtolOOBTest(PointerToCallStrtol Strtol) { EXPECT_EQ(array, endptr); Strtol(array + 2, NULL, 0); EXPECT_EQ(array, endptr); - delete array; + free(array); } TEST(AddressSanitizer, StrtollOOBTest) { @@ -1622,7 +1622,7 @@ TEST(AddressSanitizer, pread) { "AddressSanitizer: heap-buffer-overflow" ".* is located 4 bytes to the right of 10-byte region"); close(fd); - delete x; + delete [] x; } TEST(AddressSanitizer, pread64) { @@ -1634,7 +1634,7 @@ TEST(AddressSanitizer, pread64) { "AddressSanitizer: heap-buffer-overflow" ".* is located 4 bytes to the right of 10-byte region"); close(fd); - delete x; + delete [] x; } TEST(AddressSanitizer, read) { @@ -1646,7 +1646,7 @@ TEST(AddressSanitizer, read) { "AddressSanitizer: heap-buffer-overflow" ".* is located 4 bytes to the right of 10-byte region"); close(fd); - delete x; + delete [] x; } #endif // defined(__linux__) && !defined(ANDROID) && !defined(__ANDROID__) @@ -1986,6 +1986,27 @@ TEST(AddressSanitizer, AttributeNoAddressSafetyTest) { Ident(NoAddressSafety)(); } +static string MismatchStr(const string &str) { + return string("AddressSanitizer: alloc-dealloc-mismatch \\(") + str; +} + +// This test is disabled until we enable alloc_dealloc_mismatch by default. +// The feature is also tested by lit tests. +TEST(AddressSanitizer, DISABLED_AllocDeallocMismatch) { + EXPECT_DEATH(free(Ident(new int)), + MismatchStr("operator new vs free")); + EXPECT_DEATH(free(Ident(new int[2])), + MismatchStr("operator new \\[\\] vs free")); + EXPECT_DEATH(delete (Ident(new int[2])), + MismatchStr("operator new \\[\\] vs operator delete")); + EXPECT_DEATH(delete (Ident((int*)malloc(2 * sizeof(int)))), + MismatchStr("malloc vs operator delete")); + EXPECT_DEATH(delete [] (Ident(new int)), + MismatchStr("operator new vs operator delete \\[\\]")); + EXPECT_DEATH(delete [] (Ident((int*)malloc(2 * sizeof(int)))), + MismatchStr("malloc vs operator delete \\[\\]")); +} + // ------------------ demo tests; run each one-by-one ------------- // e.g. --gtest_filter=*DemoOOBLeftHigh --gtest_also_run_disabled_tests TEST(AddressSanitizer, DISABLED_DemoThreadedTest) { |