summaryrefslogtreecommitdiffstats
path: root/clang/lib/StaticAnalyzer/Checkers
diff options
context:
space:
mode:
authorJordan Rose <jordan_rose@apple.com>2013-03-07 01:23:25 +0000
committerJordan Rose <jordan_rose@apple.com>2013-03-07 01:23:25 +0000
commitb41977f85254c4f8512200e5006fa56d55316d52 (patch)
tree5fdf5358f3421cde3ebc2ca578a33b3b3ced1f90 /clang/lib/StaticAnalyzer/Checkers
parent6f09928d5bb71f91b85a95388766bbee7c0754ea (diff)
downloadbcm5719-llvm-b41977f85254c4f8512200e5006fa56d55316d52.tar.gz
bcm5719-llvm-b41977f85254c4f8512200e5006fa56d55316d52.zip
[analyzer] Check for returning null references in ReturnUndefChecker.
Officially in the C++ standard, a null reference cannot exist. However, it's still very easy to create one: int &getNullRef() { int *p = 0; return *p; } We already check that binds to reference regions don't create null references. This patch checks that we don't create null references by returning, either. <rdar://problem/13364378> llvm-svn: 176601
Diffstat (limited to 'clang/lib/StaticAnalyzer/Checkers')
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/ReturnUndefChecker.cpp96
1 files changed, 66 insertions, 30 deletions
diff --git a/clang/lib/StaticAnalyzer/Checkers/ReturnUndefChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/ReturnUndefChecker.cpp
index 1fa4ab9ff72..7a5d9936010 100644
--- a/clang/lib/StaticAnalyzer/Checkers/ReturnUndefChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/ReturnUndefChecker.cpp
@@ -24,9 +24,13 @@ using namespace clang;
using namespace ento;
namespace {
-class ReturnUndefChecker :
- public Checker< check::PreStmt<ReturnStmt> > {
- mutable OwningPtr<BuiltinBug> BT;
+class ReturnUndefChecker : public Checker< check::PreStmt<ReturnStmt> > {
+ mutable OwningPtr<BuiltinBug> BT_Undef;
+ mutable OwningPtr<BuiltinBug> BT_NullReference;
+
+ void emitUndef(CheckerContext &C, const Expr *RetE) const;
+ void checkReference(CheckerContext &C, const Expr *RetE,
+ DefinedOrUnknownSVal RetVal) const;
public:
void checkPreStmt(const ReturnStmt *RS, CheckerContext &C) const;
};
@@ -34,43 +38,75 @@ public:
void ReturnUndefChecker::checkPreStmt(const ReturnStmt *RS,
CheckerContext &C) const {
-
const Expr *RetE = RS->getRetValue();
if (!RetE)
return;
-
- if (!C.getState()->getSVal(RetE, C.getLocationContext()).isUndef())
- return;
-
- // "return;" is modeled to evaluate to an UndefinedValue. Allow UndefinedValue
- // to be returned in functions returning void to support the following pattern:
- // void foo() {
- // return;
- // }
- // void test() {
- // return foo();
- // }
+ SVal RetVal = C.getSVal(RetE);
+
const StackFrameContext *SFC = C.getStackFrame();
QualType RT = CallEvent::getDeclaredResultType(SFC->getDecl());
- if (!RT.isNull() && RT->isSpecificBuiltinType(BuiltinType::Void))
+
+ if (RetVal.isUndef()) {
+ // "return;" is modeled to evaluate to an UndefinedVal. Allow UndefinedVal
+ // to be returned in functions returning void to support this pattern:
+ // void foo() {
+ // return;
+ // }
+ // void test() {
+ // return foo();
+ // }
+ if (RT.isNull() || !RT->isVoidType())
+ emitUndef(C, RetE);
return;
+ }
- ExplodedNode *N = C.generateSink();
+ if (RT.isNull())
+ return;
+ if (RT->isReferenceType()) {
+ checkReference(C, RetE, RetVal.castAs<DefinedOrUnknownSVal>());
+ return;
+ }
+}
+
+static void emitBug(CheckerContext &C, BuiltinBug &BT, const Expr *RetE,
+ const Expr *TrackingE = 0) {
+ ExplodedNode *N = C.generateSink();
if (!N)
return;
-
- if (!BT)
- BT.reset(new BuiltinBug("Garbage return value",
- "Undefined or garbage value returned to caller"));
-
- BugReport *report =
- new BugReport(*BT, BT->getDescription(), N);
-
- report->addRange(RetE->getSourceRange());
- bugreporter::trackNullOrUndefValue(N, RetE, *report);
-
- C.emitReport(report);
+
+ BugReport *Report = new BugReport(BT, BT.getDescription(), N);
+
+ Report->addRange(RetE->getSourceRange());
+ bugreporter::trackNullOrUndefValue(N, TrackingE ? TrackingE : RetE, *Report);
+
+ C.emitReport(Report);
+}
+
+void ReturnUndefChecker::emitUndef(CheckerContext &C, const Expr *RetE) const {
+ if (!BT_Undef)
+ BT_Undef.reset(new BuiltinBug("Garbage return value",
+ "Undefined or garbage value "
+ "returned to caller"));
+ emitBug(C, *BT_Undef, RetE);
+}
+
+void ReturnUndefChecker::checkReference(CheckerContext &C, const Expr *RetE,
+ DefinedOrUnknownSVal RetVal) const {
+ ProgramStateRef StNonNull, StNull;
+ llvm::tie(StNonNull, StNull) = C.getState()->assume(RetVal);
+
+ if (StNonNull) {
+ // Going forward, assume the location is non-null.
+ C.addTransition(StNonNull);
+ return;
+ }
+
+ // The return value is known to be null. Emit a bug report.
+ if (!BT_NullReference)
+ BT_NullReference.reset(new BuiltinBug("Returning null reference"));
+
+ emitBug(C, *BT_NullReference, RetE, bugreporter::getDerefExpr(RetE));
}
void ento::registerReturnUndefChecker(CheckerManager &mgr) {
OpenPOWER on IntegriCloud