From: Alexander Potapenko Date: Mon, 21 Jan 2013 14:49:55 +0000 (+0000) Subject: [ASan] Move Mac-specific tests to asan_mac_test.cc X-Git-Tag: llvmorg-3.3.0-rc1~7437 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=c21aa5cb39f14c13b1d6b72328845c4575700ab3;p=platform%2Fupstream%2Fllvm.git [ASan] Move Mac-specific tests to asan_mac_test.cc llvm-svn: 173048 --- diff --git a/compiler-rt/lib/asan/tests/CMakeLists.txt b/compiler-rt/lib/asan/tests/CMakeLists.txt index f8ded289e361..0d5c6bd76000 100644 --- a/compiler-rt/lib/asan/tests/CMakeLists.txt +++ b/compiler-rt/lib/asan/tests/CMakeLists.txt @@ -140,7 +140,9 @@ macro(add_asan_tests_for_arch arch) asan_compile(ASAN_INST_TEST_OBJECTS asan_test.cc ${arch} ${ASAN_UNITTEST_INSTRUMENTED_CFLAGS}) if (APPLE) - asan_compile(ASAN_INST_TEST_OBJECTS asan_mac_test.mm ${arch} + asan_compile(ASAN_INST_TEST_OBJECTS asan_mac_test.cc ${arch} + ${ASAN_UNITTEST_INSTRUMENTED_CFLAGS}) + asan_compile(ASAN_INST_TEST_OBJECTS asan_mac_test_helpers.mm ${arch} ${ASAN_UNITTEST_INSTRUMENTED_CFLAGS} -ObjC) endif() # Uninstrumented tests. diff --git a/compiler-rt/lib/asan/tests/asan_mac_test.cc b/compiler-rt/lib/asan/tests/asan_mac_test.cc new file mode 100644 index 000000000000..cabdfd711ea2 --- /dev/null +++ b/compiler-rt/lib/asan/tests/asan_mac_test.cc @@ -0,0 +1,236 @@ +//===-- asan_test_mac.cc --------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +//===----------------------------------------------------------------------===// + +#include "asan_test_utils.h" + +#include "asan_mac_test.h" + +#include +#include // For MAC_OS_X_VERSION_* +#include + +TEST(AddressSanitizerMac, CFAllocatorDefaultDoubleFree) { + EXPECT_DEATH( + CFAllocatorDefaultDoubleFree(NULL), + "attempting double-free"); +} + +void CFAllocator_DoubleFreeOnPthread() { + pthread_t child; + PTHREAD_CREATE(&child, NULL, CFAllocatorDefaultDoubleFree, NULL); + PTHREAD_JOIN(child, NULL); // Shouldn't be reached. +} + +TEST(AddressSanitizerMac, CFAllocatorDefaultDoubleFree_ChildPhread) { + EXPECT_DEATH(CFAllocator_DoubleFreeOnPthread(), "attempting double-free"); +} + +namespace { + +void *GLOB; + +void *CFAllocatorAllocateToGlob(void *unused) { + GLOB = CFAllocatorAllocate(NULL, 100, /*hint*/0); + return NULL; +} + +void *CFAllocatorDeallocateFromGlob(void *unused) { + char *p = (char*)GLOB; + p[100] = 'A'; // ASan should report an error here. + CFAllocatorDeallocate(NULL, GLOB); + return NULL; +} + +void CFAllocator_PassMemoryToAnotherThread() { + pthread_t th1, th2; + PTHREAD_CREATE(&th1, NULL, CFAllocatorAllocateToGlob, NULL); + PTHREAD_JOIN(th1, NULL); + PTHREAD_CREATE(&th2, NULL, CFAllocatorDeallocateFromGlob, NULL); + PTHREAD_JOIN(th2, NULL); +} + +TEST(AddressSanitizerMac, CFAllocator_PassMemoryToAnotherThread) { + EXPECT_DEATH(CFAllocator_PassMemoryToAnotherThread(), + "heap-buffer-overflow"); +} + +} // namespace + +// TODO(glider): figure out whether we still need these tests. Is it correct +// to intercept the non-default CFAllocators? +TEST(AddressSanitizerMac, DISABLED_CFAllocatorSystemDefaultDoubleFree) { + EXPECT_DEATH( + CFAllocatorSystemDefaultDoubleFree(), + "attempting double-free"); +} + +// We're intercepting malloc, so kCFAllocatorMalloc is routed to ASan. +TEST(AddressSanitizerMac, CFAllocatorMallocDoubleFree) { + EXPECT_DEATH(CFAllocatorMallocDoubleFree(), "attempting double-free"); +} + +TEST(AddressSanitizerMac, DISABLED_CFAllocatorMallocZoneDoubleFree) { + EXPECT_DEATH(CFAllocatorMallocZoneDoubleFree(), "attempting double-free"); +} + +// For libdispatch tests below we check that ASan got to the shadow byte +// legend, i.e. managed to print the thread stacks (this almost certainly +// means that the libdispatch task creation has been intercepted correctly). +TEST(AddressSanitizerMac, GCDDispatchAsync) { + // Make sure the whole ASan report is printed, i.e. that we don't die + // on a CHECK. + EXPECT_DEATH(TestGCDDispatchAsync(), "Shadow byte legend"); +} + +TEST(AddressSanitizerMac, GCDDispatchSync) { + // Make sure the whole ASan report is printed, i.e. that we don't die + // on a CHECK. + EXPECT_DEATH(TestGCDDispatchSync(), "Shadow byte legend"); +} + + +TEST(AddressSanitizerMac, GCDReuseWqthreadsAsync) { + // Make sure the whole ASan report is printed, i.e. that we don't die + // on a CHECK. + EXPECT_DEATH(TestGCDReuseWqthreadsAsync(), "Shadow byte legend"); +} + +TEST(AddressSanitizerMac, GCDReuseWqthreadsSync) { + // Make sure the whole ASan report is printed, i.e. that we don't die + // on a CHECK. + EXPECT_DEATH(TestGCDReuseWqthreadsSync(), "Shadow byte legend"); +} + +TEST(AddressSanitizerMac, GCDDispatchAfter) { + // Make sure the whole ASan report is printed, i.e. that we don't die + // on a CHECK. + EXPECT_DEATH(TestGCDDispatchAfter(), "Shadow byte legend"); +} + +TEST(AddressSanitizerMac, GCDSourceEvent) { + // Make sure the whole ASan report is printed, i.e. that we don't die + // on a CHECK. + EXPECT_DEATH(TestGCDSourceEvent(), "Shadow byte legend"); +} + +TEST(AddressSanitizerMac, GCDSourceCancel) { + // Make sure the whole ASan report is printed, i.e. that we don't die + // on a CHECK. + EXPECT_DEATH(TestGCDSourceCancel(), "Shadow byte legend"); +} + +TEST(AddressSanitizerMac, GCDGroupAsync) { + // Make sure the whole ASan report is printed, i.e. that we don't die + // on a CHECK. + EXPECT_DEATH(TestGCDGroupAsync(), "Shadow byte legend"); +} + +void *MallocIntrospectionLockWorker(void *_) { + const int kNumPointers = 100; + int i; + void *pointers[kNumPointers]; + for (i = 0; i < kNumPointers; i++) { + pointers[i] = malloc(i + 1); + } + for (i = 0; i < kNumPointers; i++) { + free(pointers[i]); + } + + return NULL; +} + +void *MallocIntrospectionLockForker(void *_) { + pid_t result = fork(); + if (result == -1) { + perror("fork"); + } + assert(result != -1); + if (result == 0) { + // Call malloc in the child process to make sure we won't deadlock. + void *ptr = malloc(42); + free(ptr); + exit(0); + } else { + // Return in the parent process. + return NULL; + } +} + +TEST(AddressSanitizerMac, MallocIntrospectionLock) { + // Incorrect implementation of force_lock and force_unlock in our malloc zone + // will cause forked processes to deadlock. + // TODO(glider): need to detect that none of the child processes deadlocked. + const int kNumWorkers = 5, kNumIterations = 100; + int i, iter; + for (iter = 0; iter < kNumIterations; iter++) { + pthread_t workers[kNumWorkers], forker; + for (i = 0; i < kNumWorkers; i++) { + PTHREAD_CREATE(&workers[i], 0, MallocIntrospectionLockWorker, 0); + } + PTHREAD_CREATE(&forker, 0, MallocIntrospectionLockForker, 0); + for (i = 0; i < kNumWorkers; i++) { + PTHREAD_JOIN(workers[i], 0); + } + PTHREAD_JOIN(forker, 0); + } +} + +void *TSDAllocWorker(void *test_key) { + if (test_key) { + void *mem = malloc(10); + pthread_setspecific(*(pthread_key_t*)test_key, mem); + } + return NULL; +} + +TEST(AddressSanitizerMac, DISABLED_TSDWorkqueueTest) { + pthread_t th; + pthread_key_t test_key; + pthread_key_create(&test_key, CallFreeOnWorkqueue); + PTHREAD_CREATE(&th, NULL, TSDAllocWorker, &test_key); + PTHREAD_JOIN(th, NULL); + pthread_key_delete(test_key); +} + +// Test that CFStringCreateCopy does not copy constant strings. +TEST(AddressSanitizerMac, CFStringCreateCopy) { + CFStringRef str = CFSTR("Hello world!\n"); + CFStringRef str2 = CFStringCreateCopy(0, str); + EXPECT_EQ(str, str2); +} + +TEST(AddressSanitizerMac, NSObjectOOB) { + // Make sure that our allocators are used for NSObjects. + EXPECT_DEATH(TestOOBNSObjects(), "heap-buffer-overflow"); +} + +// Make sure that correct pointer is passed to free() when deallocating a +// NSURL object. +// See http://code.google.com/p/address-sanitizer/issues/detail?id=70. +TEST(AddressSanitizerMac, NSURLDeallocation) { + TestNSURLDeallocation(); +} + +// See http://code.google.com/p/address-sanitizer/issues/detail?id=109. +TEST(AddressSanitizerMac, Mstats) { + malloc_statistics_t stats1, stats2; + malloc_zone_statistics(/*all zones*/NULL, &stats1); + const size_t kMallocSize = 100000; + void *alloc = Ident(malloc(kMallocSize)); + malloc_zone_statistics(/*all zones*/NULL, &stats2); + EXPECT_GT(stats2.blocks_in_use, stats1.blocks_in_use); + EXPECT_GE(stats2.size_in_use - stats1.size_in_use, kMallocSize); + free(alloc); + // Even the default OSX allocator may not change the stats after free(). +} + diff --git a/compiler-rt/lib/asan/tests/asan_mac_test.mm b/compiler-rt/lib/asan/tests/asan_mac_test.mm deleted file mode 100644 index 4cbd2bb247fd..000000000000 --- a/compiler-rt/lib/asan/tests/asan_mac_test.mm +++ /dev/null @@ -1,237 +0,0 @@ -// Mac OS X 10.6 or higher only. -#include -#include -#include -#include -#include - -#import -#import -#import - -// This is a (void*)(void*) function so it can be passed to pthread_create. -void *CFAllocatorDefaultDoubleFree(void *unused) { - void *mem = CFAllocatorAllocate(kCFAllocatorDefault, 5, 0); - CFAllocatorDeallocate(kCFAllocatorDefault, mem); - CFAllocatorDeallocate(kCFAllocatorDefault, mem); - return 0; -} - -void CFAllocatorSystemDefaultDoubleFree() { - void *mem = CFAllocatorAllocate(kCFAllocatorSystemDefault, 5, 0); - CFAllocatorDeallocate(kCFAllocatorSystemDefault, mem); - CFAllocatorDeallocate(kCFAllocatorSystemDefault, mem); -} - -void CFAllocatorMallocDoubleFree() { - void *mem = CFAllocatorAllocate(kCFAllocatorMalloc, 5, 0); - CFAllocatorDeallocate(kCFAllocatorMalloc, mem); - CFAllocatorDeallocate(kCFAllocatorMalloc, mem); -} - -void CFAllocatorMallocZoneDoubleFree() { - void *mem = CFAllocatorAllocate(kCFAllocatorMallocZone, 5, 0); - CFAllocatorDeallocate(kCFAllocatorMallocZone, mem); - CFAllocatorDeallocate(kCFAllocatorMallocZone, mem); -} - -__attribute__((noinline)) -void access_memory(char *a) { - *a = 0; -} - -// Test the +load instrumentation. -// Because the +load methods are invoked before anything else is initialized, -// it makes little sense to wrap the code below into a gTest test case. -// If AddressSanitizer doesn't instrument the +load method below correctly, -// everything will just crash. - -char kStartupStr[] = - "If your test didn't crash, AddressSanitizer is instrumenting " - "the +load methods correctly."; - -@interface LoadSomething : NSObject { -} -@end - -@implementation LoadSomething - -+(void) load { - for (size_t i = 0; i < strlen(kStartupStr); i++) { - access_memory(&kStartupStr[i]); // make sure no optimizations occur. - } - // Don't print anything here not to interfere with the death tests. -} - -@end - -void worker_do_alloc(int size) { - char * volatile mem = (char * volatile)malloc(size); - mem[0] = 0; // Ok - free(mem); -} - -void worker_do_crash(int size) { - char * volatile mem = (char * volatile)malloc(size); - access_memory(&mem[size]); // BOOM - free(mem); -} - -// Tests for the Grand Central Dispatch. See -// http://developer.apple.com/library/mac/#documentation/Performance/Reference/GCD_libdispatch_Ref/Reference/reference.html -// for the reference. - -void TestGCDDispatchAsync() { - dispatch_queue_t queue = dispatch_get_global_queue(0, 0); - dispatch_block_t block = ^{ worker_do_crash(1024); }; - // dispatch_async() runs the task on a worker thread that does not go through - // pthread_create(). We need to verify that AddressSanitizer notices that the - // thread has started. - dispatch_async(queue, block); - // TODO(glider): this is hacky. Need to wait for the worker instead. - sleep(1); -} - -void TestGCDDispatchSync() { - dispatch_queue_t queue = dispatch_get_global_queue(2, 0); - dispatch_block_t block = ^{ worker_do_crash(1024); }; - // dispatch_sync() runs the task on a worker thread that does not go through - // pthread_create(). We need to verify that AddressSanitizer notices that the - // thread has started. - dispatch_sync(queue, block); - // TODO(glider): this is hacky. Need to wait for the worker instead. - sleep(1); -} - -// libdispatch spawns a rather small number of threads and reuses them. We need -// to make sure AddressSanitizer handles the reusing correctly. -void TestGCDReuseWqthreadsAsync() { - dispatch_queue_t queue = dispatch_get_global_queue(0, 0); - dispatch_block_t block_alloc = ^{ worker_do_alloc(1024); }; - dispatch_block_t block_crash = ^{ worker_do_crash(1024); }; - for (int i = 0; i < 100; i++) { - dispatch_async(queue, block_alloc); - } - dispatch_async(queue, block_crash); - // TODO(glider): this is hacky. Need to wait for the workers instead. - sleep(1); -} - -// Try to trigger abnormal behaviour of dispatch_sync() being unhandled by us. -void TestGCDReuseWqthreadsSync() { - dispatch_queue_t queue[4]; - queue[0] = dispatch_get_global_queue(2, 0); - queue[1] = dispatch_get_global_queue(0, 0); - queue[2] = dispatch_get_global_queue(-2, 0); - queue[3] = dispatch_queue_create("my_queue", NULL); - dispatch_block_t block_alloc = ^{ worker_do_alloc(1024); }; - dispatch_block_t block_crash = ^{ worker_do_crash(1024); }; - for (int i = 0; i < 1000; i++) { - dispatch_sync(queue[i % 4], block_alloc); - } - dispatch_sync(queue[3], block_crash); - // TODO(glider): this is hacky. Need to wait for the workers instead. - sleep(1); -} - -void TestGCDDispatchAfter() { - dispatch_queue_t queue = dispatch_get_global_queue(0, 0); - dispatch_block_t block_crash = ^{ worker_do_crash(1024); }; - // Schedule the event one second from the current time. - dispatch_time_t milestone = - dispatch_time(DISPATCH_TIME_NOW, 1LL * NSEC_PER_SEC); - dispatch_after(milestone, queue, block_crash); - // Let's wait for a bit longer now. - // TODO(glider): this is still hacky. - sleep(2); -} - -void worker_do_deallocate(void *ptr) { - free(ptr); -} - -void CallFreeOnWorkqueue(void *tsd) { - dispatch_queue_t queue = dispatch_get_global_queue(0, 0); - dispatch_block_t block_dealloc = ^{ worker_do_deallocate(tsd); }; - dispatch_async(queue, block_dealloc); - // Do not wait for the worker to free the memory -- nobody is going to touch - // it. -} - -void TestGCDSourceEvent() { - dispatch_queue_t queue = dispatch_get_global_queue(0, 0); - dispatch_source_t timer = - dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue); - // Schedule the timer one second from the current time. - dispatch_time_t milestone = - dispatch_time(DISPATCH_TIME_NOW, 1LL * NSEC_PER_SEC); - - dispatch_source_set_timer(timer, milestone, DISPATCH_TIME_FOREVER, 0); - char * volatile mem = (char * volatile)malloc(10); - dispatch_source_set_event_handler(timer, ^{ - access_memory(&mem[10]); - }); - dispatch_resume(timer); - sleep(2); -} - -void TestGCDSourceCancel() { - dispatch_queue_t queue = dispatch_get_global_queue(0, 0); - dispatch_source_t timer = - dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue); - // Schedule the timer one second from the current time. - dispatch_time_t milestone = - dispatch_time(DISPATCH_TIME_NOW, 1LL * NSEC_PER_SEC); - - dispatch_source_set_timer(timer, milestone, DISPATCH_TIME_FOREVER, 0); - char * volatile mem = (char * volatile)malloc(10); - // Both dispatch_source_set_cancel_handler() and - // dispatch_source_set_event_handler() use dispatch_barrier_async_f(). - // It's tricky to test dispatch_source_set_cancel_handler() separately, - // so we test both here. - dispatch_source_set_event_handler(timer, ^{ - dispatch_source_cancel(timer); - }); - dispatch_source_set_cancel_handler(timer, ^{ - access_memory(&mem[10]); - }); - dispatch_resume(timer); - sleep(2); -} - -void TestGCDGroupAsync() { - dispatch_queue_t queue = dispatch_get_global_queue(0, 0); - dispatch_group_t group = dispatch_group_create(); - char * volatile mem = (char * volatile)malloc(10); - dispatch_group_async(group, queue, ^{ - access_memory(&mem[10]); - }); - dispatch_group_wait(group, DISPATCH_TIME_FOREVER); -} - -@interface FixedArray : NSObject { - int items[10]; -} -@end - -@implementation FixedArray --(int) access: (int)index { - return items[index]; -} -@end - -void TestOOBNSObjects() { - id anObject = [FixedArray new]; - [anObject access:1]; - [anObject access:11]; - [anObject release]; -} - -void TestNSURLDeallocation() { - NSURL *base = - [[NSURL alloc] initWithString:@"file://localhost/Users/glider/Library/"]; - volatile NSURL *u = - [[NSURL alloc] initWithString:@"Saved Application State" - relativeToURL:base]; - [u release]; -} diff --git a/compiler-rt/lib/asan/tests/asan_mac_test_helpers.mm b/compiler-rt/lib/asan/tests/asan_mac_test_helpers.mm new file mode 100644 index 000000000000..4cbd2bb247fd --- /dev/null +++ b/compiler-rt/lib/asan/tests/asan_mac_test_helpers.mm @@ -0,0 +1,237 @@ +// Mac OS X 10.6 or higher only. +#include +#include +#include +#include +#include + +#import +#import +#import + +// This is a (void*)(void*) function so it can be passed to pthread_create. +void *CFAllocatorDefaultDoubleFree(void *unused) { + void *mem = CFAllocatorAllocate(kCFAllocatorDefault, 5, 0); + CFAllocatorDeallocate(kCFAllocatorDefault, mem); + CFAllocatorDeallocate(kCFAllocatorDefault, mem); + return 0; +} + +void CFAllocatorSystemDefaultDoubleFree() { + void *mem = CFAllocatorAllocate(kCFAllocatorSystemDefault, 5, 0); + CFAllocatorDeallocate(kCFAllocatorSystemDefault, mem); + CFAllocatorDeallocate(kCFAllocatorSystemDefault, mem); +} + +void CFAllocatorMallocDoubleFree() { + void *mem = CFAllocatorAllocate(kCFAllocatorMalloc, 5, 0); + CFAllocatorDeallocate(kCFAllocatorMalloc, mem); + CFAllocatorDeallocate(kCFAllocatorMalloc, mem); +} + +void CFAllocatorMallocZoneDoubleFree() { + void *mem = CFAllocatorAllocate(kCFAllocatorMallocZone, 5, 0); + CFAllocatorDeallocate(kCFAllocatorMallocZone, mem); + CFAllocatorDeallocate(kCFAllocatorMallocZone, mem); +} + +__attribute__((noinline)) +void access_memory(char *a) { + *a = 0; +} + +// Test the +load instrumentation. +// Because the +load methods are invoked before anything else is initialized, +// it makes little sense to wrap the code below into a gTest test case. +// If AddressSanitizer doesn't instrument the +load method below correctly, +// everything will just crash. + +char kStartupStr[] = + "If your test didn't crash, AddressSanitizer is instrumenting " + "the +load methods correctly."; + +@interface LoadSomething : NSObject { +} +@end + +@implementation LoadSomething + ++(void) load { + for (size_t i = 0; i < strlen(kStartupStr); i++) { + access_memory(&kStartupStr[i]); // make sure no optimizations occur. + } + // Don't print anything here not to interfere with the death tests. +} + +@end + +void worker_do_alloc(int size) { + char * volatile mem = (char * volatile)malloc(size); + mem[0] = 0; // Ok + free(mem); +} + +void worker_do_crash(int size) { + char * volatile mem = (char * volatile)malloc(size); + access_memory(&mem[size]); // BOOM + free(mem); +} + +// Tests for the Grand Central Dispatch. See +// http://developer.apple.com/library/mac/#documentation/Performance/Reference/GCD_libdispatch_Ref/Reference/reference.html +// for the reference. + +void TestGCDDispatchAsync() { + dispatch_queue_t queue = dispatch_get_global_queue(0, 0); + dispatch_block_t block = ^{ worker_do_crash(1024); }; + // dispatch_async() runs the task on a worker thread that does not go through + // pthread_create(). We need to verify that AddressSanitizer notices that the + // thread has started. + dispatch_async(queue, block); + // TODO(glider): this is hacky. Need to wait for the worker instead. + sleep(1); +} + +void TestGCDDispatchSync() { + dispatch_queue_t queue = dispatch_get_global_queue(2, 0); + dispatch_block_t block = ^{ worker_do_crash(1024); }; + // dispatch_sync() runs the task on a worker thread that does not go through + // pthread_create(). We need to verify that AddressSanitizer notices that the + // thread has started. + dispatch_sync(queue, block); + // TODO(glider): this is hacky. Need to wait for the worker instead. + sleep(1); +} + +// libdispatch spawns a rather small number of threads and reuses them. We need +// to make sure AddressSanitizer handles the reusing correctly. +void TestGCDReuseWqthreadsAsync() { + dispatch_queue_t queue = dispatch_get_global_queue(0, 0); + dispatch_block_t block_alloc = ^{ worker_do_alloc(1024); }; + dispatch_block_t block_crash = ^{ worker_do_crash(1024); }; + for (int i = 0; i < 100; i++) { + dispatch_async(queue, block_alloc); + } + dispatch_async(queue, block_crash); + // TODO(glider): this is hacky. Need to wait for the workers instead. + sleep(1); +} + +// Try to trigger abnormal behaviour of dispatch_sync() being unhandled by us. +void TestGCDReuseWqthreadsSync() { + dispatch_queue_t queue[4]; + queue[0] = dispatch_get_global_queue(2, 0); + queue[1] = dispatch_get_global_queue(0, 0); + queue[2] = dispatch_get_global_queue(-2, 0); + queue[3] = dispatch_queue_create("my_queue", NULL); + dispatch_block_t block_alloc = ^{ worker_do_alloc(1024); }; + dispatch_block_t block_crash = ^{ worker_do_crash(1024); }; + for (int i = 0; i < 1000; i++) { + dispatch_sync(queue[i % 4], block_alloc); + } + dispatch_sync(queue[3], block_crash); + // TODO(glider): this is hacky. Need to wait for the workers instead. + sleep(1); +} + +void TestGCDDispatchAfter() { + dispatch_queue_t queue = dispatch_get_global_queue(0, 0); + dispatch_block_t block_crash = ^{ worker_do_crash(1024); }; + // Schedule the event one second from the current time. + dispatch_time_t milestone = + dispatch_time(DISPATCH_TIME_NOW, 1LL * NSEC_PER_SEC); + dispatch_after(milestone, queue, block_crash); + // Let's wait for a bit longer now. + // TODO(glider): this is still hacky. + sleep(2); +} + +void worker_do_deallocate(void *ptr) { + free(ptr); +} + +void CallFreeOnWorkqueue(void *tsd) { + dispatch_queue_t queue = dispatch_get_global_queue(0, 0); + dispatch_block_t block_dealloc = ^{ worker_do_deallocate(tsd); }; + dispatch_async(queue, block_dealloc); + // Do not wait for the worker to free the memory -- nobody is going to touch + // it. +} + +void TestGCDSourceEvent() { + dispatch_queue_t queue = dispatch_get_global_queue(0, 0); + dispatch_source_t timer = + dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue); + // Schedule the timer one second from the current time. + dispatch_time_t milestone = + dispatch_time(DISPATCH_TIME_NOW, 1LL * NSEC_PER_SEC); + + dispatch_source_set_timer(timer, milestone, DISPATCH_TIME_FOREVER, 0); + char * volatile mem = (char * volatile)malloc(10); + dispatch_source_set_event_handler(timer, ^{ + access_memory(&mem[10]); + }); + dispatch_resume(timer); + sleep(2); +} + +void TestGCDSourceCancel() { + dispatch_queue_t queue = dispatch_get_global_queue(0, 0); + dispatch_source_t timer = + dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue); + // Schedule the timer one second from the current time. + dispatch_time_t milestone = + dispatch_time(DISPATCH_TIME_NOW, 1LL * NSEC_PER_SEC); + + dispatch_source_set_timer(timer, milestone, DISPATCH_TIME_FOREVER, 0); + char * volatile mem = (char * volatile)malloc(10); + // Both dispatch_source_set_cancel_handler() and + // dispatch_source_set_event_handler() use dispatch_barrier_async_f(). + // It's tricky to test dispatch_source_set_cancel_handler() separately, + // so we test both here. + dispatch_source_set_event_handler(timer, ^{ + dispatch_source_cancel(timer); + }); + dispatch_source_set_cancel_handler(timer, ^{ + access_memory(&mem[10]); + }); + dispatch_resume(timer); + sleep(2); +} + +void TestGCDGroupAsync() { + dispatch_queue_t queue = dispatch_get_global_queue(0, 0); + dispatch_group_t group = dispatch_group_create(); + char * volatile mem = (char * volatile)malloc(10); + dispatch_group_async(group, queue, ^{ + access_memory(&mem[10]); + }); + dispatch_group_wait(group, DISPATCH_TIME_FOREVER); +} + +@interface FixedArray : NSObject { + int items[10]; +} +@end + +@implementation FixedArray +-(int) access: (int)index { + return items[index]; +} +@end + +void TestOOBNSObjects() { + id anObject = [FixedArray new]; + [anObject access:1]; + [anObject access:11]; + [anObject release]; +} + +void TestNSURLDeallocation() { + NSURL *base = + [[NSURL alloc] initWithString:@"file://localhost/Users/glider/Library/"]; + volatile NSURL *u = + [[NSURL alloc] initWithString:@"Saved Application State" + relativeToURL:base]; + [u release]; +} diff --git a/compiler-rt/lib/asan/tests/asan_test.cc b/compiler-rt/lib/asan/tests/asan_test.cc index 5fa65b2af5dc..6d1ac5326cbd 100644 --- a/compiler-rt/lib/asan/tests/asan_test.cc +++ b/compiler-rt/lib/asan/tests/asan_test.cc @@ -37,11 +37,7 @@ #ifndef __APPLE__ #include -#else -#include -#include // For MAC_OS_X_VERSION_* -#include -#endif // __APPLE__ +#endif #if ASAN_HAS_EXCEPTIONS # define ASAN_THROW(x) throw (x) @@ -1097,15 +1093,6 @@ TEST(AddressSanitizer, StrLenOOBTest) { free(heap_string); } -static inline char* MallocAndMemsetString(size_t size, char ch) { - char *s = Ident((char*)malloc(size)); - memset(s, ch, size); - return s; -} -static inline char* MallocAndMemsetString(size_t size) { - return MallocAndMemsetString(size, 'z'); -} - #ifndef __APPLE__ TEST(AddressSanitizer, StrNLenOOBTest) { size_t size = Ident(123); @@ -1126,6 +1113,15 @@ TEST(AddressSanitizer, StrNLenOOBTest) { } #endif +static inline char* MallocAndMemsetString(size_t size, char ch) { + char *s = Ident((char*)malloc(size)); + memset(s, ch, size); + return s; +} +static inline char* MallocAndMemsetString(size_t size) { + return MallocAndMemsetString(size, 'z'); +} + TEST(AddressSanitizer, StrDupOOBTest) { size_t size = Ident(42); char *str = MallocAndMemsetString(size); @@ -2160,223 +2156,6 @@ TEST(AddressSanitizer, BufferOverflowAfterManyFrees) { delete [] Ident(x); } -#ifdef __APPLE__ -#include "asan_mac_test.h" -TEST(AddressSanitizerMac, CFAllocatorDefaultDoubleFree) { - EXPECT_DEATH( - CFAllocatorDefaultDoubleFree(NULL), - "attempting double-free"); -} - -void CFAllocator_DoubleFreeOnPthread() { - pthread_t child; - PTHREAD_CREATE(&child, NULL, CFAllocatorDefaultDoubleFree, NULL); - PTHREAD_JOIN(child, NULL); // Shouldn't be reached. -} - -TEST(AddressSanitizerMac, CFAllocatorDefaultDoubleFree_ChildPhread) { - EXPECT_DEATH(CFAllocator_DoubleFreeOnPthread(), "attempting double-free"); -} - -namespace { - -void *GLOB; - -void *CFAllocatorAllocateToGlob(void *unused) { - GLOB = CFAllocatorAllocate(NULL, 100, /*hint*/0); - return NULL; -} - -void *CFAllocatorDeallocateFromGlob(void *unused) { - char *p = (char*)GLOB; - p[100] = 'A'; // ASan should report an error here. - CFAllocatorDeallocate(NULL, GLOB); - return NULL; -} - -void CFAllocator_PassMemoryToAnotherThread() { - pthread_t th1, th2; - PTHREAD_CREATE(&th1, NULL, CFAllocatorAllocateToGlob, NULL); - PTHREAD_JOIN(th1, NULL); - PTHREAD_CREATE(&th2, NULL, CFAllocatorDeallocateFromGlob, NULL); - PTHREAD_JOIN(th2, NULL); -} - -TEST(AddressSanitizerMac, CFAllocator_PassMemoryToAnotherThread) { - EXPECT_DEATH(CFAllocator_PassMemoryToAnotherThread(), - "heap-buffer-overflow"); -} - -} // namespace - -// TODO(glider): figure out whether we still need these tests. Is it correct -// to intercept the non-default CFAllocators? -TEST(AddressSanitizerMac, DISABLED_CFAllocatorSystemDefaultDoubleFree) { - EXPECT_DEATH( - CFAllocatorSystemDefaultDoubleFree(), - "attempting double-free"); -} - -// We're intercepting malloc, so kCFAllocatorMalloc is routed to ASan. -TEST(AddressSanitizerMac, CFAllocatorMallocDoubleFree) { - EXPECT_DEATH(CFAllocatorMallocDoubleFree(), "attempting double-free"); -} - -TEST(AddressSanitizerMac, DISABLED_CFAllocatorMallocZoneDoubleFree) { - EXPECT_DEATH(CFAllocatorMallocZoneDoubleFree(), "attempting double-free"); -} - -// For libdispatch tests below we check that ASan got to the shadow byte -// legend, i.e. managed to print the thread stacks (this almost certainly -// means that the libdispatch task creation has been intercepted correctly). -TEST(AddressSanitizerMac, GCDDispatchAsync) { - // Make sure the whole ASan report is printed, i.e. that we don't die - // on a CHECK. - EXPECT_DEATH(TestGCDDispatchAsync(), "Shadow byte legend"); -} - -TEST(AddressSanitizerMac, GCDDispatchSync) { - // Make sure the whole ASan report is printed, i.e. that we don't die - // on a CHECK. - EXPECT_DEATH(TestGCDDispatchSync(), "Shadow byte legend"); -} - - -TEST(AddressSanitizerMac, GCDReuseWqthreadsAsync) { - // Make sure the whole ASan report is printed, i.e. that we don't die - // on a CHECK. - EXPECT_DEATH(TestGCDReuseWqthreadsAsync(), "Shadow byte legend"); -} - -TEST(AddressSanitizerMac, GCDReuseWqthreadsSync) { - // Make sure the whole ASan report is printed, i.e. that we don't die - // on a CHECK. - EXPECT_DEATH(TestGCDReuseWqthreadsSync(), "Shadow byte legend"); -} - -TEST(AddressSanitizerMac, GCDDispatchAfter) { - // Make sure the whole ASan report is printed, i.e. that we don't die - // on a CHECK. - EXPECT_DEATH(TestGCDDispatchAfter(), "Shadow byte legend"); -} - -TEST(AddressSanitizerMac, GCDSourceEvent) { - // Make sure the whole ASan report is printed, i.e. that we don't die - // on a CHECK. - EXPECT_DEATH(TestGCDSourceEvent(), "Shadow byte legend"); -} - -TEST(AddressSanitizerMac, GCDSourceCancel) { - // Make sure the whole ASan report is printed, i.e. that we don't die - // on a CHECK. - EXPECT_DEATH(TestGCDSourceCancel(), "Shadow byte legend"); -} - -TEST(AddressSanitizerMac, GCDGroupAsync) { - // Make sure the whole ASan report is printed, i.e. that we don't die - // on a CHECK. - EXPECT_DEATH(TestGCDGroupAsync(), "Shadow byte legend"); -} - -void *MallocIntrospectionLockWorker(void *_) { - const int kNumPointers = 100; - int i; - void *pointers[kNumPointers]; - for (i = 0; i < kNumPointers; i++) { - pointers[i] = malloc(i + 1); - } - for (i = 0; i < kNumPointers; i++) { - free(pointers[i]); - } - - return NULL; -} - -void *MallocIntrospectionLockForker(void *_) { - pid_t result = fork(); - if (result == -1) { - perror("fork"); - } - assert(result != -1); - if (result == 0) { - // Call malloc in the child process to make sure we won't deadlock. - void *ptr = malloc(42); - free(ptr); - exit(0); - } else { - // Return in the parent process. - return NULL; - } -} - -TEST(AddressSanitizerMac, MallocIntrospectionLock) { - // Incorrect implementation of force_lock and force_unlock in our malloc zone - // will cause forked processes to deadlock. - // TODO(glider): need to detect that none of the child processes deadlocked. - const int kNumWorkers = 5, kNumIterations = 100; - int i, iter; - for (iter = 0; iter < kNumIterations; iter++) { - pthread_t workers[kNumWorkers], forker; - for (i = 0; i < kNumWorkers; i++) { - PTHREAD_CREATE(&workers[i], 0, MallocIntrospectionLockWorker, 0); - } - PTHREAD_CREATE(&forker, 0, MallocIntrospectionLockForker, 0); - for (i = 0; i < kNumWorkers; i++) { - PTHREAD_JOIN(workers[i], 0); - } - PTHREAD_JOIN(forker, 0); - } -} - -void *TSDAllocWorker(void *test_key) { - if (test_key) { - void *mem = malloc(10); - pthread_setspecific(*(pthread_key_t*)test_key, mem); - } - return NULL; -} - -TEST(AddressSanitizerMac, DISABLED_TSDWorkqueueTest) { - pthread_t th; - pthread_key_t test_key; - pthread_key_create(&test_key, CallFreeOnWorkqueue); - PTHREAD_CREATE(&th, NULL, TSDAllocWorker, &test_key); - PTHREAD_JOIN(th, NULL); - pthread_key_delete(test_key); -} - -// Test that CFStringCreateCopy does not copy constant strings. -TEST(AddressSanitizerMac, CFStringCreateCopy) { - CFStringRef str = CFSTR("Hello world!\n"); - CFStringRef str2 = CFStringCreateCopy(0, str); - EXPECT_EQ(str, str2); -} - -TEST(AddressSanitizerMac, NSObjectOOB) { - // Make sure that our allocators are used for NSObjects. - EXPECT_DEATH(TestOOBNSObjects(), "heap-buffer-overflow"); -} - -// Make sure that correct pointer is passed to free() when deallocating a -// NSURL object. -// See http://code.google.com/p/address-sanitizer/issues/detail?id=70. -TEST(AddressSanitizerMac, NSURLDeallocation) { - TestNSURLDeallocation(); -} - -// See http://code.google.com/p/address-sanitizer/issues/detail?id=109. -TEST(AddressSanitizerMac, Mstats) { - malloc_statistics_t stats1, stats2; - malloc_zone_statistics(/*all zones*/NULL, &stats1); - const size_t kMallocSize = 100000; - void *alloc = Ident(malloc(kMallocSize)); - malloc_zone_statistics(/*all zones*/NULL, &stats2); - EXPECT_GT(stats2.blocks_in_use, stats1.blocks_in_use); - EXPECT_GE(stats2.size_in_use - stats1.size_in_use, kMallocSize); - free(alloc); - // Even the default OSX allocator may not change the stats after free(). -} -#endif // __APPLE__ // Test that instrumentation of stack allocations takes into account // AllocSize of a type, and not its StoreSize (16 vs 10 bytes for long double).