Example #1
0
void UndefResultChecker::checkPostStmt(const BinaryOperator *B,
                                       CheckerContext &C) const {
  ProgramStateRef state = C.getState();
  const LocationContext *LCtx = C.getLocationContext();
  if (state->getSVal(B, LCtx).isUndef()) {

    // Do not report assignments of uninitialized values inside swap functions.
    // This should allow to swap partially uninitialized structs
    // (radar://14129997)
    if (const FunctionDecl *EnclosingFunctionDecl =
        dyn_cast<FunctionDecl>(C.getStackFrame()->getDecl()))
      if (C.getCalleeName(EnclosingFunctionDecl) == "swap")
        return;

    // Generate an error node.
    ExplodedNode *N = C.generateErrorNode();
    if (!N)
      return;

    if (!BT)
      BT.reset(
          new BuiltinBug(this, "Result of operation is garbage or undefined"));

    SmallString<256> sbuf;
    llvm::raw_svector_ostream OS(sbuf);
    const Expr *Ex = nullptr;
    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";
    }
    auto report = llvm::make_unique<BugReport>(*BT, OS.str(), N);
    if (Ex) {
      report->addRange(Ex->getSourceRange());
      bugreporter::trackNullOrUndefValue(N, Ex, *report);
    }
    else
      bugreporter::trackNullOrUndefValue(N, B, *report);

    C.emitReport(std::move(report));
  }
}
Example #2
0
void ReturnUndefChecker::checkPreStmt(const ReturnStmt *RS,
                                      CheckerContext &C) const {
  const Expr *RetE = RS->getRetValue();
  if (!RetE)
    return;
  SVal RetVal = C.getSVal(RetE);

  const StackFrameContext *SFC = C.getStackFrame();
  QualType RT = CallEvent::getDeclaredResultType(SFC->getDecl());

  if (RetVal.isUndef()) {
    // "return;" is modeled to evaluate to an UndefinedVal. Allow UndefinedVal
    // to be returned in functions returning void to support this pattern:
    //   void foo() {
    //     return;
    //   }
    //   void test() {
    //     return foo();
    //   }
    if (RT.isNull() || !RT->isVoidType())
      emitUndef(C, RetE);
    return;
  }

  if (RT.isNull())
    return;

  if (RT->isReferenceType()) {
    checkReference(C, RetE, RetVal.castAs<DefinedOrUnknownSVal>());
    return;
  }
}
void UndefinedAssignmentChecker::checkBind(SVal location, SVal val,
        const Stmt *StoreE,
        CheckerContext &C) const {
    if (!val.isUndef())
        return;

    // Do not report assignments of uninitialized values inside swap functions.
    // This should allow to swap partially uninitialized structs
    // (radar://14129997)
    if (const FunctionDecl *EnclosingFunctionDecl =
                dyn_cast<FunctionDecl>(C.getStackFrame()->getDecl()))
        if (C.getCalleeName(EnclosingFunctionDecl) == "swap")
            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);
}
bool TestAfterDivZeroChecker::hasDivZeroMap(SVal Var,
                                            const CheckerContext &C) const {
  SymbolRef SR = Var.getAsSymbol();
  if (!SR)
    return false;

  ZeroState ZS(SR, C.getBlockID(), C.getStackFrame());
  return C.getState()->contains<DivZeroMap>(ZS);
}
void TestAfterDivZeroChecker::setDivZeroMap(SVal Var, CheckerContext &C) const {
  SymbolRef SR = Var.getAsSymbol();
  if (!SR)
    return;

  ProgramStateRef State = C.getState();
  State =
      State->add<DivZeroMap>(ZeroState(SR, C.getBlockID(), C.getStackFrame()));
  C.addTransition(State);
}
static Optional<nonloc::LazyCompoundVal>
getObjectVal(const CXXConstructorDecl *CtorDecl, CheckerContext &Context) {

  Loc ThisLoc = Context.getSValBuilder().getCXXThis(CtorDecl->getParent(),
                                                    Context.getStackFrame());
  // Getting the value for 'this'.
  SVal This = Context.getState()->getSVal(ThisLoc);

  // Getting the value for '*this'.
  SVal Object = Context.getState()->getSVal(This.castAs<Loc>());

  return Object.getAs<nonloc::LazyCompoundVal>();
}
void TestAfterDivZeroChecker::reportBug(SVal Val, CheckerContext &C) const {
  if (ExplodedNode *N = C.generateSink(C.getState())) {
    if (!DivZeroBug)
      DivZeroBug.reset(new BuiltinBug(this, "Division by zero"));

    BugReport *R =
        new BugReport(*DivZeroBug, "Value being compared against zero has "
                                   "already been used for division",
                      N);

    R->addVisitor(new DivisionBRVisitor(Val.getAsSymbol(), C.getStackFrame()));
    C.emitReport(R);
  }
}
void TestAfterDivZeroChecker::reportBug(SVal Val, CheckerContext &C) const {
  if (ExplodedNode *N = C.generateSink(C.getState())) {
    if (!DivZeroBug)
      DivZeroBug.reset(new BuiltinBug(this, "Division by zero"));

    auto R = llvm::make_unique<BugReport>(
        *DivZeroBug, "Value being compared against zero has already been used "
                     "for division",
        N);

    R->addVisitor(llvm::make_unique<DivisionBRVisitor>(Val.getAsSymbol(),
                                                       C.getStackFrame()));
    C.emitReport(std::move(R));
  }
}
void TestAfterDivZeroChecker::checkEndFunction(CheckerContext &C) const {
  ProgramStateRef State = C.getState();

  DivZeroMapTy DivZeroes = State->get<DivZeroMap>();
  if (DivZeroes.isEmpty())
    return;

  DivZeroMapTy::Factory &F = State->get_context<DivZeroMap>();
  for (llvm::ImmutableSet<ZeroState>::iterator I = DivZeroes.begin(),
                                               E = DivZeroes.end();
       I != E; ++I) {
    ZeroState ZS = *I;
    if (ZS.getStackFrameContext() == C.getStackFrame())
      DivZeroes = F.remove(DivZeroes, ZS);
  }
  C.addTransition(State->set<DivZeroMap>(DivZeroes));
}
  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 ReturnUndefChecker::checkPreStmt(const ReturnStmt *RS,
                                      CheckerContext &C) const {
  const Expr *RetE = RS->getRetValue();
  if (!RetE)
    return;
  SVal RetVal = C.getSVal(RetE);

  const StackFrameContext *SFC = C.getStackFrame();
  QualType RT = CallEvent::getDeclaredResultType(SFC->getDecl());

  if (RetVal.isUndef()) {
    // "return;" is modeled to evaluate to an UndefinedVal. Allow UndefinedVal
    // to be returned in functions returning void to support this pattern:
    //   void foo() {
    //     return;
    //   }
    //   void test() {
    //     return foo();
    //   }
    if (!RT.isNull() && RT->isVoidType())
      return;

    // Not all blocks have explicitly-specified return types; if the return type
    // is not available, but the return value expression has 'void' type, assume
    // Sema already checked it.
    if (RT.isNull() && isa<BlockDecl>(SFC->getDecl()) &&
        RetE->getType()->isVoidType())
      return;

    emitUndef(C, RetE);
    return;
  }

  if (RT.isNull())
    return;

  if (RT->isReferenceType()) {
    checkReference(C, RetE, RetVal.castAs<DefinedOrUnknownSVal>());
    return;
  }
}
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);
}
void UndefinedAssignmentChecker::checkBind(SVal location, SVal val,
                                           const Stmt *StoreE,
                                           CheckerContext &C) const {
  if (!val.isUndef())
    return;

  // Do not report assignments of uninitialized values inside swap functions.
  // This should allow to swap partially uninitialized structs
  // (radar://14129997)
  if (const FunctionDecl *EnclosingFunctionDecl =
      dyn_cast<FunctionDecl>(C.getStackFrame()->getDecl()))
    if (C.getCalleeName(EnclosingFunctionDecl) == "swap")
      return;

  ExplodedNode *N = C.generateErrorNode();

  if (!N)
    return;

  static const char *const DefaultMsg =
      "Assigned value is garbage or undefined";
  if (!BT)
    BT.reset(new BuiltinBug(this, DefaultMsg));

  // Generate a report for this bug.
  llvm::SmallString<128> Str;
  llvm::raw_svector_ostream OS(Str);

  const Expr *ex = nullptr;

  while (StoreE) {
    if (const UnaryOperator *U = dyn_cast<UnaryOperator>(StoreE)) {
      OS << "The expression is an uninitialized value. "
            "The computed value will also be garbage";

      ex = U->getSubExpr();
      break;
    }

    if (const BinaryOperator *B = dyn_cast<BinaryOperator>(StoreE)) {
      if (B->isCompoundAssignmentOp()) {
        if (C.getSVal(B->getLHS()).isUndef()) {
          OS << "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();
    }

    if (const auto *CD =
            dyn_cast<CXXConstructorDecl>(C.getStackFrame()->getDecl())) {
      if (CD->isImplicit()) {
        for (auto I : CD->inits()) {
          if (I->getInit()->IgnoreImpCasts() == StoreE) {
            OS << "Value assigned to field '" << I->getMember()->getName()
               << "' in implicit constructor is garbage or undefined";
            break;
          }
        }
      }
    }

    break;
  }

  if (OS.str().empty())
    OS << DefaultMsg;

  auto R = llvm::make_unique<BugReport>(*BT, OS.str(), N);
  if (ex) {
    R->addRange(ex->getSourceRange());
    bugreporter::trackExpressionValue(N, ex, *R);
  }
  C.emitReport(std::move(R));
}
void UninitializedObjectChecker::checkEndFunction(
    const ReturnStmt *RS, CheckerContext &Context) const {

  const auto *CtorDecl = dyn_cast_or_null<CXXConstructorDecl>(
      Context.getLocationContext()->getDecl());
  if (!CtorDecl)
    return;

  if (!CtorDecl->isUserProvided())
    return;

  if (CtorDecl->getParent()->isUnion())
    return;

  // This avoids essentially the same error being reported multiple times.
  if (willObjectBeAnalyzedLater(CtorDecl, Context))
    return;

  Optional<nonloc::LazyCompoundVal> Object = getObjectVal(CtorDecl, Context);
  if (!Object)
    return;

  FindUninitializedFields F(Context.getState(), Object->getRegion(),
                            CheckPointeeInitialization);

  const UninitFieldMap &UninitFields = F.getUninitFields();

  if (UninitFields.empty())
    return;

  // In non-pedantic mode, if Object's region doesn't contain a single
  // initialized field, we'll assume that Object was intentionally left
  // uninitialized.
  if (!IsPedantic && !F.isAnyFieldInitialized())
    return;

  // There are uninitialized fields in the record.

  ExplodedNode *Node = Context.generateNonFatalErrorNode(Context.getState());
  if (!Node)
    return;

  PathDiagnosticLocation LocUsedForUniqueing;
  const Stmt *CallSite = Context.getStackFrame()->getCallSite();
  if (CallSite)
    LocUsedForUniqueing = PathDiagnosticLocation::createBegin(
        CallSite, Context.getSourceManager(), Node->getLocationContext());

  // For Plist consumers that don't support notes just yet, we'll convert notes
  // to warnings.
  if (ShouldConvertNotesToWarnings) {
    for (const auto &Pair : UninitFields) {

      auto Report = llvm::make_unique<BugReport>(
          *BT_uninitField, Pair.second, Node, LocUsedForUniqueing,
          Node->getLocationContext()->getDecl());
      Context.emitReport(std::move(Report));
    }
    return;
  }

  SmallString<100> WarningBuf;
  llvm::raw_svector_ostream WarningOS(WarningBuf);
  WarningOS << UninitFields.size() << " uninitialized field"
            << (UninitFields.size() == 1 ? "" : "s")
            << " at the end of the constructor call";

  auto Report = llvm::make_unique<BugReport>(
      *BT_uninitField, WarningOS.str(), Node, LocUsedForUniqueing,
      Node->getLocationContext()->getDecl());

  for (const auto &Pair : UninitFields) {
    Report->addNote(Pair.second,
                    PathDiagnosticLocation::create(Pair.first->getDecl(),
                                                   Context.getSourceManager()));
  }
  Context.emitReport(std::move(Report));
}