Exemple #1
0
void UnixAPIChecker::ReportOpenBug(CheckerContext &C,
                                   ProgramStateRef State,
                                   const char *Msg,
                                   SourceRange SR) const {
  ExplodedNode *N = C.generateSink(State);
  if (!N)
    return;

  LazyInitialize(BT_open, "Improper use of 'open'");

  BugReport *Report = new BugReport(*BT_open, Msg, N);
  Report->addRange(SR);
  C.emitReport(Report);
}
Exemple #2
0
void StackAddrEscapeChecker::EmitStackError(CheckerContext &C, const MemRegion *R,
                                          const Expr *RetE) const {
  ExplodedNode *N = C.generateSink();

  if (!N)
    return;

  if (!BT_returnstack)
   BT_returnstack.reset(
                 new BuiltinBug("Return of address to stack-allocated memory"));

  // Generate a report for this bug.
  SmallString<512> buf;
  llvm::raw_svector_ostream os(buf);
  SourceRange range = genName(os, R, C.getASTContext());
  os << " returned to caller";
  BugReport *report = new BugReport(*BT_returnstack, os.str(), N);
  report->addRange(RetE->getSourceRange());
  if (range.isValid())
    report->addRange(range);

  C.emitReport(report);
}
void UndefinedAssignmentChecker::checkBind(SVal location, SVal val,
                                           const Stmt *StoreE,
                                           CheckerContext &C) const {
  if (!val.isUndef())
    return;

  ExplodedNode *N = C.generateSink();

  if (!N)
    return;

  const char *str = "Assigned value is garbage or undefined";

  if (!BT)
    BT.reset(new BuiltinBug(str));

  // Generate a report for this bug.
  const Expr *ex = 0;

  while (StoreE) {
    if (const BinaryOperator *B = dyn_cast<BinaryOperator>(StoreE)) {
      if (B->isCompoundAssignmentOp()) {
        ProgramStateRef state = C.getState();
        if (state->getSVal(B->getLHS(), C.getLocationContext()).isUndef()) {
          str = "The left expression of the compound assignment is an "
                "uninitialized value. The computed value will also be garbage";
          ex = B->getLHS();
          break;
        }
      }

      ex = B->getRHS();
      break;
    }

    if (const DeclStmt *DS = dyn_cast<DeclStmt>(StoreE)) {
      const VarDecl *VD = dyn_cast<VarDecl>(DS->getSingleDecl());
      ex = VD->getInit();
    }

    break;
  }

  BugReport *R = new BugReport(*BT, str, N);
  if (ex) {
    R->addRange(ex->getSourceRange());
    bugreporter::trackNullOrUndefValue(N, ex, *R);
  }
  C.emitReport(R);
}
Exemple #4
0
void PthreadLockChecker::reportUseDestroyedBug(CheckerContext &C,
                                               const CallExpr *CE) const {
  if (!BT_destroylock)
    BT_destroylock.reset(new BugType(this, "Use destroyed lock",
                                     "Lock checker"));
  ExplodedNode *N = C.generateSink();
  if (!N)
    return;
  BugReport *Report = new BugReport(*BT_destroylock,
                                    "This lock has already been destroyed",
                                    N);
  Report->addRange(CE->getArg(0)->getSourceRange());
  C.emitReport(Report);
}
void
UndefCapturedBlockVarChecker::checkPostStmt(const BlockExpr *BE,
                                            CheckerContext &C) const {
  if (!BE->getBlockDecl()->hasCaptures())
    return;

  ProgramStateRef state = C.getState();
  const BlockDataRegion *R =
    cast<BlockDataRegion>(state->getSVal(BE,
                                         C.getLocationContext()).getAsRegion());

  BlockDataRegion::referenced_vars_iterator I = R->referenced_vars_begin(),
                                            E = R->referenced_vars_end();

  for (; I != E; ++I) {
    // This VarRegion is the region associated with the block; we need
    // the one associated with the encompassing context.
    const VarRegion *VR = I.getCapturedRegion();
    const VarDecl *VD = VR->getDecl();

    if (VD->hasAttr<BlocksAttr>() || !VD->hasLocalStorage())
      continue;

    // Get the VarRegion associated with VD in the local stack frame.
    if (Optional<UndefinedVal> V =
          state->getSVal(I.getOriginalRegion()).getAs<UndefinedVal>()) {
      if (ExplodedNode *N = C.generateSink()) {
        if (!BT)
          BT.reset(
              new BuiltinBug(this, "uninitialized variable captured by block"));

        // Generate a bug report.
        SmallString<128> buf;
        llvm::raw_svector_ostream os(buf);

        os << "Variable '" << VD->getName()
           << "' is uninitialized when captured by block";

        BugReport *R = new BugReport(*BT, os.str(), N);
        if (const Expr *Ex = FindBlockDeclRefExpr(BE->getBody(), VD))
          R->addRange(Ex->getSourceRange());
        R->addVisitor(llvm::make_unique<FindLastStoreBRVisitor>(
            *V, VR, /*EnableNullFPSuppression*/ false));
        R->disablePathPruning();
        // need location of block
        C.emitReport(R);
      }
    }
  }
}
// Handle operator calls. First, if it is operator=, check the argument,
// and handle assigning and set target state appropriately. Otherwise, for
// other operators, check the args for bad iterators and handle comparisons.
void IteratorsChecker::checkPreStmt(const CXXOperatorCallExpr *OCE,
                                    CheckerContext &C) const
{
  const LocationContext *LC = C.getLocationContext();
  const ProgramState *state = C.getState();
  OverloadedOperatorKind Kind = OCE->getOperator();
  if (Kind == OO_Equal) {
    checkExpr(C, OCE->getArg(1));
    state = handleAssign(state, OCE->getArg(0), OCE->getArg(1), LC);
    C.addTransition(state);
    return;
  }
  else {
    checkArgs(C, OCE);
    // If it is a compare and both are iterators, ensure that they are for
    // the same container.
    if (Kind == OO_EqualEqual || Kind == OO_ExclaimEqual ||
        Kind == OO_Less || Kind == OO_LessEqual ||
        Kind == OO_Greater || Kind == OO_GreaterEqual) {
      const MemRegion *MR0, *MR1;
      MR0 = getRegion(state, OCE->getArg(0), LC);
      if (!MR0)
        return;
      MR1 = getRegion(state, OCE->getArg(1), LC);
      if (!MR1)
        return;
      const RefState *RS0, *RS1;
      RS0 = state->get<IteratorState>(MR0);
      if (!RS0)
        return;
      RS1 = state->get<IteratorState>(MR1);
      if (!RS1)
        return;
      if (RS0->getMemRegion() != RS1->getMemRegion()) {
      if (ExplodedNode *N = C.addTransition()) {
          if (!BT_Incompatible)
            const_cast<IteratorsChecker*>(this)->BT_Incompatible =
              new BuiltinBug(
                      "Cannot compare iterators from different containers");

          BugReport *R = new BugReport(*BT_Incompatible,
                                        BT_Incompatible->getDescription(), N);
          R->addRange(OCE->getSourceRange());
          C.EmitReport(R);
        }
      }
    }
  }
}
void UndefResultChecker::checkPostStmt(const BinaryOperator *B,
                                       CheckerContext &C) const {
  const ProgramState *state = C.getState();
  const LocationContext *LCtx = C.getLocationContext();
  if (state->getSVal(B, LCtx).isUndef()) {
    // Generate an error node.
    ExplodedNode *N = C.generateSink();
    if (!N)
      return;
    
    if (!BT)
      BT.reset(new BuiltinBug("Result of operation is garbage or undefined"));

    llvm::SmallString<256> sbuf;
    llvm::raw_svector_ostream OS(sbuf);
    const Expr *Ex = NULL;
    bool isLeft = true;
    
    if (state->getSVal(B->getLHS(), LCtx).isUndef()) {
      Ex = B->getLHS()->IgnoreParenCasts();
      isLeft = true;
    }
    else if (state->getSVal(B->getRHS(), LCtx).isUndef()) {
      Ex = B->getRHS()->IgnoreParenCasts();
      isLeft = false;
    }
    
    if (Ex) {
      OS << "The " << (isLeft ? "left" : "right")
         << " operand of '"
         << BinaryOperator::getOpcodeStr(B->getOpcode())
         << "' is a garbage value";
    }          
    else {
      // Neither operand was undefined, but the result is undefined.
      OS << "The result of the '"
         << BinaryOperator::getOpcodeStr(B->getOpcode())
         << "' expression is undefined";
    }
    BugReport *report = new BugReport(*BT, OS.str(), N);
    if (Ex) {
      report->addRange(Ex->getSourceRange());
      report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, Ex));
    }
    else
      report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, B));
    C.EmitReport(report);
  }
}
void
UndefCapturedBlockVarChecker::checkPostStmt(const BlockExpr *BE,
                                            CheckerContext &C) const {
  if (!BE->getBlockDecl()->hasCaptures())
    return;

  ProgramStateRef state = C.getState();
  const BlockDataRegion *R =
    cast<BlockDataRegion>(state->getSVal(BE,
                                         C.getLocationContext()).getAsRegion());

  BlockDataRegion::referenced_vars_iterator I = R->referenced_vars_begin(),
                                            E = R->referenced_vars_end();

  for (; I != E; ++I) {
    // This VarRegion is the region associated with the block; we need
    // the one associated with the encompassing context.
    const VarRegion *VR = *I;
    const VarDecl *VD = VR->getDecl();

    if (VD->getAttr<BlocksAttr>() || !VD->hasLocalStorage())
      continue;

    // Get the VarRegion associated with VD in the local stack frame.
    const LocationContext *LC = C.getLocationContext();
    VR = C.getSValBuilder().getRegionManager().getVarRegion(VD, LC);
    SVal VRVal = state->getSVal(VR);

    if (VRVal.isUndef())
      if (ExplodedNode *N = C.generateSink()) {
        if (!BT)
          BT.reset(new BuiltinBug("uninitialized variable captured by block"));

        // Generate a bug report.
        SmallString<128> buf;
        llvm::raw_svector_ostream os(buf);

        os << "Variable '" << VD->getName() 
           << "' is uninitialized when captured by block";

        BugReport *R = new BugReport(*BT, os.str(), N);
        if (const Expr *Ex = FindBlockDeclRefExpr(BE->getBody(), VD))
          R->addRange(Ex->getSourceRange());
        R->addVisitor(new FindLastStoreBRVisitor(VRVal, VR));
        // need location of block
        C.EmitReport(R);
      }
  }
}
BugReport *MacOSKeychainAPIChecker::
  generateAllocatedDataNotReleasedReport(const AllocationPair &AP,
                                         ExplodedNode *N) const {
  const ADFunctionInfo &FI = FunctionsToTrack[AP.second->AllocatorIdx];
  initBugType();
  llvm::SmallString<70> sbuf;
  llvm::raw_svector_ostream os(sbuf);

  os << "Allocated data is not released: missing a call to '"
      << FunctionsToTrack[FI.DeallocatorIdx].Name << "'.";
  BugReport *Report = new BugReport(*BT, os.str(), N);
  Report->addVisitor(new SecKeychainBugVisitor(AP.first));
  Report->addRange(SourceRange());
  return Report;
}
Exemple #10
0
void TaintTesterChecker::checkPostStmt(const Expr *E,
                                       CheckerContext &C) const {
  ProgramStateRef State = C.getState();
  if (!State)
    return;

  if (State->isTainted(E, C.getLocationContext())) {
    if (ExplodedNode *N = C.addTransition()) {
      initBugType();
      BugReport *report = new BugReport(*BT, "tainted",N);
      report->addRange(E->getSourceRange());
      C.emitReport(report);
    }
  }
}
void CallAndMessageChecker::emitBadCall(BugType *BT, CheckerContext &C,
                                        const Expr *BadE) {
  ExplodedNode *N = C.generateSink();
  if (!N)
    return;

  BugReport *R = new BugReport(*BT, BT->getName(), N);
  if (BadE) {
    R->addRange(BadE->getSourceRange());
    if (BadE->isGLValue())
      BadE = bugreporter::getDerefExpr(BadE);
    bugreporter::trackNullOrUndefValue(N, BadE, *R);
  }
  C.emitReport(R);
}
void UndefBranchChecker::checkBranchCondition(const Stmt *Condition,
                                              CheckerContext &Ctx) const {
  SVal X = Ctx.getState()->getSVal(Condition, Ctx.getLocationContext());
  if (X.isUndef()) {
    // Generate a sink node, which implicitly marks both outgoing branches as
    // infeasible.
    ExplodedNode *N = Ctx.generateSink();
    if (N) {
      if (!BT)
        BT.reset(new BuiltinBug(
            this, "Branch condition evaluates to a garbage value"));

      // What's going on here: we want to highlight the subexpression of the
      // condition that is the most likely source of the "uninitialized
      // branch condition."  We do a recursive walk of the condition's
      // subexpressions and roughly look for the most nested subexpression
      // that binds to Undefined.  We then highlight that expression's range.

      // Get the predecessor node and check if is a PostStmt with the Stmt
      // being the terminator condition.  We want to inspect the state
      // of that node instead because it will contain main information about
      // the subexpressions.

      // Note: any predecessor will do.  They should have identical state,
      // since all the BlockEdge did was act as an error sink since the value
      // had to already be undefined.
      assert (!N->pred_empty());
      const Expr *Ex = cast<Expr>(Condition);
      ExplodedNode *PrevN = *N->pred_begin();
      ProgramPoint P = PrevN->getLocation();
      ProgramStateRef St = N->getState();

      if (Optional<PostStmt> PS = P.getAs<PostStmt>())
        if (PS->getStmt() == Ex)
          St = PrevN->getState();

      FindUndefExpr FindIt(St, Ctx.getLocationContext());
      Ex = FindIt.FindExpr(Ex);

      // Emit the bug report.
      BugReport *R = new BugReport(*BT, BT->getDescription(), N);
      bugreporter::trackNullOrUndefValue(N, Ex, *R);
      R->addRange(Ex->getSourceRange());

      Ctx.emitReport(R);
    }
  }
}
Exemple #13
0
void
UndefinedArraySubscriptChecker::checkPreStmt(const ArraySubscriptExpr *A,
                                             CheckerContext &C) const {
  if (C.getState()->getSVal(A->getIdx(), C.getLocationContext()).isUndef()) {
    if (ExplodedNode *N = C.generateSink()) {
      if (!BT)
        BT.reset(new BuiltinBug("Array subscript is undefined"));

      // Generate a report for this bug.
      BugReport *R = new BugReport(*BT, BT->getName(), N);
      R->addRange(A->getIdx()->getSourceRange());
      bugreporter::trackNullOrUndefValue(N, A->getIdx(), *R);
      C.emitReport(R);
    }
  }
}
void SimpleStreamChecker::reportDoubleClose(SymbolRef FileDescSym,
                                            const CallEvent &Call,
                                            CheckerContext &C) const {
  // We reached a bug, stop exploring the path here by generating a sink.
  ExplodedNode *ErrNode = C.generateSink();
  // If we've already reached this node on another path, return.
  if (!ErrNode)
    return;

  // Generate the report.
  BugReport *R = new BugReport(*DoubleCloseBugType,
      "Closing a previously closed file stream", ErrNode);
  R->addRange(Call.getSourceRange());
  R->markInteresting(FileDescSym);
  C.emitReport(R);
}
void MacOSXAPIChecker::CheckDispatchOnce(CheckerContext &C, const CallExpr *CE,
                                         StringRef FName) const {
  if (CE->getNumArgs() < 1)
    return;

  // Check if the first argument is stack allocated.  If so, issue a warning
  // because that's likely to be bad news.
  ProgramStateRef state = C.getState();
  const MemRegion *R =
    state->getSVal(CE->getArg(0), C.getLocationContext()).getAsRegion();
  if (!R || !isa<StackSpaceRegion>(R->getMemorySpace()))
    return;

  ExplodedNode *N = C.generateSink(state);
  if (!N)
    return;

  if (!BT_dispatchOnce)
    BT_dispatchOnce.reset(new BugType("Improper use of 'dispatch_once'",
                                      "Mac OS X API"));

  // Handle _dispatch_once.  In some versions of the OS X SDK we have the case
  // that dispatch_once is a macro that wraps a call to _dispatch_once.
  // _dispatch_once is then a function which then calls the real dispatch_once.
  // Users do not care; they just want the warning at the top-level call.
  if (CE->getLocStart().isMacroID()) {
    StringRef TrimmedFName = FName.ltrim("_");
    if (TrimmedFName != FName)
      FName = TrimmedFName;
  }
  
  SmallString<256> S;
  llvm::raw_svector_ostream os(S);
  os << "Call to '" << FName << "' uses";
  if (const VarRegion *VR = dyn_cast<VarRegion>(R))
    os << " the local variable '" << VR->getDecl()->getName() << '\'';
  else
    os << " stack allocated memory";
  os << " for the predicate value.  Using such transient memory for "
        "the predicate is potentially dangerous.";
  if (isa<VarRegion>(R) && isa<StackLocalsSpaceRegion>(R->getMemorySpace()))
    os << "  Perhaps you intended to declare the variable as 'static'?";

  BugReport *report = new BugReport(*BT_dispatchOnce, os.str(), N);
  report->addRange(CE->getArg(0)->getSourceRange());
  C.emitReport(report);
}
  void DanglingDelegateChecker::verifyIvarDynamicStateAgainstStaticFacts(const Expr &expr, const ObjCIvarDecl *ivarDecl,  CheckerContext &context) const {

    // first retrieve the dangerous 'static' facts we know about the ivar
    if (!ivarDecl) {
      return;
    }
    const ObjCImplFacts *facts = getCurrentFacts(getCurrentTopClassInterface(context));
    if (!facts || facts->_ivarFactsMap.find(ivarDecl) == facts->_ivarFactsMap.end()) {
      // not an interesting ivar (no entry)
      return;
    }
    const IvarFacts &ivarFacts = facts->_ivarFactsMap.at(ivarDecl);
    std::string ivarName = ivarDecl->getNameAsString();

    // second retrieve the current 'dynamic' state of the ivar
    ProgramStateRef state = context.getState();
    const IvarDynamicState emptyIds;
    const IvarDynamicState *ids = state->get<IvarMap>(ivarDecl);
    if (!ids) {
      ids = &emptyIds;
    }

    // Verify that all the dangerous properties have been cleared
    const StringSet &dangerousProperties = ivarFacts._mayStoreSelfInUnsafeProperty;
    const StringSet &clearedProperties = ids->_assignPropertyWasCleared;
    const std::function<void(StringRef)> emitBug([this, &context, &expr](StringRef str) {
       BugReport *report = new BugReport(*_bugType, str, context.getPredecessor());
       report->addRange(expr.getSourceRange());
       context.emitReport(report);
    });

    verifyAndReportDangerousProperties(dangerousProperties,
                                       clearedProperties,
                                       ivarName,
                                       ivarDecl->getType(),
                                       "",
                                       emitBug);

    // Verify that the object in the ivar is not 'observing' self (TODO)
//    if (ivarFacts._mayObserveSelf && !ids->_observerWasCleared) {
//    }

    // Verify that the object in the ivar is not 'targeting' self (TODO)
//    if (ivarFacts._mayTargetSelf && !ids->_targetWasCleared) {
//    }

  }
void NilArgChecker::WarnNilArg(CheckerContext &C,
                               const ObjCMethodCall &msg,
                               unsigned int Arg) const
{
  if (!BT)
    BT.reset(new APIMisuse("nil argument"));
  
  if (ExplodedNode *N = C.generateSink()) {
    SmallString<128> sbuf;
    llvm::raw_svector_ostream os(sbuf);
    os << "Argument to '" << GetReceiverInterfaceName(msg) << "' method '"
       << msg.getSelector().getAsString() << "' cannot be nil";

    BugReport *R = new BugReport(*BT, os.str(), N);
    R->addRange(msg.getArgSourceRange(Arg));
    C.emitReport(R);
  }
}
void ObjCContainersChecker::checkPreStmt(const CallExpr *CE,
                                         CheckerContext &C) const {
  StringRef Name = C.getCalleeName(CE);
  if (Name.empty() || CE->getNumArgs() < 2)
    return;

  // Check the array access.
  if (Name.equals("CFArrayGetValueAtIndex")) {
    ProgramStateRef State = C.getState();
    // Retrieve the size.
    // Find out if we saw this array symbol before and have information about it.
    const Expr *ArrayExpr = CE->getArg(0);
    SymbolRef ArraySym = getArraySym(ArrayExpr, C);
    if (!ArraySym)
      return;

    const DefinedSVal *Size = State->get<ArraySizeMap>(ArraySym);

    if (!Size)
      return;

    // Get the index.
    const Expr *IdxExpr = CE->getArg(1);
    SVal IdxVal = State->getSVal(IdxExpr, C.getLocationContext());
    if (IdxVal.isUnknownOrUndef())
      return;
    DefinedSVal Idx = cast<DefinedSVal>(IdxVal);
    
    // Now, check if 'Idx in [0, Size-1]'.
    const QualType T = IdxExpr->getType();
    ProgramStateRef StInBound = State->assumeInBound(Idx, *Size, true, T);
    ProgramStateRef StOutBound = State->assumeInBound(Idx, *Size, false, T);
    if (StOutBound && !StInBound) {
      ExplodedNode *N = C.generateSink(StOutBound);
      if (!N)
        return;
      initBugType();
      BugReport *R = new BugReport(*BT, "Index is out of bounds", N);
      R->addRange(IdxExpr->getSourceRange());
      C.EmitReport(R);
      return;
    }
  }
}
Exemple #19
0
void CallAndMessageChecker::checkPreObjCMessage(ObjCMessage msg,
                                                CheckerContext &C) const {

  ProgramStateRef state = C.getState();
  const LocationContext *LCtx = C.getLocationContext();

  // FIXME: Handle 'super'?
  if (const Expr *receiver = msg.getInstanceReceiver()) {
    SVal recVal = state->getSVal(receiver, LCtx);
    if (recVal.isUndef()) {
      if (ExplodedNode *N = C.generateSink()) {
        if (!BT_msg_undef)
          BT_msg_undef.reset(new BuiltinBug("Receiver in message expression is "
                                            "an uninitialized value"));
        BugReport *R =
          new BugReport(*BT_msg_undef, BT_msg_undef->getName(), N);
        R->addRange(receiver->getSourceRange());
        R->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N,
                                                                   receiver));
        C.EmitReport(R);
      }
      return;
    } else {
      // Bifurcate the state into nil and non-nil ones.
      DefinedOrUnknownSVal receiverVal = cast<DefinedOrUnknownSVal>(recVal);
  
      ProgramStateRef notNilState, nilState;
      llvm::tie(notNilState, nilState) = state->assume(receiverVal);
  
      // Handle receiver must be nil.
      if (nilState && !notNilState) {
        HandleNilReceiver(C, state, msg);
        return;
      }
    }
  }

  const char *bugDesc = msg.isPropertySetter() ?
                     "Argument for property setter is an uninitialized value"
                   : "Argument in message expression is an uninitialized value";
  // Check for any arguments that are uninitialized/undefined.
  PreVisitProcessArgs(C, CallOrObjCMessage(msg, state, LCtx),
                      bugDesc, BT_msg_arg);
}
// FIXME: Eventually this should be rolled into the MallocChecker, but this
// check is more basic and is valuable for widespread use.
void UnixAPIChecker::CheckMallocZero(CheckerContext &C,
                                     const CallExpr *CE) const {

  // Sanity check that malloc takes one argument.
  if (CE->getNumArgs() != 1)
    return;

  // Check if the allocation size is 0.
  const ProgramState *state = C.getState();
  SVal argVal = state->getSVal(CE->getArg(0));

  if (argVal.isUnknownOrUndef())
    return;
  
  const ProgramState *trueState, *falseState;
  llvm::tie(trueState, falseState) = state->assume(cast<DefinedSVal>(argVal));
  
  // Is the value perfectly constrained to zero?
  if (falseState && !trueState) {
    ExplodedNode *N = C.generateSink(falseState);
    if (!N)
      return;
    
    // FIXME: Add reference to CERT advisory, and/or C99 standard in bug
    // output.

    LazyInitialize(BT_mallocZero, "Undefined allocation of 0 bytes");
    
    BugReport *report =
      new BugReport(*BT_mallocZero, "Call to 'malloc' has an allocation"
                                            " size of 0 bytes", N);
    report->addRange(CE->getArg(0)->getSourceRange());
    report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N,
                                                                CE->getArg(0)));
    C.EmitReport(report);
    return;
  }
  // Assume the the value is non-zero going forward.
  assert(trueState);
  if (trueState != state) {
    C.addTransition(trueState);
  }
}
void NilArgChecker::WarnIfNilArg(CheckerContext &C,
                                 const ObjCMethodCall &msg,
                                 unsigned int Arg,
                                 FoundationClass Class,
                                 bool CanBeSubscript) const {
    // Check if the argument is nil.
    ProgramStateRef State = C.getState();
    if (!State->isNull(msg.getArgSVal(Arg)).isConstrainedTrue())
        return;

    if (!BT)
        BT.reset(new APIMisuse("nil argument"));

    if (ExplodedNode *N = C.generateSink()) {
        SmallString<128> sbuf;
        llvm::raw_svector_ostream os(sbuf);

        if (CanBeSubscript && msg.getMessageKind() == OCM_Subscript) {

            if (Class == FC_NSArray) {
                os << "Array element cannot be nil";
            } else if (Class == FC_NSDictionary) {
                if (Arg == 0)
                    os << "Dictionary object cannot be nil";
                else {
                    assert(Arg == 1);
                    os << "Dictionary key cannot be nil";
                }
            } else
                llvm_unreachable("Missing foundation class for the subscript expr");

        } else {
            os << "Argument to '" << GetReceiverInterfaceName(msg) << "' method '"
               << msg.getSelector().getAsString() << "' cannot be nil";
        }

        BugReport *R = new BugReport(*BT, os.str(), N);
        R->addRange(msg.getArgSourceRange(Arg));
        bugreporter::trackNullOrUndefValue(N, msg.getArgExpr(Arg), *R);
        C.emitReport(R);
    }
}
  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);
    }
  }
bool GenericTaintChecker::generateReportIfTainted(const Expr *E,
                                                  const char Msg[],
                                                  CheckerContext &C) const {
  assert(E);

  // Check for taint.
  ProgramStateRef State = C.getState();
  if (!State->isTainted(getPointedToSymbol(C, E)) &&
      !State->isTainted(E, C.getLocationContext()))
    return false;

  // Generate diagnostic.
  if (ExplodedNode *N = C.addTransition()) {
    initBugType();
    BugReport *report = new BugReport(*BT, Msg, N);
    report->addRange(E->getSourceRange());
    C.emitReport(report);
    return true;
  }
  return false;
}
void ReturnUndefChecker::checkPreStmt(const ReturnStmt *RS,
                                      CheckerContext &C) const {
 
  const Expr *RetE = RS->getRetValue();
  if (!RetE)
    return;
  
  if (!C.getState()->getSVal(RetE, C.getLocationContext()).isUndef())
    return;
  
  // "return;" is modeled to evaluate to an UndefinedValue. Allow UndefinedValue
  // to be returned in functions returning void to support the following pattern:
  // void foo() {
  //  return;
  // }
  // void test() {
  //   return foo();
  // }
  const StackFrameContext *SFC = C.getStackFrame();
  QualType RT = CallEvent::getDeclaredResultType(SFC->getDecl());
  if (!RT.isNull() && RT->isSpecificBuiltinType(BuiltinType::Void))
    return;

  ExplodedNode *N = C.generateSink();

  if (!N)
    return;
  
  if (!BT)
    BT.reset(new BuiltinBug("Garbage return value",
                            "Undefined or garbage value returned to caller"));
    
  BugReport *report = 
    new BugReport(*BT, BT->getDescription(), N);

  report->addRange(RetE->getSourceRange());
  bugreporter::trackNullOrUndefValue(N, RetE, *report);

  C.emitReport(report);
}
Exemple #25
0
// Generates an error report, indicating that the function whose name is given
// will perform a zero byte allocation.
// Returns false if an error occured, true otherwise.
bool UnixAPIChecker::ReportZeroByteAllocation(CheckerContext &C,
                                              const ProgramState *falseState,
                                              const Expr *arg,
                                              const char *fn_name) const {
  ExplodedNode *N = C.generateSink(falseState);
  if (!N)
    return false;

  LazyInitialize(BT_mallocZero,
    "Undefined allocation of 0 bytes (CERT MEM04-C; CWE-131)");

  llvm::SmallString<256> S;
  llvm::raw_svector_ostream os(S);    
  os << "Call to '" << fn_name << "' has an allocation size of 0 bytes";
  BugReport *report = new BugReport(*BT_mallocZero, os.str(), N);

  report->addRange(arg->getSourceRange());
  report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, arg));
  C.EmitReport(report);

  return true;
}
Exemple #26
0
void UnixAPIChecker::CheckPthreadOnce(CheckerContext &C,
                                      const CallExpr *CE) const {

  // This is similar to 'CheckDispatchOnce' in the MacOSXAPIChecker.
  // They can possibly be refactored.

  if (CE->getNumArgs() < 1)
    return;

  // Check if the first argument is stack allocated.  If so, issue a warning
  // because that's likely to be bad news.
  const ProgramState *state = C.getState();
  const MemRegion *R =
    state->getSVal(CE->getArg(0), C.getLocationContext()).getAsRegion();
  if (!R || !isa<StackSpaceRegion>(R->getMemorySpace()))
    return;

  ExplodedNode *N = C.generateSink(state);
  if (!N)
    return;

  llvm::SmallString<256> S;
  llvm::raw_svector_ostream os(S);
  os << "Call to 'pthread_once' uses";
  if (const VarRegion *VR = dyn_cast<VarRegion>(R))
    os << " the local variable '" << VR->getDecl()->getName() << '\'';
  else
    os << " stack allocated memory";
  os << " for the \"control\" value.  Using such transient memory for "
  "the control value is potentially dangerous.";
  if (isa<VarRegion>(R) && isa<StackLocalsSpaceRegion>(R->getMemorySpace()))
    os << "  Perhaps you intended to declare the variable as 'static'?";

  LazyInitialize(BT_pthreadOnce, "Improper use of 'pthread_once'");

  BugReport *report = new BugReport(*BT_pthreadOnce, os.str(), N);
  report->addRange(CE->getArg(0)->getSourceRange());
  C.EmitReport(report);
}
void TypeConfusionChecker::reportBug(CheckerContext &C, SourceRange SR,
                                       StringRef Message, StringRef declName) const {
  const char *name = "Type confusion checker";
  const char *desc = "Flags unsafe casts";

  ExplodedNode *EN = C.generateSink();
  if (!EN)
    return;

  if (!BT)
    BT.reset(new BuiltinBug(this, name, desc));

  BugReport *R = new BugReport(*BT, Message, EN);
  R->addRange(SR);

  Diag.encodeBugInfo(declName, C);
  for (auto &i : Diag.getBugInfoDiag()) {
      R->addExtraText(i);
   }

  C.emitReport(R);
}
Exemple #28
0
void PointerSubChecker::checkPreStmt(const BinaryOperator *B,
                                     CheckerContext &C) const {
  // When doing pointer subtraction, if the two pointers do not point to the
  // same memory chunk, emit a warning.
  if (B->getOpcode() != BO_Sub)
    return;

  ProgramStateRef state = C.getState();
  const LocationContext *LCtx = C.getLocationContext();
  SVal LV = state->getSVal(B->getLHS(), LCtx);
  SVal RV = state->getSVal(B->getRHS(), LCtx);

  const MemRegion *LR = LV.getAsRegion();
  const MemRegion *RR = RV.getAsRegion();

  if (!(LR && RR))
    return;

  const MemRegion *BaseLR = LR->getBaseRegion();
  const MemRegion *BaseRR = RR->getBaseRegion();

  if (BaseLR == BaseRR)
    return;

  // Allow arithmetic on different symbolic regions.
  if (isa<SymbolicRegion>(BaseLR) || isa<SymbolicRegion>(BaseRR))
    return;

  if (ExplodedNode *N = C.addTransition()) {
    if (!BT)
      BT.reset(
          new BuiltinBug(this, "Pointer subtraction",
                         "Subtraction of two pointers that do not point to "
                         "the same memory chunk may cause incorrect result."));
    BugReport *R = new BugReport(*BT, BT->getDescription(), N);
    R->addRange(B->getSourceRange());
    C.emitReport(R);
  }
}
void ClassReleaseChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
                                              CheckerContext &C) const {

  if (!BT) {
    BT.reset(new APIMisuse(
        this, "message incorrectly sent to class instead of class instance"));

    ASTContext &Ctx = C.getASTContext();
    releaseS = GetNullarySelector("release", Ctx);
    retainS = GetNullarySelector("retain", Ctx);
    autoreleaseS = GetNullarySelector("autorelease", Ctx);
    drainS = GetNullarySelector("drain", Ctx);
  }

  if (msg.isInstanceMessage())
    return;
  const ObjCInterfaceDecl *Class = msg.getReceiverInterface();
  assert(Class);

  Selector S = msg.getSelector();
  if (!(S == releaseS || S == retainS || S == autoreleaseS || S == drainS))
    return;

  if (ExplodedNode *N = C.addTransition()) {
    SmallString<200> buf;
    llvm::raw_svector_ostream os(buf);

    os << "The '";
    S.print(os);
    os << "' message should be sent to instances "
          "of class '" << Class->getName()
       << "' and not the class directly";

    BugReport *report = new BugReport(*BT, os.str(), N);
    report->addRange(msg.getSourceRange());
    C.emitReport(report);
  }
}
void CastToStructChecker::checkPreStmt(const CastExpr *CE,
                                       CheckerContext &C) const {
    const Expr *E = CE->getSubExpr();
    ASTContext &Ctx = C.getASTContext();
    QualType OrigTy = Ctx.getCanonicalType(E->getType());
    QualType ToTy = Ctx.getCanonicalType(CE->getType());

    const PointerType *OrigPTy = dyn_cast<PointerType>(OrigTy.getTypePtr());
    const PointerType *ToPTy = dyn_cast<PointerType>(ToTy.getTypePtr());

    if (!ToPTy || !OrigPTy)
        return;

    QualType OrigPointeeTy = OrigPTy->getPointeeType();
    QualType ToPointeeTy = ToPTy->getPointeeType();

    if (!ToPointeeTy->isStructureOrClassType())
        return;

    // We allow cast from void*.
    if (OrigPointeeTy->isVoidType())
        return;

    // Now the cast-to-type is struct pointer, the original type is not void*.
    if (!OrigPointeeTy->isRecordType()) {
        if (ExplodedNode *N = C.addTransition()) {
            if (!BT)
                BT.reset(
                    new BuiltinBug(this, "Cast from non-struct type to struct type",
                                   "Casting a non-structure type to a structure type "
                                   "and accessing a field can lead to memory access "
                                   "errors or data corruption."));
            BugReport *R = new BugReport(*BT,BT->getDescription(), N);
            R->addRange(CE->getSourceRange());
            C.emitReport(R);
        }
    }
}