Improve checker and test suite:
1) Split into two checkers for def/undef behavior.
2) Refactor message composing
3) Add comments
4) Add test cases
http://reviews.llvm.org/D4066
Files:
lib/StaticAnalyzer/Checkers/Checkers.td
lib/StaticAnalyzer/Checkers/IntegerOverflowChecker.cpp
test/Analysis/integer-overflow.cpp
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<Alpha>, Hidden;
+def Different : Package<"different">;
+def DifferentAlpha : Package<"different">, InPackage<Alpha>, Hidden;
+
def Security : Package <"security">;
def InsecureAPI : Package<"insecureAPI">, InPackage<Security>;
def SecurityAlpha : Package<"security">, InPackage<Alpha>, Hidden;
def Taint : Package<"taint">, InPackage<SecurityAlpha>, Hidden;
+def UndefBehavior : Package<"undefbehavior">;
+def UndefBehaviorAlpha : Package<"undefbehavior">, InPackage<Alpha>, Hidden;
+
def Unix : Package<"unix">;
def UnixAlpha : Package<"unix">, InPackage<Alpha>, Hidden;
def CString : Package<"cstring">, InPackage<Unix>, Hidden;
@@ -43,9 +49,6 @@
def CoreFoundation : Package<"coreFoundation">, InPackage<OSX>;
def Containers : Package<"containers">, InPackage<CoreFoundation>;
-def Different : Package<"different">;
-def DifferentAlpha : Package<"different">, InPackage<Alpha>, 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<DifferentAlpha>,
- 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<CallExpr>,
check::PostStmt<MemberExpr>,
check::Bind> {
- mutable std::unique_ptr<BuiltinBug> BT;
+ mutable std::unique_ptr<BuiltinBug> BT_Def, BT_Undef;
+ /// Stores SourceLocations in which overflows happened for reducing the amount
+ /// of equivalent warnings.
mutable std::set<SourceLocation> 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<DefinedOrUnknownSVal> 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<DefinedOrUnknownSVal> 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<DefinedOrUnknownSVal> 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<DefinedOrUnknownSVal>
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<DefinedOrUnknownSVal>
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<DefinedOrUnknownSVal>
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<DefinedOrUnknownSVal>();
}
+
+ 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<IntegerLiteral>(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<UnaryOperator>(E))
+ if ((UO->getOpcode() == UO_Plus || UO->getOpcode() == UO_Minus) &&
+ isa<IntegerLiteral>(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<FieldRegion>(Mem) ||
+ return isInWhiteList(Val, C.getState()) || isa<FieldRegion>(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<MemberExpr>(S)) {
@@ -386,21 +470,20 @@
}
if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(S))
- if (isa<DeclRefExpr>(ICE->getSubExpr()) && isGoodSink(C.getSVal(ICE),
- State))
+ if (isa<DeclRefExpr>(ICE->getSubExpr()) && isInWhiteList(C.getSVal(ICE),
+ State))
return true;
if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(S))
if (const VarDecl *VarD = dyn_cast<VarDecl>(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<ExternalSym>();
- for (ExternalSymTy::iterator I = ES.begin(); I != ES.end(); ++I) {
+ WhiteListTy ES = C.getState()->get<WhiteList>();
+ 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<Expr>(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<ExternalSym>().contains(Val) ? State
- : State->add<ExternalSym>(Val);
+IntegerOverflowChecker::addToWhiteList(const SVal &Val,
+ ProgramStateRef State) const {
+ return State->get<WhiteList>().contains(Val) ? State
+ : State->add<WhiteList>(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<Expr>(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<ExternalSym>().contains(V);
+inline bool IntegerOverflowChecker::isInWhiteList(const SVal &V,
+ ProgramStateRef State) const {
+ return State->get<WhiteList>().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<DefinedOrUnknownSVal> 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<DefinedOrUnknownSVal> CondOverflow =
- checkMul(C, NewExprTypeSizeVal, ElementCount, ArrSize->getType());
+ bool isOverflow;
+ Optional<DefinedOrUnknownSVal> 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<IntegerOverflowChecker>();
-}
+#define REGISTER_CHECKER(name) \
+ void ento::register##name(CheckerManager &mgr) { \
+ IntegerOverflowChecker *checker = \
+ mgr.registerChecker<IntegerOverflowChecker>(); \
+ 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)}}
}
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits