aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/Checker/AdjustedReturnValueChecker.cpp90
-rw-r--r--lib/Checker/CMakeLists.txt3
-rw-r--r--lib/Checker/GRExprEngine.cpp1
-rw-r--r--lib/Checker/GRExprEngineInternalChecks.h1
-rw-r--r--test/Analysis/misc-ps.m14
5 files changed, 108 insertions, 1 deletions
diff --git a/lib/Checker/AdjustedReturnValueChecker.cpp b/lib/Checker/AdjustedReturnValueChecker.cpp
new file mode 100644
index 0000000000..898ce76a99
--- /dev/null
+++ b/lib/Checker/AdjustedReturnValueChecker.cpp
@@ -0,0 +1,90 @@
+//== AdjustedReturnValueChecker.cpp -----------------------------*- 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 AdjustedReturnValueChecker, a simple check to see if the
+// return value of a function call is different than the one the caller thinks
+// it is.
+//
+//===----------------------------------------------------------------------===//
+
+#include "GRExprEngineInternalChecks.h"
+#include "clang/Checker/PathSensitive/GRExprEngine.h"
+#include "clang/Checker/BugReporter/BugReporter.h"
+#include "clang/Checker/PathSensitive/CheckerVisitor.h"
+#include "clang/Basic/SourceManager.h"
+#include "llvm/ADT/SmallString.h"
+
+using namespace clang;
+
+namespace {
+class AdjustedReturnValueChecker :
+ public CheckerVisitor<AdjustedReturnValueChecker> {
+public:
+ AdjustedReturnValueChecker() {}
+
+ void PostVisitCallExpr(CheckerContext &C, const CallExpr *CE);
+
+ static void *getTag() {
+ static int x = 0; return &x;
+ }
+};
+}
+
+void clang::RegisterAdjustedReturnValueChecker(GRExprEngine &Eng) {
+ Eng.registerCheck(new AdjustedReturnValueChecker());
+}
+
+void AdjustedReturnValueChecker::PostVisitCallExpr(CheckerContext &C,
+ const CallExpr *CE) {
+
+ // Fetch the signature of the called function.
+ const GRState *state = C.getState();
+
+ SVal V = state->getSVal(CE);
+ if (V.isUnknown())
+ return;
+
+ const MemRegion *callee = state->getSVal(CE->getCallee()).getAsRegion();
+ if (!callee)
+ return;
+
+ QualType actualResultTy;
+
+ if (const FunctionTextRegion *FT = dyn_cast<FunctionTextRegion>(callee)) {
+ const FunctionDecl *FD = FT->getDecl();
+ actualResultTy = FD->getResultType();
+ }
+ else if (const BlockDataRegion *BD = dyn_cast<BlockDataRegion>(callee)) {
+ const BlockTextRegion *BR = BD->getCodeRegion();
+ const BlockPointerType *BT =
+ BR->getLocationType(C.getASTContext())->getAs<BlockPointerType>();
+ const FunctionType *FT = BT->getPointeeType()->getAs<FunctionType>();
+ actualResultTy = FT->getResultType();
+ }
+
+ // Can this happen?
+ if (actualResultTy.isNull())
+ return;
+
+ // For now, ignore references.
+ if (actualResultTy->getAs<ReferenceType>())
+ return;
+
+ // Get the result type of the call.
+ QualType expectedResultTy = CE->getType();
+
+ // Are they the same?
+ if (expectedResultTy != actualResultTy) {
+ // FIXME: Do more checking and actual emit an error. At least performing
+ // the cast avoids some assertion failures elsewhere.
+ SValuator &SVator = C.getSValuator();
+ const SValuator::CastResult &R = SVator.EvalCast(V, state, expectedResultTy, actualResultTy);
+ C.GenerateNode(R.getState()->BindExpr(CE, R.getSVal()));
+ }
+}
diff --git a/lib/Checker/CMakeLists.txt b/lib/Checker/CMakeLists.txt
index 033ad33814..130378a247 100644
--- a/lib/Checker/CMakeLists.txt
+++ b/lib/Checker/CMakeLists.txt
@@ -1,6 +1,7 @@
set(LLVM_NO_RTTI 1)
add_clang_library(clangChecker
+ AdjustedReturnValueChecker.cpp
ArrayBoundChecker.cpp
AttrNonNullChecker.cpp
BasicConstraintManager.cpp
@@ -26,8 +27,8 @@ add_clang_library(clangChecker
DivZeroChecker.cpp
Environment.cpp
ExplodedGraph.cpp
- FlatStore.cpp
FixedAddressChecker.cpp
+ FlatStore.cpp
GRBlockCounter.cpp
GRCoreEngine.cpp
GRExprEngine.cpp
diff --git a/lib/Checker/GRExprEngine.cpp b/lib/Checker/GRExprEngine.cpp
index 52e4fc12f8..1e16bde5f1 100644
--- a/lib/Checker/GRExprEngine.cpp
+++ b/lib/Checker/GRExprEngine.cpp
@@ -301,6 +301,7 @@ static void RegisterInternalChecks(GRExprEngine &Eng) {
// their associated BugType will get registered with the BugReporter
// automatically. Note that the check itself is owned by the GRExprEngine
// object.
+ RegisterAdjustedReturnValueChecker(Eng);
RegisterAttrNonNullChecker(Eng);
RegisterCallAndMessageChecker(Eng);
RegisterDereferenceChecker(Eng);
diff --git a/lib/Checker/GRExprEngineInternalChecks.h b/lib/Checker/GRExprEngineInternalChecks.h
index e2354ed098..1246703b86 100644
--- a/lib/Checker/GRExprEngineInternalChecks.h
+++ b/lib/Checker/GRExprEngineInternalChecks.h
@@ -19,6 +19,7 @@ namespace clang {
class GRExprEngine;
+void RegisterAdjustedReturnValueChecker(GRExprEngine &Eng);
void RegisterAttrNonNullChecker(GRExprEngine &Eng);
void RegisterDereferenceChecker(GRExprEngine &Eng);
void RegisterDivZeroChecker(GRExprEngine &Eng);
diff --git a/test/Analysis/misc-ps.m b/test/Analysis/misc-ps.m
index 88c73bc22e..949c51019f 100644
--- a/test/Analysis/misc-ps.m
+++ b/test/Analysis/misc-ps.m
@@ -879,3 +879,17 @@ void foo_rev95192(int **x) {
**x = 1; // no-warning
}
+//===----------------------------------------------------------------------===//
+// Handle casts of a function to a function pointer with a different return
+// value. We don't yet emit an error for such cases, but we now we at least
+// don't crash when the return value gets interpreted in a way that
+// violates our invariants.
+//===----------------------------------------------------------------------===//
+
+void *foo_rev95267();
+int bar_rev95267() {
+ char (*Callback_rev95267)(void) = (char (*)(void)) foo_rev95267;
+ if ((*Callback_rev95267)() == (char) 0)
+ return 1;
+ return 0;
+}