asan_mac_test_helpers.mm revision 8c745fc58d8aca66db1170b1cdbbc0a7743c20a0
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 (size_t 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 = (char * volatile)malloc(size);
70  mem[0] = 0; // Ok
71  free(mem);
72}
73
74void worker_do_crash(int size) {
75  char * volatile mem = (char * volatile)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 = (char * volatile)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 = (char * volatile)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 = (char * volatile)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