mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-16 22:36:34 +00:00

Discourse mail: https://discourse.llvm.org/t/analyzer-why-do-we-suck-at-modeling-c-dynamic-memory/65667 malloc() returns a piece of uninitialized dynamic memory. We were (almost) always able to model this behaviour. Its C++ counterpart, operator new is a lot more complex, because it allows for initialization, the most complicated of which is the usage of constructors. We gradually became better in modeling constructors, but for some reason, most likely for reasons lost in history, we never actually modeled the case when the memory returned by operator new was just simply uninitialized. This patch (attempts) to fix this tiny little error. Differential Revision: https://reviews.llvm.org/D135375
468 lines
13 KiB
C++
468 lines
13 KiB
C++
// RUN: %clang_analyze_cc1 -std=c++11 %s \
|
|
// RUN: -analyzer-checker=core \
|
|
// RUN: -analyzer-checker=cplusplus.NewDelete \
|
|
// RUN: -analyzer-checker=cplusplus.PlacementNew \
|
|
// RUN: -analyzer-output=text -verify \
|
|
// RUN: -triple x86_64-unknown-linux-gnu
|
|
|
|
#include "Inputs/system-header-simulator-cxx.h"
|
|
|
|
void f() {
|
|
short s; // expected-note {{'s' declared without an initial value}}
|
|
long *lp = ::new (&s) long; // expected-warning{{Storage provided to placement new is only 2 bytes, whereas the allocated type requires 8 bytes}} expected-note 3 {{}}
|
|
(void)lp;
|
|
}
|
|
|
|
namespace testArrayNew {
|
|
void f() {
|
|
short s; // expected-note {{'s' declared without an initial value}}
|
|
char *buf = ::new (&s) char[8]; // expected-warning{{Storage provided to placement new is only 2 bytes, whereas the allocated type requires 8 bytes}} expected-note 3 {{}}
|
|
(void)buf;
|
|
}
|
|
} // namespace testArrayNew
|
|
|
|
namespace testBufferInOtherFun {
|
|
void f(void *place) {
|
|
long *lp = ::new (place) long; // expected-warning{{Storage provided to placement new is only 2 bytes, whereas the allocated type requires 8 bytes}} expected-note 1 {{}}
|
|
(void)lp;
|
|
}
|
|
void g() {
|
|
short buf; // expected-note {{'buf' declared without an initial value}}
|
|
f(&buf); // expected-note 2 {{}}
|
|
}
|
|
} // namespace testBufferInOtherFun
|
|
|
|
namespace testArrayBuffer {
|
|
void f(void *place) {
|
|
long *lp = ::new (place) long; // expected-warning{{Storage provided to placement new is only 2 bytes, whereas the allocated type requires 8 bytes}} expected-note 1 {{}}
|
|
(void)lp;
|
|
}
|
|
void g() {
|
|
char buf[2]; // expected-note {{'buf' initialized here}}
|
|
f(&buf); // expected-note 2 {{}}
|
|
}
|
|
} // namespace testArrayBuffer
|
|
|
|
namespace testGlobalPtrAsPlace {
|
|
void *gptr = nullptr;
|
|
short gs;
|
|
void f() {
|
|
gptr = &gs; // expected-note {{Value assigned to 'gptr'}}
|
|
}
|
|
void g() {
|
|
f(); // expected-note 2 {{}}
|
|
long *lp = ::new (gptr) long; // expected-warning{{Storage provided to placement new is only 2 bytes, whereas the allocated type requires 8 bytes}} expected-note 1 {{}}
|
|
(void)lp;
|
|
}
|
|
} // namespace testGlobalPtrAsPlace
|
|
|
|
namespace testRvalue {
|
|
short gs;
|
|
void *f() {
|
|
return &gs;
|
|
}
|
|
void g() {
|
|
long *lp = ::new (f()) long; // expected-warning{{Storage provided to placement new is only 2 bytes, whereas the allocated type requires 8 bytes}} expected-note 1 {{}}
|
|
(void)lp;
|
|
}
|
|
} // namespace testRvalue
|
|
|
|
namespace testNoWarning {
|
|
void *f();
|
|
void g() {
|
|
long *lp = ::new (f()) long;
|
|
(void)lp;
|
|
}
|
|
} // namespace testNoWarning
|
|
|
|
namespace testPtrToArrayAsPlace {
|
|
void f() {
|
|
//char *st = new char [8];
|
|
char buf[3]; // expected-note {{'buf' initialized here}}
|
|
void *st = buf; // expected-note {{'st' initialized here}}
|
|
long *lp = ::new (st) long; // expected-warning{{Storage provided to placement new is only 3 bytes, whereas the allocated type requires 8 bytes}} expected-note 1 {{}}
|
|
(void)lp;
|
|
}
|
|
} // namespace testPtrToArrayAsPlace
|
|
|
|
namespace testPtrToArrayWithOffsetAsPlace {
|
|
void f() {
|
|
int buf[3]; // expected-note {{'buf' initialized here}}
|
|
long *lp = ::new (buf + 2) long; // expected-warning{{Storage provided to placement new is only 4 bytes, whereas the allocated type requires 8 bytes}} expected-note 1 {{}}
|
|
(void)lp;
|
|
}
|
|
} // namespace testPtrToArrayWithOffsetAsPlace
|
|
|
|
namespace testZeroSize {
|
|
void f() {
|
|
int buf[3]; // expected-note {{'buf' initialized here}}
|
|
long *lp = ::new (buf + 3) long; // expected-warning{{Storage provided to placement new is only 0 bytes, whereas the allocated type requires 8 bytes}} expected-note 1 {{}}
|
|
(void)lp;
|
|
}
|
|
} // namespace testZeroSize
|
|
|
|
namespace testNegativeSize {
|
|
void f() {
|
|
int buf[3]; // expected-note {{'buf' initialized here}}
|
|
long *lp = ::new (buf + 4) long; // expected-warning{{Storage provided to placement new is only -4 bytes, whereas the allocated type requires 8 bytes}} expected-note 1 {{}}
|
|
(void)lp;
|
|
}
|
|
} // namespace testNegativeSize
|
|
|
|
namespace testHeapAllocatedBuffer {
|
|
void g2() {
|
|
char *buf = new char[2]; // expected-note {{'buf' initialized here}}
|
|
// FIXME: The message is misleading -- we should
|
|
// state that a pointer to an uninitialized value
|
|
// is stored.
|
|
// expected-note@-4{{Storing uninitialized value}}
|
|
long *lp = ::new (buf) long; // expected-warning{{Storage provided to placement new is only 2 bytes, whereas the allocated type requires 8 bytes}} expected-note 1 {{}}
|
|
(void)lp;
|
|
}
|
|
} // namespace testHeapAllocatedBuffer
|
|
|
|
namespace testMultiDimensionalArray {
|
|
void f() {
|
|
char buf[2][3]; // expected-note {{'buf' initialized here}}
|
|
long *lp = ::new (buf) long; // expected-warning{{Storage provided to placement new is only 6 bytes, whereas the allocated type requires 8 bytes}} expected-note 1 {{}}
|
|
(void)lp;
|
|
}
|
|
} // namespace testMultiDimensionalArray
|
|
|
|
namespace testMultiDimensionalArray2 {
|
|
void f() {
|
|
char buf[2][3]; // expected-note {{'buf' initialized here}}
|
|
long *lp = ::new (buf + 1) long; // expected-warning{{Storage provided to placement new is only 3 bytes, whereas the allocated type requires 8 bytes}} expected-note 1 {{}}
|
|
(void)lp;
|
|
}
|
|
} // namespace testMultiDimensionalArray2
|
|
|
|
namespace testMultiDimensionalArray3 {
|
|
void f() {
|
|
char buf[2][3]; // expected-note {{'buf' initialized here}}
|
|
long *lp = ::new (&buf[1][1]) long; // expected-warning{{Storage provided to placement new is only 2 bytes, whereas the allocated type requires 8 bytes}} expected-note 1 {{}}
|
|
(void)lp;
|
|
}
|
|
} // namespace testMultiDimensionalArray3
|
|
|
|
namespace testHierarchy {
|
|
struct Base {
|
|
char a[2];
|
|
};
|
|
struct Derived : Base {
|
|
char x[2];
|
|
int y;
|
|
};
|
|
void f() {
|
|
Base b; // expected-note {{'b' initialized here}}
|
|
Derived *dp = ::new (&b) Derived; // expected-warning{{Storage provided to placement new is only 2 bytes, whereas the allocated type requires 8 bytes}} expected-note 1 {{}}
|
|
(void)dp;
|
|
}
|
|
} // namespace testHierarchy
|
|
|
|
namespace testArrayTypesAllocation {
|
|
void f1() {
|
|
struct S {
|
|
short a;
|
|
};
|
|
|
|
// bad (not enough space).
|
|
const unsigned N = 32;
|
|
alignas(S) unsigned char buffer1[sizeof(S) * N]; // expected-note {{'buffer1' initialized here}}
|
|
::new (buffer1) S[N]; // expected-warning{{Storage provided to placement new is only 64 bytes, whereas the allocated array type requires more space for internal needs}} expected-note 1 {{}}
|
|
}
|
|
|
|
void f2() {
|
|
struct S {
|
|
short a;
|
|
};
|
|
|
|
// maybe ok but we need to warn.
|
|
const unsigned N = 32;
|
|
alignas(S) unsigned char buffer2[sizeof(S) * N + sizeof(int)]; // expected-note {{'buffer2' initialized here}}
|
|
::new (buffer2) S[N]; // expected-warning{{68 bytes is possibly not enough for array allocation which requires 64 bytes. Current overhead requires the size of 4 bytes}} expected-note 1 {{}}
|
|
}
|
|
} // namespace testArrayTypesAllocation
|
|
|
|
namespace testStructAlign {
|
|
void f1() {
|
|
struct X {
|
|
char a[9];
|
|
} Xi; // expected-note {{'Xi' initialized here}}
|
|
|
|
// bad (struct X is aligned to char).
|
|
::new (&Xi.a) long; // expected-warning{{Storage type is aligned to 1 bytes but allocated type is aligned to 8 bytes}} expected-note 1 {{}}
|
|
}
|
|
|
|
void f2() {
|
|
struct X {
|
|
char a;
|
|
char b;
|
|
long c;
|
|
} Xi;
|
|
|
|
// ok (struct X is aligned to long).
|
|
::new (&Xi.a) long;
|
|
}
|
|
|
|
void f3() {
|
|
struct X {
|
|
char a;
|
|
char b;
|
|
long c;
|
|
} Xi; // expected-note {{'Xi' initialized here}}
|
|
|
|
// bad (struct X is aligned to long but field 'b' is aligned to 1 because of its offset)
|
|
::new (&Xi.b) long; // expected-warning{{Storage type is aligned to 1 bytes but allocated type is aligned to 8 bytes}} expected-note 1 {{}}
|
|
}
|
|
|
|
void f4() {
|
|
struct X {
|
|
char a;
|
|
struct alignas(alignof(short)) Y {
|
|
char b;
|
|
char c;
|
|
} y;
|
|
long d;
|
|
} Xi; // expected-note {{'Xi' initialized here}}
|
|
|
|
// bad. 'b' is aligned to short
|
|
::new (&Xi.y.b) long; // expected-warning{{Storage type is aligned to 2 bytes but allocated type is aligned to 8 bytes}} expected-note 1 {{}}
|
|
}
|
|
|
|
void f5() {
|
|
short b[10]; // expected-note {{'b' initialized here}}
|
|
|
|
::new (&b) long; // expected-warning{{Storage type is aligned to 2 bytes but allocated type is aligned to 8 bytes}} expected-note 1 {{}}
|
|
}
|
|
|
|
void f6() {
|
|
short b[10]; // expected-note {{'b' initialized here}}
|
|
|
|
// bad (same as previous but checks ElementRegion case)
|
|
::new (&b[0]) long; // expected-warning{{Storage type is aligned to 2 bytes but allocated type is aligned to 8 bytes}} expected-note 1 {{}}
|
|
}
|
|
|
|
void f7() {
|
|
alignas(alignof(long)) short b[10];
|
|
|
|
// ok. aligned to long(ok). offset 4*2(ok)
|
|
::new (&b[4]) long;
|
|
}
|
|
|
|
void f8() {
|
|
alignas(alignof(long)) short b[10]; // expected-note {{'b' initialized here}}
|
|
|
|
// ok. aligned to long(ok). offset 3*2(ok)
|
|
::new (&b[3]) long; // expected-warning{{Storage type is aligned to 6 bytes but allocated type is aligned to 8 bytes}} expected-note 1 {{}}
|
|
}
|
|
|
|
void f9() {
|
|
struct X {
|
|
char a;
|
|
alignas(alignof(long)) char b[20];
|
|
} Xi; // expected-note {{'Xi' initialized here}}
|
|
|
|
// ok. aligned to long(ok). offset 8*1(ok)
|
|
::new (&Xi.b[8]) long;
|
|
|
|
// bad. aligned to long(ok). offset 1*1(ok)
|
|
::new (&Xi.b[1]) long; // expected-warning{{Storage type is aligned to 1 bytes but allocated type is aligned to 8 bytes}} expected-note 1 {{}}
|
|
}
|
|
|
|
void f10() {
|
|
struct X {
|
|
char a[8];
|
|
alignas(2) char b;
|
|
} Xi; // expected-note {{'Xi' initialized here}}
|
|
|
|
// bad (struct X is aligned to 2).
|
|
::new (&Xi.a) long; // expected-warning{{Storage type is aligned to 2 bytes but allocated type is aligned to 8 bytes}} expected-note 1 {{}}
|
|
}
|
|
|
|
void f11() {
|
|
struct X {
|
|
char a;
|
|
char b;
|
|
struct Y {
|
|
long c;
|
|
} d;
|
|
} Xi;
|
|
|
|
// ok (struct X is aligned to long).
|
|
::new (&Xi.a) long;
|
|
}
|
|
|
|
void f12() {
|
|
struct alignas(alignof(long)) X {
|
|
char a;
|
|
char b;
|
|
} Xi;
|
|
|
|
// ok (struct X is aligned to long).
|
|
::new (&Xi.a) long;
|
|
}
|
|
|
|
void test13() {
|
|
struct Y {
|
|
char a[10];
|
|
};
|
|
|
|
struct X {
|
|
Y b[10];
|
|
} Xi; // expected-note {{'Xi' initialized here}}
|
|
|
|
// bad. X,A are aligned to 'char'
|
|
::new (&Xi.b[0].a) long; // expected-warning{{Storage type is aligned to 1 bytes but allocated type is aligned to 8 bytes}} expected-note 1 {{}}
|
|
}
|
|
|
|
void test14() {
|
|
struct Y {
|
|
char a[10];
|
|
};
|
|
|
|
struct alignas(alignof(long)) X {
|
|
Y b[10];
|
|
} Xi;
|
|
|
|
// ok. X is aligned to 'long' and field 'a' goes with zero offset
|
|
::new (&Xi.b[0].a) long;
|
|
}
|
|
|
|
void test15() {
|
|
struct alignas(alignof(long)) Y {
|
|
char a[10];
|
|
};
|
|
|
|
struct X {
|
|
Y b[10];
|
|
} Xi;
|
|
|
|
// ok. X is aligned to 'long' because it contains struct 'Y' which is aligned to 'long'
|
|
::new (&Xi.b[0].a) long;
|
|
}
|
|
|
|
void test16() {
|
|
struct alignas(alignof(long)) Y {
|
|
char p;
|
|
char a[10];
|
|
};
|
|
|
|
struct X {
|
|
Y b[10];
|
|
} Xi; // expected-note {{'Xi' initialized here}}
|
|
|
|
// bad. aligned to long(ok). offset 1(bad)
|
|
::new (&Xi.b[0].a) long; // expected-warning{{Storage type is aligned to 1 bytes but allocated type is aligned to 8 bytes}} expected-note 1 {{}}
|
|
}
|
|
|
|
void test17() {
|
|
struct alignas(alignof(long)) Y {
|
|
char p;
|
|
char a[10];
|
|
};
|
|
|
|
struct X {
|
|
Y b[10];
|
|
} Xi;
|
|
|
|
// ok. aligned to long(ok). offset 1+7*1(ok)
|
|
::new (&Xi.b[0].a[7]) long;
|
|
}
|
|
|
|
void test18() {
|
|
struct Y {
|
|
char p;
|
|
alignas(alignof(long)) char a[10];
|
|
};
|
|
|
|
struct X {
|
|
Y b[10];
|
|
} Xi; // expected-note {{'Xi' initialized here}}
|
|
|
|
// ok. aligned to long(ok). offset 8*1(ok)
|
|
::new (&Xi.b[0].a[8]) long;
|
|
|
|
// bad. aligned to long(ok). offset 1(bad)
|
|
::new (&Xi.b[0].a[1]) long; // expected-warning{{Storage type is aligned to 1 bytes but allocated type is aligned to 8 bytes}} expected-note 1 {{}}
|
|
}
|
|
|
|
void test19() {
|
|
struct Z {
|
|
char p;
|
|
char c[10];
|
|
};
|
|
|
|
struct Y {
|
|
char p;
|
|
Z b[10];
|
|
};
|
|
|
|
struct X {
|
|
Y a[10];
|
|
} Xi; // expected-note {{'Xi' initialized here}}
|
|
|
|
// bad. all structures X,Y,Z are aligned to char
|
|
::new (&Xi.a[1].b[1].c) long; // expected-warning{{Storage type is aligned to 1 bytes but allocated type is aligned to 8 bytes}} expected-note 1 {{}}
|
|
}
|
|
|
|
void test20() {
|
|
struct Z {
|
|
char p;
|
|
alignas(alignof(long)) char c[10];
|
|
};
|
|
|
|
struct Y {
|
|
char p;
|
|
Z b[10];
|
|
};
|
|
|
|
struct X {
|
|
Y a[10];
|
|
} Xi;
|
|
|
|
// ok. field 'c' is aligned to 'long'
|
|
::new (&Xi.a[1].b[1].c) long;
|
|
}
|
|
|
|
void test21() {
|
|
struct Z {
|
|
char p;
|
|
char c[10];
|
|
};
|
|
|
|
struct Y {
|
|
char p;
|
|
Z b[10];
|
|
};
|
|
|
|
struct alignas(alignof(long)) X {
|
|
Y a[10];
|
|
} Xi; // expected-note {{'Xi' initialized here}}
|
|
|
|
// ok. aligned to long(ok). offset 1+7*1(ok)
|
|
::new (&Xi.a[0].b[0].c[7]) long; // expected-warning{{Storage type is aligned to 1 bytes but allocated type is aligned to 8 bytes}} expected-note 1 {{}}
|
|
}
|
|
|
|
void test22() {
|
|
struct alignas(alignof(long)) Y {
|
|
char p;
|
|
char a[10][10];
|
|
};
|
|
|
|
struct X {
|
|
Y b[10];
|
|
} Xi; // expected-note {{'Xi' initialized here}}
|
|
|
|
// ok. aligned to long(ok). offset ok. 1(field 'a' offset) + 0*10(index '0' * first dimension size '10') + 7*1(index '7')
|
|
::new (&Xi.b[0].a[0][7]) long;
|
|
|
|
// ok. aligned to long(ok). offset ok. 1(field 'a' offset) + 1*10(index '1' * first dimension size '10') + 5*1(index '5')
|
|
::new (&Xi.b[0].a[1][5]) long;
|
|
|
|
// bad. aligned to long(ok). offset ok. 1(field 'a' offset) + 1*10(index '1' * first dimension size '10') + 6*1(index '5')
|
|
::new (&Xi.b[0].a[1][6]) long; // expected-warning{{Storage type is aligned to 1 bytes but allocated type is aligned to 8 bytes}} expected-note 1 {{}}
|
|
}
|
|
|
|
} // namespace testStructAlign
|