Skip to content

Commit c240537

Browse files
committed
Add test from PR #109804
1 parent c429df6 commit c240537

File tree

1 file changed

+149
-1
lines changed

1 file changed

+149
-1
lines changed

clang/test/Analysis/out-of-bounds.c

Lines changed: 149 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
1-
// RUN: %clang_analyze_cc1 -Wno-array-bounds -analyzer-checker=core,alpha.security.ArrayBoundV2,debug.ExprInspection -verify %s
1+
// RUN: %clang_analyze_cc1 -Wno-array-bounds -analyzer-checker=core,alpha.security.ArrayBoundV2,debug.ExprInspection -verify=expected,eagerlyassume %s
2+
// RUN: %clang_analyze_cc1 -Wno-array-bounds -analyzer-checker=core,alpha.security.ArrayBoundV2,debug.ExprInspection -analyzer-config eagerly-assume=false -verify %s
3+
4+
// Note that eagerly-assume=false is tested separately because the
5+
// WeakLoopAssumption suppression heuristic uses different code paths to
6+
// achieve the same result with and without eagerly-assume.
27

38
void clang_analyzer_eval(int);
49

@@ -194,3 +199,146 @@ char test_comparison_with_extent_symbol(struct incomplete *p) {
194199
return ((char *)p)[-1]; // no-warning
195200
}
196201

202+
// WeakLoopAssumption suppression
203+
///////////////////////////////////////////////////////////////////////
204+
205+
int GlobalArray[100];
206+
int loop_suppress_after_zero_iterations(unsigned len) {
207+
for (unsigned i = 0; i < len; i++)
208+
if (GlobalArray[i] > 0)
209+
return GlobalArray[i];
210+
// Previously this would have produced an overflow warning because splitting
211+
// the state on the loop condition introduced an execution path where the
212+
// analyzer thinks that len == 0.
213+
// There are many situations where the programmer knows that an argument is
214+
// positive, but this is not indicated in the source code, so we must avoid
215+
// reporting errors (especially out of bounds errors) on these branches,
216+
// because otherwise we'd get prohibitively many false positives.
217+
return GlobalArray[len - 1]; // no-warning
218+
}
219+
220+
void loop_report_in_second_iteration(int len) {
221+
int buf[1] = {0};
222+
for (int i = 0; i < len; i++) {
223+
// When a programmer writes a loop, we may assume that they intended at
224+
// least two iterations.
225+
buf[i] = 1; // expected-warning{{Out of bound access to memory}}
226+
}
227+
}
228+
229+
void loop_suppress_in_third_iteration(int len) {
230+
int buf[2] = {0};
231+
for (int i = 0; i < len; i++) {
232+
// We should suppress array bounds errors on the third and later iterations
233+
// of loops, because sometimes programmers write a loop in sitiuations
234+
// where they know that there will be at most two iterations, but the
235+
// analyzer cannot deduce this property.
236+
buf[i] = 1; // no-warning
237+
}
238+
}
239+
240+
int no_suppression_when_no_assumption_zero_iterations(unsigned len) {
241+
if (len != 0) {
242+
// This 'if' introduces a state split between len == 0 and len != 0.
243+
}
244+
245+
// On the branch where we assumed that len is zero, this loop will be
246+
// skipped. We (intentionally) don't suppress this execution path becasue
247+
// here the analyzer doesn't assume anything new when it evaluates the loop
248+
// condition.
249+
for (unsigned i = 0; i < len; i++)
250+
if (GlobalArray[i] > 0)
251+
return GlobalArray[i];
252+
253+
return GlobalArray[len - 1]; // expected-warning{{Out of bound access to memory}}
254+
}
255+
256+
void no_suppression_when_no_assumption_third_iteration(int len) {
257+
if (len < 20) {
258+
// This 'if' introduces a state split with len >= 20 on one branch.
259+
}
260+
261+
int buf[2] = {0};
262+
for (int i = 0; i < len; i++) {
263+
// As in no_suppression_when_no_assumption_zero_iterations, the suppression
264+
// only activates when the analyzer assumes something new in the loop
265+
// condition. On the branch where `len >= 20` entering the third iteration
266+
// doesn't involve a new assumption, so this warning is not suppressed:
267+
buf[i] = 1; // expected-warning{{Out of bound access to memory}}
268+
}
269+
}
270+
271+
void loop_suppress_in_third_iteration_logical_and(int len, int flag) {
272+
int buf[2] = {0};
273+
for (int i = 0; i < len && flag; i++) {
274+
// FIXME: In this case the checker should suppress the warning the same way
275+
// as it's suppressed in loop_suppress_in_third_iteration, but the
276+
// suppression is not activated because the terminator statement associated
277+
// with the loop is just the expression 'flag', while 'i < len' is a
278+
// separate terminator statement that's associated with the
279+
// short-circuiting operator '&&'.
280+
// I have seen a real-world FP that looks like this, but it is much rarer
281+
// than the basic setup.
282+
buf[i] = 1; // expected-warning{{Out of bound access to memory}}
283+
}
284+
}
285+
286+
void loop_suppress_in_third_iteration_logical_and_2(int len, int flag) {
287+
int buf[2] = {0};
288+
for (int i = 0; flag && i < len; i++) {
289+
// If the two operands of '&&' are flipped, the suppression works, because
290+
// then 'flag' is the terminator statement associated with '&&' (which
291+
// determines whether the short-circuiting happens or not) and 'i < len' is
292+
// the terminator statement of the loop itself.
293+
buf[i] = 1; // no-warning
294+
}
295+
}
296+
297+
void loop_suppress_in_third_iteration_cast(int len) {
298+
int buf[2] = {0};
299+
for (int i = 0; (unsigned)(i < len); i++) {
300+
// The behavior of this suppression is slightly different under
301+
// eagerly-assume=true (the default) and eagerly-assume=false:
302+
// * When eager assumptions are disabled, it's enough to look for cases
303+
// where we get two non-null states from splitting the state over the
304+
// 'SVal' that represents the full loop condition.
305+
// * When eager assumptions are enabled, we also accept situations when the
306+
// loop condition expression triggers an eager state split and therefore
307+
// we won't see a state split at the "normal" point because it's executed
308+
// on two already separated execution paths.
309+
// However, for the sake of simplicity we don't activate the suppression in
310+
// cases when _a subexpression_ of the loop condition triggers an eager
311+
// assumption. There are already many differences between analysis with and
312+
// without eager assumptions, so it would be pointless to write more
313+
// complicated code to eliminate these rare differences.
314+
buf[i] = 1; // eagerlyassume-warning{{Out of bound access to memory}}
315+
}
316+
}
317+
318+
int coinflip(void);
319+
int do_while_report_after_one_iteration(void) {
320+
int i = 0;
321+
do {
322+
i++;
323+
} while (coinflip());
324+
// Unlike `loop_suppress_after_zero_iterations`, running just one iteration
325+
// in a do-while is not a corner case that would produce too many false
326+
// positives, so don't suppress bounds errors in these situations.
327+
return GlobalArray[i-2]; // expected-warning{{Out of bound access to memory}}
328+
}
329+
330+
void do_while_report_in_second_iteration(int len) {
331+
int buf[1] = {0};
332+
int i = 0;
333+
do {
334+
buf[i] = 1; // expected-warning{{Out of bound access to memory}}
335+
} while (i++ < len);
336+
}
337+
338+
void do_while_suppress_in_third_iteration(int len) {
339+
int buf[2] = {0};
340+
int i = 0;
341+
do {
342+
buf[i] = 1; // no-warning
343+
} while (i++ < len);
344+
}

0 commit comments

Comments
 (0)