aboutsummaryrefslogtreecommitdiff
path: root/lib/Analysis/GRExprEngineInternalChecks.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/Analysis/GRExprEngineInternalChecks.cpp')
-rw-r--r--lib/Analysis/GRExprEngineInternalChecks.cpp961
1 files changed, 961 insertions, 0 deletions
diff --git a/lib/Analysis/GRExprEngineInternalChecks.cpp b/lib/Analysis/GRExprEngineInternalChecks.cpp
new file mode 100644
index 000000000000..9aea12447dd5
--- /dev/null
+++ b/lib/Analysis/GRExprEngineInternalChecks.cpp
@@ -0,0 +1,961 @@
+//=-- 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 <typename ITERATOR> inline
+ExplodedNode<GRState>* GetNode(ITERATOR I) {
+ return *I;
+}
+
+template <> inline
+ExplodedNode<GRState>* GetNode(GRExprEngine::undef_arg_iterator I) {
+ return I->first;
+}
+
+//===----------------------------------------------------------------------===//
+// Forward declarations for bug reporter visitors.
+//===----------------------------------------------------------------------===//
+
+static const Stmt *GetDerefExpr(const ExplodedNode<GRState> *N);
+static const Stmt *GetReceiverExpr(const ExplodedNode<GRState> *N);
+static const Stmt *GetDenomExpr(const ExplodedNode<GRState> *N);
+static const Stmt *GetCalleeExpr(const ExplodedNode<GRState> *N);
+static const Stmt *GetRetValExpr(const ExplodedNode<GRState> *N);
+
+static void registerTrackNullOrUndefValue(BugReporterContext& BRC,
+ const Stmt *ValExpr,
+ const ExplodedNode<GRState>* N);
+
+//===----------------------------------------------------------------------===//
+// Bug Descriptions.
+//===----------------------------------------------------------------------===//
+
+namespace {
+
+class VISIBILITY_HIDDEN BuiltinBugReport : public RangedBugReport {
+public:
+ BuiltinBugReport(BugType& bt, const char* desc,
+ ExplodedNode<GRState> *n)
+ : RangedBugReport(bt, desc, n) {}
+
+ BuiltinBugReport(BugType& bt, const char *shortDesc, const char *desc,
+ ExplodedNode<GRState> *n)
+ : RangedBugReport(bt, shortDesc, desc, n) {}
+
+ void registerInitialVisitors(BugReporterContext& BRC,
+ const ExplodedNode<GRState>* 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<GRState>* N,
+ BuiltinBugReport *R) {}
+
+ template <typename ITER> void Emit(BugReporter& BR, ITER I, ITER E);
+};
+
+
+template <typename ITER>
+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<GRState>* N) {
+ static_cast<BuiltinBug&>(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<GRState>* 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<PostStmt>((*I)->getLocation());
+ ObjCMessageExpr *ME = cast<ObjCMessageExpr>(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<GRState>* 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<PostStmt>((*I)->getLocation());
+ ObjCMessageExpr *ME = cast<ObjCMessageExpr>(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<GRState>* 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<GRState>* 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<GRState>* 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<GRState>* 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<GRState> *n,
+ const Stmt *arg)
+ : BuiltinBugReport(bt, desc, n), Arg(arg) {}
+
+ ArgReport(BugType& bt, const char *shortDesc, const char *desc,
+ ExplodedNode<GRState> *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<GRState>* N,
+ BuiltinBugReport *R) {
+ registerTrackNullOrUndefValue(BRC, static_cast<ArgReport*>(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<GRState>* N = *I;
+ Stmt *S = cast<PostStmt>(N->getLocation()).getStmt();
+ Expr* E = cast<ObjCMessageExpr>(S)->getReceiver();
+ assert (E && "Receiver cannot be NULL");
+ report->addRange(E->getSourceRange());
+ BR.EmitReport(report);
+ }
+ }
+
+ void registerInitialVisitors(BugReporterContext& BRC,
+ const ExplodedNode<GRState>* 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<GRState>* N = *I;
+ Stmt *S = cast<PostStmt>(N->getLocation()).getStmt();
+ Expr* E = cast<ReturnStmt>(S)->getRetValue();
+ assert (E && "Return expression cannot be NULL");
+
+ // Get the value associated with E.
+ loc::MemRegionVal V =
+ cast<loc::MemRegionVal>(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<CompoundLiteralRegion>(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<AllocaRegion>(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<GRState>* 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<Expr>(*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<BlockEdge>((*I)->getLocation());
+ Expr* Ex = cast<Expr>(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<GRState> *N = *(*I)->pred_begin();
+ ProgramPoint P = N->getLocation();
+ const GRState* St = (*I)->getState();
+
+ if (PostStmt* PS = dyn_cast<PostStmt>(&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<GRState>* N,
+ BuiltinBugReport *R) {
+ registerTrackNullOrUndefValue(BRC, static_cast<ArgReport*>(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<PostStmt>(N->getLocation());
+ DeclStmt *DS = cast<DeclStmt>(PS.getStmt());
+ VarDecl* VD = cast<VarDecl>(*DS->decl_begin());
+ QualType T = Eng.getContext().getCanonicalType(VD->getType());
+ VariableArrayType* VT = cast<VariableArrayType>(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<GRState>* N,
+ BuiltinBugReport *R) {
+ registerTrackNullOrUndefValue(BRC, static_cast<ArgReport*>(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<GRState>* N, GRStateManager& VMgr) {
+ CallExpr* CE = cast<CallExpr>(cast<PostStmt>(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<NonNullAttr>();
+
+ 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<GRState> *N) {
+ // Pattern match for a few useful cases (do something smarter later):
+ // a[0], p->f, *p
+ const Stmt *S = N->getLocationAs<PostStmt>()->getStmt();
+
+ if (const UnaryOperator *U = dyn_cast<UnaryOperator>(S)) {
+ if (U->getOpcode() == UnaryOperator::Deref)
+ return U->getSubExpr()->IgnoreParenCasts();
+ }
+ else if (const MemberExpr *ME = dyn_cast<MemberExpr>(S)) {
+ return ME->getBase()->IgnoreParenCasts();
+ }
+ else if (const ArraySubscriptExpr *AE = dyn_cast<ArraySubscriptExpr>(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<GRState> *N) {
+ const Stmt *S = N->getLocationAs<PostStmt>()->getStmt();
+ if (const ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(S))
+ return ME->getReceiver();
+ return NULL;
+}
+
+static const Stmt *GetDenomExpr(const ExplodedNode<GRState> *N) {
+ const Stmt *S = N->getLocationAs<PostStmt>()->getStmt();
+ if (const BinaryOperator *BE = dyn_cast<BinaryOperator>(S))
+ return BE->getRHS();
+ return NULL;
+}
+
+static const Stmt *GetCalleeExpr(const ExplodedNode<GRState> *N) {
+ const Stmt *S = N->getLocationAs<PostStmt>()->getStmt();
+ if (const CallExpr *CE = dyn_cast<CallExpr>(S))
+ return CE->getCallee();
+ return NULL;
+}
+
+static const Stmt *GetRetValExpr(const ExplodedNode<GRState> *N) {
+ const Stmt *S = N->getLocationAs<PostStmt>()->getStmt();
+ if (const ReturnStmt *RS = dyn_cast<ReturnStmt>(S))
+ return RS->getRetValue();
+ return NULL;
+}
+
+namespace {
+class VISIBILITY_HIDDEN FindLastStoreBRVisitor : public BugReporterVisitor {
+ const MemRegion *R;
+ SVal V;
+ bool satisfied;
+ const ExplodedNode<GRState> *StoreSite;
+public:
+ FindLastStoreBRVisitor(SVal v, const MemRegion *r)
+ : R(r), V(v), satisfied(false), StoreSite(0) {}
+
+ PathDiagnosticPiece* VisitNode(const ExplodedNode<GRState> *N,
+ const ExplodedNode<GRState> *PrevN,
+ BugReporterContext& BRC) {
+
+ if (satisfied)
+ return NULL;
+
+ if (!StoreSite) {
+ GRStateManager &StateMgr = BRC.getStateManager();
+ const ExplodedNode<GRState> *Node = N, *Last = NULL;
+
+ for ( ; Node ; Last = Node, Node = Node->getFirstPred()) {
+
+ if (const VarRegion *VR = dyn_cast<VarRegion>(R)) {
+ if (const PostStmt *P = Node->getLocationAs<PostStmt>())
+ if (const DeclStmt *DS = P->getStmtAs<DeclStmt>())
+ 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<PostStmt>()) {
+ if (const DeclStmt *DS = PS->getStmtAs<DeclStmt>()) {
+
+ if (const VarRegion *VR = dyn_cast<VarRegion>(R)) {
+ os << "Variable '" << VR->getDecl()->getNameAsString() << "' ";
+ }
+ else
+ return NULL;
+
+ if (isa<loc::ConcreteInt>(V)) {
+ bool b = false;
+ ASTContext &C = BRC.getASTContext();
+ if (R->isBoundable(C)) {
+ if (const TypedRegion *TR = dyn_cast<TypedRegion>(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<nonloc::ConcreteInt>(V)) {
+ os << "initialized to " << cast<nonloc::ConcreteInt>(V).getValue();
+ }
+ else if (V.isUndef()) {
+ if (isa<VarRegion>(R)) {
+ const VarDecl *VD = cast<VarDecl>(DS->getSingleDecl());
+ if (VD->getInit())
+ os << "initialized to a garbage value";
+ else
+ os << "declared without an initial value";
+ }
+ }
+ }
+ }
+
+ if (os.str().empty()) {
+ if (isa<loc::ConcreteInt>(V)) {
+ bool b = false;
+ ASTContext &C = BRC.getASTContext();
+ if (R->isBoundable(C)) {
+ if (const TypedRegion *TR = dyn_cast<TypedRegion>(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<VarRegion>(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<BlockEdge>(&P)) {
+ CFGBlock *BSrc = BE->getSrc();
+ S = BSrc->getTerminatorCondition();
+ }
+ else if (PostStmt *PS = dyn_cast<PostStmt>(&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<GRState> *N,
+ const ExplodedNode<GRState> *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<Loc>(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<BlockEdge>(&P)) {
+ CFGBlock *BSrc = BE->getSrc();
+ S = BSrc->getTerminatorCondition();
+ }
+ else if (PostStmt *PS = dyn_cast<PostStmt>(&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<GRState>* N) {
+
+ if (!S)
+ return;
+
+ GRStateManager &StateMgr = BRC.getStateManager();
+ const GRState *state = N->getState();
+
+ if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(S)) {
+ if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) {
+ const VarRegion *R =
+ StateMgr.getRegionManager().getVarRegion(VD);
+
+ // What did we load?
+ SVal V = StateMgr.GetSVal(state, S);
+
+ if (isa<loc::ConcreteInt>(V) || isa<nonloc::ConcreteInt>(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<loc::MemRegionVal>(&V)) {
+ const SubRegion *R = cast<SubRegion>(L->getRegion());
+ while (R && !isa<SymbolicRegion>(R)) {
+ R = dyn_cast<SubRegion>(R->getSuperRegion());
+ }
+
+ if (R) {
+ assert(isa<SymbolicRegion>(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);
+}