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
99 lines
2.3 KiB
C++
99 lines
2.3 KiB
C++
// UNSUPPORTED: z3
|
|
// RUN: %clang_analyze_cc1 -w -fcxx-exceptions -analyzer-checker=core -analyzer-checker=alpha.core.PointerArithm,alpha.core.CastToStruct -analyzer-max-loop 64 -verify %s
|
|
// RUN: %clang_analyze_cc1 -w -analyzer-checker=core -analyzer-checker=cplusplus -fcxx-exceptions -analyzer-checker alpha.core.PointerArithm,alpha.core.CastToStruct -analyzer-max-loop 63 -verify %s
|
|
|
|
// These tests used to hit an assertion in the bug report. Test case from http://llvm.org/PR24184.
|
|
typedef struct {
|
|
int cbData;
|
|
unsigned pbData;
|
|
} CRYPT_DATA_BLOB;
|
|
|
|
typedef enum { DT_NONCE_FIXED } DATA_TYPE;
|
|
int a;
|
|
typedef int *vcreate_t(int *, DATA_TYPE, int, int);
|
|
void fn1(unsigned, unsigned) {
|
|
char b = 0;
|
|
for (; 1; a++, &b + a * 0)
|
|
;
|
|
}
|
|
|
|
vcreate_t fn2;
|
|
struct A {
|
|
CRYPT_DATA_BLOB value;
|
|
int m_fn1() {
|
|
int c;
|
|
value.pbData == 0;
|
|
fn1(0, 0);
|
|
}
|
|
};
|
|
struct B {
|
|
A IkeHashAlg;
|
|
A IkeGType;
|
|
A NoncePhase1_r;
|
|
};
|
|
class C {
|
|
int m_fn2(B *);
|
|
void m_fn3(B *, int, int, int);
|
|
};
|
|
int C::m_fn2(B *p1) {
|
|
int *d;
|
|
int e = p1->IkeHashAlg.m_fn1();
|
|
unsigned f = p1->IkeGType.m_fn1(), h;
|
|
int g;
|
|
d = fn2(0, DT_NONCE_FIXED, (char)0, p1->NoncePhase1_r.value.cbData);
|
|
h = 0 | 0;
|
|
m_fn3(p1, 0, 0, 0);
|
|
}
|
|
|
|
// case 2:
|
|
typedef struct {
|
|
int cbData;
|
|
unsigned char *pbData;
|
|
} CRYPT_DATA_BLOB_1;
|
|
typedef unsigned uint32_t;
|
|
void fn1_1(void *p1, const void *p2) { p1 != p2; }
|
|
|
|
void fn2_1(uint32_t *p1, unsigned char *p2, uint32_t p3) {
|
|
unsigned i = 0;
|
|
for (0; i < p3; i++)
|
|
fn1_1(p1 + i, p2 + i * 0); // expected-warning {{Pointer arithmetic on non-array variables relies on memory layout, which is dangerous}}
|
|
}
|
|
|
|
struct A_1 {
|
|
CRYPT_DATA_BLOB_1 value;
|
|
uint32_t m_fn1() {
|
|
uint32_t a;
|
|
if (value.pbData)
|
|
fn2_1(&a, value.pbData, value.cbData);
|
|
return 0;
|
|
}
|
|
};
|
|
struct {
|
|
A_1 HashAlgId;
|
|
} *b;
|
|
void fn3() {
|
|
uint32_t c, d;
|
|
d = b->HashAlgId.m_fn1();
|
|
d << 0 | 0 | 0;
|
|
c = 0;
|
|
0 | 1 << 0 | 0 && b;
|
|
}
|
|
|
|
// case 3:
|
|
struct ST {
|
|
char c;
|
|
};
|
|
char *p;
|
|
int foo1(ST);
|
|
int foo2() {
|
|
ST *p1 = (ST *)(p); // expected-warning{{Casting a non-structure type to a structure type and accessing a field can lead to memory access errors or data corruption}}
|
|
while (p1->c & 0x0F || p1->c & 0x07)
|
|
p1 = p1 + foo1(*p1);
|
|
}
|
|
|
|
int foo3(int *node) {
|
|
int i = foo2();
|
|
if (i)
|
|
return foo2();
|
|
}
|