summaryrefslogtreecommitdiff
path: root/lib/asan
diff options
context:
space:
mode:
authorKostya Serebryany <kcc@google.com>2012-12-21 08:53:59 +0000
committerKostya Serebryany <kcc@google.com>2012-12-21 08:53:59 +0000
commitfe6d91684bcda766593800f6307233f1a33d31f6 (patch)
tree309fa82b7a4e10b4f11f37992eff2937ad3f74b7 /lib/asan
parentd37c272621befc2f3b17c9e581ed970fbfbfae64 (diff)
downloadcompiler-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.cc39
-rw-r--r--lib/asan/asan_allocator.h11
-rw-r--r--lib/asan/asan_allocator2.cc40
-rw-r--r--lib/asan/asan_flags.h2
-rw-r--r--lib/asan/asan_mac.cc4
-rw-r--r--lib/asan/asan_malloc_linux.cc6
-rw-r--r--lib/asan/asan_malloc_mac.cc10
-rw-r--r--lib/asan/asan_malloc_win.cc2
-rw-r--r--lib/asan/asan_new_delete.cc26
-rw-r--r--lib/asan/asan_report.cc20
-rw-r--r--lib/asan/asan_report.h4
-rw-r--r--lib/asan/asan_rtl.cc2
-rw-r--r--lib/asan/lit_tests/deep_stack_uaf.cc2
-rw-r--r--lib/asan/lit_tests/malloc_delete_mismatch.cc26
-rw-r--r--lib/asan/tests/asan_noinst_test.cc15
-rw-r--r--lib/asan/tests/asan_test.cc31
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) {