xref: /llvm-project/clang/test/Analysis/out-of-bounds-diagnostics.c (revision a017ed04cc9bcc75b3c3ef35c923dbe7dc4606f8)
1 // RUN: %clang_analyze_cc1 -Wno-array-bounds -analyzer-output=text        \
2 // RUN:     -analyzer-checker=core,alpha.security.ArrayBoundV2,unix.Malloc,optin.taint -verify %s
3 
4 int TenElements[10];
5 
6 void arrayUnderflow(void) {
7   TenElements[-3] = 5;
8   // expected-warning@-1 {{Out of bound access to memory preceding 'TenElements'}}
9   // expected-note@-2 {{Access of 'TenElements' at negative byte offset -12}}
10 }
11 
12 int underflowWithDeref(void) {
13   int *p = TenElements;
14   --p;
15   return *p;
16   // expected-warning@-1 {{Out of bound access to memory preceding 'TenElements'}}
17   // expected-note@-2 {{Access of 'TenElements' at negative byte offset -4}}
18 }
19 
20 int rng(void);
21 int getIndex(void) {
22   switch (rng()) {
23     case 1: return -152;
24     case 2: return -160;
25     case 3: return -168;
26     default: return -172;
27   }
28 }
29 
30 void gh86959(void) {
31   // Previously code like this produced many almost-identical bug reports that
32   // only differed in the offset value. Verify that now we only see one report.
33 
34   // expected-note@+1 {{Entering loop body}}
35   while (rng())
36     TenElements[getIndex()] = 10;
37   // expected-warning@-1 {{Out of bound access to memory preceding 'TenElements'}}
38   // expected-note@-2 {{Access of 'TenElements' at negative byte offset -688}}
39 }
40 
41 int scanf(const char *restrict fmt, ...);
42 
43 void taintedIndex(void) {
44   int index;
45   scanf("%d", &index);
46   // expected-note@-1 {{Taint originated here}}
47   // expected-note@-2 {{Taint propagated to the 2nd argument}}
48   TenElements[index] = 5;
49   // expected-warning@-1 {{Potential out of bound access to 'TenElements' with tainted index}}
50   // expected-note@-2 {{Access of 'TenElements' with a tainted index that may be negative or too large}}
51 }
52 
53 void taintedIndexNonneg(void) {
54   int index;
55   scanf("%d", &index);
56   // expected-note@-1 {{Taint originated here}}
57   // expected-note@-2 {{Taint propagated to the 2nd argument}}
58 
59   // expected-note@+2 {{Assuming 'index' is >= 0}}
60   // expected-note@+1 {{Taking false branch}}
61   if (index < 0)
62     return;
63 
64   TenElements[index] = 5;
65   // expected-warning@-1 {{Potential out of bound access to 'TenElements' with tainted index}}
66   // expected-note@-2 {{Access of 'TenElements' with a tainted index that may be too large}}
67 }
68 
69 void taintedIndexUnsigned(void) {
70   unsigned index;
71   scanf("%u", &index);
72   // expected-note@-1 {{Taint originated here}}
73   // expected-note@-2 {{Taint propagated to the 2nd argument}}
74 
75   TenElements[index] = 5;
76   // expected-warning@-1 {{Potential out of bound access to 'TenElements' with tainted index}}
77   // expected-note@-2 {{Access of 'TenElements' with a tainted index that may be too large}}
78 }
79 
80 int *taintedIndexAfterTheEndPtr(void) {
81   // NOTE: Technically speaking, this testcase does not trigger any UB because
82   // &TenElements[10] is the after-the-end pointer which is well-defined; but
83   // this is a bug-prone situation and far from the idiomatic use of
84   // `&TenElements[size]`, so it's better to report an error. This report can
85   // be easily silenced by writing TenElements+index instead of
86   // &TenElements[index].
87   int index;
88   scanf("%d", &index);
89   // expected-note@-1 {{Taint originated here}}
90   // expected-note@-2 {{Taint propagated to the 2nd argument}}
91   if (index < 0 || index > 10)
92     return TenElements;
93   // expected-note@-2 {{Assuming 'index' is >= 0}}
94   // expected-note@-3 {{Left side of '||' is false}}
95   // expected-note@-4 {{Assuming 'index' is <= 10}}
96   // expected-note@-5 {{Taking false branch}}
97   return &TenElements[index];
98   // expected-warning@-1 {{Potential out of bound access to 'TenElements' with tainted index}}
99   // expected-note@-2 {{Access of 'TenElements' with a tainted index that may be too large}}
100 }
101 
102 void taintedOffset(void) {
103   int index;
104   scanf("%d", &index);
105   // expected-note@-1 {{Taint originated here}}
106   // expected-note@-2 {{Taint propagated to the 2nd argument}}
107   int *p = TenElements + index;
108   p[0] = 5;
109   // expected-warning@-1 {{Potential out of bound access to 'TenElements' with tainted offset}}
110   // expected-note@-2 {{Access of 'TenElements' with a tainted offset that may be negative or too large}}
111 }
112 
113 void arrayOverflow(void) {
114   TenElements[12] = 5;
115   // expected-warning@-1 {{Out of bound access to memory after the end of 'TenElements'}}
116   // expected-note@-2 {{Access of 'TenElements' at index 12, while it holds only 10 'int' elements}}
117 }
118 
119 void flippedOverflow(void) {
120   12[TenElements] = 5;
121   // expected-warning@-1 {{Out of bound access to memory after the end of 'TenElements'}}
122   // expected-note@-2 {{Access of 'TenElements' at index 12, while it holds only 10 'int' elements}}
123 }
124 
125 int *afterTheEndPtr(void) {
126   // This is an unusual but standard-compliant way of writing (TenElements + 10).
127   return &TenElements[10]; // no-warning
128 }
129 
130 int useAfterTheEndPtr(void) {
131   // ... but dereferencing the after-the-end pointer is still invalid.
132   return *afterTheEndPtr();
133   // expected-warning@-1 {{Out of bound access to memory after the end of 'TenElements'}}
134   // expected-note@-2 {{Access of 'TenElements' at index 10, while it holds only 10 'int' elements}}
135 }
136 
137 int *afterAfterTheEndPtr(void) {
138   // This is UB, it's invalid to form an after-after-the-end pointer.
139   return &TenElements[11];
140   // expected-warning@-1 {{Out of bound access to memory after the end of 'TenElements'}}
141   // expected-note@-2 {{Access of 'TenElements' at index 11, while it holds only 10 'int' elements}}
142 }
143 
144 int *potentialAfterTheEndPtr(int idx) {
145   if (idx < 10) { /* ...do something... */ }
146   // expected-note@-1 {{Assuming 'idx' is >= 10}}
147   // expected-note@-2 {{Taking false branch}}
148   return &TenElements[idx];
149   // expected-warning@-1 {{Out of bound access to memory after the end of 'TenElements'}}
150   // expected-note@-2 {{Access of 'TenElements' at an overflowing index, while it holds only 10 'int' elements}}
151   // NOTE: On the idx >= 10 branch the normal "optimistic" behavior would've
152   // been continuing with the assumption that idx == 10 and the return value is
153   // a legitimate after-the-end pointer. The checker deviates from this by
154   // reporting an error because this situation is very suspicious and far from
155   // the idiomatic `&TenElements[size]` expressions. If the report is FP, the
156   // developer can easily silence it by writing TenElements+idx instead of
157   // &TenElements[idx].
158 }
159 
160 int overflowOrUnderflow(int arg) {
161   // expected-note@+2 {{Assuming 'arg' is < 0}}
162   // expected-note@+1 {{Taking false branch}}
163   if (arg >= 0)
164     return 0;
165 
166   return TenElements[arg - 1];
167   // expected-warning@-1 {{Out of bound access to memory around 'TenElements'}}
168   // expected-note@-2 {{Access of 'TenElements' at a negative or overflowing index, while it holds only 10 'int' elements}}
169 }
170 
171 char TwoElements[2] = {11, 22};
172 char overflowOrUnderflowConcrete(int arg) {
173   // expected-note@#cond {{Assuming 'arg' is < 3}}
174   // expected-note@#cond {{Left side of '||' is false}}
175   // expected-note@#cond {{Assuming 'arg' is not equal to 0}}
176   // expected-note@#cond {{Left side of '||' is false}}
177   // expected-note@#cond {{Assuming 'arg' is not equal to 1}}
178   // expected-note@#cond {{Taking false branch}}
179   if (arg >= 3 || arg == 0 || arg == 1) // #cond
180     return 0;
181 
182   return TwoElements[arg];
183   // expected-warning@-1 {{Out of bound access to memory around 'TwoElements'}}
184   // expected-note@-2 {{Access of 'TwoElements' at a negative or overflowing index, while it holds only 2 'char' elements}}
185 }
186 
187 int scalar;
188 int scalarOverflow(void) {
189   return (&scalar)[1];
190   // expected-warning@-1 {{Out of bound access to memory after the end of 'scalar'}}
191   // expected-note@-2 {{Access of 'scalar' at index 1, while it holds only a single 'int' element}}
192 }
193 
194 int oneElementArray[1];
195 int oneElementArrayOverflow(void) {
196   return oneElementArray[1];
197   // expected-warning@-1 {{Out of bound access to memory after the end of 'oneElementArray'}}
198   // expected-note@-2 {{Access of 'oneElementArray' at index 1, while it holds only a single 'int' element}}
199 }
200 
201 struct vec {
202   int len;
203   double elems[64];
204 } v;
205 
206 double arrayInStruct(void) {
207   return v.elems[64];
208   // expected-warning@-1 {{Out of bound access to memory after the end of 'v.elems'}}
209   // expected-note@-2 {{Access of 'v.elems' at index 64, while it holds only 64 'double' elements}}
210 }
211 
212 double arrayInStructPtr(struct vec *pv) {
213   return pv->elems[64];
214   // expected-warning@-1 {{Out of bound access to memory after the end of the field 'elems'}}
215   // expected-note@-2 {{Access of the field 'elems' at index 64, while it holds only 64 'double' elements}}
216 }
217 
218 struct item {
219   int a, b;
220 } itemArray[20] = {0};
221 
222 int arrayOfStructs(void) {
223   return itemArray[35].a;
224   // expected-warning@-1 {{Out of bound access to memory after the end of 'itemArray'}}
225   // expected-note@-2 {{Access of 'itemArray' at index 35, while it holds only 20 'struct item' elements}}
226 }
227 
228 int arrayOfStructsArrow(void) {
229   return (itemArray + 35)->b;
230   // expected-warning@-1 {{Out of bound access to memory after the end of 'itemArray'}}
231   // expected-note@-2 {{Access of 'itemArray' at index 35, while it holds only 20 'struct item' elements}}
232 }
233 
234 short convertedArray(void) {
235   return ((short*)TenElements)[47];
236   // expected-warning@-1 {{Out of bound access to memory after the end of 'TenElements'}}
237   // expected-note@-2 {{Access of 'TenElements' at index 47, while it holds only 20 'short' elements}}
238 }
239 
240 struct two_bytes {
241   char lo, hi;
242 };
243 
244 struct two_bytes convertedArray2(void) {
245   // We report this with byte offsets because the offset is not divisible by the element size.
246   struct two_bytes a = {0, 0};
247   char *p = (char*)&a;
248   return *((struct two_bytes*)(p + 7));
249   // expected-warning@-1 {{Out of bound access to memory after the end of 'a'}}
250   // expected-note@-2 {{Access of 'a' at byte offset 7, while it holds only 2 bytes}}
251 }
252 
253 int intFromString(void) {
254   // We report this with byte offsets because the extent is not divisible by the element size.
255   return ((const int*)"this is a string of 33 characters")[20];
256   // expected-warning@-1 {{Out of bound access to memory after the end of the string literal}}
257   // expected-note@-2 {{Access of the string literal at byte offset 80, while it holds only 34 bytes}}
258 }
259 
260 int intFromStringDivisible(void) {
261   // However, this is reported with indices/elements, because the extent
262   // (of the string that consists of 'a', 'b', 'c' and '\0') happens to be a
263   // multiple of 4 bytes (= sizeof(int)).
264   return ((const int*)"abc")[20];
265   // expected-warning@-1 {{Out of bound access to memory after the end of the string literal}}
266   // expected-note@-2 {{Access of the string literal at index 20, while it holds only a single 'int' element}}
267 }
268 
269 typedef __typeof(sizeof(int)) size_t;
270 void *malloc(size_t size);
271 
272 int *mallocRegion(void) {
273   int *mem = (int*)malloc(2*sizeof(int));
274 
275   mem[3] = -2;
276   // expected-warning@-1 {{Out of bound access to memory after the end of the heap area}}
277   // expected-note@-2 {{Access of the heap area at index 3, while it holds only 2 'int' elements}}
278   return mem;
279 }
280 
281 int *custom_calloc(size_t a, size_t b) {
282   size_t res;
283 
284   return __builtin_mul_overflow(a, b, &res) ? 0 : malloc(res);
285 }
286 
287 int *mallocRegionOverflow(void) {
288   int *mem = (int*)custom_calloc(10, sizeof(int));
289 
290   mem[20] = 10;
291   // expected-warning@-1 {{Out of bound access to memory after the end of the heap area}}
292   // expected-note@-2 {{Access of the heap area at index 20, while it holds only 10 'int' elements}}
293   return mem;
294 }
295 
296 int *mallocRegionDeref(void) {
297   int *mem = (int*)malloc(2*sizeof(int));
298 
299   *(mem + 3) = -2;
300   // expected-warning@-1 {{Out of bound access to memory after the end of the heap area}}
301   // expected-note@-2 {{Access of the heap area at index 3, while it holds only 2 'int' elements}}
302   return mem;
303 }
304 
305 void *alloca(size_t size);
306 
307 int allocaRegion(void) {
308   int *mem = (int*)alloca(2*sizeof(int));
309   mem[3] = -2;
310   // expected-warning@-1 {{Out of bound access to memory after the end of the memory returned by 'alloca'}}
311   // expected-note@-2 {{Access of the memory returned by 'alloca' at index 3, while it holds only 2 'int' elements}}
312   return *mem;
313 }
314 
315 int *symbolicExtent(int arg) {
316   // expected-note@+2 {{Assuming 'arg' is < 5}}
317   // expected-note@+1 {{Taking false branch}}
318   if (arg >= 5)
319     return 0;
320   int *mem = (int*)malloc(arg);
321 
322   // TODO: without the following reference to 'arg', the analyzer would discard
323   // the range information about (the symbolic value of) 'arg'. This is
324   // incorrect because while the variable itself is inaccessible, it becomes
325   // the symbolic extent of 'mem', so we still want to reason about its
326   // potential values.
327   (void)arg;
328 
329   mem[8] = -2;
330   // expected-warning@-1 {{Out of bound access to memory after the end of the heap area}}
331   // expected-note@-2 {{Access of 'int' element in the heap area at index 8}}
332   return mem;
333 }
334 
335 int *symbolicExtentDiscardedRangeInfo(int arg) {
336   // This is a copy of the case 'symbolicExtent' without the '(void)arg' hack.
337   // TODO: if the analyzer can detect the out-of-bounds access within this
338   // testcase, then remove this and the `(void)arg` hack from `symbolicExtent`.
339   if (arg >= 5)
340     return 0;
341   int *mem = (int*)malloc(arg);
342   mem[8] = -2;
343   return mem;
344 }
345 
346 void symbolicIndex(int arg) {
347   // expected-note@+2 {{Assuming 'arg' is >= 12}}
348   // expected-note@+1 {{Taking true branch}}
349   if (arg >= 12)
350     TenElements[arg] = -2;
351   // expected-warning@-1 {{Out of bound access to memory after the end of 'TenElements'}}
352   // expected-note@-2 {{Access of 'TenElements' at an overflowing index, while it holds only 10 'int' elements}}
353 }
354 
355 int *nothingIsCertain(int x, int y) {
356   if (x >= 2)
357     return 0;
358   int *mem = (int*)malloc(x);
359 
360   if (y >= 8)
361     mem[y] = -2;
362   // FIXME: this should produce
363   //   {{Out of bound access to memory after the end of the heap area}}
364   //   {{Access of 'int' element in the heap area at an overflowing index}}
365   // but apparently the analyzer isn't smart enough to deduce this.
366 
367   // Keep constraints alive. (Without this, the overeager garbage collection of
368   // constraints would _also_ prevent the intended behavior in this testcase.)
369   (void)x;
370 
371   return mem;
372 }
373