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. 13void *CFAllocatorDefaultDoubleFree(void *unused) { 14 void *mem = CFAllocatorAllocate(kCFAllocatorDefault, 5, 0); 15 CFAllocatorDeallocate(kCFAllocatorDefault, mem); 16 CFAllocatorDeallocate(kCFAllocatorDefault, mem); 17 return 0; 18} 19 20void CFAllocatorSystemDefaultDoubleFree() { 21 void *mem = CFAllocatorAllocate(kCFAllocatorSystemDefault, 5, 0); 22 CFAllocatorDeallocate(kCFAllocatorSystemDefault, mem); 23 CFAllocatorDeallocate(kCFAllocatorSystemDefault, mem); 24} 25 26void CFAllocatorMallocDoubleFree() { 27 void *mem = CFAllocatorAllocate(kCFAllocatorMalloc, 5, 0); 28 CFAllocatorDeallocate(kCFAllocatorMalloc, mem); 29 CFAllocatorDeallocate(kCFAllocatorMalloc, mem); 30} 31 32void CFAllocatorMallocZoneDoubleFree() { 33 void *mem = CFAllocatorAllocate(kCFAllocatorMallocZone, 5, 0); 34 CFAllocatorDeallocate(kCFAllocatorMallocZone, mem); 35 CFAllocatorDeallocate(kCFAllocatorMallocZone, mem); 36} 37 38__attribute__((noinline)) 39void 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 49char 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 68void worker_do_alloc(int size) { 69 char * volatile mem = malloc(size); 70 mem[0] = 0; // Ok 71 free(mem); 72} 73 74void 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 84void 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 95void 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. 108void 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. 121void 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 137void 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 149void worker_do_deallocate(void *ptr) { 150 free(ptr); 151} 152 153void 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 161void 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 178void 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 202void 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 223void TestOOBNSObjects() { 224 id anObject = [FixedArray new]; 225 [anObject access:1]; 226 [anObject access:11]; 227 [anObject release]; 228} 229 230void 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