1 | //===-- asan_test_mac.cpp -------------------------------------------------===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | // |
9 | // This file is a part of AddressSanitizer, an address sanity checker. |
10 | // |
11 | //===----------------------------------------------------------------------===// |
12 | |
13 | #include "asan_test_utils.h" |
14 | |
15 | #include "asan_mac_test.h" |
16 | |
17 | #include <malloc/malloc.h> |
18 | #include <AvailabilityMacros.h> // For MAC_OS_X_VERSION_* |
19 | #include <CoreFoundation/CFString.h> |
20 | |
21 | TEST(AddressSanitizerMac, CFAllocatorDefaultDoubleFree) { |
22 | EXPECT_DEATH( |
23 | CFAllocatorDefaultDoubleFree(NULL), |
24 | "attempting double-free" ); |
25 | } |
26 | |
27 | void CFAllocator_DoubleFreeOnPthread() { |
28 | pthread_t child; |
29 | PTHREAD_CREATE(&child, NULL, CFAllocatorDefaultDoubleFree, NULL); |
30 | PTHREAD_JOIN(child, NULL); // Shouldn't be reached. |
31 | } |
32 | |
33 | TEST(AddressSanitizerMac, CFAllocatorDefaultDoubleFree_ChildPhread) { |
34 | EXPECT_DEATH(CFAllocator_DoubleFreeOnPthread(), "attempting double-free" ); |
35 | } |
36 | |
37 | namespace { |
38 | |
39 | void *GLOB; |
40 | |
41 | void *CFAllocatorAllocateToGlob(void *unused) { |
42 | GLOB = CFAllocatorAllocate(NULL, 100, /*hint*/0); |
43 | return NULL; |
44 | } |
45 | |
46 | void *CFAllocatorDeallocateFromGlob(void *unused) { |
47 | char *p = (char*)GLOB; |
48 | p[100] = 'A'; // ASan should report an error here. |
49 | CFAllocatorDeallocate(NULL, GLOB); |
50 | return NULL; |
51 | } |
52 | |
53 | void CFAllocator_PassMemoryToAnotherThread() { |
54 | pthread_t th1, th2; |
55 | PTHREAD_CREATE(&th1, NULL, CFAllocatorAllocateToGlob, NULL); |
56 | PTHREAD_JOIN(th1, NULL); |
57 | PTHREAD_CREATE(&th2, NULL, CFAllocatorDeallocateFromGlob, NULL); |
58 | PTHREAD_JOIN(th2, NULL); |
59 | } |
60 | |
61 | TEST(AddressSanitizerMac, CFAllocator_PassMemoryToAnotherThread) { |
62 | EXPECT_DEATH(CFAllocator_PassMemoryToAnotherThread(), |
63 | "heap-buffer-overflow" ); |
64 | } |
65 | |
66 | } // namespace |
67 | |
68 | // TODO(glider): figure out whether we still need these tests. Is it correct |
69 | // to intercept the non-default CFAllocators? |
70 | TEST(AddressSanitizerMac, DISABLED_CFAllocatorSystemDefaultDoubleFree) { |
71 | EXPECT_DEATH( |
72 | CFAllocatorSystemDefaultDoubleFree(), |
73 | "attempting double-free" ); |
74 | } |
75 | |
76 | // We're intercepting malloc, so kCFAllocatorMalloc is routed to ASan. |
77 | TEST(AddressSanitizerMac, CFAllocatorMallocDoubleFree) { |
78 | EXPECT_DEATH(CFAllocatorMallocDoubleFree(), "attempting double-free" ); |
79 | } |
80 | |
81 | TEST(AddressSanitizerMac, DISABLED_CFAllocatorMallocZoneDoubleFree) { |
82 | EXPECT_DEATH(CFAllocatorMallocZoneDoubleFree(), "attempting double-free" ); |
83 | } |
84 | |
85 | // For libdispatch tests below we check that ASan got to the shadow byte |
86 | // legend, i.e. managed to print the thread stacks (this almost certainly |
87 | // means that the libdispatch task creation has been intercepted correctly). |
88 | TEST(AddressSanitizerMac, GCDDispatchAsync) { |
89 | // Make sure the whole ASan report is printed, i.e. that we don't die |
90 | // on a CHECK. |
91 | EXPECT_DEATH(TestGCDDispatchAsync(), "Shadow byte legend" ); |
92 | } |
93 | |
94 | TEST(AddressSanitizerMac, GCDDispatchSync) { |
95 | // Make sure the whole ASan report is printed, i.e. that we don't die |
96 | // on a CHECK. |
97 | EXPECT_DEATH(TestGCDDispatchSync(), "Shadow byte legend" ); |
98 | } |
99 | |
100 | |
101 | TEST(AddressSanitizerMac, GCDReuseWqthreadsAsync) { |
102 | // Make sure the whole ASan report is printed, i.e. that we don't die |
103 | // on a CHECK. |
104 | EXPECT_DEATH(TestGCDReuseWqthreadsAsync(), "Shadow byte legend" ); |
105 | } |
106 | |
107 | TEST(AddressSanitizerMac, GCDReuseWqthreadsSync) { |
108 | // Make sure the whole ASan report is printed, i.e. that we don't die |
109 | // on a CHECK. |
110 | EXPECT_DEATH(TestGCDReuseWqthreadsSync(), "Shadow byte legend" ); |
111 | } |
112 | |
113 | TEST(AddressSanitizerMac, GCDDispatchAfter) { |
114 | // Make sure the whole ASan report is printed, i.e. that we don't die |
115 | // on a CHECK. |
116 | EXPECT_DEATH(TestGCDDispatchAfter(), "Shadow byte legend" ); |
117 | } |
118 | |
119 | TEST(AddressSanitizerMac, GCDSourceEvent) { |
120 | // Make sure the whole ASan report is printed, i.e. that we don't die |
121 | // on a CHECK. |
122 | EXPECT_DEATH(TestGCDSourceEvent(), "Shadow byte legend" ); |
123 | } |
124 | |
125 | TEST(AddressSanitizerMac, GCDSourceCancel) { |
126 | // Make sure the whole ASan report is printed, i.e. that we don't die |
127 | // on a CHECK. |
128 | EXPECT_DEATH(TestGCDSourceCancel(), "Shadow byte legend" ); |
129 | } |
130 | |
131 | TEST(AddressSanitizerMac, GCDGroupAsync) { |
132 | // Make sure the whole ASan report is printed, i.e. that we don't die |
133 | // on a CHECK. |
134 | EXPECT_DEATH(TestGCDGroupAsync(), "Shadow byte legend" ); |
135 | } |
136 | |
137 | void *MallocIntrospectionLockWorker(void *_) { |
138 | const int kNumPointers = 100; |
139 | int i; |
140 | void *pointers[kNumPointers]; |
141 | for (i = 0; i < kNumPointers; i++) { |
142 | pointers[i] = malloc(size: i + 1); |
143 | } |
144 | for (i = 0; i < kNumPointers; i++) { |
145 | free(ptr: pointers[i]); |
146 | } |
147 | |
148 | return NULL; |
149 | } |
150 | |
151 | void *MallocIntrospectionLockForker(void *_) { |
152 | pid_t result = fork(); |
153 | if (result == -1) { |
154 | perror(s: "fork" ); |
155 | } |
156 | assert(result != -1); |
157 | if (result == 0) { |
158 | // Call malloc in the child process to make sure we won't deadlock. |
159 | void *ptr = malloc(size: 42); |
160 | free(ptr: ptr); |
161 | exit(status: 0); |
162 | } else { |
163 | // Return in the parent process. |
164 | return NULL; |
165 | } |
166 | } |
167 | |
168 | TEST(AddressSanitizerMac, MallocIntrospectionLock) { |
169 | // Incorrect implementation of force_lock and force_unlock in our malloc zone |
170 | // will cause forked processes to deadlock. |
171 | // TODO(glider): need to detect that none of the child processes deadlocked. |
172 | const int kNumWorkers = 5, kNumIterations = 100; |
173 | int i, iter; |
174 | for (iter = 0; iter < kNumIterations; iter++) { |
175 | pthread_t workers[kNumWorkers], forker; |
176 | for (i = 0; i < kNumWorkers; i++) { |
177 | PTHREAD_CREATE(&workers[i], 0, MallocIntrospectionLockWorker, 0); |
178 | } |
179 | PTHREAD_CREATE(&forker, 0, MallocIntrospectionLockForker, 0); |
180 | for (i = 0; i < kNumWorkers; i++) { |
181 | PTHREAD_JOIN(workers[i], 0); |
182 | } |
183 | PTHREAD_JOIN(forker, 0); |
184 | } |
185 | } |
186 | |
187 | void *TSDAllocWorker(void *test_key) { |
188 | if (test_key) { |
189 | void *mem = malloc(size: 10); |
190 | pthread_setspecific(*(pthread_key_t*)test_key, mem); |
191 | } |
192 | return NULL; |
193 | } |
194 | |
195 | TEST(AddressSanitizerMac, DISABLED_TSDWorkqueueTest) { |
196 | pthread_t th; |
197 | pthread_key_t test_key; |
198 | pthread_key_create(&test_key, CallFreeOnWorkqueue); |
199 | PTHREAD_CREATE(&th, NULL, TSDAllocWorker, &test_key); |
200 | PTHREAD_JOIN(th, NULL); |
201 | pthread_key_delete(test_key); |
202 | } |
203 | |
204 | // Test that CFStringCreateCopy does not copy constant strings. |
205 | TEST(AddressSanitizerMac, CFStringCreateCopy) { |
206 | CFStringRef str = CFSTR("Hello world!\n" ); |
207 | CFStringRef str2 = CFStringCreateCopy(0, str); |
208 | EXPECT_EQ(str, str2); |
209 | } |
210 | |
211 | TEST(AddressSanitizerMac, NSObjectOOB) { |
212 | // Make sure that our allocators are used for NSObjects. |
213 | EXPECT_DEATH(TestOOBNSObjects(), "heap-buffer-overflow" ); |
214 | } |
215 | |
216 | // Make sure that correct pointer is passed to free() when deallocating a |
217 | // NSURL object. |
218 | // See https://github.com/google/sanitizers/issues/70. |
219 | TEST(AddressSanitizerMac, NSURLDeallocation) { |
220 | TestNSURLDeallocation(); |
221 | } |
222 | |
223 | // See https://github.com/google/sanitizers/issues/109. |
224 | TEST(AddressSanitizerMac, Mstats) { |
225 | malloc_statistics_t stats1, stats2; |
226 | malloc_zone_statistics(/*all zones*/NULL, &stats1); |
227 | const size_t kMallocSize = 100000; |
228 | void *alloc = Ident(malloc(size: kMallocSize)); |
229 | malloc_zone_statistics(/*all zones*/NULL, &stats2); |
230 | EXPECT_GT(stats2.blocks_in_use, stats1.blocks_in_use); |
231 | EXPECT_GE(stats2.size_in_use - stats1.size_in_use, kMallocSize); |
232 | free(ptr: alloc); |
233 | // Even the default OSX allocator may not change the stats after free(). |
234 | } |
235 | |
236 | |