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 getInitialStateForGlobalStaticVar(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. Index: lib/StaticAnalyzer/Core/ExprEngine.cpp =================================================================== --- lib/StaticAnalyzer/Core/ExprEngine.cpp +++ lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -103,8 +103,107 @@ // Utility methods. //===----------------------------------------------------------------------===// +/** Recursively check if variable is changed in code. */ +static bool isChanged(const Stmt *S, const VarDecl *VD, bool Write) { + if (!S) + return false; + if (const BinaryOperator *B = dyn_cast(S)) { + if (B->isAssignmentOp()) + return isChanged(B->getLHS(), VD, true) || + isChanged(B->getRHS(), VD, Write); + } else if (const UnaryOperator *U = dyn_cast(S)) { + // Operators that mean operand is written. + // AddrOf is here because the address might be used to write the operand + // later indirectly. + 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(S)) { + return Write && D->getDecl() == VD; + } + + for (const Stmt *Child : S->children()) { + if (isChanged(Child, VD, Write)) + return true; + } + return false; +} + +/** Is variable changed in function or method? */ +static bool isChanged(const Decl *D, const VarDecl *VD) { + if (isa(D)) + return isChanged(D->getBody(), VD, false); + if (const auto *Rec = dyn_cast(D)) { + for (const auto *RecChild : Rec->decls()) { + if (isChanged(RecChild, VD)) + return true; + } + } + return false; +} + +/** Get initial state for global static variable */ +ProgramStateRef ExprEngine::getInitialStateForGlobalStaticVar( + const LocationContext *LCtx, ProgramStateRef State, const VarDecl *VD) { + // 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 Constraint = + Constraint_untested.getAs(); + if (!Constraint) + return State; + return State->assume(*Constraint, true); +} + +static void getGlobalStaticVars(const Stmt *S, + llvm::SmallSet *Vars) { + if (!S) + return; + if (const DeclRefExpr *D = dyn_cast(S)) { + const VarDecl *VD = dyn_cast(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(InitLoc->getDecl())) { + llvm::SmallSet Vars; + getGlobalStaticVars(FD->getBody(), &Vars); + for (const VarDecl *VD : Vars) { + state = getInitialState(InitLoc, state, VD); + } + } + const Decl *D = InitLoc->getDecl(); // Preconditions. 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}} +}