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