244 lines
9.2 KiB
C
244 lines
9.2 KiB
C
// RUN: %clang_analyze_cc1 -Wno-array-bounds -analyzer-output=text \
|
|
// RUN: -analyzer-checker=core,alpha.security.ArrayBoundV2,unix.Malloc,alpha.security.taint -verify %s
|
|
|
|
int array[10];
|
|
|
|
void arrayUnderflow(void) {
|
|
array[-3] = 5;
|
|
// expected-warning@-1 {{Out of bound access to memory preceding 'array'}}
|
|
// expected-note@-2 {{Access of 'array' at negative byte offset -12}}
|
|
}
|
|
|
|
int underflowWithDeref(void) {
|
|
int *p = array;
|
|
--p;
|
|
return *p;
|
|
// expected-warning@-1 {{Out of bound access to memory preceding 'array'}}
|
|
// expected-note@-2 {{Access of 'array' at negative byte offset -4}}
|
|
}
|
|
|
|
int scanf(const char *restrict fmt, ...);
|
|
|
|
void taintedIndex(void) {
|
|
int index;
|
|
scanf("%d", &index);
|
|
// expected-note@-1 {{Taint originated here}}
|
|
// expected-note@-2 {{Taint propagated to the 2nd argument}}
|
|
array[index] = 5;
|
|
// expected-warning@-1 {{Potential out of bound access to 'array' with tainted index}}
|
|
// expected-note@-2 {{Access of 'array' with a tainted index that may be too large}}
|
|
}
|
|
|
|
int *taintedIndexAfterTheEndPtr(void) {
|
|
// NOTE: Technically speaking, this testcase does not trigger any UB because
|
|
// &array[10] is the after-the-end pointer which is well-defined; but this is
|
|
// a bug-prone situation and far from the idiomatic use of `&array[size]`, so
|
|
// it's better to report an error. This report can be easily silenced by
|
|
// writing array+index instead of &array[index].
|
|
int index;
|
|
scanf("%d", &index);
|
|
// expected-note@-1 {{Taint originated here}}
|
|
// expected-note@-2 {{Taint propagated to the 2nd argument}}
|
|
if (index < 0 || index > 10)
|
|
return array;
|
|
// expected-note@-2 {{Assuming 'index' is >= 0}}
|
|
// expected-note@-3 {{Left side of '||' is false}}
|
|
// expected-note@-4 {{Assuming 'index' is <= 10}}
|
|
// expected-note@-5 {{Taking false branch}}
|
|
return &array[index];
|
|
// expected-warning@-1 {{Potential out of bound access to 'array' with tainted index}}
|
|
// expected-note@-2 {{Access of 'array' with a tainted index that may be too large}}
|
|
}
|
|
|
|
void taintedOffset(void) {
|
|
int index;
|
|
scanf("%d", &index);
|
|
// expected-note@-1 {{Taint originated here}}
|
|
// expected-note@-2 {{Taint propagated to the 2nd argument}}
|
|
int *p = array + index;
|
|
p[0] = 5;
|
|
// expected-warning@-1 {{Potential out of bound access to 'array' with tainted offset}}
|
|
// expected-note@-2 {{Access of 'array' with a tainted offset that may be too large}}
|
|
}
|
|
|
|
void arrayOverflow(void) {
|
|
array[12] = 5;
|
|
// expected-warning@-1 {{Out of bound access to memory after the end of 'array'}}
|
|
// expected-note@-2 {{Access of 'array' at index 12, while it holds only 10 'int' elements}}
|
|
}
|
|
|
|
void flippedOverflow(void) {
|
|
12[array] = 5;
|
|
// expected-warning@-1 {{Out of bound access to memory after the end of 'array'}}
|
|
// expected-note@-2 {{Access of 'array' at index 12, while it holds only 10 'int' elements}}
|
|
}
|
|
|
|
int *afterTheEndPtr(void) {
|
|
// This is an unusual but standard-compliant way of writing (array + 10).
|
|
return &array[10]; // no-warning
|
|
}
|
|
|
|
int useAfterTheEndPtr(void) {
|
|
// ... but dereferencing the after-the-end pointer is still invalid.
|
|
return *afterTheEndPtr();
|
|
// expected-warning@-1 {{Out of bound access to memory after the end of 'array'}}
|
|
// expected-note@-2 {{Access of 'array' at index 10, while it holds only 10 'int' elements}}
|
|
}
|
|
|
|
int *afterAfterTheEndPtr(void) {
|
|
// This is UB, it's invalid to form an after-after-the-end pointer.
|
|
return &array[11];
|
|
// expected-warning@-1 {{Out of bound access to memory after the end of 'array'}}
|
|
// expected-note@-2 {{Access of 'array' at index 11, while it holds only 10 'int' elements}}
|
|
}
|
|
|
|
int *potentialAfterTheEndPtr(int idx) {
|
|
if (idx < 10) { /* ...do something... */ }
|
|
// expected-note@-1 {{Assuming 'idx' is >= 10}}
|
|
// expected-note@-2 {{Taking false branch}}
|
|
return &array[idx];
|
|
// expected-warning@-1 {{Out of bound access to memory after the end of 'array'}}
|
|
// expected-note@-2 {{Access of 'array' at an overflowing index, while it holds only 10 'int' elements}}
|
|
// NOTE: On the idx >= 10 branch the normal "optimistic" behavior would've
|
|
// been continuing with the assumption that idx == 10 and the return value is
|
|
// a legitimate after-the-end pointer. The checker deviates from this by
|
|
// reporting an error because this situation is very suspicious and far from
|
|
// the idiomatic `&array[size]` expressions. If the report is FP, the
|
|
// developer can easily silence it by writing array+idx instead of
|
|
// &array[idx].
|
|
}
|
|
|
|
int scalar;
|
|
int scalarOverflow(void) {
|
|
return (&scalar)[1];
|
|
// expected-warning@-1 {{Out of bound access to memory after the end of 'scalar'}}
|
|
// expected-note@-2 {{Access of 'scalar' at index 1, while it holds only a single 'int' element}}
|
|
}
|
|
|
|
int oneElementArray[1];
|
|
int oneElementArrayOverflow(void) {
|
|
return oneElementArray[1];
|
|
// expected-warning@-1 {{Out of bound access to memory after the end of 'oneElementArray'}}
|
|
// expected-note@-2 {{Access of 'oneElementArray' at index 1, while it holds only a single 'int' element}}
|
|
}
|
|
|
|
struct vec {
|
|
int len;
|
|
double elems[64];
|
|
} v;
|
|
|
|
double arrayInStruct(void) {
|
|
return v.elems[64];
|
|
// expected-warning@-1 {{Out of bound access to memory after the end of 'v.elems'}}
|
|
// expected-note@-2 {{Access of 'v.elems' at index 64, while it holds only 64 'double' elements}}
|
|
}
|
|
|
|
double arrayInStructPtr(struct vec *pv) {
|
|
return pv->elems[64];
|
|
// expected-warning@-1 {{Out of bound access to memory after the end of the field 'elems'}}
|
|
// expected-note@-2 {{Access of the field 'elems' at index 64, while it holds only 64 'double' elements}}
|
|
}
|
|
|
|
struct item {
|
|
int a, b;
|
|
} itemArray[20] = {0};
|
|
|
|
int arrayOfStructs(void) {
|
|
return itemArray[35].a;
|
|
// expected-warning@-1 {{Out of bound access to memory after the end of 'itemArray'}}
|
|
// expected-note@-2 {{Access of 'itemArray' at index 35, while it holds only 20 'struct item' elements}}
|
|
}
|
|
|
|
int arrayOfStructsArrow(void) {
|
|
return (itemArray + 35)->b;
|
|
// expected-warning@-1 {{Out of bound access to memory after the end of 'itemArray'}}
|
|
// expected-note@-2 {{Access of 'itemArray' at index 35, while it holds only 20 'struct item' elements}}
|
|
}
|
|
|
|
short convertedArray(void) {
|
|
return ((short*)array)[47];
|
|
// expected-warning@-1 {{Out of bound access to memory after the end of 'array'}}
|
|
// expected-note@-2 {{Access of 'array' at index 47, while it holds only 20 'short' elements}}
|
|
}
|
|
|
|
struct two_bytes {
|
|
char lo, hi;
|
|
};
|
|
|
|
struct two_bytes convertedArray2(void) {
|
|
// We report this with byte offsets because the offset is not divisible by the element size.
|
|
struct two_bytes a = {0, 0};
|
|
char *p = (char*)&a;
|
|
return *((struct two_bytes*)(p + 7));
|
|
// expected-warning@-1 {{Out of bound access to memory after the end of 'a'}}
|
|
// expected-note@-2 {{Access of 'a' at byte offset 7, while it holds only 2 bytes}}
|
|
}
|
|
|
|
int intFromString(void) {
|
|
// We report this with byte offsets because the extent is not divisible by the element size.
|
|
return ((const int*)"this is a string of 33 characters")[20];
|
|
// expected-warning@-1 {{Out of bound access to memory after the end of the string literal}}
|
|
// expected-note@-2 {{Access of the string literal at byte offset 80, while it holds only 34 bytes}}
|
|
}
|
|
|
|
int intFromStringDivisible(void) {
|
|
// However, this is reported with indices/elements, because the extent
|
|
// (of the string that consists of 'a', 'b', 'c' and '\0') happens to be a
|
|
// multiple of 4 bytes (= sizeof(int)).
|
|
return ((const int*)"abc")[20];
|
|
// expected-warning@-1 {{Out of bound access to memory after the end of the string literal}}
|
|
// expected-note@-2 {{Access of the string literal at index 20, while it holds only a single 'int' element}}
|
|
}
|
|
|
|
typedef __typeof(sizeof(int)) size_t;
|
|
void *malloc(size_t size);
|
|
|
|
int *mallocRegion(void) {
|
|
int *mem = (int*)malloc(2*sizeof(int));
|
|
mem[3] = -2;
|
|
// expected-warning@-1 {{Out of bound access to memory after the end of the heap area}}
|
|
// expected-note@-2 {{Access of the heap area at index 3, while it holds only 2 'int' elements}}
|
|
return mem;
|
|
}
|
|
|
|
void *alloca(size_t size);
|
|
|
|
int allocaRegion(void) {
|
|
int *mem = (int*)alloca(2*sizeof(int));
|
|
mem[3] = -2;
|
|
// expected-warning@-1 {{Out of bound access to memory after the end of the memory returned by 'alloca'}}
|
|
// expected-note@-2 {{Access of the memory returned by 'alloca' at index 3, while it holds only 2 'int' elements}}
|
|
return *mem;
|
|
}
|
|
|
|
int *unknownExtent(int arg) {
|
|
if (arg >= 2)
|
|
return 0;
|
|
int *mem = (int*)malloc(arg);
|
|
mem[8] = -2;
|
|
// FIXME: this should produce
|
|
// {{Out of bound access to memory after the end of the heap area}}
|
|
// {{Access of 'int' element in the heap area at index 8}}
|
|
return mem;
|
|
}
|
|
|
|
void unknownIndex(int arg) {
|
|
// expected-note@+2 {{Assuming 'arg' is >= 12}}
|
|
// expected-note@+1 {{Taking true branch}}
|
|
if (arg >= 12)
|
|
array[arg] = -2;
|
|
// expected-warning@-1 {{Out of bound access to memory after the end of 'array'}}
|
|
// expected-note@-2 {{Access of 'array' at an overflowing index, while it holds only 10 'int' elements}}
|
|
}
|
|
|
|
int *nothingIsCertain(int x, int y) {
|
|
if (x >= 2)
|
|
return 0;
|
|
int *mem = (int*)malloc(x);
|
|
if (y >= 8)
|
|
mem[y] = -2;
|
|
// FIXME: this should produce
|
|
// {{Out of bound access to memory after the end of the heap area}}
|
|
// {{Access of 'int' element in the heap area at an overflowing index}}
|
|
return mem;
|
|
}
|