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