Index: lib/StaticAnalyzer/Checkers/CMakeLists.txt =================================================================== --- lib/StaticAnalyzer/Checkers/CMakeLists.txt +++ lib/StaticAnalyzer/Checkers/CMakeLists.txt @@ -37,6 +37,7 @@ FixedAddressChecker.cpp GenericTaintChecker.cpp IdenticalExprChecker.cpp + IntegerOverflowChecker.cpp IvarInvalidationChecker.cpp LLVMConventionsChecker.cpp MacOSKeychainAPIChecker.cpp Index: lib/StaticAnalyzer/Checkers/Checkers.td =================================================================== --- lib/StaticAnalyzer/Checkers/Checkers.td +++ lib/StaticAnalyzer/Checkers/Checkers.td @@ -43,6 +43,9 @@ 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">; @@ -479,6 +482,17 @@ DescFile<"ObjCContainersChecker.cpp">; } + +//===----------------------------------------------------------------------===// +// 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 =================================================================== --- /dev/null +++ lib/StaticAnalyzer/Checkers/IntegerOverflowChecker.cpp @@ -0,0 +1,621 @@ +//=== IntegerOverflowChecker.cpp - integer overflows checker ----*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines IntegerOverflowChecker, which checks arithmetic operations +// for integer overflows. This check corresponds to CWE-190. +// +//===----------------------------------------------------------------------===// + +#include "ClangSACheckers.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" + +using namespace clang; +using namespace ento; +using namespace nonloc; + +namespace { + +class IntegerOverflowChecker : public Checker, + check::PostStmt, + check::PostStmt, + check::PostStmt, + check::Bind> { + mutable std::unique_ptr BT; + + 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) {} + }; + + void reportBug(const std::string &Msg, CheckerContext &C, + const SourceLocation &SL) const; + + Optional checkAdd(CheckerContext &C, const SVal &Lhs, + const SVal &Rhs, + QualType BinType) const; + + Optional checkSub(CheckerContext &C, const SVal &Lhs, + const SVal &Rhs, + const QualType &BinType) const; + + 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; + + void processStates(ProgramStateRef StateOverflow, + ProgramStateRef StateNotOverflow, const OutputPack &Pack, + CheckerContext &C, const SourceLocation &SL) const; + + bool makeUSubHeuristics(const BinaryOperator *BO) const; + + bool makeGlobalsMembersHeuristics(const SVal &Val, const Stmt *S, + CheckerContext &C) const; + + bool hasGlobalVariablesOrMembers(const Stmt *S, CheckerContext &C) const; + + bool hasGlobalVariablesOrMembers(const SymExpr *SE, CheckerContext &C) const; + + ProgramStateRef addGoodSink(const Stmt *S, ProgramStateRef State, + const LocationContext *LCtx) const; + + inline ProgramStateRef addGoodSink(const SVal &SV, + ProgramStateRef State) const; + + bool isGoodSink(const Stmt *S, ProgramStateRef State, + const LocationContext *LCtx) const; + + inline bool isGoodSink(const SVal &Val, ProgramStateRef State) const; + +public: + /// \brief Contains checks for such operations as addition, multiplication, + /// and subtraction. + void checkPostStmt(const BinaryOperator *B, CheckerContext &C) const; + + /// \brief Contains check for new[]. + void checkPostStmt(const CXXNewExpr *NE, CheckerContext &C) const; + + void checkPostStmt(const CallExpr *CE, CheckerContext &C) const; + + void checkPostStmt(const MemberExpr *ME, CheckerContext &C) const; + + void checkBind(const SVal &Loc, const SVal &Val, const Stmt *S, + CheckerContext &C) const; +}; +} // end anonymous namespace + +REGISTER_LIST_WITH_PROGRAMSTATE(ExternalSym, SVal) + +void IntegerOverflowChecker::reportBug(const std::string &Msg, + CheckerContext &C, + const SourceLocation &SL) const { + if (const ExplodedNode *N = C.generateSink(C.getState())) { + if (!BT) + BT.reset(new BuiltinBug(this, "Integer overflow", + "Arithmetic operation resulted in an overflow")); + + BugReport *R = new BugReport(*BT, Msg, N); + C.emitReport(R); + OverflowLoc.insert(SL); + } +} + +Optional +IntegerOverflowChecker::checkAdd(CheckerContext &C, const SVal &Lhs, + const SVal &Rhs, QualType BinType) const { + SVal CondOverflow; + ProgramStateRef State = C.getState(); + SValBuilder &SvalBuilder = C.getSValBuilder(); + SVal NullSval = SvalBuilder.makeZeroVal(BinType); + QualType CondType = SvalBuilder.getConditionType(); + SVal ValArgSum = SvalBuilder.evalBinOp(State, BO_Add, Lhs, Rhs, BinType); + if (BinType->isSignedIntegerType()) { + // For positive operands + // rhs > 0 + SVal CondRhsGtNull = SvalBuilder.evalBinOp(State, BO_GT, Rhs, NullSval, + CondType); + // lhs > 0 + SVal CondLhsGtNull = SvalBuilder.evalBinOp(State, BO_GT, Lhs, NullSval, + CondType); + // rhs > 0 && lhs > 0 + SVal CondArgsGtNull = SvalBuilder.evalBinOp(State, BO_And, CondRhsGtNull, + CondLhsGtNull, CondType); + // lhs+rhs<=0 + SVal CondArgSumLtNull = SvalBuilder.evalBinOp(State, BO_LE, ValArgSum, + NullSval, CondType); + + SVal CondPositiveOverflow = + SvalBuilder.evalBinOp(State, BO_And, CondArgsGtNull, CondArgSumLtNull, + CondType); + // For negative operands + // lhs < 0 + SVal CondLhsLtNull = SvalBuilder.evalBinOp(State, BO_LT, Rhs, NullSval, + CondType); + // rhs < 0 + SVal CondRhsLtNull = SvalBuilder.evalBinOp(State, BO_LT, Lhs, NullSval, + CondType); + // rhs < 0 && lhs < 0 + SVal CondArgsLtNull = SvalBuilder.evalBinOp(State, BO_And, CondLhsLtNull, + CondRhsLtNull, CondType); + + // lhs+rhs>=0 + SVal CondArgSumGtNull = SvalBuilder.evalBinOp(State, BO_GE, ValArgSum, + NullSval, CondType); + + SVal CondNegativeOverflow = + SvalBuilder.evalBinOp(State, BO_And, CondArgsLtNull, CondArgSumGtNull, + CondType); + + CondOverflow = SvalBuilder.evalBinOp(State, BO_Or, CondPositiveOverflow, + CondNegativeOverflow, CondType); + } else { + // lhs > sum + SVal CondLhsGtArgSum = SvalBuilder.evalBinOp(State, BO_GT, Lhs, ValArgSum, + CondType); + // rhs > sum + SVal CondRhsGtArgSum = SvalBuilder.evalBinOp(State, BO_GT, Rhs, ValArgSum, + CondType); + // lhs > sum && rhs > sum + CondOverflow = SvalBuilder.evalBinOp(State, BO_And, CondLhsGtArgSum, + CondRhsGtArgSum, CondType); + } + + return CondOverflow.getAs(); +} + +Optional +IntegerOverflowChecker::checkSub(CheckerContext &C, const SVal &Lhs, + const SVal &Rhs, + const QualType &BinType) const { + SVal CondOverflow; + ProgramStateRef State = C.getState(); + SValBuilder &SvalBuilder = C.getSValBuilder(); + SVal NullSval = SvalBuilder.makeZeroVal(BinType); + QualType CondType = SvalBuilder.getConditionType(); + SVal ValArgSub = SvalBuilder.evalBinOp(State, BO_Sub, Lhs, Rhs, BinType); + if (BinType->isSignedIntegerType()) { + // When first operand is negative + // lhs < 0 + SVal CondLhsLtNull = SvalBuilder.evalBinOp(State, BO_LT, Lhs, NullSval, + CondType); + // rhs > 0 + SVal CondRhsGtNull = SvalBuilder.evalBinOp(State, BO_GT, Rhs, NullSval, + CondType); + // rhs > 0 && lhs < 0 + SVal CondLhsLtNullRhsGtNull = + SvalBuilder.evalBinOp(State, BO_And, CondLhsLtNull, CondRhsGtNull, + CondType); + // lhs-rhs >= 0 + SVal CondArgSubGeNull = SvalBuilder.evalBinOp(State, BO_GE, ValArgSub, + NullSval, CondType); + + // rhs > 0 && lhs < 0 && lhs-rhs >= 0 + SVal CondNegativeOverflow = + SvalBuilder.evalBinOp(State, BO_And, CondLhsLtNullRhsGtNull, + CondArgSubGeNull, CondType); + + // When first operand is positive + // lhs > 0 + SVal CondLhsGtNull = SvalBuilder.evalBinOp(State, BO_GT, Lhs, NullSval, + CondType); + // rhs < 0 + SVal CondRhsLtNull = SvalBuilder.evalBinOp(State, BO_LT, Rhs, NullSval, + CondType); + // rhs < 0 && lhs > 0 + SVal CondLhsGtNullRhsLtNull = + SvalBuilder.evalBinOp(State, BO_And, CondLhsGtNull, CondRhsLtNull, + CondType); + // lhs - rhs <= 0 + SVal CondArgSubLeNull = SvalBuilder.evalBinOp(State, BO_LE, ValArgSub, + NullSval, CondType); + + // rhs < 0 && lhs > 0 && lhs - rhs <= 0 + SVal CondPositiveOverflow = + SvalBuilder.evalBinOp(State, BO_And, CondLhsGtNullRhsLtNull, + CondArgSubLeNull, CondType); + + CondOverflow = SvalBuilder.evalBinOp(State, BO_Or, CondNegativeOverflow, + CondPositiveOverflow, CondType); + } else + CondOverflow = SvalBuilder.evalBinOp(State, BO_LT, Lhs, Rhs, CondType); + + return CondOverflow.getAs(); +} + +Optional +IntegerOverflowChecker::checkMul(CheckerContext &C, const SVal &Lhs, + const SVal &Rhs, + const QualType &BinType) const { + ProgramStateRef State = C.getState(); + ProgramStateRef CondNotOverflow, CondPossibleOverflow; + SValBuilder &SvalBuilder = C.getSValBuilder(); + SVal NullSval = SvalBuilder.makeZeroVal(BinType); + QualType CondType = SvalBuilder.getConditionType(); + + // lhs == 0 + SVal LhsNotNull = SvalBuilder.evalBinOp(State, BO_NE, Lhs, NullSval, + CondType); + + // rhs == 0 + SVal RhsNotNull = SvalBuilder.evalBinOp(State, BO_NE, Rhs, NullSval, + CondType); + + Optional CondOverflow = + SvalBuilder.evalBinOp(State, BO_And, LhsNotNull, RhsNotNull, CondType) + .getAs(); + + if (!CondOverflow.hasValue()) + return CondOverflow; + + std::tie(CondPossibleOverflow, CondNotOverflow) = + State->assume(*CondOverflow); + + if (CondNotOverflow && CondPossibleOverflow) + return CondOverflow; + + if (CondPossibleOverflow) { + // lhs * rhs + SVal ValMulti = SvalBuilder.evalBinOp(State, BO_Mul, Lhs, Rhs, BinType); + // First operand(lhs) is not 0 + // (lhs * rhs)/lhs + SVal ValDiv = SvalBuilder.evalBinOp(State, BO_Div, ValMulti, Lhs, BinType); + // (lhs * rhs)/lhs != rhs + + CondOverflow = SvalBuilder.evalBinOp(State, BO_NE, ValDiv, Rhs, CondType) + .getAs(); + } + 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() + " : "); + if (from != std::string::npos) { + size_t to = StreamRange.str().find("\n", from); + from += Stream.str().length(); + Stream << StreamRange.str().substr(from, to - from); + } + } +} + +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; + } + + if (StateOverflow) { + Msg.assign("Integer overflow while " + Pack.Operation + ". " + Pack.LValue + + " AND " + Pack.RValue); + reportBug(Msg, C, SL); + } +} + +// We ignore intentional underflow with subtracting X from zero - the minimal +// unsigned value. +bool +IntegerOverflowChecker::makeUSubHeuristics(const BinaryOperator *BO) const { + const Expr *ExprLhs = BO->getLHS()->IgnoreParenCasts(); + if (isa(ExprLhs)) { + const IntegerLiteral *IL = dyn_cast(ExprLhs); + return IL->getValue().isMinValue(); + } + 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()) && + (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); + else if (const MemRegion *Mem = Val.getAsRegion()) + return isGoodSink(Val, C.getState()) || isa(Mem) || + Mem->hasGlobalsOrParametersStorage(); + + return false; +} + +bool +IntegerOverflowChecker::hasGlobalVariablesOrMembers(const Stmt *S, + CheckerContext &C) const { + if (S == NULL || S->getStmtClass() == Stmt::IntegerLiteralClass) + return false; + + ProgramStateRef State = C.getState(); + const LocationContext *LCtx = C.getLocationContext(); + + if ((S->getStmtClass() != Stmt::ImplicitCastExprClass) && + isGoodSink(S, State, LCtx)) + return true; + + if (const MemberExpr *MExpr = dyn_cast(S)) { + if (MExpr->getMemberDecl()->isFunctionOrFunctionTemplate()) + return hasGlobalVariablesOrMembers(MExpr->getMemberDecl()->getBody(), C); + // We found member usage! + return true; + } + + if (const ImplicitCastExpr *ICE = dyn_cast(S)) + if (isa(ICE->getSubExpr()) && isGoodSink(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)) + return true; + } + + // We will not surrender! + for (clang::Stmt::const_child_iterator I = S->child_begin(); + I != S->child_end(); I++) + if (hasGlobalVariablesOrMembers(*I, C)) + return true; + + return false; +} + +bool +IntegerOverflowChecker::hasGlobalVariablesOrMembers(const SymExpr *SE, + CheckerContext &C) const { + ExternalSymTy ES = C.getState()->get(); + for (ExternalSymTy::iterator I = ES.begin(); I != ES.end(); ++I) { + SVal Val = *I; + SymbolRef SR = Val.getAsSymbol(); + if (SR == SE) + return true; + } + // SymbolCast + if (const SymbolCast *SC = dyn_cast(SE)) + return hasGlobalVariablesOrMembers(SC->getOperand(), C); + // BinarySymExpr + if (const IntSymExpr *ISE = dyn_cast(SE)) + return hasGlobalVariablesOrMembers(ISE->getRHS(), C); + if (const SymIntExpr *SIE = dyn_cast(SE)) + return hasGlobalVariablesOrMembers(SIE->getLHS(), C); + if (const SymSymExpr *SSE = dyn_cast(SE)) + return (hasGlobalVariablesOrMembers(SSE->getLHS(), C) || + hasGlobalVariablesOrMembers(SSE->getRHS(), C)); + // SymbolData + if (const SymbolRegionValue *SRV = dyn_cast(SE)) { + const TypedValueRegion *TVR = SRV->getRegion(); + return isa(TVR) || TVR->hasGlobalsOrParametersStorage(); + } + if (const SymbolDerived *SD = dyn_cast(SE)) { + const TypedValueRegion *TVR = SD->getRegion(); + return isa(TVR) || TVR->hasGlobalsOrParametersStorage(); + } + if (const SymbolConjured *SC = dyn_cast(SE)) + return hasGlobalVariablesOrMembers(SC->getStmt(), C); + + return false; +} + +ProgramStateRef +IntegerOverflowChecker::addGoodSink(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); +} + +inline ProgramStateRef +IntegerOverflowChecker::addGoodSink(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 { + if (const Expr *E = dyn_cast_or_null(S)) + S = E->IgnoreParens(); + return isGoodSink(State->getSVal(S, LCtx), State); +} + +inline bool IntegerOverflowChecker::isGoodSink(const SVal &V, + ProgramStateRef State) const { + return State->get().contains(V); +} + +void IntegerOverflowChecker::checkPostStmt(const BinaryOperator *B, + CheckerContext &C) const { + if (OverflowLoc.find(B->getExprLoc()) != OverflowLoc.end()) + return; + + if (!B->getLHS()->getType()->isIntegerType() || + !B->getRHS()->getType()->isIntegerType()) + return; + + ProgramStateRef State = C.getState(); + QualType BinType = B->getType(); + const Expr *ExprLhs = B->getLHS(); + const Expr *ExprRhs = B->getRHS(); + SVal Lhs = C.getSVal(ExprLhs); + SVal Rhs = C.getSVal(ExprRhs); + + 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)); + return; + } + if (makeGlobalsMembersHeuristics(Rhs, ExprRhs, C)) { + C.addTransition(addGoodSink(Rhs, State)); + 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) + return; + + 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; + + std::tie(StateOverflow, StateNotOverflow) = State->assume(*CondOverflow); + + Pack.Operation = "addition"; + + processStates(StateOverflow, StateNotOverflow, Pack, C, B->getExprLoc()); + } else if (Op == BO_Sub || Op == BO_SubAssign) { + if ((BinType->isUnsignedIntegerType()) && makeUSubHeuristics(B)) + return; + + CondOverflow = checkSub(C, Lhs, Rhs, BinType); + if (!CondOverflow) + return; + + std::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; + + std::tie(StateOverflow, StateNotOverflow) = State->assume(*CondOverflow); + + Pack.Operation = "multiplication"; + + processStates(StateOverflow, StateNotOverflow, Pack, C, B->getExprLoc()); + } +} + +void IntegerOverflowChecker::checkPostStmt(const CXXNewExpr *NewExpr, + CheckerContext &C) const { + if (NewExpr->getOperatorNew()->getOverloadedOperator() != OO_Array_New) + return; + + SValBuilder &SvalBuilder = C.getSValBuilder(); + QualType NewExprType = NewExpr->getAllocatedType(); + + uint64_t NewExprTypeSize = C.getASTContext().getTypeSizeInChars(NewExprType) + .getQuantity(); + SVal NewExprTypeSizeVal = SvalBuilder.makeIntVal(NewExprTypeSize, true); + + 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)); + 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()); + + Optional CondOverflow = + checkMul(C, NewExprTypeSizeVal, ElementCount, ArrSize->getType()); + + if (!CondOverflow) + return; + + std::tie(StateOverflow, StateNotOverflow) = State->assume(*CondOverflow); + + Pack.Operation = "memory allocation"; + + processStates(StateOverflow, StateNotOverflow, Pack, C, + NewExpr->getExprLoc()); +} + +void IntegerOverflowChecker::checkPostStmt(const CallExpr *CE, + CheckerContext &C) const { + if (makeGlobalsMembersHeuristics(C.getSVal(CE), CE, C)) + C.addTransition(addGoodSink(CE, C.getState(), C.getLocationContext())); +} + +void IntegerOverflowChecker::checkPostStmt(const MemberExpr *ME, + CheckerContext &C) const { + C.addTransition(addGoodSink(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())); +} + +void ento::registerIntegerOverflowChecker(CheckerManager &mgr) { + mgr.registerChecker(); +} Index: test/Analysis/integer-overflow.cpp =================================================================== --- /dev/null +++ test/Analysis/integer-overflow.cpp @@ -0,0 +1,105 @@ +// 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)) +#define INT_MIN (-INT_MAX - 1) +#define UINT_MAX (~0U) +#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_MIN (-LLONG_MAX - 1) +#define ULLONG_MAX (~0ULL) + +int randomInt(); + +// Addition : signed +void signAddEW_1(void) { + INT_MAX + 1; // expected-warning{{Integer overflow}} +} +void signAddEW_2(void) { + INT_MIN + INT_MIN; // expected-warning{{Integer overflow}} +} +void signAddEW_3(void) { + LONG_MAX + 1; // expected-warning{{Integer overflow}} +} +void signAddNW_4(void) { + SHRT_MAX + 1; // no-warning +} +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}} + else if (a < INT_MAX) + a + 2; // no-warning +} + +// Addition : unsigned +void unsignAddEW_1(void) { + UINT_MAX + 1; // expected-warning{{Integer overflow}} +} +void unsignAddEW_2(void) { + 1 + (unsigned)-1; // expected-warning{{Integer overflow}} +} +void unsignAddEW_3(void) { + ULONG_MAX + 1; // expected-warning{{Integer overflow}} +} + +// Subtraction : signed +void signSubEW_1(void) { + INT_MIN - 1; // expected-warning{{Integer overflow}} +} +void signSubEW_2(void) { + -INT_MAX - 2; // expected-warning{{Integer overflow}} +} +void signSubNW_3(void) { + -INT_MAX - 1; // no-warning +} +void signSubEW_4(void) { + LONG_MIN - 1; // expected-warning{{Integer overflow}} +} + +// Subtraction : unsigned +void unsignSubNW_1(void) { + 0 - (unsigned)1; // no-warning +} +void unsignSubEW_2(void) { + int a = 0; + a - (unsigned)1; // expected-warning{{Integer overflow}} +} + +// Multiplication : signed +void signMulEW_1(void) { + (INT_MAX / 2) * 3; // expected-warning{{Integer overflow}} +} +void signMulNW_2(void) { + INT_MAX * 0; // no-warning +} +void signMulNW_3(void) { + 0 * INT_MAX; // no-warning +} +void signMulEW_4(void) { + INT_MIN * (-1); // expected-warning{{Integer overflow}} +} +void signMulEW_5(void) { + (LONG_MAX / 2) * 3; // expected-warning{{Integer overflow}} +} + +// Multiplication : unsigned +void unsignMulEW_1(void) { + (UINT_MAX / 2) * 3; // expected-warning{{Integer overflow}} +} +void unsignMulEW_2(void) { + (ULONG_MAX / 2) * 3; // expected-warning{{Integer overflow}} +} + +// 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}} +}