242 lines · cpp
1//===-- asan_test_mac.cpp -------------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8//9// This file is a part of AddressSanitizer, an address sanity checker.10//11//===----------------------------------------------------------------------===//12 13#include "asan_test_utils.h"14 15#include "asan_mac_test.h"16 17#include <malloc/malloc.h>18#include <AvailabilityMacros.h> // For MAC_OS_X_VERSION_*19#include <CoreFoundation/CFString.h>20 21TEST(AddressSanitizerMac, CFAllocatorDefaultDoubleFree) {22 EXPECT_DEATH(23 CFAllocatorDefaultDoubleFree(NULL),24 "attempting double-free");25}26 27void CFAllocator_DoubleFreeOnPthread() {28 pthread_t child;29 PTHREAD_CREATE(&child, NULL, CFAllocatorDefaultDoubleFree, NULL);30 PTHREAD_JOIN(child, NULL); // Shouldn't be reached.31}32 33TEST(AddressSanitizerMac, CFAllocatorDefaultDoubleFree_ChildPhread) {34 EXPECT_DEATH(CFAllocator_DoubleFreeOnPthread(), "attempting double-free");35}36 37namespace {38 39void *GLOB;40 41void *CFAllocatorAllocateToGlob(void *unused) {42 GLOB = CFAllocatorAllocate(NULL, 100, /*hint*/0);43 return NULL;44}45 46void *CFAllocatorDeallocateFromGlob(void *unused) {47 char *p = (char*)GLOB;48 p[100] = 'A'; // ASan should report an error here.49 CFAllocatorDeallocate(NULL, GLOB);50 return NULL;51}52 53void CFAllocator_PassMemoryToAnotherThread() {54 pthread_t th1, th2;55 PTHREAD_CREATE(&th1, NULL, CFAllocatorAllocateToGlob, NULL);56 PTHREAD_JOIN(th1, NULL);57 PTHREAD_CREATE(&th2, NULL, CFAllocatorDeallocateFromGlob, NULL);58 PTHREAD_JOIN(th2, NULL);59}60 61TEST(AddressSanitizerMac, CFAllocator_PassMemoryToAnotherThread) {62 EXPECT_DEATH(CFAllocator_PassMemoryToAnotherThread(),63 "heap-buffer-overflow");64}65 66} // namespace67 68// TODO(glider): figure out whether we still need these tests. Is it correct69// to intercept the non-default CFAllocators?70TEST(AddressSanitizerMac, DISABLED_CFAllocatorSystemDefaultDoubleFree) {71 EXPECT_DEATH(72 CFAllocatorSystemDefaultDoubleFree(),73 "attempting double-free");74}75 76// We're intercepting malloc, so kCFAllocatorMalloc is routed to ASan.77TEST(AddressSanitizerMac, CFAllocatorMallocDoubleFree) {78 EXPECT_DEATH(CFAllocatorMallocDoubleFree(), "attempting double-free");79}80 81TEST(AddressSanitizerMac, DISABLED_CFAllocatorMallocZoneDoubleFree) {82 EXPECT_DEATH(CFAllocatorMallocZoneDoubleFree(), "attempting double-free");83}84 85// For libdispatch tests below we check that ASan got to the shadow byte86// legend, i.e. managed to print the thread stacks (this almost certainly87// means that the libdispatch task creation has been intercepted correctly).88TEST(AddressSanitizerMac, GCDDispatchAsync) {89 // Make sure the whole ASan report is printed, i.e. that we don't die90 // on a CHECK.91 EXPECT_DEATH(TestGCDDispatchAsync(), "Shadow byte legend");92}93 94TEST(AddressSanitizerMac, GCDDispatchSync) {95 // Make sure the whole ASan report is printed, i.e. that we don't die96 // on a CHECK.97 EXPECT_DEATH(TestGCDDispatchSync(), "Shadow byte legend");98}99 100 101TEST(AddressSanitizerMac, GCDReuseWqthreadsAsync) {102 // Make sure the whole ASan report is printed, i.e. that we don't die103 // on a CHECK.104 EXPECT_DEATH(TestGCDReuseWqthreadsAsync(), "Shadow byte legend");105}106 107TEST(AddressSanitizerMac, GCDReuseWqthreadsSync) {108 // Make sure the whole ASan report is printed, i.e. that we don't die109 // on a CHECK.110 EXPECT_DEATH(TestGCDReuseWqthreadsSync(), "Shadow byte legend");111}112 113TEST(AddressSanitizerMac, GCDDispatchAfter) {114 // Make sure the whole ASan report is printed, i.e. that we don't die115 // on a CHECK.116 EXPECT_DEATH(TestGCDDispatchAfter(), "Shadow byte legend");117}118 119TEST(AddressSanitizerMac, GCDDispatchApply) {120 // Make sure the whole ASan report is printed, i.e. that we don't die121 // on a CHECK.122 EXPECT_DEATH(TestGCDDispatchApply(), "Shadow byte legend");123}124 125TEST(AddressSanitizerMac, GCDSourceEvent) {126 // Make sure the whole ASan report is printed, i.e. that we don't die127 // on a CHECK.128 EXPECT_DEATH(TestGCDSourceEvent(), "Shadow byte legend");129}130 131TEST(AddressSanitizerMac, GCDSourceCancel) {132 // Make sure the whole ASan report is printed, i.e. that we don't die133 // on a CHECK.134 EXPECT_DEATH(TestGCDSourceCancel(), "Shadow byte legend");135}136 137TEST(AddressSanitizerMac, GCDGroupAsync) {138 // Make sure the whole ASan report is printed, i.e. that we don't die139 // on a CHECK.140 EXPECT_DEATH(TestGCDGroupAsync(), "Shadow byte legend");141}142 143void *MallocIntrospectionLockWorker(void *_) {144 const int kNumPointers = 100;145 int i;146 void *pointers[kNumPointers];147 for (i = 0; i < kNumPointers; i++) {148 pointers[i] = malloc(i + 1);149 }150 for (i = 0; i < kNumPointers; i++) {151 free(pointers[i]);152 }153 154 return NULL;155}156 157void *MallocIntrospectionLockForker(void *_) {158 pid_t result = fork();159 if (result == -1) {160 perror("fork");161 }162 assert(result != -1);163 if (result == 0) {164 // Call malloc in the child process to make sure we won't deadlock.165 void *ptr = malloc(42);166 free(ptr);167 exit(0);168 } else {169 // Return in the parent process.170 return NULL;171 }172}173 174TEST(AddressSanitizerMac, MallocIntrospectionLock) {175 // Incorrect implementation of force_lock and force_unlock in our malloc zone176 // will cause forked processes to deadlock.177 // TODO(glider): need to detect that none of the child processes deadlocked.178 const int kNumWorkers = 5, kNumIterations = 100;179 int i, iter;180 for (iter = 0; iter < kNumIterations; iter++) {181 pthread_t workers[kNumWorkers], forker;182 for (i = 0; i < kNumWorkers; i++) {183 PTHREAD_CREATE(&workers[i], 0, MallocIntrospectionLockWorker, 0);184 }185 PTHREAD_CREATE(&forker, 0, MallocIntrospectionLockForker, 0);186 for (i = 0; i < kNumWorkers; i++) {187 PTHREAD_JOIN(workers[i], 0);188 }189 PTHREAD_JOIN(forker, 0);190 }191}192 193void *TSDAllocWorker(void *test_key) {194 if (test_key) {195 void *mem = malloc(10);196 pthread_setspecific(*(pthread_key_t*)test_key, mem);197 }198 return NULL;199}200 201TEST(AddressSanitizerMac, DISABLED_TSDWorkqueueTest) {202 pthread_t th;203 pthread_key_t test_key;204 pthread_key_create(&test_key, CallFreeOnWorkqueue);205 PTHREAD_CREATE(&th, NULL, TSDAllocWorker, &test_key);206 PTHREAD_JOIN(th, NULL);207 pthread_key_delete(test_key);208}209 210// Test that CFStringCreateCopy does not copy constant strings.211TEST(AddressSanitizerMac, CFStringCreateCopy) {212 CFStringRef str = CFSTR("Hello world!\n");213 CFStringRef str2 = CFStringCreateCopy(0, str);214 EXPECT_EQ(str, str2);215}216 217TEST(AddressSanitizerMac, NSObjectOOB) {218 // Make sure that our allocators are used for NSObjects.219 EXPECT_DEATH(TestOOBNSObjects(), "heap-buffer-overflow");220}221 222// Make sure that correct pointer is passed to free() when deallocating a223// NSURL object.224// See https://github.com/google/sanitizers/issues/70.225TEST(AddressSanitizerMac, NSURLDeallocation) {226 TestNSURLDeallocation();227}228 229// See https://github.com/google/sanitizers/issues/109.230TEST(AddressSanitizerMac, Mstats) {231 malloc_statistics_t stats1, stats2;232 malloc_zone_statistics(/*all zones*/NULL, &stats1);233 const size_t kMallocSize = 100000;234 void *alloc = Ident(malloc(kMallocSize));235 malloc_zone_statistics(/*all zones*/NULL, &stats2);236 EXPECT_GT(stats2.blocks_in_use, stats1.blocks_in_use);237 EXPECT_GE(stats2.size_in_use - stats1.size_in_use, kMallocSize);238 free(alloc);239 // Even the default OSX allocator may not change the stats after free().240}241 242