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