//==- UninitializedValues.cpp - Find Uninitialized Values -------*- C++ --*-==// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file implements Uninitialized Values analysis for source-level CFGs. // //===----------------------------------------------------------------------===// #include "clang/Analysis/Analyses/UninitializedValues.h" #include "clang/Analysis/Visitors/CFGRecStmtDeclVisitor.h" #include "clang/Analysis/LocalCheckers.h" #include "clang/Analysis/AnalysisDiagnostic.h" #include "clang/AST/ASTContext.h" #include "clang/Analysis/FlowSensitive/DataflowSolver.h" #include "llvm/Support/Compiler.h" #include "llvm/ADT/SmallPtrSet.h" using namespace clang; //===----------------------------------------------------------------------===// // Dataflow initialization logic. //===----------------------------------------------------------------------===// namespace { class VISIBILITY_HIDDEN RegisterDecls : public CFGRecStmtDeclVisitor { UninitializedValues::AnalysisDataTy& AD; public: RegisterDecls(UninitializedValues::AnalysisDataTy& ad) : AD(ad) {} void VisitVarDecl(VarDecl* VD) { AD.Register(VD); } CFG& getCFG() { return AD.getCFG(); } }; } // end anonymous namespace void UninitializedValues::InitializeValues(const CFG& cfg) { RegisterDecls R(getAnalysisData()); cfg.VisitBlockStmts(R); } //===----------------------------------------------------------------------===// // Transfer functions. //===----------------------------------------------------------------------===// namespace { class VISIBILITY_HIDDEN TransferFuncs : public CFGStmtVisitor { UninitializedValues::ValTy V; UninitializedValues::AnalysisDataTy& AD; public: TransferFuncs(UninitializedValues::AnalysisDataTy& ad) : AD(ad) {} UninitializedValues::ValTy& getVal() { return V; } CFG& getCFG() { return AD.getCFG(); } void SetTopValue(UninitializedValues::ValTy& X) { X.setDeclValues(AD); X.resetBlkExprValues(AD); } bool VisitDeclRefExpr(DeclRefExpr* DR); bool VisitBinaryOperator(BinaryOperator* B); bool VisitUnaryOperator(UnaryOperator* U); bool VisitStmt(Stmt* S); bool VisitCallExpr(CallExpr* C); bool VisitDeclStmt(DeclStmt* D); bool VisitConditionalOperator(ConditionalOperator* C); bool BlockStmt_VisitObjCForCollectionStmt(ObjCForCollectionStmt* S); bool Visit(Stmt *S); bool BlockStmt_VisitExpr(Expr* E); void VisitTerminator(CFGBlock* B) { } }; static const bool Initialized = false; static const bool Uninitialized = true; bool TransferFuncs::VisitDeclRefExpr(DeclRefExpr* DR) { if (VarDecl* VD = dyn_cast(DR->getDecl())) if (VD->isBlockVarDecl()) { if (AD.Observer) AD.Observer->ObserveDeclRefExpr(V, AD, DR, VD); // Pseudo-hack to prevent cascade of warnings. If an accessed variable // is uninitialized, then we are already going to flag a warning for // this variable, which a "source" of uninitialized values. // We can otherwise do a full "taint" of uninitialized values. The // client has both options by toggling AD.FullUninitTaint. if (AD.FullUninitTaint) return V(VD,AD); } return Initialized; } static VarDecl* FindBlockVarDecl(Expr* E) { // Blast through casts and parentheses to find any DeclRefExprs that // refer to a block VarDecl. if (DeclRefExpr* DR = dyn_cast(E->IgnoreParenCasts())) if (VarDecl* VD = dyn_cast(DR->getDecl())) if (VD->isBlockVarDecl()) return VD; return NULL; } bool TransferFuncs::VisitBinaryOperator(BinaryOperator* B) { if (VarDecl* VD = FindBlockVarDecl(B->getLHS())) if (B->isAssignmentOp()) { if (B->getOpcode() == BinaryOperator::Assign) return V(VD,AD) = Visit(B->getRHS()); else // Handle +=, -=, *=, etc. We do want '&', not '&&'. return V(VD,AD) = Visit(B->getLHS()) & Visit(B->getRHS()); } return VisitStmt(B); } bool TransferFuncs::VisitDeclStmt(DeclStmt* S) { for (DeclStmt::decl_iterator I=S->decl_begin(), E=S->decl_end(); I!=E; ++I) { VarDecl *VD = dyn_cast(*I); if (VD && VD->isBlockVarDecl()) { if (Stmt* I = VD->getInit()) V(VD,AD) = AD.FullUninitTaint ? V(cast(I),AD) : Initialized; else { // Special case for declarations of array types. For things like: // // char x[10]; // // we should treat "x" as being initialized, because the variable // "x" really refers to the memory block. Clearly x[1] is // uninitialized, but expressions like "(char *) x" really do refer to // an initialized value. This simple dataflow analysis does not reason // about the contents of arrays, although it could be potentially // extended to do so if the array were of constant size. if (VD->getType()->isArrayType()) V(VD,AD) = Initialized; else V(VD,AD) = Uninitialized; } } } return Uninitialized; // Value is never consumed. } bool TransferFuncs::VisitCallExpr(CallExpr* C) { VisitChildren(C); return Initialized; } bool TransferFuncs::VisitUnaryOperator(UnaryOperator* U) { switch (U->getOpcode()) { case UnaryOperator::AddrOf: { VarDecl* VD = FindBlockVarDecl(U->getSubExpr()); if (VD && VD->isBlockVarDecl()) return V(VD,AD) = Initialized; break; } default: break; } return Visit(U->getSubExpr()); } bool TransferFuncs::BlockStmt_VisitObjCForCollectionStmt(ObjCForCollectionStmt* S) { // This represents a use of the 'collection' bool x = Visit(S->getCollection()); if (x == Uninitialized) return Uninitialized; // This represents an initialization of the 'element' value. Stmt* Element = S->getElement(); VarDecl* VD = 0; if (DeclStmt* DS = dyn_cast(Element)) VD = cast(DS->getSingleDecl()); else { Expr* ElemExpr = cast(Element)->IgnoreParens(); // Initialize the value of the reference variable. if (DeclRefExpr* DR = dyn_cast(ElemExpr)) VD = cast(DR->getDecl()); else return Visit(ElemExpr); } V(VD,AD) = Initialized; return Initialized; } bool TransferFuncs::VisitConditionalOperator(ConditionalOperator* C) { Visit(C->getCond()); bool rhsResult = Visit(C->getRHS()); // Handle the GNU extension for missing LHS. if (Expr *lhs = C->getLHS()) return Visit(lhs) & rhsResult; // Yes: we want &, not &&. else return rhsResult; } bool TransferFuncs::VisitStmt(Stmt* S) { bool x = Initialized; // We don't stop at the first subexpression that is Uninitialized because // evaluating some subexpressions may result in propogating "Uninitialized" // or "Initialized" to variables referenced in the other subexpressions. for (Stmt::child_iterator I=S->child_begin(), E=S->child_end(); I!=E; ++I) if (*I && Visit(*I) == Uninitialized) x = Uninitialized; return x; } bool TransferFuncs::Visit(Stmt *S) { if (AD.isTracked(static_cast(S))) return V(static_cast(S),AD); else return static_cast*>(this)->Visit(S); } bool TransferFuncs::BlockStmt_VisitExpr(Expr* E) { bool x = static_cast*>(this)->Visit(E); if (AD.isTracked(E)) V(E,AD) = x; return x; } } // end anonymous namespace //===----------------------------------------------------------------------===// // Merge operator. // // In our transfer functions we take the approach that any // combination of uninitialized values, e.g. // Uninitialized + ___ = Uninitialized. // // Merges take the same approach, preferring soundness. At a confluence point, // if any predecessor has a variable marked uninitialized, the value is // uninitialized at the confluence point. //===----------------------------------------------------------------------===// namespace { typedef StmtDeclBitVector_Types::Union Merge; typedef DataflowSolver Solver; } //===----------------------------------------------------------------------===// // Uninitialized values checker. Scan an AST and flag variable uses //===----------------------------------------------------------------------===// UninitializedValues_ValueTypes::ObserverTy::~ObserverTy() {} namespace { class VISIBILITY_HIDDEN UninitializedValuesChecker : public UninitializedValues::ObserverTy { ASTContext &Ctx; Diagnostic &Diags; llvm::SmallPtrSet AlreadyWarned; public: UninitializedValuesChecker(ASTContext &ctx, Diagnostic &diags) : Ctx(ctx), Diags(diags) {} virtual void ObserveDeclRefExpr(UninitializedValues::ValTy& V, UninitializedValues::AnalysisDataTy& AD, DeclRefExpr* DR, VarDecl* VD) { assert ( AD.isTracked(VD) && "Unknown VarDecl."); if (V(VD,AD) == Uninitialized) if (AlreadyWarned.insert(VD)) Diags.Report(Ctx.getFullLoc(DR->getSourceRange().getBegin()), diag::warn_uninit_val); } }; } // end anonymous namespace namespace clang { void CheckUninitializedValues(CFG& cfg, ASTContext &Ctx, Diagnostic &Diags, bool FullUninitTaint) { // Compute the uninitialized values information. UninitializedValues U(cfg); U.getAnalysisData().FullUninitTaint = FullUninitTaint; Solver S(U); S.runOnCFG(cfg); // Scan for DeclRefExprs that use uninitialized values. UninitializedValuesChecker Observer(Ctx,Diags); U.getAnalysisData().Observer = &Observer; S.runOnAllBlocks(cfg); } } // end namespace clang