[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
This commit is contained in:
@@ -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();
|
||||
@@ -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.
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -119,7 +119,7 @@ SValBuilder::getRegionValueSymbolVal(const TypedValueRegion *region) {
|
||||
|
||||
if (T->isNullPtrType())
|
||||
return makeZeroVal(T);
|
||||
|
||||
|
||||
if (!SymbolManager::canSymbolicate(T))
|
||||
return UnknownVal();
|
||||
|
||||
@@ -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);
|
||||
|
||||
111
clang/test/Analysis/globals.cpp
Normal file
111
clang/test/Analysis/globals.cpp
Normal file
@@ -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}}
|
||||
}
|
||||
Reference in New Issue
Block a user