Added path-sensitive checking for null pointer values passed to function arguments marked nonnull.
This implements <rdar://problem/6069935>
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@53891 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/Analysis/BasicObjCFoundationChecks.cpp b/lib/Analysis/BasicObjCFoundationChecks.cpp
index 4475ed2..37cfc28 100644
--- a/lib/Analysis/BasicObjCFoundationChecks.cpp
+++ b/lib/Analysis/BasicObjCFoundationChecks.cpp
@@ -126,7 +126,7 @@
delete *I;
}
- virtual bool Audit(ExplodedNode<ValueState>* N);
+ virtual bool Audit(ExplodedNode<ValueState>* N, ValueStateManager&);
virtual void EmitWarnings(BugReporter& BR);
@@ -153,7 +153,8 @@
-bool BasicObjCFoundationChecks::Audit(ExplodedNode<ValueState>* N) {
+bool BasicObjCFoundationChecks::Audit(ExplodedNode<ValueState>* N,
+ ValueStateManager&) {
ObjCMessageExpr* ME =
cast<ObjCMessageExpr>(cast<PostStmt>(N->getLocation()).getStmt());
@@ -348,7 +349,7 @@
virtual ~AuditCFNumberCreate() {}
- virtual bool Audit(ExplodedNode<ValueState>* N);
+ virtual bool Audit(ExplodedNode<ValueState>* N, ValueStateManager&);
virtual void EmitWarnings(BugReporter& BR) {
Desc.EmitWarnings(BR);
@@ -454,7 +455,7 @@
}
#endif
-bool AuditCFNumberCreate::Audit(ExplodedNode<ValueState>* N) {
+bool AuditCFNumberCreate::Audit(ExplodedNode<ValueState>* N,ValueStateManager&){
CallExpr* CE = cast<CallExpr>(cast<PostStmt>(N->getLocation()).getStmt());
Expr* Callee = CE->getCallee();
RVal CallV = GetRVal(N->getState(), Callee);
diff --git a/lib/Analysis/GRExprEngine.cpp b/lib/Analysis/GRExprEngine.cpp
index 8dee018..f0c0d8d 100644
--- a/lib/Analysis/GRExprEngine.cpp
+++ b/lib/Analysis/GRExprEngine.cpp
@@ -85,7 +85,7 @@
}
}
- virtual bool Audit(NodeTy* N) {
+ virtual bool Audit(NodeTy* N, ValueStateManager& VMgr) {
Stmt* S = cast<PostStmt>(N->getLocation()).getStmt();
void* key = reinterpret_cast<void*>((uintptr_t) S->getStmtClass());
MapTy::iterator MI = M.find(key);
@@ -96,7 +96,7 @@
bool isSink = false;
for (Checks::iterator I=MI->second.begin(), E=MI->second.end(); I!=E; ++I)
- isSink |= (*I)->Audit(N);
+ isSink |= (*I)->Audit(N, VMgr);
return isSink;
}
diff --git a/lib/Analysis/GRSimpleVals.cpp b/lib/Analysis/GRSimpleVals.cpp
index 36e7233..38327cb 100644
--- a/lib/Analysis/GRSimpleVals.cpp
+++ b/lib/Analysis/GRSimpleVals.cpp
@@ -335,6 +335,63 @@
}
}
+//===----------------------------------------------------------------------===//
+// __attribute__(nonnull) checking
+
+class VISIBILITY_HIDDEN CheckAttrNonNull : public GRSimpleAPICheck {
+ SimpleBugType BT;
+ std::list<RangedBugReport> Reports;
+
+public:
+ CheckAttrNonNull() :
+ BT("'nonnull' argument passed null",
+ "Null pointer passed as an argument to a 'nonnull' parameter") {}
+
+
+ virtual bool Audit(ExplodedNode<ValueState>* N, ValueStateManager& VMgr) {
+ CallExpr* CE = cast<CallExpr>(cast<PostStmt>(N->getLocation()).getStmt());
+ const ValueState* state = N->getState();
+
+ RVal X = VMgr.GetRVal(state, CE->getCallee());
+
+ if (!isa<lval::FuncVal>(X))
+ return false;
+
+ FunctionDecl* FD = dyn_cast<FunctionDecl>(cast<lval::FuncVal>(X).getDecl());
+ 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;
+
+ RangedBugReport R(BT, N);
+ R.addRange((*I)->getSourceRange());
+ Reports.push_back(R);
+ hasError = true;
+ }
+
+ return hasError;
+ }
+
+ virtual void EmitWarnings(BugReporter& BR) {
+ for (std::list<RangedBugReport>::iterator I=Reports.begin(),
+ E=Reports.end(); I!=E; ++I)
+ BR.EmitWarning(*I);
+ }
+};
+
+//===----------------------------------------------------------------------===//
+// Check registration.
void GRSimpleVals::RegisterChecks(GRExprEngine& Eng) {
@@ -360,6 +417,7 @@
Check = CreateAuditCFNumberCreate(Ctx, VMgr);
Eng.AddCheck(Check, Stmt::CallExprClass);
+ Eng.AddCheck(new CheckAttrNonNull(), Stmt::CallExprClass);
}
//===----------------------------------------------------------------------===//
diff --git a/lib/Analysis/ValueState.cpp b/lib/Analysis/ValueState.cpp
index 2c3e6da..1cf6954 100644
--- a/lib/Analysis/ValueState.cpp
+++ b/lib/Analysis/ValueState.cpp
@@ -26,6 +26,15 @@
return T ? T->contains(&V) : false;
}
+bool ValueState::isEqual(SymbolID sym, const llvm::APSInt& V) const {
+
+ // Retrieve the EQ-set associated with the given symbol.
+ const ConstEqTy::data_type* T = ConstEq.lookup(sym);
+
+ // See if V is present in the EQ-set.
+ return T ? **T == V : false;
+}
+
const llvm::APSInt* ValueState::getSymVal(SymbolID sym) const {
ConstEqTy::data_type* T = ConstEq.lookup(sym);
return T ? *T : NULL;
@@ -296,6 +305,35 @@
P->PrintCheckerState(Out, CheckerState, nl, sep);
}
+
+//===----------------------------------------------------------------------===//
+// Queries.
+//===----------------------------------------------------------------------===//
+
+bool ValueStateManager::isEqual(const ValueState* state, Expr* Ex,
+ const llvm::APSInt& Y) {
+ RVal V = GetRVal(state, Ex);
+
+ if (lval::ConcreteInt* X = dyn_cast<lval::ConcreteInt>(&V))
+ return X->getValue() == Y;
+
+ if (nonlval::ConcreteInt* X = dyn_cast<nonlval::ConcreteInt>(&V))
+ return X->getValue() == Y;
+
+ if (nonlval::SymbolVal* X = dyn_cast<nonlval::SymbolVal>(&V))
+ return state->isEqual(X->getSymbol(), Y);
+
+ if (lval::SymbolVal* X = dyn_cast<lval::SymbolVal>(&V))
+ return state->isEqual(X->getSymbol(), Y);
+
+ return false;
+}
+
+bool ValueStateManager::isEqual(const ValueState* state, Expr* Ex,
+ uint64_t x) {
+ return isEqual(state, Ex, BasicVals.getValue(x, Ex->getType()));
+}
+
//===----------------------------------------------------------------------===//
// "Assume" logic.
//===----------------------------------------------------------------------===//