1 | // RUN: %clang_analyze_cc1 -analyzer-checker=core,unix.Malloc -analyzer-store=region -verify -fblocks %s |
2 | #import "Inputs/system-header-simulator-objc.h" |
3 | #import "Inputs/system-header-simulator-for-malloc.h" |
4 | |
5 | // Done with headers. Start testing. |
6 | void testNSDatafFreeWhenDoneNoError(NSUInteger dataLength) { |
7 | unsigned char *data = (unsigned char *)malloc(42); |
8 | NSData *nsdata = [NSData dataWithBytesNoCopy:data length:dataLength]; |
9 | } |
10 | |
11 | void testNSDataFreeWhenDoneYES(NSUInteger dataLength) { |
12 | unsigned char *data = (unsigned char *)malloc(42); |
13 | NSData *nsdata = [NSData dataWithBytesNoCopy:data length:dataLength freeWhenDone:1]; // no-warning |
14 | } |
15 | |
16 | void testNSDataFreeWhenDoneYES2(NSUInteger dataLength) { |
17 | unsigned char *data = (unsigned char *)malloc(42); |
18 | NSData *nsdata = [[NSData alloc] initWithBytesNoCopy:data length:dataLength freeWhenDone:1]; // no-warning |
19 | } |
20 | |
21 | void testNSDataFreeWhenDoneYES2_with_wrapper(NSUInteger dataLength) { |
22 | unsigned char *data = (unsigned char *)malloc(42); |
23 | Wrapper *nsdata = [[Wrapper alloc] initWithBytesNoCopy:data length:dataLength]; // no-warning |
24 | } |
25 | |
26 | void testNSStringFreeWhenDoneYES3(NSUInteger dataLength) { |
27 | unsigned char *data = (unsigned char *)malloc(42); |
28 | NSString *nsstr = [[NSString alloc] initWithBytesNoCopy:data length:dataLength encoding:NSUTF8StringEncoding freeWhenDone:1]; |
29 | } |
30 | |
31 | void testNSStringFreeWhenDoneYES4(NSUInteger dataLength) { |
32 | unichar *data = (unichar*)malloc(42); |
33 | NSString *nsstr = [[NSString alloc] initWithCharactersNoCopy:data length:dataLength freeWhenDone:1]; |
34 | free(data); //expected-warning {{Attempt to free non-owned memory}} |
35 | } |
36 | |
37 | void testNSStringFreeWhenDoneYES(NSUInteger dataLength) { |
38 | unsigned char *data = (unsigned char *)malloc(42); |
39 | NSString *nsstr = [[NSString alloc] initWithBytesNoCopy:data length:dataLength encoding:NSUTF8StringEncoding freeWhenDone:1]; // no-warning |
40 | } |
41 | |
42 | void testNSStringFreeWhenDoneYES2(NSUInteger dataLength) { |
43 | unichar *data = (unichar*)malloc(42); |
44 | NSString *nsstr = [[NSString alloc] initWithCharactersNoCopy:data length:dataLength freeWhenDone:1]; // no-warning |
45 | } |
46 | |
47 | |
48 | void testNSDataFreeWhenDoneNO(NSUInteger dataLength) { |
49 | unsigned char *data = (unsigned char *)malloc(42); |
50 | NSData *nsdata = [NSData dataWithBytesNoCopy:data length:dataLength freeWhenDone:0]; // expected-warning{{leak}} |
51 | } |
52 | |
53 | void testNSDataFreeWhenDoneNO2(NSUInteger dataLength) { |
54 | unsigned char *data = (unsigned char *)malloc(42); |
55 | NSData *nsdata = [[NSData alloc] initWithBytesNoCopy:data length:dataLength freeWhenDone:0]; // expected-warning{{leak}} |
56 | } |
57 | |
58 | |
59 | void testNSStringFreeWhenDoneNO(NSUInteger dataLength) { |
60 | unsigned char *data = (unsigned char *)malloc(42); |
61 | NSString *nsstr = [[NSString alloc] initWithBytesNoCopy:data length:dataLength encoding:NSUTF8StringEncoding freeWhenDone:0]; // expected-warning{{leak}} |
62 | } |
63 | |
64 | void testNSStringFreeWhenDoneNO2(NSUInteger dataLength) { |
65 | unichar *data = (unichar*)malloc(42); |
66 | NSString *nsstr = [[NSString alloc] initWithCharactersNoCopy:data length:dataLength freeWhenDone:0]; // expected-warning{{leak}} |
67 | } |
68 | |
69 | void testOffsetFree() { |
70 | int *p = (int *)malloc(sizeof(int)); |
71 | NSData *nsdata = [NSData dataWithBytesNoCopy:++p length:sizeof(int) freeWhenDone:1]; // expected-warning{{Argument to +dataWithBytesNoCopy:length:freeWhenDone: is offset by 4 bytes from the start of memory allocated by malloc()}} |
72 | } |
73 | |
74 | void testRelinquished1() { |
75 | void *data = malloc(42); |
76 | NSData *nsdata = [NSData dataWithBytesNoCopy:data length:42 freeWhenDone:1]; |
77 | free(data); // expected-warning {{Attempt to free non-owned memory}} |
78 | } |
79 | |
80 | void testRelinquished2() { |
81 | void *data = malloc(42); |
82 | NSData *nsdata; |
83 | free(data); |
84 | [NSData dataWithBytesNoCopy:data length:42]; // expected-warning {{Use of memory after it is freed}} |
85 | } |
86 | |
87 | @interface My |
88 | + (void)param:(void *)p; |
89 | @end |
90 | |
91 | void testUseAfterFree() { |
92 | int *p = (int *)malloc(sizeof(int)); |
93 | free(p); |
94 | [My param:p]; // expected-warning{{Use of memory after it is freed}} |
95 | } |
96 | |
97 | void testNoCopy() { |
98 | char *p = (char *)calloc(sizeof(int), 1); |
99 | CustomData *w = [CustomData somethingNoCopy:p]; // no-warning |
100 | } |
101 | |
102 | void testFreeWhenDone() { |
103 | char *p = (char *)calloc(sizeof(int), 1); |
104 | CustomData *w = [CustomData something:p freeWhenDone:1]; // no-warning |
105 | } |
106 | |
107 | void testFreeWhenDonePositive() { |
108 | char *p = (char *)calloc(sizeof(int), 1); |
109 | CustomData *w = [CustomData something:p freeWhenDone:0]; // expected-warning{{leak}} |
110 | } |
111 | |
112 | void testFreeWhenDoneNoCopy() { |
113 | int *p = (int *)malloc(sizeof(int)); |
114 | CustomData *w = [CustomData somethingNoCopy:p length:sizeof(int) freeWhenDone:1]; // no-warning |
115 | } |
116 | |
117 | void testFreeWhenDoneNoCopyPositive() { |
118 | int *p = (int *)malloc(sizeof(int)); |
119 | CustomData *w = [CustomData somethingNoCopy:p length:sizeof(int) freeWhenDone:0]; // expected-warning{{leak}} |
120 | } |
121 | |
122 | // Test CF/NS...NoCopy. PR12100: Pointers can escape when custom deallocators are provided. |
123 | void testNSDatafFreeWhenDone(NSUInteger dataLength) { |
124 | CFStringRef str; |
125 | char *bytes = (char*)malloc(12); |
126 | str = CFStringCreateWithCStringNoCopy(0, bytes, NSNEXTSTEPStringEncoding, 0); // no warning |
127 | CFRelease(str); // default allocator also frees bytes |
128 | } |
129 | |
130 | void stringWithExternalContentsExample(void) { |
131 | #define BufferSize 1000 |
132 | CFMutableStringRef mutStr; |
133 | UniChar *myBuffer; |
134 | |
135 | myBuffer = (UniChar *)malloc(BufferSize * sizeof(UniChar)); |
136 | |
137 | mutStr = CFStringCreateMutableWithExternalCharactersNoCopy(0, myBuffer, 0, BufferSize, kCFAllocatorNull); // expected-warning{{leak}} |
138 | |
139 | CFRelease(mutStr); |
140 | //free(myBuffer); |
141 | } |
142 | |
143 | // PR12101 : pointers can escape through custom deallocators set on creation of a container. |
144 | void TestCallbackReleasesMemory(CFDictionaryKeyCallBacks keyCallbacks) { |
145 | void *key = malloc(12); |
146 | void *val = malloc(12); |
147 | CFMutableDictionaryRef x = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &keyCallbacks, &kCFTypeDictionaryValueCallBacks); |
148 | CFDictionarySetValue(x, key, val); |
149 | return;// no-warning |
150 | } |
151 | |
152 | NSData *radar10976702() { |
153 | void *bytes = malloc(10); |
154 | return [NSData dataWithBytesNoCopy:bytes length:10]; // no-warning |
155 | } |
156 | |
157 | void testBlocks() { |
158 | int *x= (int*)malloc(sizeof(int)); |
159 | int (^myBlock)(int) = ^(int num) { |
160 | free(x); |
161 | return num; |
162 | }; |
163 | myBlock(3); |
164 | } |
165 | |
166 | // Test NSMapInsert. |
167 | @interface NSMapTable : NSObject <NSCopying, NSCoding, NSFastEnumeration> |
168 | @end |
169 | extern void *NSMapGet(NSMapTable *table, const void *key); |
170 | extern void NSMapInsert(NSMapTable *table, const void *key, const void *value); |
171 | extern void NSMapInsertKnownAbsent(NSMapTable *table, const void *key, const void *value); |
172 | char *strdup(const char *s); |
173 | |
174 | NSString * radar11152419(NSString *string1, NSMapTable *map) { |
175 | const char *strkey = "key"; |
176 | NSString *string = ( NSString *)NSMapGet(map, strkey); |
177 | if (!string) { |
178 | string = [string1 copy]; |
179 | NSMapInsert(map, strdup(strkey), (void*)string); // no warning |
180 | NSMapInsertKnownAbsent(map, strdup(strkey), (void*)string); // no warning |
181 | } |
182 | return string; |
183 | } |
184 | |
185 | // Test that we handle pointer escaping through OSAtomicEnqueue. |
186 | typedef volatile struct { |
187 | void *opaque1; |
188 | long opaque2; |
189 | } OSQueueHead; |
190 | extern "C" void OSAtomicEnqueue( OSQueueHead *__list, void *__new, size_t __offset) __attribute__((weak_import)); |
191 | static inline void radar11111210(OSQueueHead *pool) { |
192 | void *newItem = malloc(4); |
193 | OSAtomicEnqueue(pool, newItem, 4); |
194 | } |
195 | |
196 | // Pointer might escape through CGDataProviderCreateWithData (radar://11187558). |
197 | typedef struct CGDataProvider *CGDataProviderRef; |
198 | typedef void (*CGDataProviderReleaseDataCallback)(void *info, const void *data, |
199 | size_t size); |
200 | extern CGDataProviderRef CGDataProviderCreateWithData(void *info, |
201 | const void *data, size_t size, |
202 | CGDataProviderReleaseDataCallback releaseData) |
203 | __attribute__((visibility("default"))); |
204 | void *calloc(size_t, size_t); |
205 | |
206 | static void releaseDataCallback (void *info, const void *data, size_t size) { |
207 | #pragma unused (info, size) |
208 | free((void*)data); |
209 | } |
210 | void testCGDataProviderCreateWithData() { |
211 | void* b = calloc(8, 8); |
212 | CGDataProviderRef p = CGDataProviderCreateWithData(0, b, 8*8, releaseDataCallback); |
213 | } |
214 | |
215 | // Assume that functions which take a function pointer can free memory even if |
216 | // they are defined in system headers and take the const pointer to the |
217 | // allocated memory. (radar://11160612) |
218 | extern CGDataProviderRef UnknownFunWithCallback(void *info, |
219 | const void *data, size_t size, |
220 | CGDataProviderReleaseDataCallback releaseData) |
221 | __attribute__((visibility("default"))); |
222 | void testUnknownFunWithCallBack() { |
223 | void* b = calloc(8, 8); |
224 | CGDataProviderRef p = UnknownFunWithCallback(0, b, 8*8, releaseDataCallback); |
225 | } |
226 | |
227 | // Test blocks. |
228 | void acceptBlockParam(void *, void (^block)(void *), unsigned); |
229 | void testCallWithBlockCallback() { |
230 | void *l = malloc(12); |
231 | acceptBlockParam(l, ^(void *i) { free(i); }, sizeof(char *)); |
232 | } |
233 | |
234 | // Test blocks in system headers. |
235 | void testCallWithBlockCallbackInSystem() { |
236 | void *l = malloc(12); |
237 | SystemHeaderFunctionWithBlockParam(l, ^(void *i) { free(i); }, sizeof(char *)); |
238 | } |
239 | |
240 | // Test escape into NSPointerArray. radar://11691035, PR13140 |
241 | void foo(NSPointerArray* pointerArray) { |
242 | |
243 | void* p1 = malloc (1024); |
244 | if (p1) { |
245 | [pointerArray addPointer:p1]; |
246 | } |
247 | |
248 | void* p2 = malloc (1024); |
249 | if (p2) { |
250 | [pointerArray insertPointer:p2 atIndex:1]; |
251 | } |
252 | |
253 | void* p3 = malloc (1024); |
254 | if (p3) { |
255 | [pointerArray replacePointerAtIndex:1 withPointer:p3]; |
256 | } |
257 | |
258 | // Freeing the buffer is allowed. |
259 | void* buffer = [pointerArray pointerAtIndex:0]; |
260 | free(buffer); |
261 | } |
262 | |
263 | void noCrashOnVariableArgumentSelector() { |
264 | NSMutableString *myString = [NSMutableString stringWithString:@"some text"]; |
265 | [myString appendFormat:@"some text = %d", 3]; |
266 | } |
267 | |
268 | void test12365078_check() { |
269 | unichar *characters = (unichar*)malloc(12); |
270 | NSString *string = [[NSString alloc] initWithCharactersNoCopy:characters length:12 freeWhenDone:1]; |
271 | if (!string) free(characters); // no-warning |
272 | } |
273 | |
274 | void test12365078_nocheck() { |
275 | unichar *characters = (unichar*)malloc(12); |
276 | NSString *string = [[NSString alloc] initWithCharactersNoCopy:characters length:12 freeWhenDone:1]; |
277 | } |
278 | |
279 | void test12365078_false_negative() { |
280 | unichar *characters = (unichar*)malloc(12); |
281 | NSString *string = [[NSString alloc] initWithCharactersNoCopy:characters length:12 freeWhenDone:1]; |
282 | if (!string) {;} |
283 | } |
284 | |
285 | void test12365078_no_malloc(unichar *characters) { |
286 | NSString *string = [[NSString alloc] initWithCharactersNoCopy:characters length:12 freeWhenDone:1]; |
287 | if (!string) {free(characters);} |
288 | } |
289 | |
290 | NSString *test12365078_no_malloc_returnValue(unichar *characters) { |
291 | NSString *string = [[NSString alloc] initWithCharactersNoCopy:characters length:12 freeWhenDone:1]; |
292 | if (!string) { |
293 | return 0; // no-warning |
294 | } |
295 | return string; |
296 | } |
297 | |
298 | void test12365078_nocheck_nomalloc(unichar *characters) { |
299 | NSString *string = [[NSString alloc] initWithCharactersNoCopy:characters length:12 freeWhenDone:1]; |
300 | free(characters); // expected-warning {{Attempt to free non-owned memory}} |
301 | } |
302 | |
303 | void test12365078_nested(unichar *characters) { |
304 | NSString *string = [[NSString alloc] initWithCharactersNoCopy:characters length:12 freeWhenDone:1]; |
305 | if (!string) { |
306 | NSString *string2 = [[NSString alloc] initWithCharactersNoCopy:characters length:12 freeWhenDone:1]; |
307 | if (!string2) { |
308 | NSString *string3 = [[NSString alloc] initWithCharactersNoCopy:characters length:12 freeWhenDone:1]; |
309 | if (!string3) { |
310 | NSString *string4 = [[NSString alloc] initWithCharactersNoCopy:characters length:12 freeWhenDone:1]; |
311 | if (!string4) |
312 | free(characters); |
313 | } |
314 | } |
315 | } |
316 | } |
317 | |
318 | void test12365078_check_positive() { |
319 | unichar *characters = (unichar*)malloc(12); |
320 | NSString *string = [[NSString alloc] initWithCharactersNoCopy:characters length:12 freeWhenDone:1]; |
321 | if (string) free(characters); // expected-warning{{Attempt to free non-owned memory}} |
322 | } |
323 | |
324 | void *test_reinterpret_cast_to_block() { |
325 | // Used to leak because the pointer was disappearing |
326 | // during the reinterpret_cast. |
327 | using BlockPtrTy = void (^)(); |
328 | struct Block {}; |
329 | Block* block = static_cast<Block*>(malloc(sizeof(Block))); |
330 | BlockPtrTy blockPtr = reinterpret_cast<BlockPtrTy>(block); // no-warning |
331 | return blockPtr; |
332 | } |
333 | |