1 | // RUN: %clang_analyze_cc1 -analyzer-checker=core -analyzer-config suppress-null-return-paths=false -verify %s |
2 | // RUN: %clang_analyze_cc1 -analyzer-checker=core -verify -DSUPPRESSED=1 %s |
3 | // RUN: %clang_analyze_cc1 -analyzer-checker=core -analyzer-config avoid-suppressing-null-argument-paths=true -DSUPPRESSED=1 -DNULL_ARGS=1 -verify %s |
4 | |
5 | int opaquePropertyCheck(void *object); |
6 | int coin(); |
7 | |
8 | int *getNull() { |
9 | return 0; |
10 | } |
11 | |
12 | int* getPtr(); |
13 | |
14 | int *dynCastToInt(void *ptr) { |
15 | if (opaquePropertyCheck(ptr)) |
16 | return (int *)ptr; |
17 | return 0; |
18 | } |
19 | |
20 | int *dynCastOrNull(void *ptr) { |
21 | if (!ptr) |
22 | return 0; |
23 | if (opaquePropertyCheck(ptr)) |
24 | return (int *)ptr; |
25 | return 0; |
26 | } |
27 | |
28 | |
29 | void testDynCast(void *p) { |
30 | int *casted = dynCastToInt(p); |
31 | *casted = 1; |
32 | #ifndef SUPPRESSED |
33 | // expected-warning@-2 {{Dereference of null pointer}} |
34 | #endif |
35 | } |
36 | |
37 | void testDynCastOrNull(void *p) { |
38 | int *casted = dynCastOrNull(p); |
39 | *casted = 1; |
40 | #ifndef SUPPRESSED |
41 | // expected-warning@-2 {{Dereference of null pointer}} |
42 | #endif |
43 | } |
44 | |
45 | |
46 | void testBranch(void *p) { |
47 | int *casted; |
48 | |
49 | // Although the report will be suppressed on one branch, it should still be |
50 | // valid on the other. |
51 | if (coin()) { |
52 | casted = dynCastToInt(p); |
53 | } else { |
54 | if (p) |
55 | return; |
56 | casted = (int *)p; |
57 | } |
58 | |
59 | *casted = 1; // expected-warning {{Dereference of null pointer}} |
60 | } |
61 | |
62 | void testBranchReversed(void *p) { |
63 | int *casted; |
64 | |
65 | // Although the report will be suppressed on one branch, it should still be |
66 | // valid on the other. |
67 | if (coin()) { |
68 | if (p) |
69 | return; |
70 | casted = (int *)p; |
71 | } else { |
72 | casted = dynCastToInt(p); |
73 | } |
74 | |
75 | *casted = 1; // expected-warning {{Dereference of null pointer}} |
76 | } |
77 | |
78 | void testMultipleStore(void *p) { |
79 | int *casted = 0; |
80 | casted = dynCastToInt(p); |
81 | *casted = 1; |
82 | #ifndef SUPPRESSED |
83 | // expected-warning@-2 {{Dereference of null pointer}} |
84 | #endif |
85 | } |
86 | |
87 | // Test that div by zero does not get suppressed. This is a policy choice. |
88 | int retZero() { |
89 | return 0; |
90 | } |
91 | int triggerDivZero () { |
92 | int y = retZero(); |
93 | return 5/y; // expected-warning {{Division by zero}} |
94 | } |
95 | |
96 | // Treat a function-like macro similarly to an inlined function, so suppress |
97 | // warnings along paths resulting from inlined checks. |
98 | #define MACRO_WITH_CHECK(a) ( ((a) != 0) ? *a : 17) |
99 | void testInlineCheckInMacro(int *p) { |
100 | int i = MACRO_WITH_CHECK(p); |
101 | (void)i; |
102 | |
103 | *p = 1; // no-warning |
104 | } |
105 | |
106 | #define MACRO_WITH_NESTED_CHECK(a) ( { int j = MACRO_WITH_CHECK(a); j; } ) |
107 | void testInlineCheckInNestedMacro(int *p) { |
108 | int i = MACRO_WITH_NESTED_CHECK(p); |
109 | (void)i; |
110 | |
111 | *p = 1; // no-warning |
112 | } |
113 | |
114 | #define NON_FUNCTION_MACRO_WITH_CHECK ( ((p) != 0) ? *p : 17) |
115 | void testNonFunctionMacro(int *p) { |
116 | int i = NON_FUNCTION_MACRO_WITH_CHECK ; |
117 | (void)i; |
118 | |
119 | *p = 1; // no-warning |
120 | } |
121 | |
122 | |
123 | // This macro will dereference its argument if the argument is NULL. |
124 | #define MACRO_WITH_ERROR(a) ( ((a) != 0) ? 0 : *a) |
125 | void testErrorInMacro(int *p) { |
126 | int i = MACRO_WITH_ERROR(p); // expected-warning {{Dereference of null pointer (loaded from variable 'p')}} |
127 | (void)i; |
128 | } |
129 | |
130 | // Here the check (the "if") is not in a macro, so we should still warn. |
131 | #define MACRO_IN_GUARD(a) (!(a)) |
132 | void testMacroUsedAsGuard(int *p) { |
133 | if (MACRO_IN_GUARD(p)) |
134 | *p = 1; // expected-warning {{Dereference of null pointer (loaded from variable 'p')}} |
135 | } |
136 | |
137 | // When a nil case split is introduced in a macro and the macro is in a guard, |
138 | // we still shouldn't warn. |
139 | int isNull(int *p); |
140 | int isEqual(int *p, int *q); |
141 | #define ISNULL(ptr) ((ptr) == 0 || isNull(ptr)) |
142 | #define ISEQUAL(a, b) ((int *)(a) == (int *)(b) || (ISNULL(a) && ISNULL(b)) || isEqual(a,b)) |
143 | #define ISNOTEQUAL(a, b) (!ISEQUAL(a, b)) |
144 | void testNestedDisjunctiveMacro(int *p, int *q) { |
145 | if (ISNOTEQUAL(p,q)) { |
146 | *p = 1; // no-warning |
147 | *q = 1; // no-warning |
148 | } |
149 | |
150 | *p = 1; // no-warning |
151 | *q = 1; // no-warning |
152 | } |
153 | |
154 | void testNestedDisjunctiveMacro2(int *p, int *q) { |
155 | if (ISEQUAL(p,q)) { |
156 | return; |
157 | } |
158 | |
159 | *p = 1; // no-warning |
160 | *q = 1; // no-warning |
161 | } |
162 | |
163 | |
164 | |
165 | // Here the check is entirely in non-macro code even though the code itself |
166 | // is a macro argument. |
167 | #define MACRO_DO_IT(a) (a) |
168 | void testErrorInArgument(int *p) { |
169 | int i = MACRO_DO_IT((p ? 0 : *p)); // expected-warning {{Dereference of null pointer (loaded from variable 'p')}}c |
170 | (void)i; |
171 | } |
172 | |
173 | // No warning should be emitted if dereference is performed from a different |
174 | // macro. |
175 | #define MACRO_CHECK(a) if (a) {} |
176 | #define MACRO_DEREF(a) (*a) |
177 | int testDifferentMacro(int *p) { |
178 | MACRO_CHECK(p); |
179 | return MACRO_DEREF(p); // no-warning |
180 | } |
181 | |
182 | // -------------------------- |
183 | // "Suppression suppression" |
184 | // -------------------------- |
185 | |
186 | void testDynCastOrNullOfNull() { |
187 | // Don't suppress when one of the arguments is NULL. |
188 | int *casted = dynCastOrNull(0); |
189 | *casted = 1; |
190 | #if !SUPPRESSED || NULL_ARGS |
191 | // expected-warning@-2 {{Dereference of null pointer}} |
192 | #endif |
193 | } |
194 | |
195 | void testDynCastOfNull() { |
196 | // Don't suppress when one of the arguments is NULL. |
197 | int *casted = dynCastToInt(0); |
198 | *casted = 1; |
199 | #if !SUPPRESSED || NULL_ARGS |
200 | // expected-warning@-2 {{Dereference of null pointer}} |
201 | #endif |
202 | } |
203 | |
204 | int *lookUpInt(int unused) { |
205 | if (coin()) |
206 | return 0; |
207 | static int x; |
208 | return &x; |
209 | } |
210 | |
211 | void testZeroIsNotNull() { |
212 | // /Do/ suppress when the argument is 0 (an integer). |
213 | int *casted = lookUpInt(0); |
214 | *casted = 1; |
215 | #ifndef SUPPRESSED |
216 | // expected-warning@-2 {{Dereference of null pointer}} |
217 | #endif |
218 | } |
219 | |
220 | void testTrackNull() { |
221 | // /Do/ suppress if the null argument came from another call returning null. |
222 | int *casted = dynCastOrNull(getNull()); |
223 | *casted = 1; |
224 | #ifndef SUPPRESSED |
225 | // expected-warning@-2 {{Dereference of null pointer}} |
226 | #endif |
227 | } |
228 | |
229 | void testTrackNullVariable() { |
230 | // /Do/ suppress if the null argument came from another call returning null. |
231 | int *ptr; |
232 | ptr = getNull(); |
233 | int *casted = dynCastOrNull(ptr); |
234 | *casted = 1; |
235 | #ifndef SUPPRESSED |
236 | // expected-warning@-2 {{Dereference of null pointer}} |
237 | #endif |
238 | } |
239 | |
240 | void inlinedIsDifferent(int inlined) { |
241 | int i; |
242 | |
243 | // We were erroneously picking up the inner stack frame's initialization, |
244 | // even though the error occurs in the outer stack frame! |
245 | int *p = inlined ? &i : getNull(); |
246 | |
247 | if (!inlined) |
248 | inlinedIsDifferent(1); |
249 | |
250 | *p = 1; |
251 | #ifndef SUPPRESSED |
252 | // expected-warning@-2 {{Dereference of null pointer}} |
253 | #endif |
254 | } |
255 | |
256 | void testInlinedIsDifferent() { |
257 | // <rdar://problem/13787723> |
258 | inlinedIsDifferent(0); |
259 | } |
260 | |
261 | |
262 | // --------------------------------------- |
263 | // FALSE NEGATIVES (over-suppression) |
264 | // --------------------------------------- |
265 | |
266 | void testNoArguments() { |
267 | // In this case the function has no branches, and MUST return null. |
268 | int *casted = getNull(); |
269 | *casted = 1; |
270 | #ifndef SUPPRESSED |
271 | // expected-warning@-2 {{Dereference of null pointer}} |
272 | #endif |
273 | } |
274 | |
275 | int *getNullIfNonNull(void *input) { |
276 | if (input) |
277 | return 0; |
278 | static int x; |
279 | return &x; |
280 | } |
281 | |
282 | void testKnownPath(void *input) { |
283 | if (!input) |
284 | return; |
285 | |
286 | // In this case we have a known value for the argument, and thus the path |
287 | // through the function doesn't ever split. |
288 | int *casted = getNullIfNonNull(input); |
289 | *casted = 1; |
290 | #ifndef SUPPRESSED |
291 | // expected-warning@-2 {{Dereference of null pointer}} |
292 | #endif |
293 | } |
294 | |
295 | int *alwaysReturnNull(void *input) { |
296 | if (opaquePropertyCheck(input)) |
297 | return 0; |
298 | return 0; |
299 | } |
300 | |
301 | void testAlwaysReturnNull(void *input) { |
302 | // In this case all paths out of the function return 0, but they are all |
303 | // dominated by a branch whose condition we don't know! |
304 | int *casted = alwaysReturnNull(input); |
305 | *casted = 1; |
306 | #ifndef SUPPRESSED |
307 | // expected-warning@-2 {{Dereference of null pointer}} |
308 | #endif |
309 | } |
310 | |
311 | int derefArg(int *p) { |
312 | return *p; |
313 | #ifndef SUPPRESSED |
314 | // expected-warning@-2 {{Dereference of null pointer}} |
315 | #endif |
316 | } |
317 | void ternaryArg(char cond) { |
318 | static int x; |
319 | derefArg(cond ? &x : getNull()); |
320 | } |
321 | |
322 | int derefArgCast(char *p) { |
323 | return *p; |
324 | #ifndef SUPPRESSED |
325 | // expected-warning@-2 {{Dereference of null pointer}} |
326 | #endif |
327 | } |
328 | void ternaryArgCast(char cond) { |
329 | static int x; |
330 | derefArgCast((char*)((unsigned)cond ? &x : getNull())); |
331 | } |
332 | |
333 | int derefAssignment(int *p) { |
334 | return *p; |
335 | #ifndef SUPPRESSED |
336 | // expected-warning@-2 {{Dereference of null pointer}} |
337 | #endif |
338 | } |
339 | |
340 | void ternaryAssignment(char cond) { |
341 | static int x; |
342 | int *p = cond ? getNull() : getPtr(); |
343 | derefAssignment(p); |
344 | } |
345 | |
346 | int *retNull(char cond) { |
347 | static int x; |
348 | return cond ? &x : getNull(); |
349 | } |
350 | int ternaryRetNull(char cond) { |
351 | int *p = retNull(cond); |
352 | return *p; |
353 | #ifndef SUPPRESSED |
354 | // expected-warning@-2 {{Dereference of null pointer}} |
355 | #endif |
356 | } |
357 | |
358 | // Test suppression of nested conditional operators. |
359 | int testConditionalOperatorSuppress(int x) { |
360 | return *(x ? getNull() : getPtr()); |
361 | #ifndef SUPPRESSED |
362 | // expected-warning@-2 {{Dereference of null pointer}} |
363 | #endif |
364 | } |
365 | int testNestedConditionalOperatorSuppress(int x) { |
366 | return *(x ? (x ? getNull() : getPtr()) : getPtr()); |
367 | #ifndef SUPPRESSED |
368 | // expected-warning@-2 {{Dereference of null pointer}} |
369 | #endif |
370 | } |
371 | int testConditionalOperator(int x) { |
372 | return *(x ? 0 : getPtr()); // expected-warning {{Dereference of null pointer}} |
373 | } |
374 | int testNestedConditionalOperator(int x) { |
375 | return *(x ? (x ? 0 : getPtr()) : getPtr()); // expected-warning {{Dereference of null pointer}} |
376 | } |
377 | |
378 | int testConditionalOperatorSuppressFloatCond(float x) { |
379 | return *(x ? getNull() : getPtr()); |
380 | #ifndef SUPPRESSED |
381 | // expected-warning@-2 {{Dereference of null pointer}} |
382 | #endif |
383 | } |
384 | |
385 | |