• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
11void CFAllocatorDefaultDoubleFree() {
12  void *mem =  CFAllocatorAllocate(kCFAllocatorDefault, 5, 0);
13  CFAllocatorDeallocate(kCFAllocatorDefault, mem);
14  CFAllocatorDeallocate(kCFAllocatorDefault, mem);
15}
16
17void CFAllocatorSystemDefaultDoubleFree() {
18  void *mem =  CFAllocatorAllocate(kCFAllocatorSystemDefault, 5, 0);
19  CFAllocatorDeallocate(kCFAllocatorSystemDefault, mem);
20  CFAllocatorDeallocate(kCFAllocatorSystemDefault, mem);
21}
22
23void CFAllocatorMallocDoubleFree() {
24  void *mem =  CFAllocatorAllocate(kCFAllocatorMalloc, 5, 0);
25  CFAllocatorDeallocate(kCFAllocatorMalloc, mem);
26  CFAllocatorDeallocate(kCFAllocatorMalloc, mem);
27}
28
29void CFAllocatorMallocZoneDoubleFree() {
30  void *mem =  CFAllocatorAllocate(kCFAllocatorMallocZone, 5, 0);
31  CFAllocatorDeallocate(kCFAllocatorMallocZone, mem);
32  CFAllocatorDeallocate(kCFAllocatorMallocZone, mem);
33}
34
35__attribute__((noinline))
36void access_memory(char *a) {
37  *a = 0;
38}
39
40// Test the +load instrumentation.
41// Because the +load methods are invoked before anything else is initialized,
42// it makes little sense to wrap the code below into a gTest test case.
43// If AddressSanitizer doesn't instrument the +load method below correctly,
44// everything will just crash.
45
46char kStartupStr[] =
47    "If your test didn't crash, AddressSanitizer is instrumenting "
48    "the +load methods correctly.";
49
50@interface LoadSomething : NSObject {
51}
52@end
53
54@implementation LoadSomething
55
56+(void) load {
57  for (int i = 0; i < strlen(kStartupStr); i++) {
58    access_memory(&kStartupStr[i]);  // make sure no optimizations occur.
59  }
60  // Don't print anything here not to interfere with the death tests.
61}
62
63@end
64
65void worker_do_alloc(int size) {
66  char * volatile mem = malloc(size);
67  mem[0] = 0; // Ok
68  free(mem);
69}
70
71void worker_do_crash(int size) {
72  char * volatile mem = malloc(size);
73  access_memory(&mem[size]);  // BOOM
74  free(mem);
75}
76
77// Tests for the Grand Central Dispatch. See
78// http://developer.apple.com/library/mac/#documentation/Performance/Reference/GCD_libdispatch_Ref/Reference/reference.html
79// for the reference.
80
81void TestGCDDispatchAsync() {
82  dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
83  dispatch_block_t block = ^{ worker_do_crash(1024); };
84  // dispatch_async() runs the task on a worker thread that does not go through
85  // pthread_create(). We need to verify that AddressSanitizer notices that the
86  // thread has started.
87  dispatch_async(queue, block);
88  // TODO(glider): this is hacky. Need to wait for the worker instead.
89  sleep(1);
90}
91
92void TestGCDDispatchSync() {
93  dispatch_queue_t queue = dispatch_get_global_queue(2, 0);
94  dispatch_block_t block = ^{ worker_do_crash(1024); };
95  // dispatch_sync() 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_sync(queue, block);
99  // TODO(glider): this is hacky. Need to wait for the worker instead.
100  sleep(1);
101}
102
103// libdispatch spawns a rather small number of threads and reuses them. We need
104// to make sure AddressSanitizer handles the reusing correctly.
105void TestGCDReuseWqthreadsAsync() {
106  dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
107  dispatch_block_t block_alloc = ^{ worker_do_alloc(1024); };
108  dispatch_block_t block_crash = ^{ worker_do_crash(1024); };
109  for (int i = 0; i < 100; i++) {
110    dispatch_async(queue, block_alloc);
111  }
112  dispatch_async(queue, block_crash);
113  // TODO(glider): this is hacky. Need to wait for the workers instead.
114  sleep(1);
115}
116
117// Try to trigger abnormal behaviour of dispatch_sync() being unhandled by us.
118void TestGCDReuseWqthreadsSync() {
119  dispatch_queue_t queue[4];
120  queue[0] = dispatch_get_global_queue(2, 0);
121  queue[1] = dispatch_get_global_queue(0, 0);
122  queue[2] = dispatch_get_global_queue(-2, 0);
123  queue[3] = dispatch_queue_create("my_queue", NULL);
124  dispatch_block_t block_alloc = ^{ worker_do_alloc(1024); };
125  dispatch_block_t block_crash = ^{ worker_do_crash(1024); };
126  for (int i = 0; i < 1000; i++) {
127    dispatch_sync(queue[i % 4], block_alloc);
128  }
129  dispatch_sync(queue[3], block_crash);
130  // TODO(glider): this is hacky. Need to wait for the workers instead.
131  sleep(1);
132}
133
134void TestGCDDispatchAfter() {
135  dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
136  dispatch_block_t block_crash = ^{ worker_do_crash(1024); };
137  // Schedule the event one second from the current time.
138  dispatch_time_t milestone =
139      dispatch_time(DISPATCH_TIME_NOW, 1LL * NSEC_PER_SEC);
140  dispatch_after(milestone, queue, block_crash);
141  // Let's wait for a bit longer now.
142  // TODO(glider): this is still hacky.
143  sleep(2);
144}
145
146void worker_do_deallocate(void *ptr) {
147  free(ptr);
148}
149
150void CallFreeOnWorkqueue(void *tsd) {
151  dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
152  dispatch_block_t block_dealloc = ^{ worker_do_deallocate(tsd); };
153  dispatch_async(queue, block_dealloc);
154  // Do not wait for the worker to free the memory -- nobody is going to touch
155  // it.
156}
157
158void TestGCDSourceEvent() {
159  dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
160  dispatch_source_t timer =
161      dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
162  // Schedule the timer one second from the current time.
163  dispatch_time_t milestone =
164      dispatch_time(DISPATCH_TIME_NOW, 1LL * NSEC_PER_SEC);
165
166  dispatch_source_set_timer(timer, milestone, DISPATCH_TIME_FOREVER, 0);
167  char * volatile mem = malloc(10);
168  dispatch_source_set_event_handler(timer, ^{
169    access_memory(&mem[10]);
170  });
171  dispatch_resume(timer);
172  sleep(2);
173}
174
175void TestGCDSourceCancel() {
176  dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
177  dispatch_source_t timer =
178      dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
179  // Schedule the timer one second from the current time.
180  dispatch_time_t milestone =
181      dispatch_time(DISPATCH_TIME_NOW, 1LL * NSEC_PER_SEC);
182
183  dispatch_source_set_timer(timer, milestone, DISPATCH_TIME_FOREVER, 0);
184  char * volatile mem = malloc(10);
185  // Both dispatch_source_set_cancel_handler() and
186  // dispatch_source_set_event_handler() use dispatch_barrier_async_f().
187  // It's tricky to test dispatch_source_set_cancel_handler() separately,
188  // so we test both here.
189  dispatch_source_set_event_handler(timer, ^{
190    dispatch_source_cancel(timer);
191  });
192  dispatch_source_set_cancel_handler(timer, ^{
193    access_memory(&mem[10]);
194  });
195  dispatch_resume(timer);
196  sleep(2);
197}
198
199void TestGCDGroupAsync() {
200  dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
201  dispatch_group_t group = dispatch_group_create();
202  char * volatile mem = malloc(10);
203  dispatch_group_async(group, queue, ^{
204    access_memory(&mem[10]);
205  });
206  dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
207}
208
209@interface FixedArray : NSObject {
210  int items[10];
211}
212@end
213
214@implementation FixedArray
215-(int) access: (int)index {
216  return items[index];
217}
218@end
219
220void TestOOBNSObjects() {
221  id anObject = [FixedArray new];
222  [anObject access:1];
223  [anObject access:11];
224  [anObject release];
225}
226