Index: lib/StaticAnalyzer/Checkers/Checkers.td =================================================================== --- lib/StaticAnalyzer/Checkers/Checkers.td +++ lib/StaticAnalyzer/Checkers/Checkers.td @@ -26,11 +26,17 @@ def DeadCode : Package<"deadcode">; def DeadCodeAlpha : Package<"deadcode">, InPackage, Hidden; +def Different : Package<"different">; +def DifferentAlpha : Package<"different">, InPackage, Hidden; + def Security : Package <"security">; def InsecureAPI : Package<"insecureAPI">, InPackage; def SecurityAlpha : Package<"security">, InPackage, Hidden; def Taint : Package<"taint">, InPackage, Hidden; +def UndefBehavior : Package<"undefbehavior">; +def UndefBehaviorAlpha : Package<"undefbehavior">, InPackage, Hidden; + def Unix : Package<"unix">; def UnixAlpha : Package<"unix">, InPackage, Hidden; def CString : Package<"cstring">, InPackage, Hidden; @@ -43,9 +49,6 @@ def CoreFoundation : Package<"coreFoundation">, InPackage; def Containers : Package<"containers">, InPackage; -def Different : Package<"different">; -def DifferentAlpha : Package<"different">, InPackage, Hidden; - def LLVM : Package<"llvm">; def Debug : Package<"debug">; @@ -221,6 +224,18 @@ } // end "alpha.deadcode" //===----------------------------------------------------------------------===// +// Different checkers. +//===----------------------------------------------------------------------===// + +let ParentPackage = DifferentAlpha in { + +def IntegerOverflowDef : Checker<"IntegerOverflow">, + HelpText<"Check for integer overflow resulting in defined behavior only">, + DescFile<"IntegerOverflowChecker.cpp">; + +} // end "alpha.different" + +//===----------------------------------------------------------------------===// // Security checkers. //===----------------------------------------------------------------------===// @@ -289,6 +304,18 @@ } // end "alpha.security.taint" //===----------------------------------------------------------------------===// +// Undefined behavior checkers. +//===----------------------------------------------------------------------===// + +let ParentPackage = UndefBehaviorAlpha in { + +def IntegerOverflowUndef : Checker<"IntegerOverflow">, + HelpText<"Check for integer overflow resulting in undefined behavior">, + DescFile<"IntegerOverflowChecker.cpp">; + +} // end "alpha.undefbehavior" + +//===----------------------------------------------------------------------===// // Unix API checkers. //===----------------------------------------------------------------------===// @@ -488,16 +515,6 @@ } //===----------------------------------------------------------------------===// -// Different checkers. -//===----------------------------------------------------------------------===// - -def IntegerOverflowChecker : Checker<"IntegerOverflow">, - InPackage, - HelpText<"Check for integer overflow">, - DescFile<"IntegerOverflowChecker.cpp">; - - -//===----------------------------------------------------------------------===// // Checkers for LLVM development. //===----------------------------------------------------------------------===// Index: lib/StaticAnalyzer/Checkers/IntegerOverflowChecker.cpp =================================================================== --- lib/StaticAnalyzer/Checkers/IntegerOverflowChecker.cpp +++ lib/StaticAnalyzer/Checkers/IntegerOverflowChecker.cpp @@ -6,9 +6,23 @@ // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// +/// +/// \file +/// \brief This defines IntegerOverflowChecker, which checks arithmetic +/// operations for integer overflows. This check corresponds to CWE-190. +/// +//===----------------------------------------------------------------------===// +// +// Check for overflow performs by checkAdd(), checkSub() and checkMul() +// functions. checkAdd() and checkSub() consist of two parts for signed integer +// overflow check and unsigned integer overflow check(wraparound). // -// This file defines IntegerOverflowChecker, which checks arithmetic operations -// for integer overflows. This check corresponds to CWE-190. +// Couple of heuristics were added for FP suppressing. USubHeuristic prevents +// warnings for intentional integer overflow while getting i.e UINT_MAX by +// subtracting 1U from 0U. GlobalsMembersHeuristic suppresses warning if +// arguments of arithmetic operation are global variables or class members. +// Sometimes CSA fails to determine right value for that type of arguments and +// inter-unit analysis assumed to be the best solution of this problem. // //===----------------------------------------------------------------------===// @@ -31,99 +45,170 @@ check::PostStmt, check::PostStmt, check::Bind> { - mutable std::unique_ptr BT; + mutable std::unique_ptr BT_Def, BT_Undef; + /// Stores SourceLocations in which overflows happened for reducing the amount + /// of equivalent warnings. mutable std::set OverflowLoc; - struct OutputPack { - const bool LValueIsTainted; - const bool RValueIsTainted; - const std::string LValue; - const std::string RValue; - std::string Operation; - OutputPack(bool LValueIsTainted, bool RValueIsTainted, - const std::string &LValue, const std::string &RValue) - : LValueIsTainted(LValueIsTainted), RValueIsTainted(RValueIsTainted), - LValue(LValue), RValue(RValue) {} + struct IntegerOverflowFilter { + DefaultBool CheckIntegerOverflowDef; + DefaultBool CheckIntegerOverflowUndef; + CheckName CheckNameIntegerOverflowDef; + CheckName CheckNameIntegerOverflowUndef; }; void reportBug(const std::string &Msg, CheckerContext &C, - const SourceLocation &SL) const; + const SourceLocation &SL, bool isUndef) const; + + std::string composeMsg(ProgramStateRef StateNotOverflow, const SVal &Lhs, + const SVal &Rhs, const Expr *ExprLhs, + const Expr *ExprRhs, bool isSigned, bool isOverflow, + BinaryOperator::Opcode *Op, CheckerContext &C) const; + /// Check if addition of \p Lhs and \p Rhs can overflow. Optional checkAdd(CheckerContext &C, const SVal &Lhs, - const SVal &Rhs, - QualType BinType) const; + const SVal &Rhs, QualType BinType, + bool &isOverflow) const; + /// Check if subtraction of \p Lhs and \p Rhs can overflow. Optional checkSub(CheckerContext &C, const SVal &Lhs, const SVal &Rhs, - const QualType &BinType) const; + const QualType &BinType, + bool &isOverflow) const; + /// Check if multiplication of \p Lhs and \p Rhs can overflow. Optional checkMul(CheckerContext &C, const SVal &Lhs, const SVal &Rhs, - const QualType &BinType) const; - - void addRangeInformation(const SVal &Val, CheckerContext &C, - llvm::raw_string_ostream &Stream) const; + const QualType &BinType, + bool &isOverflow) const; - void processStates(ProgramStateRef StateOverflow, - ProgramStateRef StateNotOverflow, const OutputPack &Pack, - CheckerContext &C, const SourceLocation &SL) const; + /// \returns dump and constraints of \p Val. + std::string getSymbolInformation(const SVal &Val, const Expr *E, + CheckerContext &C) const; + /// We ignore intentional underflow because of subtracting X from zero - the + /// minimum unsigned value. bool makeUSubHeuristics(const BinaryOperator *BO) const; + /// \returns true if there are suspicions that the actual value might be lose + /// by analyzer. bool makeGlobalsMembersHeuristics(const SVal &Val, const Stmt *S, CheckerContext &C) const; + /// Check if \p S should be ignored when participates in overflow. bool hasGlobalVariablesOrMembers(const Stmt *S, CheckerContext &C) const; + /// Check if \p SE should be ignored when participates in overflow. bool hasGlobalVariablesOrMembers(const SymExpr *SE, CheckerContext &C) const; - ProgramStateRef addGoodSink(const Stmt *S, ProgramStateRef State, - const LocationContext *LCtx) const; + ProgramStateRef addToWhiteList(const Stmt *S, ProgramStateRef State, + const LocationContext *LCtx) const; - inline ProgramStateRef addGoodSink(const SVal &SV, - ProgramStateRef State) const; + inline ProgramStateRef addToWhiteList(const SVal &SV, + ProgramStateRef State) const; - bool isGoodSink(const Stmt *S, ProgramStateRef State, - const LocationContext *LCtx) const; + bool isInWhiteList(const Stmt *S, ProgramStateRef State, + const LocationContext *LCtx) const; - inline bool isGoodSink(const SVal &Val, ProgramStateRef State) const; + inline bool isInWhiteList(const SVal &Val, ProgramStateRef State) const; public: - /// \brief Contains checks for such operations as addition, multiplication, - /// and subtraction. - void checkPreStmt(const BinaryOperator *B, CheckerContext &C) const; + IntegerOverflowFilter Filter; - /// \brief Contains check for new[]. + /// Check addition, multiplication, and subtraction for overflow. + void checkPostStmt(const BinaryOperator *B, CheckerContext &C) const; + + /// Contains check for new[]. void checkPostStmt(const CXXNewExpr *NE, CheckerContext &C) const; + /// Note if value returned by a call should be ignored when participates in + /// overflow. void checkPostStmt(const CallExpr *CE, CheckerContext &C) const; + /// Make MemberExpr ignored. void checkPostStmt(const MemberExpr *ME, CheckerContext &C) const; + /// Note if value which is handled by checkBind should be ignored when + /// participates in overflow. void checkBind(const SVal &Loc, const SVal &Val, const Stmt *S, CheckerContext &C) const; }; } // end anonymous namespace -REGISTER_LIST_WITH_PROGRAMSTATE(ExternalSym, SVal) +/// WhiteList stores symbols change of which can be missed by analyzer. +REGISTER_LIST_WITH_PROGRAMSTATE(WhiteList, SVal) void IntegerOverflowChecker::reportBug(const std::string &Msg, CheckerContext &C, - const SourceLocation &SL) const { + const SourceLocation &SL, + bool isUndef) const { if (const ExplodedNode *N = C.generateSink(C.getState())) { - if (!BT) - BT.reset(new BuiltinBug("Integer overflow")); - - BugReport *R = new BugReport(*BT, Msg, N); + if (isUndef && !BT_Undef) + BT_Undef.reset(new BuiltinBug( + Filter.CheckNameIntegerOverflowUndef, "Integer overflow", + "Arithmetic operation resulted in an overflow")); + else if (!isUndef && !BT_Def) + BT_Def.reset( + new BuiltinBug(Filter.CheckNameIntegerOverflowDef, "Integer overflow", + "Arithmetic operation resulted in an overflow")); + BugReport *R = new BugReport(isUndef ? *BT_Undef : *BT_Def, Msg, N); C.emitReport(R); OverflowLoc.insert(SL); } } +std::string +IntegerOverflowChecker::composeMsg(ProgramStateRef StateNotOverflow, + const SVal &Lhs, const SVal &Rhs, + const Expr *ExprLhs, const Expr *ExprRhs, + bool isSigned, bool isOverflow, + BinaryOperator::Opcode *Op, + CheckerContext &C) const { + std::string Msg; + std::string ErrorType = (!Op || isOverflow) ? "Overflow" : "Underflow"; + if (StateNotOverflow) { + Msg.assign("Possible integer " + ErrorType + ": "); + if (C.getState()->isTainted(Lhs)) + Msg.append("left operand is tainted. "); + else + Msg.append("right operand is tainted. "); + } else { + if (isSigned) + Msg.assign("Undefined behavior: "); + + Msg.append("Integer " + ErrorType + ". "); + } + std::string Operation, Preposition; + + if (!Op || *Op == BO_Mul || *Op == BO_MulAssign) { + Operation = "Multiplication of "; + Preposition = " with "; + } else if (*Op == BO_Add || *Op == BO_AddAssign) { + Operation = "Addition of "; + Preposition = " with "; + } else { + Operation = "Subtraction of "; + Preposition = " from "; + } + + if (Op && (*Op == BO_Sub || (*Op == BO_SubAssign))) + Msg.append(Operation + getSymbolInformation(Rhs, ExprRhs, C) + Preposition + + getSymbolInformation(Lhs, ExprLhs, C)); + else + Msg.append(Operation + getSymbolInformation(Lhs, ExprLhs, C) + Preposition + + getSymbolInformation(Rhs, ExprRhs, C)); + + if (!Op) + Msg.append(" while memory allocation."); + + return Msg; +} + Optional IntegerOverflowChecker::checkAdd(CheckerContext &C, const SVal &Lhs, - const SVal &Rhs, QualType BinType) const { + const SVal &Rhs, QualType BinType, + bool &isOverflow) const { SVal CondOverflow; ProgramStateRef State = C.getState(); SValBuilder &SvalBuilder = C.getSValBuilder(); @@ -166,10 +251,13 @@ SVal CondNegativeOverflow = SvalBuilder.evalBinOp(State, BO_And, CondArgsLtNull, CondArgSumGtNull, CondType); + if (!CondPositiveOverflow.isZeroConstant()) + isOverflow = true; CondOverflow = SvalBuilder.evalBinOp(State, BO_Or, CondPositiveOverflow, CondNegativeOverflow, CondType); } else { + isOverflow = true; // lhs > sum SVal CondLhsGtArgSum = SvalBuilder.evalBinOp(State, BO_GT, Lhs, ValArgSum, CondType); @@ -186,8 +274,8 @@ Optional IntegerOverflowChecker::checkSub(CheckerContext &C, const SVal &Lhs, - const SVal &Rhs, - const QualType &BinType) const { + const SVal &Rhs, const QualType &BinType, + bool &isOverflow) const { SVal CondOverflow; ProgramStateRef State = C.getState(); SValBuilder &SvalBuilder = C.getSValBuilder(); @@ -206,7 +294,7 @@ SVal CondLhsLtNullRhsGtNull = SvalBuilder.evalBinOp(State, BO_And, CondLhsLtNull, CondRhsGtNull, CondType); - // lhs-rhs >= 0 + // lhs - rhs >= 0 SVal CondArgSubGeNull = SvalBuilder.evalBinOp(State, BO_GE, ValArgSub, NullSval, CondType); @@ -237,6 +325,8 @@ CondOverflow = SvalBuilder.evalBinOp(State, BO_Or, CondNegativeOverflow, CondPositiveOverflow, CondType); + if (!CondPositiveOverflow.isZeroConstant()) + isOverflow = true; } else CondOverflow = SvalBuilder.evalBinOp(State, BO_LT, Lhs, Rhs, CondType); @@ -245,8 +335,8 @@ Optional IntegerOverflowChecker::checkMul(CheckerContext &C, const SVal &Lhs, - const SVal &Rhs, - const QualType &BinType) const { + const SVal &Rhs, const QualType &BinType, + bool &isOverflow) const { ProgramStateRef State = C.getState(); ProgramStateRef CondNotOverflow, CondPossibleOverflow; SValBuilder &SvalBuilder = C.getSValBuilder(); @@ -268,7 +358,7 @@ if (!CondOverflow.hasValue()) return CondOverflow; - llvm::tie(CondPossibleOverflow, CondNotOverflow) = + std::tie(CondPossibleOverflow, CondNotOverflow) = State->assume(*CondOverflow); if (CondNotOverflow && CondPossibleOverflow) @@ -285,52 +375,47 @@ CondOverflow = SvalBuilder.evalBinOp(State, BO_NE, ValDiv, Rhs, CondType) .getAs(); } + + isOverflow = BinType->isUnsignedIntegerOrEnumerationType() || + SvalBuilder.evalBinOp(State, BO_LT, Lhs, NullSval, CondType) + .isZeroConstant() == + SvalBuilder.evalBinOp(State, BO_LT, Rhs, NullSval, CondType) + .isZeroConstant(); + return CondOverflow; } -void IntegerOverflowChecker::addRangeInformation( - const SVal &Val, CheckerContext &C, - llvm::raw_string_ostream &Stream) const { - std::string S; - llvm::raw_string_ostream StreamRange(S); - if (Val.getSubKind() == nonloc::SymbolValKind) { - C.getState()->getConstraintManager().print(C.getState(), StreamRange, "\n", - "\n"); - size_t from = StreamRange.str().find(Stream.str() + " : "); +std::string +IntegerOverflowChecker::getSymbolInformation(const SVal &Val, const Expr *E, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + std::string StreamRangeStr, SValDumpStr; + llvm::raw_string_ostream StreamRange(StreamRangeStr), SValDump(SValDumpStr); + Val.dumpToStream(SValDump); + if (Val.getSubKind() == SymbolValKind) { + State->getConstraintManager().print(State, StreamRange, "\n", "\n"); + StreamRange.flush(); + size_t from = StreamRangeStr.find(SValDump.str() + " : "); if (from != std::string::npos) { - size_t to = StreamRange.str().find("\n", from); - from += Stream.str().length(); - Stream << StreamRange.str().substr(from, to - from); + size_t to = StreamRangeStr.find("\n", from); + from += SValDump.str().length(); + SValDump.str().append(StreamRangeStr.substr(from, to - from)); } } -} + if (!E || isa(E->IgnoreParenCasts())) + return SValDump.str(); -void IntegerOverflowChecker::processStates(ProgramStateRef StateOverflow, - ProgramStateRef StateNotOverflow, - const OutputPack &Pack, - CheckerContext &C, - const SourceLocation &SL) const { - std::string Msg; - if (StateOverflow && StateNotOverflow) { - if (Pack.LValueIsTainted) { - Msg.assign("Possible integer overflow while " + Pack.Operation + - ". Left operand is tainted: " + Pack.LValue + " AND " + - Pack.RValue); - reportBug(Msg, C, SL); - } else if (Pack.RValueIsTainted) { - Msg.assign("Possible integer overflow while " + Pack.Operation + - ". Right operand is tainted: " + Pack.LValue + " AND " + - Pack.RValue); - reportBug(Msg, C, SL); - } - return; - } + E = E->IgnoreParens(); + if (const UnaryOperator *UO = dyn_cast(E)) + if ((UO->getOpcode() == UO_Plus || UO->getOpcode() == UO_Minus) && + isa(UO->getSubExpr())) + return SValDump.str(); - if (StateOverflow) { - Msg.assign("Integer overflow while " + Pack.Operation + ". " + Pack.LValue + - " AND " + Pack.RValue); - reportBug(Msg, C, SL); - } + SValDump << " ("; + E->printPretty(SValDump, 0, C.getASTContext().getPrintingPolicy()); + SValDump << ")"; + + return SValDump.str(); } // We ignore intentional underflow with subtracting X from zero - the minimal @@ -345,21 +430,20 @@ return false; } -// Don't track global variables and class members we can't reason about. bool IntegerOverflowChecker::makeGlobalsMembersHeuristics(const SVal &Val, const Stmt *S, CheckerContext &C)const { if (Val.isConstant()) { - bool good = isGoodSink(Val, C.getState()) && + bool good = isInWhiteList(Val, C.getState()) && (S->getStmtClass() != Stmt::IntegerLiteralClass) && (S->getStmtClass() != Stmt::ImplicitCastExprClass); return good ? true : hasGlobalVariablesOrMembers(S, C); } else if (const SymExpr *SE = Val.getAsSymExpr()) - return isGoodSink(Val, C.getState()) ? true - : hasGlobalVariablesOrMembers(SE, C); + return isInWhiteList(Val, C.getState()) ? true + : hasGlobalVariablesOrMembers(SE, C); else if (const MemRegion *Mem = Val.getAsRegion()) - return isGoodSink(Val, C.getState()) || isa(Mem) || + return isInWhiteList(Val, C.getState()) || isa(Mem) || Mem->hasGlobalsOrParametersStorage(); return false; @@ -375,7 +459,7 @@ const LocationContext *LCtx = C.getLocationContext(); if ((S->getStmtClass() != Stmt::ImplicitCastExprClass) && - isGoodSink(S, State, LCtx)) + isInWhiteList(S, State, LCtx)) return true; if (const MemberExpr *MExpr = dyn_cast(S)) { @@ -386,21 +470,20 @@ } if (const ImplicitCastExpr *ICE = dyn_cast(S)) - if (isa(ICE->getSubExpr()) && isGoodSink(C.getSVal(ICE), - State)) + if (isa(ICE->getSubExpr()) && isInWhiteList(C.getSVal(ICE), + State)) return true; if (const DeclRefExpr *DRE = dyn_cast(S)) if (const VarDecl *VarD = dyn_cast(DRE->getDecl())) { Loc VLoc = C.getStoreManager().getLValueVar(VarD, LCtx); SVal VVal = C.getStoreManager().getBinding(State->getStore(), VLoc); - if (isGoodSink(VVal, State)) + if (isInWhiteList(VVal, State)) return true; } // We will not surrender! - for (clang::Stmt::const_child_iterator I = S->child_begin(); - I != S->child_end(); I++) + for (auto I = S->child_begin(); I != S->child_end(); I++) if (hasGlobalVariablesOrMembers(*I, C)) return true; @@ -410,8 +493,8 @@ bool IntegerOverflowChecker::hasGlobalVariablesOrMembers(const SymExpr *SE, CheckerContext &C) const { - ExternalSymTy ES = C.getState()->get(); - for (ExternalSymTy::iterator I = ES.begin(); I != ES.end(); ++I) { + WhiteListTy ES = C.getState()->get(); + for (auto I = ES.begin(); I != ES.end(); ++I) { SVal Val = *I; SymbolRef SR = Val.getAsSymbol(); if (SR == SE) @@ -444,31 +527,30 @@ } ProgramStateRef -IntegerOverflowChecker::addGoodSink(const Stmt *S, ProgramStateRef State, - const LocationContext *LCtx) const { +IntegerOverflowChecker::addToWhiteList(const Stmt *S, ProgramStateRef State, + const LocationContext *LCtx) const { if (const Expr *E = dyn_cast_or_null(S)) S = E->IgnoreParens(); - return addGoodSink(State->getSVal(S, LCtx), State); + return addToWhiteList(State->getSVal(S, LCtx), State); } inline ProgramStateRef -IntegerOverflowChecker::addGoodSink(const SVal &Val, - ProgramStateRef State) const { - return State->get().contains(Val) ? State - : State->add(Val); +IntegerOverflowChecker::addToWhiteList(const SVal &Val, + ProgramStateRef State) const { + return State->get().contains(Val) ? State + : State->add(Val); } -// Returns true, if given Stmt contains global variables/class members. -bool IntegerOverflowChecker::isGoodSink(const Stmt *S, ProgramStateRef State, - const LocationContext *LCtx) const { +bool IntegerOverflowChecker::isInWhiteList(const Stmt *S, ProgramStateRef State, + const LocationContext *LCtx) const { if (const Expr *E = dyn_cast_or_null(S)) S = E->IgnoreParens(); - return isGoodSink(State->getSVal(S, LCtx), State); + return isInWhiteList(State->getSVal(S, LCtx), State); } -inline bool IntegerOverflowChecker::isGoodSink(const SVal &V, - ProgramStateRef State) const { - return State->get().contains(V); +inline bool IntegerOverflowChecker::isInWhiteList(const SVal &V, + ProgramStateRef State) const { + return State->get().contains(V); } void IntegerOverflowChecker::checkPostStmt(const BinaryOperator *B, @@ -480,10 +562,6 @@ !B->getRHS()->getType()->isIntegerType()) return; - BinaryOperator::Opcode Op = B->getOpcode(); - if (Op!=BO_Add && Op!=BO_Mul && Op!=BO_Sub) - return; - ProgramStateRef State = C.getState(); QualType BinType = B->getType(); const Expr *ExprLhs = B->getLHS(); @@ -491,28 +569,21 @@ SVal Lhs = C.getSVal(ExprLhs); SVal Rhs = C.getSVal(ExprRhs); - //if (makeConjHeuristics(Lhs) || makeConjHeuristics(Rhs)) return; - - SVal CondOverflow; - ProgramStateRef StateOverflow, StateNotOverflow; - - std::string SLvalue, SRvalue; - llvm::raw_string_ostream LValue(SLvalue), RValue(SRvalue); - Lhs.dumpToStream(LValue); - Rhs.dumpToStream(RValue); - - addRangeInformation(Rhs, C, RValue); - addRangeInformation(Lhs, C, LValue); - if (makeGlobalsMembersHeuristics(Lhs, ExprLhs, C)) { - C.addTransition(addGoodSink(Lhs, State)); + C.addTransition(addToWhiteList(Lhs, State)); return; } if (makeGlobalsMembersHeuristics(Rhs, ExprRhs, C)) { - C.addTransition(addGoodSink(Rhs, State)); + C.addTransition(addToWhiteList(Rhs, State)); return; } + if (!Filter.CheckIntegerOverflowDef && BinType->isUnsignedIntegerType()) + return; + + if (!Filter.CheckIntegerOverflowUndef && BinType->isSignedIntegerType()) + return; + BinaryOperator::Opcode Op = B->getOpcode(); if (Op != BO_Add && Op != BO_Mul && Op != BO_Sub && Op != BO_AddAssign && Op != BO_MulAssign && Op != BO_SubAssign) @@ -521,109 +592,101 @@ Optional CondOverflow; ProgramStateRef StateOverflow, StateNotOverflow; - OutputPack Pack(State->isTainted(Lhs), State->isTainted(Rhs), LValue.str(), - RValue.str()); - - if (Op == BO_Add || Op == BO_AddAssign) { - CondOverflow = checkAdd(C, Lhs, Rhs, BinType); - if (!CondOverflow) - return; - - llvm::tie(StateOverflow, StateNotOverflow) = State->assume(*CondOverflow); - - Pack.Operation = "addition"; - - processStates(StateOverflow, StateNotOverflow, Pack, C, B->getExprLoc()); - } else if (Op == BO_Sub || Op == BO_SubAssign) { + bool isOverflow = false; + if (Op == BO_Add || Op == BO_AddAssign) + CondOverflow = checkAdd(C, Lhs, Rhs, BinType, isOverflow); + else if (Op == BO_Sub || Op == BO_SubAssign) { if ((BinType->isUnsignedIntegerType()) && makeUSubHeuristics(B)) return; + CondOverflow = checkSub(C, Lhs, Rhs, BinType, isOverflow); + } else if (Op == BO_Mul || Op == BO_MulAssign) + CondOverflow = checkMul(C, Lhs, Rhs, BinType, isOverflow); - CondOverflow = checkSub(C, Lhs, Rhs, BinType); - if (!CondOverflow) - return; - - llvm::tie(StateOverflow, StateNotOverflow) = State->assume(*CondOverflow); - - Pack.Operation = "subtraction"; - - processStates(StateOverflow, StateNotOverflow, Pack, C, B->getExprLoc()); - } else if (Op == BO_Mul || Op == BO_MulAssign) { - CondOverflow = checkMul(C, Lhs, Rhs, BinType); + if (!CondOverflow) + return; - if (!CondOverflow) - return; + std::tie(StateOverflow, StateNotOverflow) = State->assume(*CondOverflow); - llvm::tie(StateOverflow, StateNotOverflow) = State->assume(*CondOverflow); + if (!StateOverflow || + (StateNotOverflow && !(State->isTainted(Lhs) || State->isTainted(Rhs)))) + return; - Pack.Operation = "multiplication"; + std::string Msg = composeMsg(StateNotOverflow, Lhs, Rhs, ExprLhs, ExprRhs, + B->getType()->isSignedIntegerOrEnumerationType(), + isOverflow, &Op, C); - processStates(StateOverflow, StateNotOverflow, Pack, C, B->getExprLoc()); - } + reportBug(Msg, C, B->getExprLoc(), BinType->isSignedIntegerType()); } void IntegerOverflowChecker::checkPostStmt(const CXXNewExpr *NewExpr, CheckerContext &C) const { - if (NewExpr->getOperatorNew()->getOverloadedOperator() != OO_Array_New) + if (!Filter.CheckIntegerOverflowDef) return; - SValBuilder &SvalBuilder = C.getSValBuilder(); - QualType NewExprType = NewExpr->getAllocatedType(); - - uint64_t NewExprTypeSize = C.getASTContext().getTypeSizeInChars(NewExprType) - .getQuantity(); - SVal NewExprTypeSizeVal = SvalBuilder.makeIntVal(NewExprTypeSize, true); + if (NewExpr->getOperatorNew()->getOverloadedOperator() != OO_Array_New) + return; const Expr *ArrSize = NewExpr->getArraySize(); SVal ElementCount = C.getSVal(ArrSize); ProgramStateRef State = C.getState(); - ProgramStateRef StateOverflow, StateNotOverflow; if (makeGlobalsMembersHeuristics(ElementCount, ArrSize, C)) { - C.addTransition(addGoodSink(ElementCount, State)); + C.addTransition(addToWhiteList(ElementCount, State)); return; } - std::string SLvalue, SRvalue; - llvm::raw_string_ostream LValue(SLvalue), RValue(SRvalue); - LValue << NewExprTypeSize; - ElementCount.dumpToStream(RValue); - - addRangeInformation(ElementCount, C, RValue); - - OutputPack Pack(false, State->isTainted(ElementCount), LValue.str(), - RValue.str()); + QualType NewExprType = NewExpr->getAllocatedType(); + uint64_t NewExprTypeSize = C.getASTContext().getTypeSizeInChars(NewExprType) + .getQuantity(); + SValBuilder &SvalBuilder = C.getSValBuilder(); + SVal NewExprTypeSizeVal = SvalBuilder.makeIntVal(NewExprTypeSize, true); - Optional CondOverflow = - checkMul(C, NewExprTypeSizeVal, ElementCount, ArrSize->getType()); + bool isOverflow; + Optional CondOverflow = checkMul(C, NewExprTypeSizeVal, + ElementCount, + ArrSize->getType(), + isOverflow); if (!CondOverflow) return; + ProgramStateRef StateOverflow, StateNotOverflow; std::tie(StateOverflow, StateNotOverflow) = State->assume(*CondOverflow); - Pack.Operation = "memory allocation"; + if (!StateOverflow || (StateNotOverflow && !State->isTainted(ElementCount))) + return; + + std::string Msg = composeMsg(StateNotOverflow, NewExprTypeSizeVal, + ElementCount, 0, ArrSize, false, isOverflow, 0, + C); - processStates(StateOverflow, StateNotOverflow, Pack, C, - NewExpr->getExprLoc()); + reportBug(Msg, C, NewExpr->getExprLoc(), false); } void IntegerOverflowChecker::checkPostStmt(const CallExpr *CE, CheckerContext &C) const { if (makeGlobalsMembersHeuristics(C.getSVal(CE), CE, C)) - C.addTransition(addGoodSink(CE, C.getState(), C.getLocationContext())); + C.addTransition(addToWhiteList(CE, C.getState(), C.getLocationContext())); } void IntegerOverflowChecker::checkPostStmt(const MemberExpr *ME, CheckerContext &C) const { - C.addTransition(addGoodSink(ME, C.getState(), C.getLocationContext())); + C.addTransition(addToWhiteList(ME, C.getState(), C.getLocationContext())); } void IntegerOverflowChecker::checkBind(const SVal &Loc, const SVal &Val, const Stmt *S, CheckerContext &C) const { if (makeGlobalsMembersHeuristics(Val, S, C)) - C.addTransition(addGoodSink(Val, C.getState())); + C.addTransition(addToWhiteList(Val, C.getState())); } -void ento::registerIntegerOverflowChecker(CheckerManager &mgr) { - mgr.registerChecker(); -} +#define REGISTER_CHECKER(name) \ + void ento::register##name(CheckerManager &mgr) { \ + IntegerOverflowChecker *checker = \ + mgr.registerChecker(); \ + checker->Filter.Check##name = true; \ + checker->Filter.CheckName##name = mgr.getCurrentCheckName(); \ + } + +REGISTER_CHECKER(IntegerOverflowDef) +REGISTER_CHECKER(IntegerOverflowUndef) Index: test/Analysis/integer-overflow.cpp =================================================================== --- test/Analysis/integer-overflow.cpp +++ test/Analysis/integer-overflow.cpp @@ -1,26 +1,29 @@ -// RUN: %clang_cc1 -Wno-unused-value -Wno-integer-overflow -Wno-constant-conversion -analyze -analyzer-checker=alpha.different.IntegerOverflow,alpha.security.taint,core.DivideZero,deadcode,core.builtin %s -verify -#define SHRT_MAX ((short)(~0U>>1)) -#define INT_MAX ((int)(~0U>>1)) +// RUN: %clang_cc1 -Wno-unused-value -Wno-integer-overflow -Wno-constant-conversion -analyze -analyzer-checker=alpha.different.IntegerOverflow,alpha.undefbehavior.IntegerOverflow,alpha.security.taint,core.DivideZero,deadcode,core.builtin %s -verify +#include "Inputs/system-header-simulator.h" + +#define SHRT_MAX ((short)(~0U >> 1)) +#define INT_MAX ((int)(~0U >> 1)) #define INT_MIN (-INT_MAX - 1) #define UINT_MAX (~0U) -#define LONG_MAX ((long)(~0UL>>1)) +#define LONG_MAX ((long)(~0UL >> 1)) #define LONG_MIN (-LONG_MAX - 1) #define ULONG_MAX (~0UL) -#define LLONG_MAX ((long long)(~0ULL>>1)) +#define LLONG_MAX ((long long)(~0ULL >> 1)) #define LLONG_MIN (-LLONG_MAX - 1) #define ULLONG_MAX (~0ULL) +char *strchr(const char *s, int c); int randomInt(); // Addition : signed void signAddEW_1(void) { - INT_MAX + 1; // expected-warning{{Integer overflow}} + INT_MAX + 1; // expected-warning{{Undefined behavior: Integer Overflow. Addition of 2147483647 S32b ((int)(~0U >> 1)) with 1 S32b}} } void signAddEW_2(void) { - INT_MIN + INT_MIN; // expected-warning{{Integer overflow}} + INT_MIN + INT_MIN; // expected-warning{{Undefined behavior: Integer Underflow. Addition of -2147483648 S32b (-((int)(~0U >> 1)) - 1) with -2147483648 S32b (-((int)(~0U >> 1)) - 1)}} } void signAddEW_3(void) { - LONG_MAX + 1; // expected-warning{{Integer overflow}} + LONG_MAX + 1; // expected-warning{{Undefined behavior: Integer Overflow. Addition of 9223372036854775807 S64b ((long)(~0UL >> 1)) with 1 S64b}} } void signAddNW_4(void) { SHRT_MAX + 1; // no-warning @@ -28,40 +31,38 @@ void signAddNW_5(int b) { if (b > INT_MAX) b + 3; // no-warning - else if (b == INT_MAX) - b + 3; // no-warning } void signAddEW_6(void) { int a = randomInt(); if (a == INT_MAX) - a + 2; // expected-warning{{Integer overflow}} + a + 2; // expected-warning{{Undefined behavior: Integer Overflow. Addition of 2147483647 S32b (a) with 2 S32b}} else if (a < INT_MAX) a + 2; // no-warning } // Addition : unsigned void unsignAddEW_1(void) { - UINT_MAX + 1; // expected-warning{{Integer overflow}} + UINT_MAX + 1; // expected-warning{{Integer Overflow. Addition of 4294967295 U32b (~0U) with 1 U32b}} } void unsignAddEW_2(void) { - 1 + (unsigned)-1; // expected-warning{{Integer overflow}} + 1 + (unsigned)-1; // expected-warning{{Integer Overflow. Addition of 1 U32b with 4294967295 U32b ((unsigned int)-1)}} } void unsignAddEW_3(void) { - ULONG_MAX + 1; // expected-warning{{Integer overflow}} + ULONG_MAX + 1; // expected-warning{{Integer Overflow. Addition of 18446744073709551615 U64b (~0UL) with 1 U64b}} } // Subtraction : signed void signSubEW_1(void) { - INT_MIN - 1; // expected-warning{{Integer overflow}} + INT_MIN - 1; // expected-warning{{Undefined behavior: Integer Underflow. Subtraction of 1 S32b from -2147483648 S32b (-((int)(~0U >> 1)) - 1)}} } void signSubEW_2(void) { - -INT_MAX - 2; // expected-warning{{Integer overflow}} + -INT_MAX - 2; // expected-warning{{Undefined behavior: Integer Underflow. Subtraction of 2 S32b from -2147483647 S32b (-((int)(~0U >> 1)))}} } void signSubNW_3(void) { -INT_MAX - 1; // no-warning } void signSubEW_4(void) { - LONG_MIN - 1; // expected-warning{{Integer overflow}} + LONG_MIN - 1; // expected-warning{{Undefined behavior: Integer Underflow. Subtraction of 1 S64b from -9223372036854775808 S64b (-((long)(~0UL >> 1)) - 1)}} } // Subtraction : unsigned @@ -70,12 +71,12 @@ } void unsignSubEW_2(void) { int a = 0; - a - (unsigned)1; // expected-warning{{Integer overflow}} + a - (unsigned)1; // expected-warning{{Integer Underflow. Subtraction of 1 U32b from 0 U32b (a)}} } // Multiplication : signed void signMulEW_1(void) { - (INT_MAX / 2) * 3; // expected-warning{{Integer overflow}} + (INT_MAX / 2) * 3; // expected-warning{{Undefined behavior: Integer Overflow. Multiplication of 1073741823 S32b (((int)(~0U >> 1)) / 2) with 3 S32b}} } void signMulNW_2(void) { INT_MAX * 0; // no-warning @@ -84,22 +85,81 @@ 0 * INT_MAX; // no-warning } void signMulEW_4(void) { - INT_MIN * (-1); // expected-warning{{Integer overflow}} + INT_MIN *(-1); // expected-warning{{Undefined behavior: Integer Overflow. Multiplication of -2147483648 S32b (-((int)(~0U >> 1)) - 1) with -1 S32b}} } void signMulEW_5(void) { - (LONG_MAX / 2) * 3; // expected-warning{{Integer overflow}} + (LONG_MAX / 2) * 3; // expected-warning{{Undefined behavior: Integer Overflow. Multiplication of 4611686018427387903 S64b (((long)(~0UL >> 1)) / 2) with 3 S64b}} } // Multiplication : unsigned void unsignMulEW_1(void) { - (UINT_MAX / 2) * 3; // expected-warning{{Integer overflow}} + (UINT_MAX / 2) * 3; // expected-warning{{Integer Overflow. Multiplication of 2147483647 U32b ((~0U) / 2) with 3 U32b}} } void unsignMulEW_2(void) { - (ULONG_MAX / 2) * 3; // expected-warning{{Integer overflow}} + (ULONG_MAX / 2) * 3; // expected-warning{{Integer Overflow. Multiplication of 9223372036854775807 U64b ((~0UL) / 2) with 3 U64b}} } // New void newEW_1(void) { // (INT_MAX / 2) * sizeof(int). Overflowed value is used in memory allocation. - new int[INT_MAX / 2]; // expected-warning{{Integer overflow}} + new int[INT_MAX / 2]; // expected-warning{{Integer Overflow. Multiplication of 4 U32b with 1073741823 S32b (((int)(~0U >> 1)) / 2) while memory allocation}} +} + +// Test cases for GlobalsMembersHeuristics +namespace HT_1 { +void test_1(int b) { + if (b == INT_MIN) + b - 1; // no-warning +} +} + +namespace HT_2 { +class C { + int a; + void foo() { + if (a == INT_MIN) + a - 1; // no-warning + } +}; +} + +namespace HT_3 { +class C { +public: + int a; +}; +void foo() { + C c; + c.a = INT_MAX; + c.a + 1; // no-warning +} +} + +namespace HT_4 { +class C { + int a; + void foo() { + a = INT_MAX; + ((a - 1) + 1) + 1; // no-warning + } +}; +} + +namespace HT_5 { +class C { + int a; + void foo() { + a = -1; + a + 1U; // no-warning + } +}; +} + +void conjTest(const char *no_proxy) { + unsigned a = 0; + if (strchr(", ", no_proxy[0])) + a++; + // FIXME: shouldn't warn + if (strchr(", ", no_proxy[0])) + a - 1; // expected-warning{{Integer Underflow. Subtraction of 1 U32b from 0 U32b (a)}} }