This pull improves the handling of placement new in`PointerArith`, fixing one family of false positives, and one of negatives: ### False Positives ```cpp Buffer buffer; int* array = new (&buffer) int[10]; ++array; // there should be no warning ``` The code above should flag the memory region `buffer` as reinterpreted, very much as `reinterpret_cast` would do. Note that in this particular case the placement new is inlined so the engine can track that `*array` points to the same region as `buffer`. This is no-op if the placement new is opaque. ### False Negatives ```cpp Buffer buffer; int* array = new (&buffer) int; ++array; // there should be a warning ``` In this case, there is an implicit cast to `void*` when calling placement new. The memory region was marked as reinterpreted, and therefore later pointer arithmetic will not raise. I have added a condition to not consider a cast to `void*` as a reinterpretation, as an array of voids does not make much sense. There are still some limitations, of course. For starters, if a single `int` is created in place of an array of `unsigned char` of exactly the same size, it will still be considered as an array. A convoluted example to make the point that I think it makes sense *not* to raise in this situation is in the test `checkPlacementNewSlices`. CPP-6868
278 lines
6.6 KiB
C++
278 lines
6.6 KiB
C++
// 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]}}
|
|
}
|