Index: include/clang/AST/Decl.h =================================================================== --- include/clang/AST/Decl.h +++ include/clang/AST/Decl.h @@ -790,6 +790,9 @@ TLS_Dynamic ///< TLS with a dynamic initializer. }; + void setModified(); + bool isModified() const; + protected: // A pointer union of Stmt * and EvaluatedStmt *. When an EvaluatedStmt, we // have allocated the auxiliary struct of information there. @@ -812,8 +815,9 @@ unsigned SClass : 3; unsigned TSCSpec : 2; unsigned InitStyle : 2; + unsigned Modified : 1; }; - enum { NumVarDeclBits = 7 }; + enum { NumVarDeclBits = 8 }; friend class ASTDeclReader; friend class StmtIteratorBase; 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/AST/Decl.cpp =================================================================== --- lib/AST/Decl.cpp +++ lib/AST/Decl.cpp @@ -1840,10 +1840,12 @@ // Everything else is implicitly initialized to false. } -VarDecl *VarDecl::Create(ASTContext &C, DeclContext *DC, - SourceLocation StartL, SourceLocation IdL, - IdentifierInfo *Id, QualType T, TypeSourceInfo *TInfo, - StorageClass S) { +void VarDecl::setModified() { VarDeclBits.Modified = true; } +bool VarDecl::isModified() const { return VarDeclBits.Modified; } + +VarDecl *VarDecl::Create(ASTContext &C, DeclContext *DC, SourceLocation StartL, + SourceLocation IdL, IdentifierInfo *Id, QualType T, + TypeSourceInfo *TInfo, StorageClass S) { return new (C, DC) VarDecl(Var, C, DC, StartL, IdL, Id, T, TInfo, S); } Index: lib/Analysis/CallGraph.cpp =================================================================== --- lib/Analysis/CallGraph.cpp +++ lib/Analysis/CallGraph.cpp @@ -33,10 +33,12 @@ CallGraphNode *CallerNode; public: - CGBuilder(CallGraph *g, CallGraphNode *N) - : G(g), CallerNode(N) {} + CGBuilder(CallGraph *g, CallGraphNode *N) : G(g), CallerNode(N) {} - void VisitStmt(Stmt *S) { VisitChildren(S); } + void VisitStmt(Stmt *S) { + markModifiedVars(S); + VisitChildren(S); + } Decl *getDeclFromCall(CallExpr *CE) { if (FunctionDecl *CalleeDecl = CE->getDirectCallee()) @@ -63,13 +65,14 @@ if (Decl *D = getDeclFromCall(CE)) addCalledDecl(D); VisitChildren(CE); + markModifiedVars(CE); } // Adds may-call edges for the ObjC message sends. void VisitObjCMessageExpr(ObjCMessageExpr *ME) { if (ObjCInterfaceDecl *IDecl = ME->getReceiverInterface()) { Selector Sel = ME->getSelector(); - + // Find the callee definition within the same translation unit. Decl *D = nullptr; if (ME->isInstanceMessage()) @@ -88,6 +91,53 @@ if (SubStmt) this->Visit(SubStmt); } + + void modifyVar(Expr *E) { + auto *D = dyn_cast(E->IgnoreParenCasts()); + if (!D) + return; + VarDecl *VD = dyn_cast(D->getDecl()); + if (VD) + VD->setModified(); + } + + void markModifiedVars(Stmt *S) { + // Increment/Decrement, taking address of variable + if (UnaryOperator *U = dyn_cast(S)) { + const UnaryOperatorKind K = U->getOpcode(); + if (K == UO_AddrOf || K == UO_PostDec || K == UO_PostInc || + K == UO_PreDec || K == UO_PreInc) + modifyVar(U->getSubExpr()); + return; + } + + // Assignments + if (BinaryOperator *B = dyn_cast(S)) { + if (!B->isAssignmentOp()) + return; + modifyVar(B->getLHS()); + if (B->getLHS()->getType()->isReferenceType()) + modifyVar(B->getRHS()); + return; + } + + // Handle reference arguments + if (auto *CE = dyn_cast(S)) { + const Decl *D = CE->getCalleeDecl(); + if (!D) + return; + const FunctionDecl *FD = dyn_cast(D); + if (!FD) + return; + unsigned N = 0; + for (const auto Arg : FD->parameters()) { + if (Arg->getType()->isReferenceType() && + !Arg->getType().isConstQualified() && N < CE->getNumArgs()) + modifyVar(CE->getArg(N)); + N++; + } + } + } }; } // end anonymous namespace Index: lib/StaticAnalyzer/Core/ExprEngine.cpp =================================================================== --- lib/StaticAnalyzer/Core/ExprEngine.cpp +++ lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -103,8 +103,71 @@ // Utility methods. //===----------------------------------------------------------------------===// +/** Get initial state for global static variable */ +ProgramStateRef ExprEngine::getInitialStateForGlobalStaticVar( + const LocationContext *LCtx, ProgramStateRef State, const VarDecl *VD) { + // Is variable changed anywhere in TU? + if (VD->isModified()) + 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 *FuncBody, + llvm::SmallSet *Vars) { + std::stack Children; + Children.push(FuncBody); + while (!Children.empty()) { + const Stmt *Child = Children.top(); + Children.pop(); + if (!Child) + continue; + for (const Stmt *C : Child->children()) { + Children.push(C); + } + if (const DeclRefExpr *D = dyn_cast(Child)) { + const VarDecl *VD = dyn_cast(D->getDecl()); + if (VD && VD->isDefinedOutsideFunctionOrMethod() && + VD->getType()->isIntegerType() && + VD->getStorageClass() == SC_Static && + !VD->getType()->isPointerType()) { + Vars->insert(VD); + } + } + } +} + 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 = getInitialStateForGlobalStaticVar(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}} +}