void CallAndMessageChecker::HandleNilReceiver(CheckerContext &C, ProgramStateRef state, ObjCMessage msg) const { ASTContext &Ctx = C.getASTContext(); // Check the return type of the message expression. A message to nil will // return different values depending on the return type and the architecture. QualType RetTy = msg.getType(Ctx); CanQualType CanRetTy = Ctx.getCanonicalType(RetTy); const LocationContext *LCtx = C.getLocationContext(); if (CanRetTy->isStructureOrClassType()) { // Structure returns are safe since the compiler zeroes them out. SVal V = C.getSValBuilder().makeZeroVal(msg.getType(Ctx)); C.addTransition(state->BindExpr(msg.getOriginExpr(), LCtx, V)); return; } // Other cases: check if sizeof(return type) > sizeof(void*) if (CanRetTy != Ctx.VoidTy && C.getLocationContext()->getParentMap() .isConsumedExpr(msg.getOriginExpr())) { // Compute: sizeof(void *) and sizeof(return type) const uint64_t voidPtrSize = Ctx.getTypeSize(Ctx.VoidPtrTy); const uint64_t returnTypeSize = Ctx.getTypeSize(CanRetTy); if (voidPtrSize < returnTypeSize && !(supportsNilWithFloatRet(Ctx.getTargetInfo().getTriple()) && (Ctx.FloatTy == CanRetTy || Ctx.DoubleTy == CanRetTy || Ctx.LongDoubleTy == CanRetTy || Ctx.LongLongTy == CanRetTy || Ctx.UnsignedLongLongTy == CanRetTy))) { if (ExplodedNode *N = C.generateSink(state)) emitNilReceiverBug(C, msg, N); return; } // 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.getSValBuilder().makeZeroVal(msg.getType(Ctx)); C.addTransition(state->BindExpr(msg.getOriginExpr(), LCtx, V)); return; } C.addTransition(state); }
void CallAndMessageChecker::emitNilReceiverBug(CheckerContext &C, const ObjCMessage &msg, ExplodedNode *N) const { if (!BT_msg_ret) BT_msg_ret.reset( new BuiltinBug("Receiver in message expression is " "'nil' and returns a garbage value")); llvm::SmallString<200> buf; llvm::raw_svector_ostream os(buf); os << "The receiver of message '" << msg.getSelector().getAsString() << "' is nil and returns a value of type '" << msg.getType(C.getASTContext()).getAsString() << "' that will be garbage"; BugReport *report = new BugReport(*BT_msg_ret, os.str(), N); if (const Expr *receiver = msg.getInstanceReceiver()) { report->addRange(receiver->getSourceRange()); report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, receiver)); } C.EmitReport(report); }