diff options
-rw-r--r-- | lib/Checker/AdjustedReturnValueChecker.cpp | 90 | ||||
-rw-r--r-- | lib/Checker/CMakeLists.txt | 3 | ||||
-rw-r--r-- | lib/Checker/GRExprEngine.cpp | 1 | ||||
-rw-r--r-- | lib/Checker/GRExprEngineInternalChecks.h | 1 | ||||
-rw-r--r-- | test/Analysis/misc-ps.m | 14 |
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; +} |