Refactor NilReceiverStructRet and NilReceiverLargerThanVoidPtrRet into
CallAndMessageChecker.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@89745 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/Analysis/CallAndMessageChecker.cpp b/lib/Analysis/CallAndMessageChecker.cpp
index 8386d03..ebc3a4f 100644
--- a/lib/Analysis/CallAndMessageChecker.cpp
+++ b/lib/Analysis/CallAndMessageChecker.cpp
@@ -14,6 +14,7 @@
#include "clang/Analysis/PathSensitive/CheckerVisitor.h"
#include "clang/Analysis/PathSensitive/BugReporter.h"
+#include "clang/AST/ParentMap.h"
#include "GRExprEngineInternalChecks.h"
using namespace clang;
@@ -26,10 +27,13 @@
BugType *BT_call_arg;
BugType *BT_msg_undef;
BugType *BT_msg_arg;
+ BugType *BT_struct_ret;
+ BugType *BT_void_ptr;
public:
CallAndMessageChecker() :
BT_call_null(0), BT_call_undef(0), BT_call_arg(0),
- BT_msg_undef(0), BT_msg_arg(0) {}
+ BT_msg_undef(0), BT_msg_arg(0), BT_struct_ret(0), BT_void_ptr(0) {}
+
static void *getTag() {
static int x = 0;
return &x;
@@ -119,8 +123,8 @@
}
// Check for any arguments that are uninitialized/undefined.
- for (ObjCMessageExpr::const_arg_iterator I = ME->arg_begin(), E = ME->arg_end();
- I != E; ++I) {
+ for (ObjCMessageExpr::const_arg_iterator I = ME->arg_begin(),
+ E = ME->arg_end(); I != E; ++I) {
if (state->getSVal(*I).isUndef()) {
if (ExplodedNode *N = C.GenerateSink()) {
if (!BT_msg_arg)
@@ -137,4 +141,112 @@
}
}
}
+
+ // Check if the receiver was nil and then return value a struct.
+ if (const Expr *Receiver = ME->getReceiver()) {
+ SVal L_untested = state->getSVal(Receiver);
+ // Assume that the receiver is not NULL.
+ DefinedOrUnknownSVal L = cast<DefinedOrUnknownSVal>(L_untested);
+ const GRState *StNotNull = state->Assume(L, true);
+
+ // Assume that the receiver is NULL.
+ const GRState *StNull = state->Assume(L, false);
+
+ if (StNull) {
+ QualType RetTy = ME->getType();
+ if (RetTy->isRecordType()) {
+ if (C.getPredecessor()->getParentMap().isConsumedExpr(ME)) {
+ // The [0 ...] expressions will return garbage. Flag either an
+ // explicit or implicit error. Because of the structure of this
+ // function we currently do not bifurfacte the state graph at
+ // this point.
+ // FIXME: We should bifurcate and fill the returned struct with
+ // garbage.
+ if (ExplodedNode* N = C.GenerateSink(StNull)) {
+ if (!StNotNull) {
+ if (!BT_struct_ret) {
+ std::string sbuf;
+ llvm::raw_string_ostream os(sbuf);
+ 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";
+ BT_struct_ret = new BuiltinBug(os.str().c_str());
+ }
+
+ EnhancedBugReport *R = new EnhancedBugReport(*BT_struct_ret,
+ BT_struct_ret->getName(), N);
+ R->addRange(Receiver->getSourceRange());
+ R->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue,
+ Receiver);
+ C.EmitReport(R);
+ return;
+ }
+ else
+ // Do not report implicit bug.
+ return;
+ }
+ }
+ } else {
+ ASTContext &Ctx = C.getASTContext();
+ if (RetTy != Ctx.VoidTy) {
+ if (C.getPredecessor()->getParentMap().isConsumedExpr(ME)) {
+ // sizeof(void *)
+ const uint64_t voidPtrSize = Ctx.getTypeSize(Ctx.VoidPtrTy);
+ // sizeof(return type)
+ const uint64_t returnTypeSize = Ctx.getTypeSize(ME->getType());
+
+ if (voidPtrSize < returnTypeSize) {
+ if (ExplodedNode* N = C.GenerateSink(StNull)) {
+ if (!StNotNull) {
+ if (!BT_struct_ret) {
+ std::string sbuf;
+ llvm::raw_string_ostream os(sbuf);
+ os << "The receiver in the message expression is 'nil' and "
+ "results in the returned value (of type '"
+ << ME->getType().getAsString()
+ << "' and of size "
+ << returnTypeSize / 8
+ << " bytes) to be garbage or otherwise undefined";
+ BT_void_ptr = new BuiltinBug(os.str().c_str());
+ }
+
+ EnhancedBugReport *R = new EnhancedBugReport(*BT_void_ptr,
+ BT_void_ptr->getName(), N);
+ R->addRange(Receiver->getSourceRange());
+ R->addVisitorCreator(
+ bugreporter::registerTrackNullOrUndefValue, Receiver);
+ C.EmitReport(R);
+ return;
+ } else
+ // Do not report implicit bug.
+ return;
+ }
+ }
+ else if (!StNotNull) {
+ // Handle the safe cases where the return value is 0 if the
+ // receiver is nil.
+ //
+ // FIXME: For now take the conservative approach that we only
+ // return null values if we *know* that the receiver is nil.
+ // This is because we can have surprises like:
+ //
+ // ... = [[NSScreens screens] objectAtIndex:0];
+ //
+ // What can happen is that [... screens] could return nil, but
+ // it most likely isn't nil. We should assume the semantics
+ // of this case unless we have *a lot* more knowledge.
+ //
+ SVal V = C.getValueManager().makeZeroVal(ME->getType());
+ C.GenerateNode(StNull->BindExpr(ME, V));
+ return;
+ }
+ }
+ }
+ }
+ }
+ // Do not propagate null state.
+ if (StNotNull)
+ C.GenerateNode(StNotNull);
+ }
}