241 lines
		
	
	
		
			7.5 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
			
		
		
	
	
			241 lines
		
	
	
		
			7.5 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
| // Mac OS X 10.6 or higher only.
 | |
| #include <dispatch/dispatch.h>
 | |
| #include <pthread.h>  // for pthread_yield_np()
 | |
| #include <stdio.h>
 | |
| #include <stdlib.h>
 | |
| #include <string.h>
 | |
| #include <unistd.h>
 | |
| 
 | |
| #import <CoreFoundation/CFBase.h>
 | |
| #import <Foundation/NSObject.h>
 | |
| #import <Foundation/NSURL.h>
 | |
| 
 | |
| // This is a (void*)(void*) function so it can be passed to pthread_create.
 | |
| void *CFAllocatorDefaultDoubleFree(void *unused) {
 | |
|   void *mem = CFAllocatorAllocate(kCFAllocatorDefault, 5, 0);
 | |
|   CFAllocatorDeallocate(kCFAllocatorDefault, mem);
 | |
|   CFAllocatorDeallocate(kCFAllocatorDefault, mem);
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| void CFAllocatorSystemDefaultDoubleFree() {
 | |
|   void *mem = CFAllocatorAllocate(kCFAllocatorSystemDefault, 5, 0);
 | |
|   CFAllocatorDeallocate(kCFAllocatorSystemDefault, mem);
 | |
|   CFAllocatorDeallocate(kCFAllocatorSystemDefault, mem);
 | |
| }
 | |
| 
 | |
| void CFAllocatorMallocDoubleFree() {
 | |
|   void *mem = CFAllocatorAllocate(kCFAllocatorMalloc, 5, 0);
 | |
|   CFAllocatorDeallocate(kCFAllocatorMalloc, mem);
 | |
|   CFAllocatorDeallocate(kCFAllocatorMalloc, mem);
 | |
| }
 | |
| 
 | |
| void CFAllocatorMallocZoneDoubleFree() {
 | |
|   void *mem = CFAllocatorAllocate(kCFAllocatorMallocZone, 5, 0);
 | |
|   CFAllocatorDeallocate(kCFAllocatorMallocZone, mem);
 | |
|   CFAllocatorDeallocate(kCFAllocatorMallocZone, mem);
 | |
| }
 | |
| 
 | |
| __attribute__((noinline))
 | |
| void access_memory(char *a) {
 | |
|   *a = 0;
 | |
| }
 | |
| 
 | |
| // Test the +load instrumentation.
 | |
| // Because the +load methods are invoked before anything else is initialized,
 | |
| // it makes little sense to wrap the code below into a gTest test case.
 | |
| // If AddressSanitizer doesn't instrument the +load method below correctly,
 | |
| // everything will just crash.
 | |
| 
 | |
| char kStartupStr[] =
 | |
|     "If your test didn't crash, AddressSanitizer is instrumenting "
 | |
|     "the +load methods correctly.";
 | |
| 
 | |
| @interface LoadSomething : NSObject {
 | |
| }
 | |
| @end
 | |
| 
 | |
| @implementation LoadSomething
 | |
| 
 | |
| +(void) load {
 | |
|   for (size_t i = 0; i < strlen(kStartupStr); i++) {
 | |
|     access_memory(&kStartupStr[i]);  // make sure no optimizations occur.
 | |
|   }
 | |
|   // Don't print anything here not to interfere with the death tests.
 | |
| }
 | |
| 
 | |
| @end
 | |
| 
 | |
| void worker_do_alloc(int size) {
 | |
|   char * volatile mem = (char * volatile)malloc(size);
 | |
|   mem[0] = 0; // Ok
 | |
|   free(mem);
 | |
| }
 | |
| 
 | |
| void worker_do_crash(int size) {
 | |
|   char * volatile mem = (char * volatile)malloc(size);
 | |
|   access_memory(&mem[size]);  // BOOM
 | |
|   free(mem);
 | |
| }
 | |
| 
 | |
| // Used by the GCD tests to avoid a race between the worker thread reporting a
 | |
| // memory error and the main thread which may exit with exit code 0 before
 | |
| // that.
 | |
| void wait_forever() {
 | |
|   volatile bool infinite = true;
 | |
|   while (infinite) pthread_yield_np();
 | |
| }
 | |
| 
 | |
| // Tests for the Grand Central Dispatch. See
 | |
| // http://developer.apple.com/library/mac/#documentation/Performance/Reference/GCD_libdispatch_Ref/Reference/reference.html
 | |
| // for the reference.
 | |
| void TestGCDDispatchAsync() {
 | |
|   dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
 | |
|   dispatch_block_t block = ^{ worker_do_crash(1024); };
 | |
|   // dispatch_async() runs the task on a worker thread that does not go through
 | |
|   // pthread_create(). We need to verify that AddressSanitizer notices that the
 | |
|   // thread has started.
 | |
|   dispatch_async(queue, block);
 | |
|   wait_forever();
 | |
| }
 | |
| 
 | |
| void TestGCDDispatchSync() {
 | |
|   dispatch_queue_t queue = dispatch_get_global_queue(2, 0);
 | |
|   dispatch_block_t block = ^{ worker_do_crash(1024); };
 | |
|   // dispatch_sync() runs the task on a worker thread that does not go through
 | |
|   // pthread_create(). We need to verify that AddressSanitizer notices that the
 | |
|   // thread has started.
 | |
|   dispatch_sync(queue, block);
 | |
|   wait_forever();
 | |
| }
 | |
| 
 | |
| // libdispatch spawns a rather small number of threads and reuses them. We need
 | |
| // to make sure AddressSanitizer handles the reusing correctly.
 | |
| void TestGCDReuseWqthreadsAsync() {
 | |
|   dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
 | |
|   dispatch_block_t block_alloc = ^{ worker_do_alloc(1024); };
 | |
|   dispatch_block_t block_crash = ^{ worker_do_crash(1024); };
 | |
|   for (int i = 0; i < 100; i++) {
 | |
|     dispatch_async(queue, block_alloc);
 | |
|   }
 | |
|   dispatch_async(queue, block_crash);
 | |
|   wait_forever();
 | |
| }
 | |
| 
 | |
| // Try to trigger abnormal behaviour of dispatch_sync() being unhandled by us.
 | |
| void TestGCDReuseWqthreadsSync() {
 | |
|   dispatch_queue_t queue[4];
 | |
|   queue[0] = dispatch_get_global_queue(2, 0);
 | |
|   queue[1] = dispatch_get_global_queue(0, 0);
 | |
|   queue[2] = dispatch_get_global_queue(-2, 0);
 | |
|   queue[3] = dispatch_queue_create("my_queue", NULL);
 | |
|   dispatch_block_t block_alloc = ^{ worker_do_alloc(1024); };
 | |
|   dispatch_block_t block_crash = ^{ worker_do_crash(1024); };
 | |
|   for (int i = 0; i < 1000; i++) {
 | |
|     dispatch_sync(queue[i % 4], block_alloc);
 | |
|   }
 | |
|   dispatch_sync(queue[3], block_crash);
 | |
|   wait_forever();
 | |
| }
 | |
| 
 | |
| void TestGCDDispatchAfter() {
 | |
|   dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
 | |
|   dispatch_block_t block_crash = ^{ worker_do_crash(1024); };
 | |
|   // Schedule the event one second from the current time.
 | |
|   dispatch_time_t milestone =
 | |
|       dispatch_time(DISPATCH_TIME_NOW, 1LL * NSEC_PER_SEC);
 | |
|   dispatch_after(milestone, queue, block_crash);
 | |
|   wait_forever();
 | |
| }
 | |
| 
 | |
| void worker_do_deallocate(void *ptr) {
 | |
|   free(ptr);
 | |
| }
 | |
| 
 | |
| void CallFreeOnWorkqueue(void *tsd) {
 | |
|   dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
 | |
|   dispatch_block_t block_dealloc = ^{ worker_do_deallocate(tsd); };
 | |
|   dispatch_async(queue, block_dealloc);
 | |
|   // Do not wait for the worker to free the memory -- nobody is going to touch
 | |
|   // it.
 | |
| }
 | |
| 
 | |
| void TestGCDSourceEvent() {
 | |
|   dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
 | |
|   dispatch_source_t timer =
 | |
|       dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
 | |
|   // Schedule the timer one second from the current time.
 | |
|   dispatch_time_t milestone =
 | |
|       dispatch_time(DISPATCH_TIME_NOW, 1LL * NSEC_PER_SEC);
 | |
| 
 | |
|   dispatch_source_set_timer(timer, milestone, DISPATCH_TIME_FOREVER, 0);
 | |
|   char * volatile mem = (char * volatile)malloc(10);
 | |
|   dispatch_source_set_event_handler(timer, ^{
 | |
|     access_memory(&mem[10]);
 | |
|   });
 | |
|   dispatch_resume(timer);
 | |
|   wait_forever();
 | |
| }
 | |
| 
 | |
| void TestGCDSourceCancel() {
 | |
|   dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
 | |
|   dispatch_source_t timer =
 | |
|       dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
 | |
|   // Schedule the timer one second from the current time.
 | |
|   dispatch_time_t milestone =
 | |
|       dispatch_time(DISPATCH_TIME_NOW, 1LL * NSEC_PER_SEC);
 | |
| 
 | |
|   dispatch_source_set_timer(timer, milestone, DISPATCH_TIME_FOREVER, 0);
 | |
|   char * volatile mem = (char * volatile)malloc(10);
 | |
|   // Both dispatch_source_set_cancel_handler() and
 | |
|   // dispatch_source_set_event_handler() use dispatch_barrier_async_f().
 | |
|   // It's tricky to test dispatch_source_set_cancel_handler() separately,
 | |
|   // so we test both here.
 | |
|   dispatch_source_set_event_handler(timer, ^{
 | |
|     dispatch_source_cancel(timer);
 | |
|   });
 | |
|   dispatch_source_set_cancel_handler(timer, ^{
 | |
|     access_memory(&mem[10]);
 | |
|   });
 | |
|   dispatch_resume(timer);
 | |
|   wait_forever();
 | |
| }
 | |
| 
 | |
| void TestGCDGroupAsync() {
 | |
|   dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
 | |
|   dispatch_group_t group = dispatch_group_create(); 
 | |
|   char * volatile mem = (char * volatile)malloc(10);
 | |
|   dispatch_group_async(group, queue, ^{
 | |
|     access_memory(&mem[10]);
 | |
|   });
 | |
|   dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
 | |
|   wait_forever();
 | |
| }
 | |
| 
 | |
| @interface FixedArray : NSObject {
 | |
|   int items[10];
 | |
| }
 | |
| @end
 | |
| 
 | |
| @implementation FixedArray
 | |
| -(int) access: (int)index {
 | |
|   return items[index];
 | |
| }
 | |
| @end
 | |
| 
 | |
| void TestOOBNSObjects() {
 | |
|   id anObject = [FixedArray new];
 | |
|   [anObject access:1];
 | |
|   [anObject access:11];
 | |
|   [anObject release];
 | |
| }
 | |
| 
 | |
| void TestNSURLDeallocation() {
 | |
|   NSURL *base =
 | |
|       [[NSURL alloc] initWithString:@"file://localhost/Users/glider/Library/"];
 | |
|   volatile NSURL *u =
 | |
|       [[NSURL alloc] initWithString:@"Saved Application State"
 | |
|                      relativeToURL:base];
 | |
|   [u release];
 | |
| }
 |