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. 14void *CFAllocatorDefaultDoubleFree(void *unused) { 15 void *mem = CFAllocatorAllocate(kCFAllocatorDefault, 5, 0); 16 CFAllocatorDeallocate(kCFAllocatorDefault, mem); 17 CFAllocatorDeallocate(kCFAllocatorDefault, mem); 18 return 0; 19} 20 21void CFAllocatorSystemDefaultDoubleFree() { 22 void *mem = CFAllocatorAllocate(kCFAllocatorSystemDefault, 5, 0); 23 CFAllocatorDeallocate(kCFAllocatorSystemDefault, mem); 24 CFAllocatorDeallocate(kCFAllocatorSystemDefault, mem); 25} 26 27void CFAllocatorMallocDoubleFree() { 28 void *mem = CFAllocatorAllocate(kCFAllocatorMalloc, 5, 0); 29 CFAllocatorDeallocate(kCFAllocatorMalloc, mem); 30 CFAllocatorDeallocate(kCFAllocatorMalloc, mem); 31} 32 33void CFAllocatorMallocZoneDoubleFree() { 34 void *mem = CFAllocatorAllocate(kCFAllocatorMallocZone, 5, 0); 35 CFAllocatorDeallocate(kCFAllocatorMallocZone, mem); 36 CFAllocatorDeallocate(kCFAllocatorMallocZone, mem); 37} 38 39__attribute__((noinline)) 40void 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 50char 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 69void worker_do_alloc(int size) { 70 char * volatile mem = (char * volatile)malloc(size); 71 mem[0] = 0; // Ok 72 free(mem); 73} 74 75void 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. 84void 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. 92void 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 102void 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. 114void 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. 126void 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 141void 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 151void worker_do_deallocate(void *ptr) { 152 free(ptr); 153} 154 155void 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 163void 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 180void 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 204void 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 226void TestOOBNSObjects() { 227 id anObject = [FixedArray new]; 228 [anObject access:1]; 229 [anObject access:11]; 230 [anObject release]; 231} 232 233void 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