aboutsummaryrefslogtreecommitdiff
path: root/lib/asan/tests/asan_mac_test.cc
diff options
context:
space:
mode:
Diffstat (limited to 'lib/asan/tests/asan_mac_test.cc')
-rw-r--r--lib/asan/tests/asan_mac_test.cc236
1 files changed, 236 insertions, 0 deletions
diff --git a/lib/asan/tests/asan_mac_test.cc b/lib/asan/tests/asan_mac_test.cc
new file mode 100644
index 000000000000..cabdfd711ea2
--- /dev/null
+++ b/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 <malloc/malloc.h>
+#include <AvailabilityMacros.h> // For MAC_OS_X_VERSION_*
+#include <CoreFoundation/CFString.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().
+}
+