aboutsummaryrefslogtreecommitdiff
path: root/lib/asan/tests/asan_mac_test.mm
blob: 4e5873b7448526fb76b91c763ede57545665344f (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
// Mac OS X 10.6 or higher only.
#include <dispatch/dispatch.h>
#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 (int 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 = malloc(size);
  mem[0] = 0; // Ok
  free(mem);
}

void worker_do_crash(int size) {
  char * volatile mem = malloc(size);
  access_memory(&mem[size]);  // BOOM
  free(mem);
}

// 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);
  // TODO(glider): this is hacky. Need to wait for the worker instead.
  sleep(1);
}

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);
  // TODO(glider): this is hacky. Need to wait for the worker instead.
  sleep(1);
}

// 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);
  // TODO(glider): this is hacky. Need to wait for the workers instead.
  sleep(1);
}

// 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);
  // TODO(glider): this is hacky. Need to wait for the workers instead.
  sleep(1);
}

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);
  // Let's wait for a bit longer now.
  // TODO(glider): this is still hacky.
  sleep(2);
}

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 = malloc(10);
  dispatch_source_set_event_handler(timer, ^{
    access_memory(&mem[10]);
  });
  dispatch_resume(timer);
  sleep(2);
}

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 = 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);
  sleep(2);
}

void TestGCDGroupAsync() {
  dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
  dispatch_group_t group = dispatch_group_create(); 
  char * volatile mem = malloc(10);
  dispatch_group_async(group, queue, ^{
    access_memory(&mem[10]);
  });
  dispatch_group_wait(group, DISPATCH_TIME_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];
}