| // RUN: %clang_analyze_cc1 -Wno-unused-value -std=c++14 -verify %s -triple x86_64-pc-linux-gnu \ | |
| // RUN: -analyzer-checker=core,debug.ExprInspection,alpha.core.PointerArithm | |
| // RUN: %clang_analyze_cc1 -Wno-unused-value -std=c++14 -verify %s -triple x86_64-pc-linux-gnu \ | |
| // RUN: -analyzer-config support-symbolic-integer-casts=true \ | |
| // RUN: -analyzer-checker=core,debug.ExprInspection,alpha.core.PointerArithm | |
| template <typename T> void clang_analyzer_dump(T); | |
| struct X { | |
| int *p; | |
| int zero; | |
| void foo () { | |
| reset(p - 1); | |
| } | |
| void reset(int *in) { | |
| while (in != p) // Loop must be entered. | |
| zero = 1; | |
| } | |
| }; | |
| int test (int *in) { | |
| X littleX; | |
| littleX.zero = 0; | |
| littleX.p = in; | |
| littleX.foo(); | |
| return 5/littleX.zero; // no-warning | |
| } | |
| class Base {}; | |
| class Derived : public Base {}; | |
| void checkPolymorphicUse() { | |
| Derived d[10]; | |
| Base *p = d; | |
| ++p; // expected-warning{{Pointer arithmetic on a pointer to base class is dangerous}} | |
| } | |
| void checkBitCasts() { | |
| long l; | |
| char *p = (char*)&l; | |
| p = p+2; | |
| } | |
| void checkBasicarithmetic(int i) { | |
| int t[10]; | |
| int *p = t; | |
| ++p; | |
| int a = 5; | |
| p = &a; | |
| ++p; // expected-warning{{Pointer arithmetic on non-array variables relies on memory layout, which is dangerous}} | |
| p = p + 2; // expected-warning{{}} | |
| p = 2 + p; // expected-warning{{}} | |
| p += 2; // expected-warning{{}} | |
| a += p[2]; // expected-warning{{}} | |
| p = i*0 + p; | |
| p = p + i*0; | |
| p += i*0; | |
| } | |
| void checkArithOnSymbolic(int*p) { | |
| ++p; | |
| p = p + 2; | |
| p = 2 + p; | |
| p += 2; | |
| (void)p[2]; | |
| } | |
| struct S { | |
| int t[10]; | |
| }; | |
| void arrayInStruct() { | |
| S s; | |
| int * p = s.t; | |
| ++p; | |
| S *sp = new S; | |
| p = sp->t; | |
| ++p; | |
| delete sp; | |
| } | |
| void checkNew() { | |
| int *p = new int; | |
| p[1] = 1; // expected-warning{{}} | |
| } | |
| void InitState(int* state) { | |
| state[1] = 1; // expected-warning{{}} | |
| } | |
| int* getArray(int size) { | |
| if (size == 0) | |
| return new int; | |
| return new int[5]; | |
| } | |
| void checkConditionalArray() { | |
| int* maybeArray = getArray(0); | |
| InitState(maybeArray); | |
| } | |
| void checkMultiDimansionalArray() { | |
| int a[5][5]; | |
| *(*(a+1)+2) = 2; | |
| } | |
| unsigned ptrSubtractionNoCrash(char *Begin, char *End) { | |
| auto N = End - Begin; | |
| if (Begin) | |
| return 0; | |
| return N; | |
| } | |
| // Bug 34309 | |
| bool ptrAsIntegerSubtractionNoCrash(__UINTPTR_TYPE__ x, char *p) { | |
| __UINTPTR_TYPE__ y = (__UINTPTR_TYPE__)p - 1; | |
| return y == x; | |
| } | |
| // Bug 34374 | |
| bool integerAsPtrSubtractionNoCrash(char *p, __UINTPTR_TYPE__ m) { | |
| auto n = p - reinterpret_cast<char*>((__UINTPTR_TYPE__)1); | |
| return n == m; | |
| } | |
| namespace Bug_55934 { | |
| struct header { | |
| unsigned a : 1; | |
| unsigned b : 1; | |
| }; | |
| struct parse_t { | |
| unsigned bits0 : 1; | |
| unsigned bits2 : 2; // <-- header | |
| unsigned bits4 : 4; | |
| }; | |
| int parse(parse_t *p) { | |
| unsigned copy = p->bits2; | |
| clang_analyzer_dump(copy); | |
| // expected-warning@-1 {{reg_$2<unsigned int Element{SymRegion{reg_$0<parse_t * p>},0 S64b,struct Bug_55934::parse_t}.bits2>}} | |
| header *bits = (header *)© | |
| clang_analyzer_dump(bits->b); | |
| // expected-warning@-1 {{derived_$4{reg_$2<unsigned int Element{SymRegion{reg_$0<parse_t * p>},0 S64b,struct Bug_55934::parse_t}.bits2>,Element{copy,0 S64b,struct Bug_55934::header}.b}}} | |
| return bits->b; // no-warning | |
| } | |
| } // namespace Bug_55934 | |
| void LValueToRValueBitCast_dumps(void *p, char (*array)[8]) { | |
| clang_analyzer_dump(p); | |
| clang_analyzer_dump(array); | |
| // expected-warning@-2 {{&SymRegion{reg_$0<void * p>}}} | |
| // expected-warning@-2 {{&SymRegion{reg_$1<char (*)[8] array>}}} | |
| clang_analyzer_dump((unsigned long)p); | |
| clang_analyzer_dump(__builtin_bit_cast(unsigned long, p)); | |
| // expected-warning@-2 {{&SymRegion{reg_$0<void * p>} [as 64 bit integer]}} | |
| // expected-warning@-2 {{&SymRegion{reg_$0<void * p>} [as 64 bit integer]}} | |
| clang_analyzer_dump((unsigned long)array); | |
| clang_analyzer_dump(__builtin_bit_cast(unsigned long, array)); | |
| // expected-warning@-2 {{&SymRegion{reg_$1<char (*)[8] array>} [as 64 bit integer]}} | |
| // expected-warning@-2 {{&SymRegion{reg_$1<char (*)[8] array>} [as 64 bit integer]}} | |
| } | |
| unsigned long ptr_arithmetic(void *p) { | |
| return __builtin_bit_cast(unsigned long, p) + 1; // no-crash | |
| } | |
| struct AllocOpaqueFlag {}; | |
| void *operator new(unsigned long, void *ptr) noexcept { return ptr; } | |
| void *operator new(unsigned long, void *ptr, AllocOpaqueFlag const &) noexcept; | |
| void *operator new[](unsigned long, void *ptr) noexcept { return ptr; } | |
| void *operator new[](unsigned long, void *ptr, | |
| AllocOpaqueFlag const &) noexcept; | |
| struct Buffer { | |
| char buf[100]; | |
| int padding; | |
| }; | |
| void checkPlacementNewArryInObject() { | |
| Buffer buffer; | |
| int *array = new (&buffer) int[10]; | |
| ++array; // no warning | |
| (void)*array; | |
| } | |
| void checkPlacementNewArrayInObjectOpaque() { | |
| Buffer buffer; | |
| int *array = new (&buffer, AllocOpaqueFlag{}) int[10]; | |
| ++array; // no warning | |
| (void)*array; | |
| } | |
| void checkPlacementNewArrayInArray() { | |
| char buffer[100]; | |
| int *array = new (buffer) int[10]; | |
| ++array; // no warning | |
| (void)*array; | |
| } | |
| void checkPlacementNewArrayInArrayOpaque() { | |
| char buffer[100]; | |
| int *array = new (buffer, AllocOpaqueFlag{}) int; | |
| ++array; // no warning | |
| (void)*array; | |
| } | |
| void checkPlacementNewObjectInObject() { | |
| Buffer buffer; | |
| int *array = new (&buffer) int; | |
| ++array; // expected-warning{{Pointer arithmetic on non-array variables relies on memory layout, which is dangerous}} | |
| (void)*array; | |
| } | |
| void checkPlacementNewObjectInObjectOpaque() { | |
| Buffer buffer; | |
| int *array = new (&buffer, AllocOpaqueFlag{}) int; | |
| ++array; // no warning (allocator is opaque) | |
| (void)*array; | |
| } | |
| void checkPlacementNewObjectInArray() { | |
| char buffer[sizeof(int)]; | |
| int *array = new (buffer) int; | |
| ++array; // no warning (FN) | |
| (void)*array; | |
| } | |
| void checkPlacementNewObjectInArrayOpaque() { | |
| char buffer[sizeof(int)]; | |
| int *array = new (buffer, AllocOpaqueFlag{}) int; | |
| ++array; // no warning (FN) | |
| (void)*array; | |
| } | |
| void checkPlacementNewSlices() { | |
| const int N = 10; | |
| char buffer[sizeof(int) * N] = {0}; | |
| int *start = new (buffer) int{0}; | |
| for (int i = 1; i < N; i++) { | |
| auto *ptr = new int(buffer[i * sizeof(int)]); | |
| *ptr = i; | |
| } | |
| ++start; // no warning | |
| (void *)start; | |
| } | |
| class BumpAlloc { | |
| char *buffer; | |
| char *offset; | |
| public: | |
| BumpAlloc(int n) : buffer(new char[n]), offset{buffer} {} | |
| ~BumpAlloc() { delete[] buffer; } | |
| void *alloc(unsigned long size) { | |
| auto *ptr = offset; | |
| offset += size; | |
| return ptr; | |
| } | |
| }; | |
| void *operator new(unsigned long size, BumpAlloc &ba) { return ba.alloc(size); } | |
| void checkPlacementSlab() { | |
| BumpAlloc bump{10}; | |
| int *ptr = new (bump) int{0}; | |
| ++ptr; // no warning | |
| (void)*ptr; | |
| BumpAlloc *why = ≎ | |
| ++why; // expected-warning {{Pointer arithmetic on non-array variables relies on memory layout, which is dangerous [alpha.core.PointerArithm]}} | |
| } |