//=-- GRExprEngineInternalChecks.cpp - Builtin GRExprEngine Checks---*- C++ -*-= // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file defines the BugType classes used by GRExprEngine to report // bugs derived from builtin checks in the path-sensitive engine. // //===----------------------------------------------------------------------===// #include "clang/Analysis/PathSensitive/BugReporter.h" #include "clang/Analysis/PathSensitive/GRExprEngine.h" #include "clang/Analysis/PathDiagnostic.h" #include "clang/Basic/SourceManager.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/raw_ostream.h" using namespace clang; //===----------------------------------------------------------------------===// // Utility functions. //===----------------------------------------------------------------------===// template inline ExplodedNode* GetNode(ITERATOR I) { return *I; } template <> inline ExplodedNode* GetNode(GRExprEngine::undef_arg_iterator I) { return I->first; } //===----------------------------------------------------------------------===// // Forward declarations for bug reporter visitors. //===----------------------------------------------------------------------===// static const Stmt *GetDerefExpr(const ExplodedNode *N); static const Stmt *GetReceiverExpr(const ExplodedNode *N); static const Stmt *GetDenomExpr(const ExplodedNode *N); static const Stmt *GetCalleeExpr(const ExplodedNode *N); static const Stmt *GetRetValExpr(const ExplodedNode *N); static void registerTrackNullOrUndefValue(BugReporterContext& BRC, const Stmt *ValExpr, const ExplodedNode* N); //===----------------------------------------------------------------------===// // Bug Descriptions. //===----------------------------------------------------------------------===// namespace { class VISIBILITY_HIDDEN BuiltinBugReport : public RangedBugReport { public: BuiltinBugReport(BugType& bt, const char* desc, ExplodedNode *n) : RangedBugReport(bt, desc, n) {} BuiltinBugReport(BugType& bt, const char *shortDesc, const char *desc, ExplodedNode *n) : RangedBugReport(bt, shortDesc, desc, n) {} void registerInitialVisitors(BugReporterContext& BRC, const ExplodedNode* N); }; class VISIBILITY_HIDDEN BuiltinBug : public BugType { GRExprEngine &Eng; protected: const std::string desc; public: BuiltinBug(GRExprEngine *eng, const char* n, const char* d) : BugType(n, "Logic errors"), Eng(*eng), desc(d) {} BuiltinBug(GRExprEngine *eng, const char* n) : BugType(n, "Logic errors"), Eng(*eng), desc(n) {} virtual void FlushReportsImpl(BugReporter& BR, GRExprEngine& Eng) = 0; void FlushReports(BugReporter& BR) { FlushReportsImpl(BR, Eng); } virtual void registerInitialVisitors(BugReporterContext& BRC, const ExplodedNode* N, BuiltinBugReport *R) {} template void Emit(BugReporter& BR, ITER I, ITER E); }; template void BuiltinBug::Emit(BugReporter& BR, ITER I, ITER E) { for (; I != E; ++I) BR.EmitReport(new BuiltinBugReport(*this, desc.c_str(), GetNode(I))); } void BuiltinBugReport::registerInitialVisitors(BugReporterContext& BRC, const ExplodedNode* N) { static_cast(getBugType()).registerInitialVisitors(BRC, N, this); } class VISIBILITY_HIDDEN NullDeref : public BuiltinBug { public: NullDeref(GRExprEngine* eng) : BuiltinBug(eng,"Null dereference", "Dereference of null pointer") {} void FlushReportsImpl(BugReporter& BR, GRExprEngine& Eng) { Emit(BR, Eng.null_derefs_begin(), Eng.null_derefs_end()); } void registerInitialVisitors(BugReporterContext& BRC, const ExplodedNode* N, BuiltinBugReport *R) { registerTrackNullOrUndefValue(BRC, GetDerefExpr(N), N); } }; class VISIBILITY_HIDDEN NilReceiverStructRet : public BuiltinBug { public: NilReceiverStructRet(GRExprEngine* eng) : BuiltinBug(eng, "'nil' receiver with struct return type") {} void FlushReportsImpl(BugReporter& BR, GRExprEngine& Eng) { for (GRExprEngine::nil_receiver_struct_ret_iterator I=Eng.nil_receiver_struct_ret_begin(), E=Eng.nil_receiver_struct_ret_end(); I!=E; ++I) { std::string sbuf; llvm::raw_string_ostream os(sbuf); PostStmt P = cast((*I)->getLocation()); ObjCMessageExpr *ME = cast(P.getStmt()); os << "The receiver in the message expression is 'nil' and results in the" " returned value (of type '" << ME->getType().getAsString() << "') to be garbage or otherwise undefined."; BuiltinBugReport *R = new BuiltinBugReport(*this, os.str().c_str(), *I); R->addRange(ME->getReceiver()->getSourceRange()); BR.EmitReport(R); } } void registerInitialVisitors(BugReporterContext& BRC, const ExplodedNode* N, BuiltinBugReport *R) { registerTrackNullOrUndefValue(BRC, GetReceiverExpr(N), N); } }; class VISIBILITY_HIDDEN NilReceiverLargerThanVoidPtrRet : public BuiltinBug { public: NilReceiverLargerThanVoidPtrRet(GRExprEngine* eng) : BuiltinBug(eng, "'nil' receiver with return type larger than sizeof(void *)") {} void FlushReportsImpl(BugReporter& BR, GRExprEngine& Eng) { for (GRExprEngine::nil_receiver_larger_than_voidptr_ret_iterator I=Eng.nil_receiver_larger_than_voidptr_ret_begin(), E=Eng.nil_receiver_larger_than_voidptr_ret_end(); I!=E; ++I) { std::string sbuf; llvm::raw_string_ostream os(sbuf); PostStmt P = cast((*I)->getLocation()); ObjCMessageExpr *ME = cast(P.getStmt()); os << "The receiver in the message expression is 'nil' and results in the" " returned value (of type '" << ME->getType().getAsString() << "' and of size " << Eng.getContext().getTypeSize(ME->getType()) / 8 << " bytes) to be garbage or otherwise undefined."; BuiltinBugReport *R = new BuiltinBugReport(*this, os.str().c_str(), *I); R->addRange(ME->getReceiver()->getSourceRange()); BR.EmitReport(R); } } void registerInitialVisitors(BugReporterContext& BRC, const ExplodedNode* N, BuiltinBugReport *R) { registerTrackNullOrUndefValue(BRC, GetReceiverExpr(N), N); } }; class VISIBILITY_HIDDEN UndefinedDeref : public BuiltinBug { public: UndefinedDeref(GRExprEngine* eng) : BuiltinBug(eng,"Dereference of undefined pointer value") {} void FlushReportsImpl(BugReporter& BR, GRExprEngine& Eng) { Emit(BR, Eng.undef_derefs_begin(), Eng.undef_derefs_end()); } void registerInitialVisitors(BugReporterContext& BRC, const ExplodedNode* N, BuiltinBugReport *R) { registerTrackNullOrUndefValue(BRC, GetDerefExpr(N), N); } }; class VISIBILITY_HIDDEN DivZero : public BuiltinBug { public: DivZero(GRExprEngine* eng) : BuiltinBug(eng,"Division-by-zero", "Division by zero or undefined value.") {} void FlushReportsImpl(BugReporter& BR, GRExprEngine& Eng) { Emit(BR, Eng.explicit_bad_divides_begin(), Eng.explicit_bad_divides_end()); } void registerInitialVisitors(BugReporterContext& BRC, const ExplodedNode* N, BuiltinBugReport *R) { registerTrackNullOrUndefValue(BRC, GetDenomExpr(N), N); } }; class VISIBILITY_HIDDEN UndefResult : public BuiltinBug { public: UndefResult(GRExprEngine* eng) : BuiltinBug(eng,"Undefined result", "Result of operation is undefined.") {} void FlushReportsImpl(BugReporter& BR, GRExprEngine& Eng) { Emit(BR, Eng.undef_results_begin(), Eng.undef_results_end()); } }; class VISIBILITY_HIDDEN BadCall : public BuiltinBug { public: BadCall(GRExprEngine *eng) : BuiltinBug(eng, "Invalid function call", "Called function pointer is a null or undefined pointer value") {} void FlushReportsImpl(BugReporter& BR, GRExprEngine& Eng) { Emit(BR, Eng.bad_calls_begin(), Eng.bad_calls_end()); } void registerInitialVisitors(BugReporterContext& BRC, const ExplodedNode* N, BuiltinBugReport *R) { registerTrackNullOrUndefValue(BRC, GetCalleeExpr(N), N); } }; class VISIBILITY_HIDDEN ArgReport : public BuiltinBugReport { const Stmt *Arg; public: ArgReport(BugType& bt, const char* desc, ExplodedNode *n, const Stmt *arg) : BuiltinBugReport(bt, desc, n), Arg(arg) {} ArgReport(BugType& bt, const char *shortDesc, const char *desc, ExplodedNode *n, const Stmt *arg) : BuiltinBugReport(bt, shortDesc, desc, n), Arg(arg) {} const Stmt *getArg() const { return Arg; } }; class VISIBILITY_HIDDEN BadArg : public BuiltinBug { public: BadArg(GRExprEngine* eng) : BuiltinBug(eng,"Uninitialized argument", "Pass-by-value argument in function call is undefined.") {} BadArg(GRExprEngine* eng, const char* d) : BuiltinBug(eng,"Uninitialized argument", d) {} void FlushReportsImpl(BugReporter& BR, GRExprEngine& Eng) { for (GRExprEngine::UndefArgsTy::iterator I = Eng.undef_arg_begin(), E = Eng.undef_arg_end(); I!=E; ++I) { // Generate a report for this bug. ArgReport *report = new ArgReport(*this, desc.c_str(), I->first, I->second); report->addRange(I->second->getSourceRange()); BR.EmitReport(report); } } void registerInitialVisitors(BugReporterContext& BRC, const ExplodedNode* N, BuiltinBugReport *R) { registerTrackNullOrUndefValue(BRC, static_cast(R)->getArg(), N); } }; class VISIBILITY_HIDDEN BadMsgExprArg : public BadArg { public: BadMsgExprArg(GRExprEngine* eng) : BadArg(eng,"Pass-by-value argument in message expression is undefined"){} void FlushReportsImpl(BugReporter& BR, GRExprEngine& Eng) { for (GRExprEngine::UndefArgsTy::iterator I=Eng.msg_expr_undef_arg_begin(), E = Eng.msg_expr_undef_arg_end(); I!=E; ++I) { // Generate a report for this bug. ArgReport *report = new ArgReport(*this, desc.c_str(), I->first, I->second); report->addRange(I->second->getSourceRange()); BR.EmitReport(report); } } }; class VISIBILITY_HIDDEN BadReceiver : public BuiltinBug { public: BadReceiver(GRExprEngine* eng) : BuiltinBug(eng,"Uninitialized receiver", "Receiver in message expression is an uninitialized value") {} void FlushReportsImpl(BugReporter& BR, GRExprEngine& Eng) { for (GRExprEngine::ErrorNodes::iterator I=Eng.undef_receivers_begin(), End = Eng.undef_receivers_end(); I!=End; ++I) { // Generate a report for this bug. BuiltinBugReport *report = new BuiltinBugReport(*this, desc.c_str(), *I); ExplodedNode* N = *I; Stmt *S = cast(N->getLocation()).getStmt(); Expr* E = cast(S)->getReceiver(); assert (E && "Receiver cannot be NULL"); report->addRange(E->getSourceRange()); BR.EmitReport(report); } } void registerInitialVisitors(BugReporterContext& BRC, const ExplodedNode* N, BuiltinBugReport *R) { registerTrackNullOrUndefValue(BRC, GetReceiverExpr(N), N); } }; class VISIBILITY_HIDDEN RetStack : public BuiltinBug { public: RetStack(GRExprEngine* eng) : BuiltinBug(eng, "Return of address to stack-allocated memory") {} void FlushReportsImpl(BugReporter& BR, GRExprEngine& Eng) { for (GRExprEngine::ret_stackaddr_iterator I=Eng.ret_stackaddr_begin(), End = Eng.ret_stackaddr_end(); I!=End; ++I) { ExplodedNode* N = *I; Stmt *S = cast(N->getLocation()).getStmt(); Expr* E = cast(S)->getRetValue(); assert (E && "Return expression cannot be NULL"); // Get the value associated with E. loc::MemRegionVal V = cast(Eng.getStateManager().GetSVal(N->getState(), E)); // Generate a report for this bug. std::string buf; llvm::raw_string_ostream os(buf); SourceRange R; // Check if the region is a compound literal. if (const CompoundLiteralRegion* CR = dyn_cast(V.getRegion())) { const CompoundLiteralExpr* CL = CR->getLiteralExpr(); os << "Address of stack memory associated with a compound literal " "declared on line " << BR.getSourceManager() .getInstantiationLineNumber(CL->getLocStart()) << " returned."; R = CL->getSourceRange(); } else if (const AllocaRegion* AR = dyn_cast(V.getRegion())) { const Expr* ARE = AR->getExpr(); SourceLocation L = ARE->getLocStart(); R = ARE->getSourceRange(); os << "Address of stack memory allocated by call to alloca() on line " << BR.getSourceManager().getInstantiationLineNumber(L) << " returned."; } else { os << "Address of stack memory associated with local variable '" << V.getRegion()->getString() << "' returned."; } RangedBugReport *report = new RangedBugReport(*this, os.str().c_str(), N); report->addRange(E->getSourceRange()); if (R.isValid()) report->addRange(R); BR.EmitReport(report); } } }; class VISIBILITY_HIDDEN RetUndef : public BuiltinBug { public: RetUndef(GRExprEngine* eng) : BuiltinBug(eng, "Uninitialized return value", "Uninitialized or undefined value returned to caller.") {} void FlushReportsImpl(BugReporter& BR, GRExprEngine& Eng) { Emit(BR, Eng.ret_undef_begin(), Eng.ret_undef_end()); } void registerInitialVisitors(BugReporterContext& BRC, const ExplodedNode* N, BuiltinBugReport *R) { registerTrackNullOrUndefValue(BRC, GetRetValExpr(N), N); } }; class VISIBILITY_HIDDEN UndefBranch : public BuiltinBug { struct VISIBILITY_HIDDEN FindUndefExpr { GRStateManager& VM; const GRState* St; FindUndefExpr(GRStateManager& V, const GRState* S) : VM(V), St(S) {} Expr* FindExpr(Expr* Ex) { if (!MatchesCriteria(Ex)) return 0; for (Stmt::child_iterator I=Ex->child_begin(), E=Ex->child_end();I!=E;++I) if (Expr* ExI = dyn_cast_or_null(*I)) { Expr* E2 = FindExpr(ExI); if (E2) return E2; } return Ex; } bool MatchesCriteria(Expr* Ex) { return VM.GetSVal(St, Ex).isUndef(); } }; public: UndefBranch(GRExprEngine *eng) : BuiltinBug(eng,"Use of uninitialized value", "Branch condition evaluates to an uninitialized value.") {} void FlushReportsImpl(BugReporter& BR, GRExprEngine& Eng) { for (GRExprEngine::undef_branch_iterator I=Eng.undef_branches_begin(), E=Eng.undef_branches_end(); I!=E; ++I) { // What's going on here: we want to highlight the subexpression of the // condition that is the most likely source of the "uninitialized // branch condition." We do a recursive walk of the condition's // subexpressions and roughly look for the most nested subexpression // that binds to Undefined. We then highlight that expression's range. BlockEdge B = cast((*I)->getLocation()); Expr* Ex = cast(B.getSrc()->getTerminatorCondition()); assert (Ex && "Block must have a terminator."); // Get the predecessor node and check if is a PostStmt with the Stmt // being the terminator condition. We want to inspect the state // of that node instead because it will contain main information about // the subexpressions. assert (!(*I)->pred_empty()); // Note: any predecessor will do. They should have identical state, // since all the BlockEdge did was act as an error sink since the value // had to already be undefined. ExplodedNode *N = *(*I)->pred_begin(); ProgramPoint P = N->getLocation(); const GRState* St = (*I)->getState(); if (PostStmt* PS = dyn_cast(&P)) if (PS->getStmt() == Ex) St = N->getState(); FindUndefExpr FindIt(Eng.getStateManager(), St); Ex = FindIt.FindExpr(Ex); ArgReport *R = new ArgReport(*this, desc.c_str(), *I, Ex); R->addRange(Ex->getSourceRange()); BR.EmitReport(R); } } void registerInitialVisitors(BugReporterContext& BRC, const ExplodedNode* N, BuiltinBugReport *R) { registerTrackNullOrUndefValue(BRC, static_cast(R)->getArg(), N); } }; class VISIBILITY_HIDDEN OutOfBoundMemoryAccess : public BuiltinBug { public: OutOfBoundMemoryAccess(GRExprEngine* eng) : BuiltinBug(eng,"Out-of-bounds memory access", "Load or store into an out-of-bound memory position.") {} void FlushReportsImpl(BugReporter& BR, GRExprEngine& Eng) { Emit(BR, Eng.explicit_oob_memacc_begin(), Eng.explicit_oob_memacc_end()); } }; class VISIBILITY_HIDDEN BadSizeVLA : public BuiltinBug { public: BadSizeVLA(GRExprEngine* eng) : BuiltinBug(eng, "Bad variable-length array (VLA) size") {} void FlushReportsImpl(BugReporter& BR, GRExprEngine& Eng) { for (GRExprEngine::ErrorNodes::iterator I = Eng.ExplicitBadSizedVLA.begin(), E = Eng.ExplicitBadSizedVLA.end(); I!=E; ++I) { // Determine whether this was a 'zero-sized' VLA or a VLA with an // undefined size. GRExprEngine::NodeTy* N = *I; PostStmt PS = cast(N->getLocation()); DeclStmt *DS = cast(PS.getStmt()); VarDecl* VD = cast(*DS->decl_begin()); QualType T = Eng.getContext().getCanonicalType(VD->getType()); VariableArrayType* VT = cast(T); Expr* SizeExpr = VT->getSizeExpr(); std::string buf; llvm::raw_string_ostream os(buf); os << "The expression used to specify the number of elements in the " "variable-length array (VLA) '" << VD->getNameAsString() << "' evaluates to "; bool isUndefined = Eng.getStateManager().GetSVal(N->getState(), SizeExpr).isUndef(); if (isUndefined) os << "an undefined or garbage value."; else os << "0. VLAs with no elements have undefined behavior."; std::string shortBuf; llvm::raw_string_ostream os_short(shortBuf); os_short << "Variable-length array '" << VD->getNameAsString() << "' " << (isUndefined ? "garbage value for array size" : "has zero elements (undefined behavior)"); ArgReport *report = new ArgReport(*this, os_short.str().c_str(), os.str().c_str(), N, SizeExpr); report->addRange(SizeExpr->getSourceRange()); BR.EmitReport(report); } } void registerInitialVisitors(BugReporterContext& BRC, const ExplodedNode* N, BuiltinBugReport *R) { registerTrackNullOrUndefValue(BRC, static_cast(R)->getArg(), N); } }; //===----------------------------------------------------------------------===// // __attribute__(nonnull) checking class VISIBILITY_HIDDEN CheckAttrNonNull : public GRSimpleAPICheck { BugType *BT; BugReporter &BR; public: CheckAttrNonNull(BugReporter &br) : BT(0), BR(br) {} virtual bool Audit(ExplodedNode* N, GRStateManager& VMgr) { CallExpr* CE = cast(cast(N->getLocation()).getStmt()); const GRState* state = N->getState(); SVal X = VMgr.GetSVal(state, CE->getCallee()); const FunctionDecl* FD = X.getAsFunctionDecl(); if (!FD) return false; const NonNullAttr* Att = FD->getAttr(); if (!Att) return false; // Iterate through the arguments of CE and check them for null. unsigned idx = 0; bool hasError = false; for (CallExpr::arg_iterator I=CE->arg_begin(), E=CE->arg_end(); I!=E; ++I, ++idx) { if (!VMgr.isEqual(state, *I, 0) || !Att->isNonNull(idx)) continue; // Lazily allocate the BugType object if it hasn't already been created. // Ownership is transferred to the BugReporter object once the BugReport // is passed to 'EmitWarning'. if (!BT) BT = new BugType("Argument with 'nonnull' attribute passed null", "API"); RangedBugReport *R = new RangedBugReport(*BT, "Null pointer passed as an argument to a " "'nonnull' parameter", N); R->addRange((*I)->getSourceRange()); BR.EmitReport(R); hasError = true; } return hasError; } }; } // end anonymous namespace //===----------------------------------------------------------------------===// // Definitions for bug reporter visitors. //===----------------------------------------------------------------------===// static const Stmt *GetDerefExpr(const ExplodedNode *N) { // Pattern match for a few useful cases (do something smarter later): // a[0], p->f, *p const Stmt *S = N->getLocationAs()->getStmt(); if (const UnaryOperator *U = dyn_cast(S)) { if (U->getOpcode() == UnaryOperator::Deref) return U->getSubExpr()->IgnoreParenCasts(); } else if (const MemberExpr *ME = dyn_cast(S)) { return ME->getBase()->IgnoreParenCasts(); } else if (const ArraySubscriptExpr *AE = dyn_cast(S)) { // Retrieve the base for arrays since BasicStoreManager doesn't know how // to reason about them. return AE->getBase(); } return NULL; } static const Stmt *GetReceiverExpr(const ExplodedNode *N) { const Stmt *S = N->getLocationAs()->getStmt(); if (const ObjCMessageExpr *ME = dyn_cast(S)) return ME->getReceiver(); return NULL; } static const Stmt *GetDenomExpr(const ExplodedNode *N) { const Stmt *S = N->getLocationAs()->getStmt(); if (const BinaryOperator *BE = dyn_cast(S)) return BE->getRHS(); return NULL; } static const Stmt *GetCalleeExpr(const ExplodedNode *N) { const Stmt *S = N->getLocationAs()->getStmt(); if (const CallExpr *CE = dyn_cast(S)) return CE->getCallee(); return NULL; } static const Stmt *GetRetValExpr(const ExplodedNode *N) { const Stmt *S = N->getLocationAs()->getStmt(); if (const ReturnStmt *RS = dyn_cast(S)) return RS->getRetValue(); return NULL; } namespace { class VISIBILITY_HIDDEN FindLastStoreBRVisitor : public BugReporterVisitor { const MemRegion *R; SVal V; bool satisfied; const ExplodedNode *StoreSite; public: FindLastStoreBRVisitor(SVal v, const MemRegion *r) : R(r), V(v), satisfied(false), StoreSite(0) {} PathDiagnosticPiece* VisitNode(const ExplodedNode *N, const ExplodedNode *PrevN, BugReporterContext& BRC) { if (satisfied) return NULL; if (!StoreSite) { GRStateManager &StateMgr = BRC.getStateManager(); const ExplodedNode *Node = N, *Last = NULL; for ( ; Node ; Last = Node, Node = Node->getFirstPred()) { if (const VarRegion *VR = dyn_cast(R)) { if (const PostStmt *P = Node->getLocationAs()) if (const DeclStmt *DS = P->getStmtAs()) if (DS->getSingleDecl() == VR->getDecl()) { Last = Node; break; } } if (StateMgr.GetSVal(Node->getState(), R) != V) break; } if (!Node || !Last) { satisfied = true; return NULL; } StoreSite = Last; } if (StoreSite != N) return NULL; satisfied = true; std::string sbuf; llvm::raw_string_ostream os(sbuf); if (const PostStmt *PS = N->getLocationAs()) { if (const DeclStmt *DS = PS->getStmtAs()) { if (const VarRegion *VR = dyn_cast(R)) { os << "Variable '" << VR->getDecl()->getNameAsString() << "' "; } else return NULL; if (isa(V)) { bool b = false; ASTContext &C = BRC.getASTContext(); if (R->isBoundable(C)) { if (const TypedRegion *TR = dyn_cast(R)) { if (C.isObjCObjectPointerType(TR->getValueType(C))) { os << "initialized to nil"; b = true; } } } if (!b) os << "initialized to a null pointer value"; } else if (isa(V)) { os << "initialized to " << cast(V).getValue(); } else if (V.isUndef()) { if (isa(R)) { const VarDecl *VD = cast(DS->getSingleDecl()); if (VD->getInit()) os << "initialized to a garbage value"; else os << "declared without an initial value"; } } } } if (os.str().empty()) { if (isa(V)) { bool b = false; ASTContext &C = BRC.getASTContext(); if (R->isBoundable(C)) { if (const TypedRegion *TR = dyn_cast(R)) { if (C.isObjCObjectPointerType(TR->getValueType(C))) { os << "nil object reference stored to "; b = true; } } } if (!b) os << "Null pointer value stored to "; } else if (V.isUndef()) { os << "Uninitialized value stored to "; } else return NULL; if (const VarRegion *VR = dyn_cast(R)) { os << '\'' << VR->getDecl()->getNameAsString() << '\''; } else return NULL; } // FIXME: Refactor this into BugReporterContext. Stmt *S = 0; ProgramPoint P = N->getLocation(); if (BlockEdge *BE = dyn_cast(&P)) { CFGBlock *BSrc = BE->getSrc(); S = BSrc->getTerminatorCondition(); } else if (PostStmt *PS = dyn_cast(&P)) { S = PS->getStmt(); } if (!S) return NULL; // Construct a new PathDiagnosticPiece. PathDiagnosticLocation L(S, BRC.getSourceManager()); return new PathDiagnosticEventPiece(L, os.str()); } }; static void registerFindLastStore(BugReporterContext& BRC, const MemRegion *R, SVal V) { BRC.addVisitor(new FindLastStoreBRVisitor(V, R)); } class VISIBILITY_HIDDEN TrackConstraintBRVisitor : public BugReporterVisitor { SVal Constraint; const bool Assumption; bool isSatisfied; public: TrackConstraintBRVisitor(SVal constraint, bool assumption) : Constraint(constraint), Assumption(assumption), isSatisfied(false) {} PathDiagnosticPiece* VisitNode(const ExplodedNode *N, const ExplodedNode *PrevN, BugReporterContext& BRC) { if (isSatisfied) return NULL; // Check if in the previous state it was feasible for this constraint // to *not* be true. GRStateManager &StateMgr = BRC.getStateManager(); bool isFeasible = false; if (StateMgr.Assume(PrevN->getState(), Constraint, !Assumption, isFeasible)) { assert(isFeasible); // Eventually we don't need 'isFeasible'. isSatisfied = true; // As a sanity check, make sure that the negation of the constraint // was infeasible in the current state. If it is feasible, we somehow // missed the transition point. isFeasible = false; if (StateMgr.Assume(N->getState(), Constraint, !Assumption, isFeasible)) { assert(isFeasible); return NULL; } // We found the transition point for the constraint. We now need to // pretty-print the constraint. (work-in-progress) std::string sbuf; llvm::raw_string_ostream os(sbuf); if (isa(Constraint)) { os << "Assuming pointer value is "; os << (Assumption ? "non-null" : "null"); } if (os.str().empty()) return NULL; // FIXME: Refactor this into BugReporterContext. Stmt *S = 0; ProgramPoint P = N->getLocation(); if (BlockEdge *BE = dyn_cast(&P)) { CFGBlock *BSrc = BE->getSrc(); S = BSrc->getTerminatorCondition(); } else if (PostStmt *PS = dyn_cast(&P)) { S = PS->getStmt(); } if (!S) return NULL; // Construct a new PathDiagnosticPiece. PathDiagnosticLocation L(S, BRC.getSourceManager()); return new PathDiagnosticEventPiece(L, os.str()); } return NULL; } }; } // end anonymous namespace static void registerTrackConstraint(BugReporterContext& BRC, SVal Constraint, bool Assumption) { BRC.addVisitor(new TrackConstraintBRVisitor(Constraint, Assumption)); } static void registerTrackNullOrUndefValue(BugReporterContext& BRC, const Stmt *S, const ExplodedNode* N) { if (!S) return; GRStateManager &StateMgr = BRC.getStateManager(); const GRState *state = N->getState(); if (const DeclRefExpr *DR = dyn_cast(S)) { if (const VarDecl *VD = dyn_cast(DR->getDecl())) { const VarRegion *R = StateMgr.getRegionManager().getVarRegion(VD); // What did we load? SVal V = StateMgr.GetSVal(state, S); if (isa(V) || isa(V) || V.isUndef()) { registerFindLastStore(BRC, R, V); } } } SVal V = StateMgr.GetSValAsScalarOrLoc(state, S); // Uncomment this to find cases where we aren't properly getting the // base value that was dereferenced. // assert(!V.isUnknownOrUndef()); // Is it a symbolic value? if (loc::MemRegionVal *L = dyn_cast(&V)) { const SubRegion *R = cast(L->getRegion()); while (R && !isa(R)) { R = dyn_cast(R->getSuperRegion()); } if (R) { assert(isa(R)); registerTrackConstraint(BRC, loc::MemRegionVal(R), false); } } } //===----------------------------------------------------------------------===// // Check registration. //===----------------------------------------------------------------------===// void GRExprEngine::RegisterInternalChecks() { // Register internal "built-in" BugTypes with the BugReporter. These BugTypes // are different than what probably many checks will do since they don't // create BugReports on-the-fly but instead wait until GRExprEngine finishes // analyzing a function. Generation of BugReport objects is done via a call // to 'FlushReports' from BugReporter. BR.Register(new NullDeref(this)); BR.Register(new UndefinedDeref(this)); BR.Register(new UndefBranch(this)); BR.Register(new DivZero(this)); BR.Register(new UndefResult(this)); BR.Register(new BadCall(this)); BR.Register(new RetStack(this)); BR.Register(new RetUndef(this)); BR.Register(new BadArg(this)); BR.Register(new BadMsgExprArg(this)); BR.Register(new BadReceiver(this)); BR.Register(new OutOfBoundMemoryAccess(this)); BR.Register(new BadSizeVLA(this)); BR.Register(new NilReceiverStructRet(this)); BR.Register(new NilReceiverLargerThanVoidPtrRet(this)); // The following checks do not need to have their associated BugTypes // explicitly registered with the BugReporter. If they issue any BugReports, // their associated BugType will get registered with the BugReporter // automatically. Note that the check itself is owned by the GRExprEngine // object. AddCheck(new CheckAttrNonNull(BR), Stmt::CallExprClass); }