413 lines · c
1// RUN: %clang_analyze_cc1 -Wno-array-bounds -analyzer-output=text \2// RUN: -analyzer-checker=core,security.ArrayBound,unix.Malloc,optin.taint -verify %s3 4// Miscellaneous tests for `security.ArrayBound` where we also verify the5// content of the 'note' diagnostics. This makes the tests sensitive to textual6// changes in the diagnostics, so prefer adding new tests to `brief-tests.c`7// unless they need to verify the correctness of 'note' diagnostics.8 9int TenElements[10];10 11void arrayUnderflow(void) {12 TenElements[-3] = 5;13 // expected-warning@-1 {{Out of bound access to memory preceding 'TenElements'}}14 // expected-note@-2 {{Access of 'int' element in 'TenElements' at negative index -3}}15}16 17int underflowWithDeref(void) {18 int *p = TenElements;19 --p;20 return *p;21 // expected-warning@-1 {{Out of bound access to memory preceding 'TenElements'}}22 // expected-note@-2 {{Access of 'int' element in 'TenElements' at negative index -1}}23}24 25char underflowReportedAsChar(void) {26 // Underflow is reported with the type of the accessed element (here 'char'),27 // not the type that appears in the declaration of the original array (which28 // would be 'int').29 return ((char *)TenElements)[-1];30 // expected-warning@-1 {{Out of bound access to memory preceding 'TenElements'}}31 // expected-note@-2 {{Access of 'char' element in 'TenElements' at negative index -1}}32}33 34struct TwoInts {35 int a, b;36};37 38struct TwoInts underflowReportedAsStruct(void) {39 // Another case where the accessed type is used for reporting the offset.40 return *(struct TwoInts*)(TenElements - 4);41 // expected-warning@-1 {{Out of bound access to memory preceding 'TenElements'}}42 // expected-note@-2 {{Access of 'struct TwoInts' element in 'TenElements' at negative index -2}}43}44 45struct TwoInts underflowOnlyByteOffset(void) {46 // In this case the negative byte offset is not a multiple of the size of the47 // accessed element, so the part "= -... * sizeof(type)" is omitted at the48 // end of the message.49 return *(struct TwoInts*)(TenElements - 3);50 // expected-warning@-1 {{Out of bound access to memory preceding 'TenElements'}}51 // expected-note@-2 {{Access of 'TenElements' at negative byte offset -12}}52}53 54 55int rng(void);56int getIndex(void) {57 switch (rng()) {58 case 1: return -152;59 case 2: return -160;60 case 3: return -168;61 default: return -172;62 }63}64 65void gh86959(void) {66 // Previously code like this produced many almost-identical bug reports that67 // only differed in the offset value. Verify that now we only see one report.68 69 // expected-note@+1 {{Entering loop body}}70 while (rng())71 TenElements[getIndex()] = 10;72 // expected-warning@-1 {{Out of bound access to memory preceding 'TenElements'}}73 // expected-note@-2 {{Access of 'int' element in 'TenElements' at negative index -172}}74}75 76int scanf(const char *fmt, ...);77 78void taintedIndex(void) {79 int index;80 scanf("%d", &index);81 // expected-note@-1 {{Taint originated here}}82 // expected-note@-2 {{Taint propagated to the 2nd argument}}83 TenElements[index] = 5;84 // expected-warning@-1 {{Potential out of bound access to 'TenElements' with tainted index}}85 // expected-note@-2 {{Access of 'TenElements' with a tainted index that may be negative or too large}}86}87 88void taintedIndexNonneg(void) {89 int index;90 scanf("%d", &index);91 // expected-note@-1 {{Taint originated here}}92 // expected-note@-2 {{Taint propagated to the 2nd argument}}93 94 // expected-note@+2 {{Assuming 'index' is >= 0}}95 // expected-note@+1 {{Taking false branch}}96 if (index < 0)97 return;98 99 TenElements[index] = 5;100 // expected-warning@-1 {{Potential out of bound access to 'TenElements' with tainted index}}101 // expected-note@-2 {{Access of 'TenElements' with a tainted index that may be too large}}102}103 104void taintedIndexUnsigned(void) {105 unsigned index;106 scanf("%u", &index);107 // expected-note@-1 {{Taint originated here}}108 // expected-note@-2 {{Taint propagated to the 2nd argument}}109 110 TenElements[index] = 5;111 // expected-warning@-1 {{Potential out of bound access to 'TenElements' with tainted index}}112 // expected-note@-2 {{Access of 'TenElements' with a tainted index that may be too large}}113}114 115int *taintedIndexAfterTheEndPtr(void) {116 // NOTE: Technically speaking, this testcase does not trigger any UB because117 // &TenElements[10] is the after-the-end pointer which is well-defined; but118 // this is a bug-prone situation and far from the idiomatic use of119 // `&TenElements[size]`, so it's better to report an error. This report can120 // be easily silenced by writing TenElements+index instead of121 // &TenElements[index].122 int index;123 scanf("%d", &index);124 // expected-note@-1 {{Taint originated here}}125 // expected-note@-2 {{Taint propagated to the 2nd argument}}126 if (index < 0 || index > 10)127 return TenElements;128 // expected-note@-2 {{Assuming 'index' is >= 0}}129 // expected-note@-3 {{Left side of '||' is false}}130 // expected-note@-4 {{Assuming 'index' is <= 10}}131 // expected-note@-5 {{Taking false branch}}132 return &TenElements[index];133 // expected-warning@-1 {{Potential out of bound access to 'TenElements' with tainted index}}134 // expected-note@-2 {{Access of 'TenElements' with a tainted index that may be too large}}135}136 137void taintedOffset(void) {138 int index;139 scanf("%d", &index);140 // expected-note@-1 {{Taint originated here}}141 // expected-note@-2 {{Taint propagated to the 2nd argument}}142 int *p = TenElements + index;143 p[0] = 5;144 // expected-warning@-1 {{Potential out of bound access to 'TenElements' with tainted offset}}145 // expected-note@-2 {{Access of 'TenElements' with a tainted offset that may be negative or too large}}146}147 148void arrayOverflow(void) {149 TenElements[12] = 5;150 // expected-warning@-1 {{Out of bound access to memory after the end of 'TenElements'}}151 // expected-note@-2 {{Access of 'TenElements' at index 12, while it holds only 10 'int' elements}}152}153 154void flippedOverflow(void) {155 12[TenElements] = 5;156 // expected-warning@-1 {{Out of bound access to memory after the end of 'TenElements'}}157 // expected-note@-2 {{Access of 'TenElements' at index 12, while it holds only 10 'int' elements}}158}159 160int *afterTheEndPtr(void) {161 // This is an unusual but standard-compliant way of writing (TenElements + 10).162 return &TenElements[10]; // no-warning163}164 165int useAfterTheEndPtr(void) {166 // ... but dereferencing the after-the-end pointer is still invalid.167 return *afterTheEndPtr();168 // expected-warning@-1 {{Out of bound access to memory after the end of 'TenElements'}}169 // expected-note@-2 {{Access of 'TenElements' at index 10, while it holds only 10 'int' elements}}170}171 172int *afterAfterTheEndPtr(void) {173 // This is UB, it's invalid to form an after-after-the-end pointer.174 return &TenElements[11];175 // expected-warning@-1 {{Out of bound access to memory after the end of 'TenElements'}}176 // expected-note@-2 {{Access of 'TenElements' at index 11, while it holds only 10 'int' elements}}177}178 179int *potentialAfterTheEndPtr(int idx) {180 if (idx < 10) { /* ...do something... */ }181 // expected-note@-1 {{Assuming 'idx' is >= 10}}182 // expected-note@-2 {{Taking false branch}}183 return &TenElements[idx];184 // expected-warning@-1 {{Out of bound access to memory after the end of 'TenElements'}}185 // expected-note@-2 {{Access of 'TenElements' at an overflowing index, while it holds only 10 'int' elements}}186 // NOTE: On the idx >= 10 branch the normal "optimistic" behavior would've187 // been continuing with the assumption that idx == 10 and the return value is188 // a legitimate after-the-end pointer. The checker deviates from this by189 // reporting an error because this situation is very suspicious and far from190 // the idiomatic `&TenElements[size]` expressions. If the report is FP, the191 // developer can easily silence it by writing TenElements+idx instead of192 // &TenElements[idx].193}194 195int overflowOrUnderflow(int arg) {196 // expected-note@+2 {{Assuming 'arg' is < 0}}197 // expected-note@+1 {{Taking false branch}}198 if (arg >= 0)199 return 0;200 201 return TenElements[arg - 1];202 // expected-warning@-1 {{Out of bound access to memory around 'TenElements'}}203 // expected-note@-2 {{Access of 'TenElements' at a negative or overflowing index, while it holds only 10 'int' elements}}204}205 206char TwoElements[2] = {11, 22};207char overflowOrUnderflowConcrete(int arg) {208 // expected-note@#cond {{Assuming 'arg' is < 3}}209 // expected-note@#cond {{Left side of '||' is false}}210 // expected-note@#cond {{Assuming 'arg' is not equal to 0}}211 // expected-note@#cond {{Left side of '||' is false}}212 // expected-note@#cond {{Assuming 'arg' is not equal to 1}}213 // expected-note@#cond {{Taking false branch}}214 if (arg >= 3 || arg == 0 || arg == 1) // #cond215 return 0;216 217 return TwoElements[arg];218 // expected-warning@-1 {{Out of bound access to memory around 'TwoElements'}}219 // expected-note@-2 {{Access of 'TwoElements' at a negative or overflowing index, while it holds only 2 'char' elements}}220}221 222int scalar;223int scalarOverflow(void) {224 return (&scalar)[1];225 // expected-warning@-1 {{Out of bound access to memory after the end of 'scalar'}}226 // expected-note@-2 {{Access of 'scalar' at index 1, while it holds only a single 'int' element}}227}228 229int oneElementArray[1];230int oneElementArrayOverflow(void) {231 return oneElementArray[1];232 // expected-warning@-1 {{Out of bound access to memory after the end of 'oneElementArray'}}233 // expected-note@-2 {{Access of 'oneElementArray' at index 1, while it holds only a single 'int' element}}234}235 236struct vec {237 int len;238 double elems[64];239} v;240 241double arrayInStruct(void) {242 return v.elems[64];243 // expected-warning@-1 {{Out of bound access to memory after the end of 'v.elems'}}244 // expected-note@-2 {{Access of 'v.elems' at index 64, while it holds only 64 'double' elements}}245}246 247double arrayInStructPtr(struct vec *pv) {248 return pv->elems[64];249 // expected-warning@-1 {{Out of bound access to memory after the end of the field 'elems'}}250 // expected-note@-2 {{Access of the field 'elems' at index 64, while it holds only 64 'double' elements}}251}252 253struct item {254 int a, b;255} itemArray[20] = {0};256 257int arrayOfStructs(void) {258 return itemArray[35].a;259 // expected-warning@-1 {{Out of bound access to memory after the end of 'itemArray'}}260 // expected-note@-2 {{Access of 'itemArray' at index 35, while it holds only 20 'struct item' elements}}261}262 263int arrayOfStructsArrow(void) {264 return (itemArray + 35)->b;265 // expected-warning@-1 {{Out of bound access to memory after the end of 'itemArray'}}266 // expected-note@-2 {{Access of 'itemArray' at index 35, while it holds only 20 'struct item' elements}}267}268 269char convertedScalar(long long var) {270 char *p = ((char*)&var);271 (void) p[3]; // no-warning272 return p[13];273 // expected-warning@-1 {{Out of bound access to memory after the end of 'var'}}274 // expected-note@-2 {{Access of 'var' at index 13, while it holds only 8 'char' elements}}275}276 277short convertedArray(void) {278 (void) ((short*)TenElements)[17]; // no-warning279 return ((short*)TenElements)[47];280 // expected-warning@-1 {{Out of bound access to memory after the end of 'TenElements'}}281 // expected-note@-2 {{Access of 'TenElements' at index 47, while it holds only 20 'short' elements}}282}283 284struct two_bytes {285 char lo, hi;286};287 288struct two_bytes convertedArray2(void) {289 // We report this with byte offsets because the offset is not divisible by the element size.290 struct two_bytes a = {0, 0};291 char *p = (char*)&a;292 return *((struct two_bytes*)(p + 7));293 // expected-warning@-1 {{Out of bound access to memory after the end of 'a'}}294 // expected-note@-2 {{Access of 'a' at byte offset 7, while it holds only 2 bytes}}295}296 297int intFromString(void) {298 // We report this with byte offsets because the extent is not divisible by the element size.299 return ((const int*)"this is a string of 33 characters")[20];300 // expected-warning@-1 {{Out of bound access to memory after the end of the string literal}}301 // expected-note@-2 {{Access of the string literal at byte offset 80, while it holds only 34 bytes}}302}303 304int intFromStringDivisible(void) {305 // However, this is reported with indices/elements, because the extent306 // (of the string that consists of 'a', 'b', 'c' and '\0') happens to be a307 // multiple of 4 bytes (= sizeof(int)).308 return ((const int*)"abc")[20];309 // expected-warning@-1 {{Out of bound access to memory after the end of the string literal}}310 // expected-note@-2 {{Access of the string literal at index 20, while it holds only a single 'int' element}}311}312 313typedef __typeof(sizeof(int)) size_t;314void *malloc(size_t size);315void free(void *mem);316 317int *mallocRegion(void) {318 int *mem = (int*)malloc(2*sizeof(int));319 320 mem[1] = 48; // no-warning321 322 mem[3] = -2;323 // expected-warning@-1 {{Out of bound access to memory after the end of the heap area}}324 // expected-note@-2 {{Access of the heap area at index 3, while it holds only 2 'int' elements}}325 return mem;326}327 328typedef struct { size_t len; int data[0]; } vec_t;329 330void mallocFlexibleArray(void) {331 vec_t *v = malloc(sizeof(vec_t) + 10 * sizeof(int));332 v->len = 10;333 v->data[1] = 5; // no-warning334 v->data[11] = 99;335 // TODO: Here ideally we would expect336 // {{Out of bound access to memory after the end of the heap area}}337 // {{Access of the heap area at index 11, while it holds only 10 'int' elements}}338 // but the analyzer cannot (yet) deduce the size of the flexible array member339 // from the size of the whole allocated area.340 free(v);341}342 343int *custom_calloc(size_t a, size_t b) {344 size_t res;345 346 return __builtin_mul_overflow(a, b, &res) ? 0 : malloc(res);347}348 349int *mallocMulOverflow(void) {350 int *mem = (int*)custom_calloc(10, sizeof(int));351 352 mem[20] = 10;353 // expected-warning@-1 {{Out of bound access to memory after the end of the heap area}}354 // expected-note@-2 {{Access of the heap area at index 20, while it holds only 10 'int' elements}}355 return mem;356}357 358int *mallocRegionDeref(void) {359 int *mem = (int*)malloc(2*sizeof(int));360 361 *(mem + 3) = -2;362 // expected-warning@-1 {{Out of bound access to memory after the end of the heap area}}363 // expected-note@-2 {{Access of the heap area at index 3, while it holds only 2 'int' elements}}364 return mem;365}366 367void *alloca(size_t size);368 369int allocaRegion(void) {370 int *mem = (int*)alloca(2*sizeof(int));371 mem[3] = -2;372 // expected-warning@-1 {{Out of bound access to memory after the end of the memory returned by 'alloca'}}373 // expected-note@-2 {{Access of the memory returned by 'alloca' at index 3, while it holds only 2 'int' elements}}374 return *mem;375}376 377int *symbolicExtent(int arg) {378 // expected-note@+2 {{Assuming 'arg' is < 5}}379 // expected-note@+1 {{Taking false branch}}380 if (arg >= 5)381 return 0;382 int *mem = (int*)malloc(arg);383 384 mem[8] = -2;385 // expected-warning@-1 {{Out of bound access to memory after the end of the heap area}}386 // expected-note@-2 {{Access of 'int' element in the heap area at index 8}}387 return mem;388}389 390void symbolicIndex(int arg) {391 // expected-note@+2 {{Assuming 'arg' is >= 12}}392 // expected-note@+1 {{Taking true branch}}393 if (arg >= 12)394 TenElements[arg] = -2;395 // expected-warning@-1 {{Out of bound access to memory after the end of 'TenElements'}}396 // expected-note@-2 {{Access of 'TenElements' at an overflowing index, while it holds only 10 'int' elements}}397}398 399int *nothingIsCertain(int x, int y) {400 if (x >= 2)401 return 0;402 int *mem = (int*)malloc(x);403 404 if (y >= 8)405 mem[y] = -2;406 // FIXME: this should produce407 // {{Out of bound access to memory after the end of the heap area}}408 // {{Access of 'int' element in the heap area at an overflowing index}}409 // but apparently the analyzer isn't smart enough to deduce this.410 411 return mem;412}413