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));
}
Example #2
0
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));
  }
}