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
21TEST(AddressSanitizerMac, CFAllocatorDefaultDoubleFree) {
22 EXPECT_DEATH(
23 CFAllocatorDefaultDoubleFree(NULL),
24 "attempting double-free");
25}
26
27void CFAllocator_DoubleFreeOnPthread() {
28 pthread_t child;
29 PTHREAD_CREATE(&child, NULL, CFAllocatorDefaultDoubleFree, NULL);
30 PTHREAD_JOIN(child, NULL); // Shouldn't be reached.
31}
32
33TEST(AddressSanitizerMac, CFAllocatorDefaultDoubleFree_ChildPhread) {
34 EXPECT_DEATH(CFAllocator_DoubleFreeOnPthread(), "attempting double-free");
35}
36
37namespace {
38
39void *GLOB;
40
41void *CFAllocatorAllocateToGlob(void *unused) {
42 GLOB = CFAllocatorAllocate(NULL, 100, /*hint*/0);
43 return NULL;
44}
45
46void *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
53void 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
61TEST(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?
70TEST(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.
77TEST(AddressSanitizerMac, CFAllocatorMallocDoubleFree) {
78 EXPECT_DEATH(CFAllocatorMallocDoubleFree(), "attempting double-free");
79}
80
81TEST(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).
88TEST(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
94TEST(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
101TEST(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
107TEST(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
113TEST(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
119TEST(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
125TEST(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
131TEST(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
137void *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
151void *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
168TEST(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
187void *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
195TEST(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.
205TEST(AddressSanitizerMac, CFStringCreateCopy) {
206 CFStringRef str = CFSTR("Hello world!\n");
207 CFStringRef str2 = CFStringCreateCopy(0, str);
208 EXPECT_EQ(str, str2);
209}
210
211TEST(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.
219TEST(AddressSanitizerMac, NSURLDeallocation) {
220 TestNSURLDeallocation();
221}
222
223// See https://github.com/google/sanitizers/issues/109.
224TEST(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

source code of compiler-rt/lib/asan/tests/asan_mac_test.cpp