//===-- 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 https://github.com/google/sanitizers/issues/70. TEST(AddressSanitizerMac, NSURLDeallocation) { TestNSURLDeallocation(); } // See https://github.com/google/sanitizers/issues/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(). }