1 | // RUN: %clang_analyze_cc1 -analyzer-checker=osx.SecKeychainAPI -fblocks %s -verify |
2 | |
3 | #include "Inputs/system-header-simulator-objc.h" |
4 | |
5 | // Fake typedefs. |
6 | typedef unsigned int OSStatus; |
7 | typedef unsigned int SecKeychainAttributeList; |
8 | typedef unsigned int SecKeychainItemRef; |
9 | typedef unsigned int SecItemClass; |
10 | typedef unsigned int UInt32; |
11 | typedef unsigned int SecProtocolType; |
12 | typedef unsigned int SecAuthenticationType; |
13 | typedef unsigned int SecKeychainAttributeInfo; |
14 | enum { |
15 | noErr = 0, |
16 | GenericError = 1 |
17 | }; |
18 | |
19 | // Functions that allocate data. |
20 | OSStatus SecKeychainItemCopyContent ( |
21 | SecKeychainItemRef itemRef, |
22 | SecItemClass *itemClass, |
23 | SecKeychainAttributeList *attrList, |
24 | UInt32 *length, |
25 | void **outData |
26 | ); |
27 | OSStatus SecKeychainFindGenericPassword ( |
28 | CFTypeRef keychainOrArray, |
29 | UInt32 serviceNameLength, |
30 | const char *serviceName, |
31 | UInt32 accountNameLength, |
32 | const char *accountName, |
33 | UInt32 *passwordLength, |
34 | void **passwordData, |
35 | SecKeychainItemRef *itemRef |
36 | ); |
37 | OSStatus SecKeychainFindInternetPassword ( |
38 | CFTypeRef keychainOrArray, |
39 | UInt32 serverNameLength, |
40 | const char *serverName, |
41 | UInt32 securityDomainLength, |
42 | const char *securityDomain, |
43 | UInt32 accountNameLength, |
44 | const char *accountName, |
45 | UInt32 pathLength, |
46 | const char *path, |
47 | UInt16 port, |
48 | SecProtocolType protocol, |
49 | SecAuthenticationType authenticationType, |
50 | UInt32 *passwordLength, |
51 | void **passwordData, |
52 | SecKeychainItemRef *itemRef |
53 | ); |
54 | OSStatus SecKeychainItemCopyAttributesAndData ( |
55 | SecKeychainItemRef itemRef, |
56 | SecKeychainAttributeInfo *info, |
57 | SecItemClass *itemClass, |
58 | SecKeychainAttributeList **attrList, |
59 | UInt32 *length, |
60 | void **outData |
61 | ); |
62 | |
63 | // Functions which free data. |
64 | OSStatus SecKeychainItemFreeContent ( |
65 | SecKeychainAttributeList *attrList, |
66 | void *data |
67 | ); |
68 | OSStatus SecKeychainItemFreeAttributesAndData ( |
69 | SecKeychainAttributeList *attrList, |
70 | void *data |
71 | ); |
72 | |
73 | void errRetVal() { |
74 | unsigned int *ptr = 0; |
75 | OSStatus st = 0; |
76 | UInt32 length; |
77 | void *outData; |
78 | st = SecKeychainItemCopyContent(2, ptr, ptr, &length, &outData); |
79 | if (st == GenericError) |
80 | SecKeychainItemFreeContent(ptr, outData); |
81 | } // expected-warning{{Allocated data is not released: missing a call to 'SecKeychainItemFreeContent'}} |
82 | |
83 | // If null is passed in, the data is not allocated, so no need for the matching free. |
84 | void fooDoNotReportNull() { |
85 | unsigned int *ptr = 0; |
86 | OSStatus st = 0; |
87 | UInt32 *length = 0; |
88 | void **outData = 0; |
89 | SecKeychainItemCopyContent(2, ptr, ptr, 0, 0); |
90 | SecKeychainItemCopyContent(2, ptr, ptr, length, outData); |
91 | }// no-warning |
92 | |
93 | void doubleAlloc() { |
94 | unsigned int *ptr = 0; |
95 | OSStatus st = 0; |
96 | UInt32 length; |
97 | void *outData; |
98 | st = SecKeychainItemCopyContent(2, ptr, ptr, &length, &outData); |
99 | st = SecKeychainItemCopyContent(2, ptr, ptr, &length, &outData); // expected-warning {{Allocated data should be released before another call to the allocator:}} |
100 | if (st == noErr) |
101 | SecKeychainItemFreeContent(ptr, outData); |
102 | } |
103 | |
104 | // Do not warn if undefined value is passed to a function. |
105 | void fooOnlyFreeUndef() { |
106 | unsigned int *ptr = 0; |
107 | OSStatus st = 0; |
108 | UInt32 length; |
109 | void *outData; |
110 | SecKeychainItemFreeContent(ptr, outData); |
111 | }// no-warning |
112 | |
113 | // Do not warn if the address is a parameter in the enclosing function. |
114 | void fooOnlyFreeParam(void *attrList, void* X) { |
115 | SecKeychainItemFreeContent(attrList, X); |
116 | }// no-warning |
117 | |
118 | // If we are returning the value, do not report. |
119 | void* returnContent() { |
120 | unsigned int *ptr = 0; |
121 | OSStatus st = 0; |
122 | UInt32 length; |
123 | void *outData; |
124 | st = SecKeychainItemCopyContent(2, ptr, ptr, &length, &outData); |
125 | return outData; |
126 | } // no-warning |
127 | |
128 | // Password was passed in as an argument and does not have to be deleted. |
129 | OSStatus getPasswordAndItem(void** password, UInt32* passwordLength) { |
130 | OSStatus err; |
131 | SecKeychainItemRef item; |
132 | err = SecKeychainFindGenericPassword(0, 3, "xx", 3, "xx", |
133 | passwordLength, password, &item); |
134 | return err; |
135 | } // no-warning |
136 | |
137 | // Make sure we do not report an error if we call free only if password != 0. |
138 | // Also, do not report double allocation if first allocation returned an error. |
139 | OSStatus testSecKeychainFindGenericPassword(UInt32* passwordLength, |
140 | CFTypeRef keychainOrArray, SecProtocolType protocol, |
141 | SecAuthenticationType authenticationType) { |
142 | OSStatus err; |
143 | SecKeychainItemRef item; |
144 | void *password; |
145 | err = SecKeychainFindGenericPassword(0, 3, "xx", 3, "xx", |
146 | passwordLength, &password, &item); |
147 | if( err == GenericError ) { |
148 | err = SecKeychainFindInternetPassword(keychainOrArray, |
149 | 16, "server", 16, "domain", 16, "account", |
150 | 16, "path", 222, protocol, authenticationType, |
151 | passwordLength, &(password), 0); |
152 | } |
153 | |
154 | if (err == noErr && password) { |
155 | SecKeychainItemFreeContent(0, password); |
156 | } |
157 | return err; |
158 | } |
159 | |
160 | int apiMismatch(SecKeychainItemRef itemRef, |
161 | SecKeychainAttributeInfo *info, |
162 | SecItemClass *itemClass) { |
163 | OSStatus st = 0; |
164 | SecKeychainAttributeList *attrList; |
165 | UInt32 length; |
166 | void *outData; |
167 | |
168 | st = SecKeychainItemCopyAttributesAndData(itemRef, info, itemClass, |
169 | &attrList, &length, &outData); |
170 | if (st == noErr) |
171 | SecKeychainItemFreeContent(attrList, outData); // expected-warning{{Deallocator doesn't match the allocator}} |
172 | return 0; |
173 | } |
174 | |
175 | int ErrorCodesFromDifferentAPISDoNotInterfere(SecKeychainItemRef itemRef, |
176 | SecKeychainAttributeInfo *info, |
177 | SecItemClass *itemClass) { |
178 | unsigned int *ptr = 0; |
179 | OSStatus st = 0; |
180 | UInt32 length; |
181 | void *outData; |
182 | OSStatus st2 = 0; |
183 | SecKeychainAttributeList *attrList; |
184 | UInt32 length2; |
185 | void *outData2; |
186 | |
187 | st2 = SecKeychainItemCopyAttributesAndData(itemRef, info, itemClass, |
188 | &attrList, &length2, &outData2); |
189 | st = SecKeychainItemCopyContent(2, ptr, ptr, &length, &outData); |
190 | if (st == noErr) { |
191 | SecKeychainItemFreeContent(ptr, outData); |
192 | if (st2 == noErr) { |
193 | SecKeychainItemFreeAttributesAndData(attrList, outData2); |
194 | } |
195 | } |
196 | return 0; // expected-warning{{Allocated data is not released: missing a call to 'SecKeychainItemFreeAttributesAndData'}} |
197 | } |
198 | |
199 | int foo(CFTypeRef keychainOrArray, SecProtocolType protocol, |
200 | SecAuthenticationType authenticationType, SecKeychainItemRef *itemRef) { |
201 | unsigned int *ptr = 0; |
202 | OSStatus st = 0; |
203 | |
204 | UInt32 length; |
205 | void *outData[5]; |
206 | |
207 | st = SecKeychainFindInternetPassword(keychainOrArray, |
208 | 16, "server", 16, "domain", 16, "account", |
209 | 16, "path", 222, protocol, authenticationType, |
210 | &length, &(outData[3]), itemRef); |
211 | if (length == 5) { |
212 | if (st == noErr) |
213 | SecKeychainItemFreeContent(ptr, outData[3]); |
214 | } |
215 | if (length) { // expected-warning{{Allocated data is not released: missing a call to 'SecKeychainItemFreeContent'}} |
216 | length++; |
217 | } |
218 | return 0; |
219 | } |
220 | |
221 | int testErrorCodeAsLHS(CFTypeRef keychainOrArray, SecProtocolType protocol, |
222 | SecAuthenticationType authenticationType, SecKeychainItemRef *itemRef) { |
223 | unsigned int *ptr = 0; |
224 | OSStatus st = 0; |
225 | UInt32 length; |
226 | void *outData; |
227 | st = SecKeychainFindInternetPassword(keychainOrArray, |
228 | 16, "server", 16, "domain", 16, "account", |
229 | 16, "path", 222, protocol, authenticationType, |
230 | &length, &outData, itemRef); |
231 | if (noErr == st) |
232 | SecKeychainItemFreeContent(ptr, outData); |
233 | |
234 | return 0; |
235 | } |
236 | |
237 | void free(void *ptr); |
238 | void deallocateWithFree() { |
239 | unsigned int *ptr = 0; |
240 | OSStatus st = 0; |
241 | UInt32 length; |
242 | void *outData; |
243 | st = SecKeychainItemCopyContent(2, ptr, ptr, &length, &outData); |
244 | if (st == noErr) |
245 | free(outData); // expected-warning{{Deallocator doesn't match the allocator: 'SecKeychainItemFreeContent' should be used}} |
246 | } |
247 | |
248 | // Typesdefs for CFStringCreateWithBytesNoCopy. |
249 | typedef char uint8_t; |
250 | typedef signed long CFIndex; |
251 | typedef UInt32 CFStringEncoding; |
252 | typedef unsigned Boolean; |
253 | typedef const struct __CFString * CFStringRef; |
254 | typedef const struct __CFAllocator * CFAllocatorRef; |
255 | extern const CFAllocatorRef kCFAllocatorDefault; |
256 | extern const CFAllocatorRef kCFAllocatorSystemDefault; |
257 | extern const CFAllocatorRef kCFAllocatorMalloc; |
258 | extern const CFAllocatorRef kCFAllocatorMallocZone; |
259 | extern const CFAllocatorRef kCFAllocatorNull; |
260 | extern const CFAllocatorRef kCFAllocatorUseContext; |
261 | CFStringRef CFStringCreateWithBytesNoCopy(CFAllocatorRef alloc, const uint8_t *bytes, CFIndex numBytes, CFStringEncoding encoding, Boolean externalFormat, CFAllocatorRef contentsDeallocator); |
262 | |
263 | void DellocWithCFStringCreate1(CFAllocatorRef alloc) { |
264 | unsigned int *ptr = 0; |
265 | OSStatus st = 0; |
266 | UInt32 length; |
267 | void *bytes; |
268 | char * x; |
269 | st = SecKeychainItemCopyContent(2, ptr, ptr, &length, &bytes); |
270 | if (st == noErr) { |
271 | CFStringRef userStr = CFStringCreateWithBytesNoCopy(alloc, bytes, length, 5, 0, kCFAllocatorDefault); // expected-warning{{Deallocator doesn't match the allocator:}} |
272 | CFRelease(userStr); |
273 | } |
274 | } |
275 | |
276 | void DellocWithCFStringCreate2(CFAllocatorRef alloc) { |
277 | unsigned int *ptr = 0; |
278 | OSStatus st = 0; |
279 | UInt32 length; |
280 | void *bytes; |
281 | char * x; |
282 | st = SecKeychainItemCopyContent(2, ptr, ptr, &length, &bytes); |
283 | if (st == noErr) { |
284 | CFStringRef userStr = CFStringCreateWithBytesNoCopy(alloc, bytes, length, 5, 0, kCFAllocatorNull); // expected-warning{{Allocated data is not released}} |
285 | CFRelease(userStr); |
286 | } |
287 | } |
288 | |
289 | void DellocWithCFStringCreate3(CFAllocatorRef alloc) { |
290 | unsigned int *ptr = 0; |
291 | OSStatus st = 0; |
292 | UInt32 length; |
293 | void *bytes; |
294 | char * x; |
295 | st = SecKeychainItemCopyContent(2, ptr, ptr, &length, &bytes); |
296 | if (st == noErr) { |
297 | CFStringRef userStr = CFStringCreateWithBytesNoCopy(alloc, bytes, length, 5, 0, kCFAllocatorUseContext); |
298 | CFRelease(userStr); |
299 | } |
300 | } |
301 | |
302 | void DellocWithCFStringCreate4(CFAllocatorRef alloc) { |
303 | unsigned int *ptr = 0; |
304 | OSStatus st = 0; |
305 | UInt32 length; |
306 | void *bytes; |
307 | char * x; |
308 | st = SecKeychainItemCopyContent(2, ptr, ptr, &length, &bytes); |
309 | if (st == noErr) { |
310 | CFStringRef userStr = CFStringCreateWithBytesNoCopy(alloc, bytes, length, 5, 0, 0); // expected-warning{{Deallocator doesn't match the allocator:}} |
311 | CFRelease(userStr); |
312 | } |
313 | } |
314 | |
315 | static CFAllocatorRef gKeychainDeallocator = 0; |
316 | |
317 | static CFAllocatorRef GetKeychainDeallocator() { |
318 | return gKeychainDeallocator; |
319 | } |
320 | |
321 | CFStringRef DellocWithCFStringCreate5(CFAllocatorRef alloc) { |
322 | unsigned int *ptr = 0; |
323 | OSStatus st = 0; |
324 | UInt32 length; |
325 | void *bytes; |
326 | char * x; |
327 | st = SecKeychainItemCopyContent(2, ptr, ptr, &length, &bytes); |
328 | if (st == noErr) { |
329 | return CFStringCreateWithBytesNoCopy(alloc, bytes, length, 5, 0, GetKeychainDeallocator()); // no-warning |
330 | } |
331 | return 0; |
332 | } |
333 | |
334 | void radar10508828() { |
335 | UInt32 pwdLen = 0; |
336 | void* pwdBytes = 0; |
337 | OSStatus rc = SecKeychainFindGenericPassword(0, 3, "foo", 3, "bar", &pwdLen, &pwdBytes, 0); |
338 | #pragma unused(rc) |
339 | if (pwdBytes) |
340 | SecKeychainItemFreeContent(0, pwdBytes); |
341 | } |
342 | |
343 | void radar10508828_20092614() { |
344 | UInt32 pwdLen = 0; |
345 | void* pwdBytes = 0; |
346 | OSStatus rc = SecKeychainFindGenericPassword(0, 3, "foo", 3, "bar", &pwdLen, &pwdBytes, 0); |
347 | SecKeychainItemFreeContent(0, pwdBytes); |
348 | } |
349 | |
350 | //Example from bug 10797. |
351 | __inline__ static |
352 | const char *__WBASLLevelString(int level) { |
353 | return "foo"; |
354 | } |
355 | |
356 | static int *bug10798(int *p, int columns, int prevRow) { |
357 | int *row = 0; |
358 | row = p + prevRow * columns; |
359 | prevRow += 2; |
360 | do { |
361 | ++prevRow; |
362 | row+=columns; |
363 | } while(10 >= row[1]); |
364 | return row; |
365 | } |
366 | |
367 | // Test inter-procedural behaviour. |
368 | |
369 | void my_FreeParam(void *attrList, void* X) { |
370 | SecKeychainItemFreeContent(attrList, X); |
371 | } |
372 | |
373 | void *my_AllocateReturn(OSStatus *st) { |
374 | unsigned int *ptr = 0; |
375 | UInt32 length; |
376 | void *outData; |
377 | *st = SecKeychainItemCopyContent(2, ptr, ptr, &length, &outData); |
378 | return outData; |
379 | } |
380 | |
381 | OSStatus my_Allocate_Param(void** password, UInt32* passwordLength) { |
382 | OSStatus err; |
383 | SecKeychainItemRef item; |
384 | err = SecKeychainFindGenericPassword(0, 3, "xx", 3, "xx", |
385 | passwordLength, password, &item); |
386 | return err; |
387 | } |
388 | |
389 | void allocAndFree1() { |
390 | unsigned int *ptr = 0; |
391 | OSStatus st = 0; |
392 | UInt32 length; |
393 | void *outData; |
394 | st = SecKeychainItemCopyContent(2, ptr, ptr, &length, &outData); |
395 | if (st == noErr) |
396 | my_FreeParam(ptr, outData); |
397 | } |
398 | |
399 | void consumeChar(char); |
400 | |
401 | void allocNoFree2(int x) { |
402 | OSStatus st = 0; |
403 | void *outData = my_AllocateReturn(&st); |
404 | if (x) { |
405 | consumeChar(*(char*)outData); // expected-warning{{Allocated data is not released:}} |
406 | return; |
407 | } else { |
408 | consumeChar(*(char*)outData); |
409 | } |
410 | return; |
411 | } |
412 | |
413 | void allocAndFree2(void *attrList) { |
414 | OSStatus st = 0; |
415 | void *outData = my_AllocateReturn(&st); |
416 | if (st == noErr) |
417 | my_FreeParam(attrList, outData); |
418 | } |
419 | |
420 | void allocNoFree3() { |
421 | UInt32 length = 32; |
422 | void *outData; |
423 | void *outData2; |
424 | OSStatus st = my_Allocate_Param(&outData, &length); // expected-warning{{Allocated data is not released}} |
425 | st = my_Allocate_Param(&outData2, &length); // expected-warning{{Allocated data is not released}} |
426 | } |
427 | |
428 | void allocAndFree3(void *attrList) { |
429 | UInt32 length = 32; |
430 | void *outData; |
431 | OSStatus st = my_Allocate_Param(&outData, &length); |
432 | if (st == noErr) |
433 | SecKeychainItemFreeContent(attrList, outData); |
434 | } |
435 | |
436 | typedef struct AuthorizationValue { |
437 | int length; |
438 | void *data; |
439 | } AuthorizationValue; |
440 | typedef struct AuthorizationCallback { |
441 | OSStatus (*SetContextVal)(AuthorizationValue *inValue); |
442 | } AuthorizationCallback; |
443 | static AuthorizationCallback cb; |
444 | int radar_19196494() { |
445 | @autoreleasepool { |
446 | AuthorizationValue login_password = {}; |
447 | UInt32 passwordLength; |
448 | void *passwordData = 0; |
449 | OSStatus err = SecKeychainFindGenericPassword(0, 0, "", 0, "", (UInt32 *)&login_password.length, (void**)&login_password.data, 0); |
450 | cb.SetContextVal(&login_password); |
451 | if (err == noErr) { |
452 | SecKeychainItemFreeContent(0, login_password.data); |
453 | } |
454 | } |
455 | return 0; |
456 | } |
457 | int radar_19196494_v2() { |
458 | @autoreleasepool { |
459 | AuthorizationValue login_password = {}; |
460 | OSStatus err = SecKeychainFindGenericPassword(0, 0, "", 0, "", (UInt32 *)&login_password.length, (void**)&login_password.data, 0); |
461 | if (!login_password.data) return 0; |
462 | cb.SetContextVal(&login_password); |
463 | if (err == noErr) { |
464 | SecKeychainItemFreeContent(0, login_password.data); |
465 | } |
466 | } |
467 | return 0; |
468 | } |
469 | |