Skip to content

Commit a2e0536

Browse files
committed
[analyzer] Treat more const variables and fields as known contants.
When loading from a variable or a field that is declared as constant, the analyzer will try to inspect its initializer and constant-fold it. Upon success, the analyzer would skip normal load and return the respective constant. The new behavior also applies to fields/elements of brace-initialized structures and arrays. Patch by Rafael Stahl! Differential Revision: https://reviews.llvm.org/D45774 llvm-svn: 331556
1 parent b6211d9 commit a2e0536

File tree

3 files changed

+163
-3
lines changed

3 files changed

+163
-3
lines changed

clang/lib/StaticAnalyzer/Core/RegionStore.cpp

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1606,7 +1606,7 @@ SVal RegionStoreManager::getBindingForElement(RegionBindingsConstRef B,
16061606
const MemRegion* superR = R->getSuperRegion();
16071607

16081608
// Check if the region is an element region of a string literal.
1609-
if (const StringRegion *StrR=dyn_cast<StringRegion>(superR)) {
1609+
if (const StringRegion *StrR = dyn_cast<StringRegion>(superR)) {
16101610
// FIXME: Handle loads from strings where the literal is treated as
16111611
// an integer, e.g., *((unsigned int*)"hello")
16121612
QualType T = Ctx.getAsArrayType(StrR->getValueType())->getElementType();
@@ -1629,6 +1629,27 @@ SVal RegionStoreManager::getBindingForElement(RegionBindingsConstRef B,
16291629
char c = (i >= length) ? '\0' : Str->getCodeUnit(i);
16301630
return svalBuilder.makeIntVal(c, T);
16311631
}
1632+
} else if (const VarRegion *VR = dyn_cast<VarRegion>(superR)) {
1633+
// Check if the containing array is const and has an initialized value.
1634+
const VarDecl *VD = VR->getDecl();
1635+
// Either the array or the array element has to be const.
1636+
if (VD->getType().isConstQualified() || R->getElementType().isConstQualified()) {
1637+
if (const Expr *Init = VD->getInit()) {
1638+
if (const auto *InitList = dyn_cast<InitListExpr>(Init)) {
1639+
// The array index has to be known.
1640+
if (auto CI = R->getIndex().getAs<nonloc::ConcreteInt>()) {
1641+
int64_t i = CI->getValue().getSExtValue();
1642+
// Return unknown value if index is out of bounds.
1643+
if (i < 0 || i >= InitList->getNumInits())
1644+
return UnknownVal();
1645+
1646+
if (const Expr *ElemInit = InitList->getInit(i))
1647+
if (Optional<SVal> V = svalBuilder.getConstantVal(ElemInit))
1648+
return *V;
1649+
}
1650+
}
1651+
}
1652+
}
16321653
}
16331654

16341655
// Check for loads from a code text region. For such loads, just give up.
@@ -1678,7 +1699,28 @@ SVal RegionStoreManager::getBindingForField(RegionBindingsConstRef B,
16781699
if (const Optional<SVal> &V = B.getDirectBinding(R))
16791700
return *V;
16801701

1681-
QualType Ty = R->getValueType();
1702+
// Is the field declared constant and has an in-class initializer?
1703+
const FieldDecl *FD = R->getDecl();
1704+
QualType Ty = FD->getType();
1705+
if (Ty.isConstQualified())
1706+
if (const Expr *Init = FD->getInClassInitializer())
1707+
if (Optional<SVal> V = svalBuilder.getConstantVal(Init))
1708+
return *V;
1709+
1710+
// If the containing record was initialized, try to get its constant value.
1711+
const MemRegion* superR = R->getSuperRegion();
1712+
if (const auto *VR = dyn_cast<VarRegion>(superR)) {
1713+
const VarDecl *VD = VR->getDecl();
1714+
QualType RecordVarTy = VD->getType();
1715+
// Either the record variable or the field has to be const qualified.
1716+
if (RecordVarTy.isConstQualified() || Ty.isConstQualified())
1717+
if (const Expr *Init = VD->getInit())
1718+
if (const auto *InitList = dyn_cast<InitListExpr>(Init))
1719+
if (const Expr *FieldInit = InitList->getInit(FD->getFieldIndex()))
1720+
if (Optional<SVal> V = svalBuilder.getConstantVal(FieldInit))
1721+
return *V;
1722+
}
1723+
16821724
return getBindingForFieldOrElementCommon(B, R, Ty);
16831725
}
16841726

clang/lib/StaticAnalyzer/Core/SValBuilder.cpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ SValBuilder::getRegionValueSymbolVal(const TypedValueRegion *region) {
119119

120120
if (T->isNullPtrType())
121121
return makeZeroVal(T);
122-
122+
123123
if (!SymbolManager::canSymbolicate(T))
124124
return UnknownVal();
125125

@@ -328,12 +328,19 @@ Optional<SVal> SValBuilder::getConstantVal(const Expr *E) {
328328
case Stmt::CXXNullPtrLiteralExprClass:
329329
return makeNull();
330330

331+
case Stmt::CStyleCastExprClass:
332+
case Stmt::CXXFunctionalCastExprClass:
333+
case Stmt::CXXConstCastExprClass:
334+
case Stmt::CXXReinterpretCastExprClass:
335+
case Stmt::CXXStaticCastExprClass:
331336
case Stmt::ImplicitCastExprClass: {
332337
const auto *CE = cast<CastExpr>(E);
333338
switch (CE->getCastKind()) {
334339
default:
335340
break;
336341
case CK_ArrayToPointerDecay:
342+
case CK_IntegralToPointer:
343+
case CK_NoOp:
337344
case CK_BitCast: {
338345
const Expr *SE = CE->getSubExpr();
339346
Optional<SVal> Val = getConstantVal(SE);

clang/test/Analysis/globals.cpp

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
// RUN: %clang_analyze_cc1 -analyzer-checker=core -verify %s
2+
3+
4+
static const unsigned long long scull = 0;
5+
void static_int()
6+
{
7+
*(int*)scull = 0; // expected-warning{{Dereference of null pointer}}
8+
}
9+
10+
const unsigned long long cull = 0;
11+
void const_int()
12+
{
13+
*(int*)cull = 0; // expected-warning{{Dereference of null pointer}}
14+
}
15+
16+
static int * const spc = 0;
17+
void static_ptr()
18+
{
19+
*spc = 0; // expected-warning{{Dereference of null pointer}}
20+
}
21+
22+
int * const pc = 0;
23+
void const_ptr()
24+
{
25+
*pc = 0; // expected-warning{{Dereference of null pointer}}
26+
}
27+
28+
const unsigned long long cull_nonnull = 4;
29+
void nonnull_int()
30+
{
31+
*(int*)(cull_nonnull - 4) = 0; // expected-warning{{Dereference of null pointer}}
32+
}
33+
34+
int * const pc_nonnull = (int*)sizeof(int);
35+
void nonnull_ptr()
36+
{
37+
*(pc_nonnull - 1) = 0; // expected-warning{{Dereference of null pointer}}
38+
}
39+
40+
int * const constcast = const_cast<int * const>((int*)sizeof(int));
41+
void cast1()
42+
{
43+
*(constcast - 1) = 0; // expected-warning{{Dereference of null pointer}}
44+
}
45+
46+
int * const recast = reinterpret_cast<int*>(sizeof(int));
47+
void cast2()
48+
{
49+
*(recast - 1) = 0; // expected-warning{{Dereference of null pointer}}
50+
}
51+
52+
int * const staticcast = static_cast<int * const>((int*)sizeof(int));
53+
void cast3()
54+
{
55+
*(staticcast - 1) = 0; // expected-warning{{Dereference of null pointer}}
56+
}
57+
58+
struct Foo { int a; };
59+
Foo * const dyncast = dynamic_cast<Foo * const>((Foo*)sizeof(Foo));
60+
void cast4()
61+
{
62+
// Do not handle dynamic_cast for now, because it may change the pointer value.
63+
(dyncast - 1)->a = 0; // no-warning
64+
}
65+
66+
typedef int * const intptrconst;
67+
int * const funccast = intptrconst(sizeof(int));
68+
void cast5()
69+
{
70+
*(funccast - 1) = 0; // expected-warning{{Dereference of null pointer}}
71+
}
72+
73+
struct S1
74+
{
75+
int * p;
76+
};
77+
const S1 s1 = {
78+
.p = (int*)sizeof(int)
79+
};
80+
void conststruct()
81+
{
82+
*(s1.p - 1) = 0; // expected-warning{{Dereference of null pointer}}
83+
}
84+
85+
struct S2
86+
{
87+
int * const p;
88+
};
89+
S2 s2 = {
90+
.p = (int*)sizeof(int)
91+
};
92+
void constfield()
93+
{
94+
*(s2.p - 1) = 0; // expected-warning{{Dereference of null pointer}}
95+
}
96+
97+
int * const parr[1] = { (int*)sizeof(int) };
98+
void constarr()
99+
{
100+
*(parr[0] - 1) = 0; // expected-warning{{Dereference of null pointer}}
101+
}
102+
103+
struct S3
104+
{
105+
int * p = (int*)sizeof(int);
106+
};
107+
void recordinit()
108+
{
109+
S3 s3;
110+
*(s3.p - 1) = 0; // expected-warning{{Dereference of null pointer}}
111+
}

0 commit comments

Comments
 (0)