Skip to content

Commit

Permalink
[analyzer] Treat more const variables and fields as known contants.
Browse files Browse the repository at this point in the history
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
  • Loading branch information
haoNoQ committed May 4, 2018
1 parent b6211d9 commit a2e0536
Show file tree
Hide file tree
Showing 3 changed files with 163 additions and 3 deletions.
46 changes: 44 additions & 2 deletions clang/lib/StaticAnalyzer/Core/RegionStore.cpp
Expand Up @@ -1606,7 +1606,7 @@ SVal RegionStoreManager::getBindingForElement(RegionBindingsConstRef B,
const MemRegion* superR = R->getSuperRegion();

// Check if the region is an element region of a string literal.
if (const StringRegion *StrR=dyn_cast<StringRegion>(superR)) {
if (const StringRegion *StrR = dyn_cast<StringRegion>(superR)) {
// FIXME: Handle loads from strings where the literal is treated as
// an integer, e.g., *((unsigned int*)"hello")
QualType T = Ctx.getAsArrayType(StrR->getValueType())->getElementType();
Expand All @@ -1629,6 +1629,27 @@ SVal RegionStoreManager::getBindingForElement(RegionBindingsConstRef B,
char c = (i >= length) ? '\0' : Str->getCodeUnit(i);
return svalBuilder.makeIntVal(c, T);
}
} else if (const VarRegion *VR = dyn_cast<VarRegion>(superR)) {
// Check if the containing array is const and has an initialized value.
const VarDecl *VD = VR->getDecl();
// Either the array or the array element has to be const.
if (VD->getType().isConstQualified() || R->getElementType().isConstQualified()) {
if (const Expr *Init = VD->getInit()) {
if (const auto *InitList = dyn_cast<InitListExpr>(Init)) {
// The array index has to be known.
if (auto CI = R->getIndex().getAs<nonloc::ConcreteInt>()) {
int64_t i = CI->getValue().getSExtValue();
// Return unknown value if index is out of bounds.
if (i < 0 || i >= InitList->getNumInits())
return UnknownVal();

if (const Expr *ElemInit = InitList->getInit(i))
if (Optional<SVal> V = svalBuilder.getConstantVal(ElemInit))
return *V;
}
}
}
}
}

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

QualType Ty = R->getValueType();
// Is the field declared constant and has an in-class initializer?
const FieldDecl *FD = R->getDecl();
QualType Ty = FD->getType();
if (Ty.isConstQualified())
if (const Expr *Init = FD->getInClassInitializer())
if (Optional<SVal> V = svalBuilder.getConstantVal(Init))
return *V;

// If the containing record was initialized, try to get its constant value.
const MemRegion* superR = R->getSuperRegion();
if (const auto *VR = dyn_cast<VarRegion>(superR)) {
const VarDecl *VD = VR->getDecl();
QualType RecordVarTy = VD->getType();
// Either the record variable or the field has to be const qualified.
if (RecordVarTy.isConstQualified() || Ty.isConstQualified())
if (const Expr *Init = VD->getInit())
if (const auto *InitList = dyn_cast<InitListExpr>(Init))
if (const Expr *FieldInit = InitList->getInit(FD->getFieldIndex()))
if (Optional<SVal> V = svalBuilder.getConstantVal(FieldInit))
return *V;
}

return getBindingForFieldOrElementCommon(B, R, Ty);
}

Expand Down
9 changes: 8 additions & 1 deletion clang/lib/StaticAnalyzer/Core/SValBuilder.cpp
Expand Up @@ -119,7 +119,7 @@ SValBuilder::getRegionValueSymbolVal(const TypedValueRegion *region) {

if (T->isNullPtrType())
return makeZeroVal(T);

if (!SymbolManager::canSymbolicate(T))
return UnknownVal();

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

case Stmt::CStyleCastExprClass:
case Stmt::CXXFunctionalCastExprClass:
case Stmt::CXXConstCastExprClass:
case Stmt::CXXReinterpretCastExprClass:
case Stmt::CXXStaticCastExprClass:
case Stmt::ImplicitCastExprClass: {
const auto *CE = cast<CastExpr>(E);
switch (CE->getCastKind()) {
default:
break;
case CK_ArrayToPointerDecay:
case CK_IntegralToPointer:
case CK_NoOp:
case CK_BitCast: {
const Expr *SE = CE->getSubExpr();
Optional<SVal> Val = getConstantVal(SE);
Expand Down
111 changes: 111 additions & 0 deletions clang/test/Analysis/globals.cpp
@@ -0,0 +1,111 @@
// RUN: %clang_analyze_cc1 -analyzer-checker=core -verify %s


static const unsigned long long scull = 0;
void static_int()
{
*(int*)scull = 0; // expected-warning{{Dereference of null pointer}}
}

const unsigned long long cull = 0;
void const_int()
{
*(int*)cull = 0; // expected-warning{{Dereference of null pointer}}
}

static int * const spc = 0;
void static_ptr()
{
*spc = 0; // expected-warning{{Dereference of null pointer}}
}

int * const pc = 0;
void const_ptr()
{
*pc = 0; // expected-warning{{Dereference of null pointer}}
}

const unsigned long long cull_nonnull = 4;
void nonnull_int()
{
*(int*)(cull_nonnull - 4) = 0; // expected-warning{{Dereference of null pointer}}
}

int * const pc_nonnull = (int*)sizeof(int);
void nonnull_ptr()
{
*(pc_nonnull - 1) = 0; // expected-warning{{Dereference of null pointer}}
}

int * const constcast = const_cast<int * const>((int*)sizeof(int));
void cast1()
{
*(constcast - 1) = 0; // expected-warning{{Dereference of null pointer}}
}

int * const recast = reinterpret_cast<int*>(sizeof(int));
void cast2()
{
*(recast - 1) = 0; // expected-warning{{Dereference of null pointer}}
}

int * const staticcast = static_cast<int * const>((int*)sizeof(int));
void cast3()
{
*(staticcast - 1) = 0; // expected-warning{{Dereference of null pointer}}
}

struct Foo { int a; };
Foo * const dyncast = dynamic_cast<Foo * const>((Foo*)sizeof(Foo));
void cast4()
{
// Do not handle dynamic_cast for now, because it may change the pointer value.
(dyncast - 1)->a = 0; // no-warning
}

typedef int * const intptrconst;
int * const funccast = intptrconst(sizeof(int));
void cast5()
{
*(funccast - 1) = 0; // expected-warning{{Dereference of null pointer}}
}

struct S1
{
int * p;
};
const S1 s1 = {
.p = (int*)sizeof(int)
};
void conststruct()
{
*(s1.p - 1) = 0; // expected-warning{{Dereference of null pointer}}
}

struct S2
{
int * const p;
};
S2 s2 = {
.p = (int*)sizeof(int)
};
void constfield()
{
*(s2.p - 1) = 0; // expected-warning{{Dereference of null pointer}}
}

int * const parr[1] = { (int*)sizeof(int) };
void constarr()
{
*(parr[0] - 1) = 0; // expected-warning{{Dereference of null pointer}}
}

struct S3
{
int * p = (int*)sizeof(int);
};
void recordinit()
{
S3 s3;
*(s3.p - 1) = 0; // expected-warning{{Dereference of null pointer}}
}

0 comments on commit a2e0536

Please sign in to comment.