Home | History | Annotate | Download | only in tests
      1 // Mac OS X 10.6 or higher only.
      2 #include <dispatch/dispatch.h>
      3 #include <pthread.h>  // for pthread_yield_np()
      4 #include <stdio.h>
      5 #include <stdlib.h>
      6 #include <string.h>
      7 #include <unistd.h>
      8 
      9 #import <CoreFoundation/CFBase.h>
     10 #import <Foundation/NSObject.h>
     11 #import <Foundation/NSURL.h>
     12 
     13 // This is a (void*)(void*) function so it can be passed to pthread_create.
     14 void *CFAllocatorDefaultDoubleFree(void *unused) {
     15   void *mem = CFAllocatorAllocate(kCFAllocatorDefault, 5, 0);
     16   CFAllocatorDeallocate(kCFAllocatorDefault, mem);
     17   CFAllocatorDeallocate(kCFAllocatorDefault, mem);
     18   return 0;
     19 }
     20 
     21 void CFAllocatorSystemDefaultDoubleFree() {
     22   void *mem = CFAllocatorAllocate(kCFAllocatorSystemDefault, 5, 0);
     23   CFAllocatorDeallocate(kCFAllocatorSystemDefault, mem);
     24   CFAllocatorDeallocate(kCFAllocatorSystemDefault, mem);
     25 }
     26 
     27 void CFAllocatorMallocDoubleFree() {
     28   void *mem = CFAllocatorAllocate(kCFAllocatorMalloc, 5, 0);
     29   CFAllocatorDeallocate(kCFAllocatorMalloc, mem);
     30   CFAllocatorDeallocate(kCFAllocatorMalloc, mem);
     31 }
     32 
     33 void CFAllocatorMallocZoneDoubleFree() {
     34   void *mem = CFAllocatorAllocate(kCFAllocatorMallocZone, 5, 0);
     35   CFAllocatorDeallocate(kCFAllocatorMallocZone, mem);
     36   CFAllocatorDeallocate(kCFAllocatorMallocZone, mem);
     37 }
     38 
     39 __attribute__((noinline))
     40 void access_memory(char *a) {
     41   *a = 0;
     42 }
     43 
     44 // Test the +load instrumentation.
     45 // Because the +load methods are invoked before anything else is initialized,
     46 // it makes little sense to wrap the code below into a gTest test case.
     47 // If AddressSanitizer doesn't instrument the +load method below correctly,
     48 // everything will just crash.
     49 
     50 char kStartupStr[] =
     51     "If your test didn't crash, AddressSanitizer is instrumenting "
     52     "the +load methods correctly.";
     53 
     54 @interface LoadSomething : NSObject {
     55 }
     56 @end
     57 
     58 @implementation LoadSomething
     59 
     60 +(void) load {
     61   for (size_t i = 0; i < strlen(kStartupStr); i++) {
     62     access_memory(&kStartupStr[i]);  // make sure no optimizations occur.
     63   }
     64   // Don't print anything here not to interfere with the death tests.
     65 }
     66 
     67 @end
     68 
     69 void worker_do_alloc(int size) {
     70   char * volatile mem = (char * volatile)malloc(size);
     71   mem[0] = 0; // Ok
     72   free(mem);
     73 }
     74 
     75 void worker_do_crash(int size) {
     76   char * volatile mem = (char * volatile)malloc(size);
     77   access_memory(&mem[size]);  // BOOM
     78   free(mem);
     79 }
     80 
     81 // Used by the GCD tests to avoid a race between the worker thread reporting a
     82 // memory error and the main thread which may exit with exit code 0 before
     83 // that.
     84 void wait_forever() {
     85   volatile bool infinite = true;
     86   while (infinite) pthread_yield_np();
     87 }
     88 
     89 // Tests for the Grand Central Dispatch. See
     90 // http://developer.apple.com/library/mac/#documentation/Performance/Reference/GCD_libdispatch_Ref/Reference/reference.html
     91 // for the reference.
     92 void TestGCDDispatchAsync() {
     93   dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
     94   dispatch_block_t block = ^{ worker_do_crash(1024); };
     95   // dispatch_async() runs the task on a worker thread that does not go through
     96   // pthread_create(). We need to verify that AddressSanitizer notices that the
     97   // thread has started.
     98   dispatch_async(queue, block);
     99   wait_forever();
    100 }
    101 
    102 void TestGCDDispatchSync() {
    103   dispatch_queue_t queue = dispatch_get_global_queue(2, 0);
    104   dispatch_block_t block = ^{ worker_do_crash(1024); };
    105   // dispatch_sync() runs the task on a worker thread that does not go through
    106   // pthread_create(). We need to verify that AddressSanitizer notices that the
    107   // thread has started.
    108   dispatch_sync(queue, block);
    109   wait_forever();
    110 }
    111 
    112 // libdispatch spawns a rather small number of threads and reuses them. We need
    113 // to make sure AddressSanitizer handles the reusing correctly.
    114 void TestGCDReuseWqthreadsAsync() {
    115   dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    116   dispatch_block_t block_alloc = ^{ worker_do_alloc(1024); };
    117   dispatch_block_t block_crash = ^{ worker_do_crash(1024); };
    118   for (int i = 0; i < 100; i++) {
    119     dispatch_async(queue, block_alloc);
    120   }
    121   dispatch_async(queue, block_crash);
    122   wait_forever();
    123 }
    124 
    125 // Try to trigger abnormal behaviour of dispatch_sync() being unhandled by us.
    126 void TestGCDReuseWqthreadsSync() {
    127   dispatch_queue_t queue[4];
    128   queue[0] = dispatch_get_global_queue(2, 0);
    129   queue[1] = dispatch_get_global_queue(0, 0);
    130   queue[2] = dispatch_get_global_queue(-2, 0);
    131   queue[3] = dispatch_queue_create("my_queue", NULL);
    132   dispatch_block_t block_alloc = ^{ worker_do_alloc(1024); };
    133   dispatch_block_t block_crash = ^{ worker_do_crash(1024); };
    134   for (int i = 0; i < 1000; i++) {
    135     dispatch_sync(queue[i % 4], block_alloc);
    136   }
    137   dispatch_sync(queue[3], block_crash);
    138   wait_forever();
    139 }
    140 
    141 void TestGCDDispatchAfter() {
    142   dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    143   dispatch_block_t block_crash = ^{ worker_do_crash(1024); };
    144   // Schedule the event one second from the current time.
    145   dispatch_time_t milestone =
    146       dispatch_time(DISPATCH_TIME_NOW, 1LL * NSEC_PER_SEC);
    147   dispatch_after(milestone, queue, block_crash);
    148   wait_forever();
    149 }
    150 
    151 void worker_do_deallocate(void *ptr) {
    152   free(ptr);
    153 }
    154 
    155 void CallFreeOnWorkqueue(void *tsd) {
    156   dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    157   dispatch_block_t block_dealloc = ^{ worker_do_deallocate(tsd); };
    158   dispatch_async(queue, block_dealloc);
    159   // Do not wait for the worker to free the memory -- nobody is going to touch
    160   // it.
    161 }
    162 
    163 void TestGCDSourceEvent() {
    164   dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    165   dispatch_source_t timer =
    166       dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
    167   // Schedule the timer one second from the current time.
    168   dispatch_time_t milestone =
    169       dispatch_time(DISPATCH_TIME_NOW, 1LL * NSEC_PER_SEC);
    170 
    171   dispatch_source_set_timer(timer, milestone, DISPATCH_TIME_FOREVER, 0);
    172   char * volatile mem = (char * volatile)malloc(10);
    173   dispatch_source_set_event_handler(timer, ^{
    174     access_memory(&mem[10]);
    175   });
    176   dispatch_resume(timer);
    177   wait_forever();
    178 }
    179 
    180 void TestGCDSourceCancel() {
    181   dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    182   dispatch_source_t timer =
    183       dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
    184   // Schedule the timer one second from the current time.
    185   dispatch_time_t milestone =
    186       dispatch_time(DISPATCH_TIME_NOW, 1LL * NSEC_PER_SEC);
    187 
    188   dispatch_source_set_timer(timer, milestone, DISPATCH_TIME_FOREVER, 0);
    189   char * volatile mem = (char * volatile)malloc(10);
    190   // Both dispatch_source_set_cancel_handler() and
    191   // dispatch_source_set_event_handler() use dispatch_barrier_async_f().
    192   // It's tricky to test dispatch_source_set_cancel_handler() separately,
    193   // so we test both here.
    194   dispatch_source_set_event_handler(timer, ^{
    195     dispatch_source_cancel(timer);
    196   });
    197   dispatch_source_set_cancel_handler(timer, ^{
    198     access_memory(&mem[10]);
    199   });
    200   dispatch_resume(timer);
    201   wait_forever();
    202 }
    203 
    204 void TestGCDGroupAsync() {
    205   dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    206   dispatch_group_t group = dispatch_group_create(); 
    207   char * volatile mem = (char * volatile)malloc(10);
    208   dispatch_group_async(group, queue, ^{
    209     access_memory(&mem[10]);
    210   });
    211   dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
    212   wait_forever();
    213 }
    214 
    215 @interface FixedArray : NSObject {
    216   int items[10];
    217 }
    218 @end
    219 
    220 @implementation FixedArray
    221 -(int) access: (int)index {
    222   return items[index];
    223 }
    224 @end
    225 
    226 void TestOOBNSObjects() {
    227   id anObject = [FixedArray new];
    228   [anObject access:1];
    229   [anObject access:11];
    230   [anObject release];
    231 }
    232 
    233 void TestNSURLDeallocation() {
    234   NSURL *base =
    235       [[NSURL alloc] initWithString:@"file://localhost/Users/glider/Library/"];
    236   volatile NSURL *u =
    237       [[NSURL alloc] initWithString:@"Saved Application State"
    238                      relativeToURL:base];
    239   [u release];
    240 }
    241