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