danielmarjamaki created this revision.
I saw a false positive where the analyzer made wrong conclusions about a static
variable.
Static variables that are not written have known values (initialized values).
This is the (simplified) code that motivated me to create this patch:
static char *allv[] = {
"rpcgen", "-s", "udp", "-s", "tcp",
};
static int allc = sizeof(allv) / sizeof(allv[0]);
static void f(void) {
int i;
for (i = 1; i < allc; i++) {
const char *p = allv[i]; // <- line 28
i++;
}
}
Clang output:
array-fp3.c:28:19: warning: Access out-of-bound array element (buffer
overflow)
const char *p = allv[i];
^~~~~~~
I added testcases that shows this patch solves both false positives and false
negatives
Repository:
rL LLVM
https://reviews.llvm.org/D37897
Files:
include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
lib/StaticAnalyzer/Core/ExprEngine.cpp
test/Analysis/global-vars.c
Index: test/Analysis/global-vars.c
===================================================================
--- test/Analysis/global-vars.c
+++ test/Analysis/global-vars.c
@@ -0,0 +1,18 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=alpha,core -verify %s
+
+// Avoid false positive.
+static char *allv[] = {"rpcgen", "-s", "udp", "-s", "tcp"};
+static int allc = sizeof(allv) / sizeof(allv[0]);
+static void falsePositive1(void) {
+ int i;
+ for (i = 1; i < allc; i++) {
+ const char *p = allv[i]; // no-warning
+ i++;
+ }
+}
+
+// Detect division by zero.
+static int zero = 0;
+void truePositive1() {
+ int x = 1000 / zero; // expected-warning{{Division by zero}}
+}
Index: lib/StaticAnalyzer/Core/ExprEngine.cpp
===================================================================
--- lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -103,8 +103,106 @@
// Utility methods.
//===----------------------------------------------------------------------===//
+static bool isChanged(const Stmt *S, const VarDecl *VD, bool W) {
+ if (!S)
+ return false;
+ if (const BinaryOperator *B = dyn_cast<BinaryOperator>(S)) {
+ if (B->isAssignmentOp())
+ return isChanged(B->getLHS(), VD, true) || isChanged(B->getRHS(), VD, W);
+ } else if (const UnaryOperator *U = dyn_cast<UnaryOperator>(S)) {
+ if (U->getOpcode() == UO_AddrOf || U->getOpcode() == UO_PreInc ||
+ U->getOpcode() == UO_PreDec || U->getOpcode() == UO_PostInc ||
+ U->getOpcode() == UO_PostDec)
+ return isChanged(U->getSubExpr(), VD, true);
+ } else if (const DeclRefExpr *D = dyn_cast<DeclRefExpr>(S)) {
+ return W && D->getDecl() == VD;
+ }
+
+ for (const Stmt *Child : S->children()) {
+ if (isChanged(Child, VD, W))
+ return true;
+ }
+ return false;
+}
+
+static bool isChanged(const Decl *D, const VarDecl *VD) {
+ if (isa<FunctionDecl>(D))
+ return isChanged(D->getBody(), VD, false);
+ if (const auto *Rec = dyn_cast<TagDecl>(D)) {
+ for (const auto *RecChild : Rec->decls()) {
+ if (isChanged(RecChild, VD))
+ return true;
+ }
+ }
+ return false;
+}
+
+ProgramStateRef ExprEngine::getInitialState(const LocationContext *LCtx,
+ ProgramStateRef State,
+ const VarDecl *VD) {
+ // Is variable used in location context?
+ const FunctionDecl *FD = dyn_cast<FunctionDecl>(LCtx->getDecl());
+ if (!FD)
+ return State;
+
+ // Is variable changed anywhere in TU?
+ for (const Decl *D : AMgr.getASTContext().getTranslationUnitDecl()->decls()) {
+ if (isChanged(D, VD))
+ return State;
+ }
+
+ // What is the initialized value?
+ llvm::APSInt InitVal;
+ if (const Expr *I = VD->getInit()) {
+ if (!I->EvaluateAsInt(InitVal, getContext()))
+ return State;
+ } else {
+ InitVal = 0;
+ }
+
+ const MemRegion *R = State->getRegion(VD, LCtx);
+ if (!R)
+ return State;
+ SVal V = State->getSVal(loc::MemRegionVal(R));
+ SVal Constraint_untested =
+ evalBinOp(State, BO_EQ, V, svalBuilder.makeIntVal(InitVal),
+ svalBuilder.getConditionType());
+ Optional<DefinedOrUnknownSVal> Constraint =
+ Constraint_untested.getAs<DefinedOrUnknownSVal>();
+ if (!Constraint)
+ return State;
+ return State->assume(*Constraint, true);
+}
+
+static void getStaticVars(const Stmt *S,
+ llvm::SmallSet<const VarDecl *, 4> *Vars) {
+ if (!S)
+ return;
+ if (const DeclRefExpr *D = dyn_cast<DeclRefExpr>(S)) {
+ const VarDecl *VD = dyn_cast<VarDecl>(D->getDecl());
+ if (VD && VD->isDefinedOutsideFunctionOrMethod() &&
+ VD->getType()->isIntegerType() && VD->getStorageClass() == SC_Static &&
+ !VD->getType()->isPointerType()) {
+ Vars->insert(VD);
+ }
+ }
+ for (const Stmt *Child : S->children()) {
+ getStaticVars(Child, Vars);
+ }
+}
+
ProgramStateRef ExprEngine::getInitialState(const LocationContext *InitLoc) {
ProgramStateRef state = StateMgr.getInitialState(InitLoc);
+
+ // Get initial states for static global variables.
+ if (const auto *FD = dyn_cast<FunctionDecl>(InitLoc->getDecl())) {
+ llvm::SmallSet<const VarDecl *, 4> Vars;
+ getStaticVars(FD->getBody(), &Vars);
+ for (const VarDecl *VD : Vars) {
+ state = getInitialState(InitLoc, state, VD);
+ }
+ }
+
const Decl *D = InitLoc->getDecl();
// Preconditions.
Index: include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
===================================================================
--- include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
+++ include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
@@ -575,15 +575,17 @@
/// \brief Default implementation of call evaluation.
void defaultEvalCall(NodeBuilder &B, ExplodedNode *Pred,
const CallEvent &Call);
+
private:
+ ProgramStateRef getInitialState(const LocationContext *LCtx,
+ const ProgramStateRef State,
+ const VarDecl *VD);
+
void evalLoadCommon(ExplodedNodeSet &Dst,
- const Expr *NodeEx, /* Eventually will be a CFGStmt */
- const Expr *BoundEx,
- ExplodedNode *Pred,
- ProgramStateRef St,
- SVal location,
- const ProgramPointTag *tag,
- QualType LoadTy);
+ const Expr *NodeEx, /* Eventually will be a CFGStmt */
+ const Expr *BoundEx, ExplodedNode *Pred,
+ ProgramStateRef St, SVal location,
+ const ProgramPointTag *tag, QualType LoadTy);
// FIXME: 'tag' should be removed, and a LocationContext should be used
// instead.
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits