void CallAndMessageChecker::emitNilReceiverBug(CheckerContext &C, const ObjCMethodCall &msg, ExplodedNode *N) const { if (!BT_msg_ret) BT_msg_ret.reset( new BuiltinBug(this, "Receiver in message expression is 'nil'")); const ObjCMessageExpr *ME = msg.getOriginExpr(); QualType ResTy = msg.getResultType(); SmallString<200> buf; llvm::raw_svector_ostream os(buf); os << "The receiver of message '"; ME->getSelector().print(os); os << "' is nil"; if (ResTy->isReferenceType()) { os << ", which results in forming a null reference"; } else { os << " and returns a value of type '"; msg.getResultType().print(os, C.getLangOpts()); os << "' that will be garbage"; } auto report = llvm::make_unique<BugReport>(*BT_msg_ret, os.str(), N); report->addRange(ME->getReceiverRange()); // FIXME: This won't track "self" in messages to super. if (const Expr *receiver = ME->getInstanceReceiver()) { bugreporter::trackNullOrUndefValue(N, receiver, *report); } C.emitReport(std::move(report)); }
void CallAndMessageChecker::emitNilReceiverBug(CheckerContext &C, const ObjCMethodCall &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")); const ObjCMessageExpr *ME = msg.getOriginExpr(); SmallString<200> buf; llvm::raw_svector_ostream os(buf); os << "The receiver of message '" << ME->getSelector().getAsString() << "' is nil and returns a value of type '"; msg.getResultType().print(os, C.getLangOpts()); os << "' that will be garbage"; BugReport *report = new BugReport(*BT_msg_ret, os.str(), N); report->addRange(ME->getReceiverRange()); // FIXME: This won't track "self" in messages to super. if (const Expr *receiver = ME->getInstanceReceiver()) { report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, receiver, report)); } C.EmitReport(report); }
void DynamicTypePropagation::reportGenericsBug( const ObjCObjectPointerType *From, const ObjCObjectPointerType *To, ExplodedNode *N, SymbolRef Sym, CheckerContext &C, const Stmt *ReportedNode) const { if (!CheckGenerics) return; initBugType(); SmallString<192> Buf; llvm::raw_svector_ostream OS(Buf); OS << "Conversion from value of type '"; QualType::print(From, Qualifiers(), OS, C.getLangOpts(), llvm::Twine()); OS << "' to incompatible type '"; QualType::print(To, Qualifiers(), OS, C.getLangOpts(), llvm::Twine()); OS << "'"; std::unique_ptr<BugReport> R( new BugReport(*ObjCGenericsBugType, OS.str(), N)); R->markInteresting(Sym); R->addVisitor(llvm::make_unique<GenericsBugVisitor>(Sym)); if (ReportedNode) R->addRange(ReportedNode->getSourceRange()); C.emitReport(std::move(R)); }
void DanglingDelegateChecker::checkPreStmt(const Stmt *stmt, CheckerContext &context) const { // hack to deal with pseudo-init methods resetInitialStateIfNeeded(context); // Next we track assignments to "interesting" ivars. // These are not objc messages so we need to deal with them separately. // no need for checking ivar assignments in non-ARC mode (the verification is on the release) if (!context.getLangOpts().ObjCAutoRefCount) { return; } const BinaryOperator *binOp = dyn_cast<BinaryOperator>(stmt); if (!binOp || !binOp->isAssignmentOp()) { return; } // look for an ivarref on the left of the assignment const Expr *lhs = binOp->getLHS()->IgnoreParenCasts(); if (!lhs || !lhs->getType()->getAsObjCInterfacePointerType()) { return; } const ObjCIvarRefExpr *ivarRef = dyn_cast<ObjCIvarRefExpr>(lhs); if (!ivarRef) { return; } const ObjCIvarDecl *ivarDecl = ivarRef->getDecl(); if (!ivarDecl) { return; } // want a non-null previous value in the ivar SVal ivarLVal = context.getSVal(lhs); const MemRegion *region = ivarLVal.getAsRegion(); if (region) { SVal ivarRVal = context.getState()->getSVal(region); if (isKnownToBeNil(ivarRVal, context)) { // we are sure that the ivar is nil => abort return; } } verifyIvarDynamicStateAgainstStaticFacts(*binOp, ivarDecl, context); }
void DanglingDelegateChecker::checkEndFunction(CheckerContext &context) const { // dealloc implicitly releases ivars only in ARC-mode if (!context.getLangOpts().ObjCAutoRefCount) { return; } const ObjCImplFacts *facts = getCurrentFacts(getCurrentTopClassInterface(context)); if (!facts) { return; } const ObjCMethodDecl *decl = dyn_cast_or_null<ObjCMethodDecl>(context.getStackFrame()->getDecl()); if (!decl || decl->getSelector().getAsString() != "dealloc") { return; } const std::function<void(StringRef)> emitBug([this, decl, &context](StringRef str) { BugReport *report = new BugReport(*_bugType, str, context.getPredecessor()); report->addRange(decl->getSourceRange()); context.emitReport(report); }); ProgramStateRef state = context.getState(); // Verify that all the dangerous properties have been cleared const IvarDynamicState emptyIds; for (auto it = facts->_ivarFactsMap.begin(), end = facts->_ivarFactsMap.end(); it != end; it++) { const ObjCIvarDecl *ivarDecl = it->first; const StringSet &dangerousProperties = it->second._mayStoreSelfInUnsafeProperty; const IvarDynamicState *ids = state->get<IvarMap>(ivarDecl); if (!ids) { ids = &emptyIds; } const StringSet &clearedProperties = ids->_assignPropertyWasCleared; verifyAndReportDangerousProperties(dangerousProperties, clearedProperties, ivarDecl->getNameAsString(), ivarDecl->getType(), "ARC-generated code", emitBug); } }
void VariadicMethodTypeChecker::checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const { if (!BT) { BT.reset(new APIMisuse(this, "Arguments passed to variadic method aren't all " "Objective-C pointer types")); ASTContext &Ctx = C.getASTContext(); arrayWithObjectsS = GetUnarySelector("arrayWithObjects", Ctx); dictionaryWithObjectsAndKeysS = GetUnarySelector("dictionaryWithObjectsAndKeys", Ctx); setWithObjectsS = GetUnarySelector("setWithObjects", Ctx); orderedSetWithObjectsS = GetUnarySelector("orderedSetWithObjects", Ctx); initWithObjectsS = GetUnarySelector("initWithObjects", Ctx); initWithObjectsAndKeysS = GetUnarySelector("initWithObjectsAndKeys", Ctx); } if (!isVariadicMessage(msg)) return; // We are not interested in the selector arguments since they have // well-defined types, so the compiler will issue a warning for them. unsigned variadicArgsBegin = msg.getSelector().getNumArgs(); // We're not interested in the last argument since it has to be nil or the // compiler would have issued a warning for it elsewhere. unsigned variadicArgsEnd = msg.getNumArgs() - 1; if (variadicArgsEnd <= variadicArgsBegin) return; // Verify that all arguments have Objective-C types. Optional<ExplodedNode*> errorNode; for (unsigned I = variadicArgsBegin; I != variadicArgsEnd; ++I) { QualType ArgTy = msg.getArgExpr(I)->getType(); if (ArgTy->isObjCObjectPointerType()) continue; // Block pointers are treaded as Objective-C pointers. if (ArgTy->isBlockPointerType()) continue; // Ignore pointer constants. if (msg.getArgSVal(I).getAs<loc::ConcreteInt>()) continue; // Ignore pointer types annotated with 'NSObject' attribute. if (C.getASTContext().isObjCNSObjectType(ArgTy)) continue; // Ignore CF references, which can be toll-free bridged. if (coreFoundation::isCFObjectRef(ArgTy)) continue; // Generate only one error node to use for all bug reports. if (!errorNode.hasValue()) errorNode = C.generateNonFatalErrorNode(); if (!errorNode.getValue()) continue; SmallString<128> sbuf; llvm::raw_svector_ostream os(sbuf); StringRef TypeName = GetReceiverInterfaceName(msg); if (!TypeName.empty()) os << "Argument to '" << TypeName << "' method '"; else os << "Argument to method '"; msg.getSelector().print(os); os << "' should be an Objective-C pointer type, not '"; ArgTy.print(os, C.getLangOpts()); os << "'"; auto R = llvm::make_unique<BugReport>(*BT, os.str(), errorNode.getValue()); R->addRange(msg.getArgSourceRange(I)); C.emitReport(std::move(R)); } }