clang  5.0.0
ConversionChecker.cpp
Go to the documentation of this file.
1 //=== ConversionChecker.cpp -------------------------------------*- C++ -*-===//
2 //
3 // The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 //
10 // Check that there is no loss of sign/precision in assignments, comparisons
11 // and multiplications.
12 //
13 // ConversionChecker uses path sensitive analysis to determine possible values
14 // of expressions. A warning is reported when:
15 // * a negative value is implicitly converted to an unsigned value in an
16 // assignment, comparison or multiplication.
17 // * assignment / initialization when source value is greater than the max
18 // value of target
19 //
20 // Many compilers and tools have similar checks that are based on semantic
21 // analysis. Those checks are sound but have poor precision. ConversionChecker
22 // is an alternative to those checks.
23 //
24 //===----------------------------------------------------------------------===//
25 #include "ClangSACheckers.h"
26 #include "clang/AST/ParentMap.h"
31 
32 using namespace clang;
33 using namespace ento;
34 
35 namespace {
36 class ConversionChecker : public Checker<check::PreStmt<ImplicitCastExpr>> {
37 public:
38  void checkPreStmt(const ImplicitCastExpr *Cast, CheckerContext &C) const;
39 
40 private:
41  mutable std::unique_ptr<BuiltinBug> BT;
42 
43  // Is there loss of precision
44  bool isLossOfPrecision(const ImplicitCastExpr *Cast, QualType DestType,
45  CheckerContext &C) const;
46 
47  // Is there loss of sign
48  bool isLossOfSign(const ImplicitCastExpr *Cast, CheckerContext &C) const;
49 
50  void reportBug(ExplodedNode *N, CheckerContext &C, const char Msg[]) const;
51 };
52 }
53 
54 void ConversionChecker::checkPreStmt(const ImplicitCastExpr *Cast,
55  CheckerContext &C) const {
56  // TODO: For now we only warn about DeclRefExpr, to avoid noise. Warn for
57  // calculations also.
58  if (!isa<DeclRefExpr>(Cast->IgnoreParenImpCasts()))
59  return;
60 
61  // Don't warn for loss of sign/precision in macros.
62  if (Cast->getExprLoc().isMacroID())
63  return;
64 
65  // Get Parent.
66  const ParentMap &PM = C.getLocationContext()->getParentMap();
67  const Stmt *Parent = PM.getParent(Cast);
68  if (!Parent)
69  return;
70 
71  bool LossOfSign = false;
72  bool LossOfPrecision = false;
73 
74  // Loss of sign/precision in binary operation.
75  if (const auto *B = dyn_cast<BinaryOperator>(Parent)) {
76  BinaryOperator::Opcode Opc = B->getOpcode();
77  if (Opc == BO_Assign) {
78  LossOfSign = isLossOfSign(Cast, C);
79  LossOfPrecision = isLossOfPrecision(Cast, Cast->getType(), C);
80  } else if (Opc == BO_AddAssign || Opc == BO_SubAssign) {
81  // No loss of sign.
82  LossOfPrecision = isLossOfPrecision(Cast, B->getLHS()->getType(), C);
83  } else if (Opc == BO_MulAssign) {
84  LossOfSign = isLossOfSign(Cast, C);
85  LossOfPrecision = isLossOfPrecision(Cast, B->getLHS()->getType(), C);
86  } else if (Opc == BO_DivAssign || Opc == BO_RemAssign) {
87  LossOfSign = isLossOfSign(Cast, C);
88  // No loss of precision.
89  } else if (Opc == BO_AndAssign) {
90  LossOfSign = isLossOfSign(Cast, C);
91  // No loss of precision.
92  } else if (Opc == BO_OrAssign || Opc == BO_XorAssign) {
93  LossOfSign = isLossOfSign(Cast, C);
94  LossOfPrecision = isLossOfPrecision(Cast, B->getLHS()->getType(), C);
95  } else if (B->isRelationalOp() || B->isMultiplicativeOp()) {
96  LossOfSign = isLossOfSign(Cast, C);
97  }
98  } else if (isa<DeclStmt>(Parent)) {
99  LossOfSign = isLossOfSign(Cast, C);
100  LossOfPrecision = isLossOfPrecision(Cast, Cast->getType(), C);
101  }
102 
103  if (LossOfSign || LossOfPrecision) {
104  // Generate an error node.
106  if (!N)
107  return;
108  if (LossOfSign)
109  reportBug(N, C, "Loss of sign in implicit conversion");
110  if (LossOfPrecision)
111  reportBug(N, C, "Loss of precision in implicit conversion");
112  }
113 }
114 
115 void ConversionChecker::reportBug(ExplodedNode *N, CheckerContext &C,
116  const char Msg[]) const {
117  if (!BT)
118  BT.reset(
119  new BuiltinBug(this, "Conversion", "Possible loss of sign/precision."));
120 
121  // Generate a report for this bug.
122  auto R = llvm::make_unique<BugReport>(*BT, Msg, N);
123  C.emitReport(std::move(R));
124 }
125 
126 // Is E value greater or equal than Val?
127 static bool isGreaterEqual(CheckerContext &C, const Expr *E,
128  unsigned long long Val) {
130  SVal EVal = C.getSVal(E);
131  if (EVal.isUnknownOrUndef())
132  return false;
133  if (!EVal.getAs<NonLoc>() && EVal.getAs<Loc>()) {
135  EVal =
136  Mgr.getStoreManager().getBinding(State->getStore(), EVal.castAs<Loc>());
137  }
138  if (EVal.isUnknownOrUndef() || !EVal.getAs<NonLoc>())
139  return false;
140 
141  SValBuilder &Bldr = C.getSValBuilder();
142  DefinedSVal V = Bldr.makeIntVal(Val, C.getASTContext().LongLongTy);
143 
144  // Is DefinedEVal greater or equal with V?
145  SVal GE = Bldr.evalBinOp(State, BO_GE, EVal, V, Bldr.getConditionType());
146  if (GE.isUnknownOrUndef())
147  return false;
149  ProgramStateRef StGE, StLT;
150  std::tie(StGE, StLT) = CM.assumeDual(State, GE.castAs<DefinedSVal>());
151  return StGE && !StLT;
152 }
153 
154 // Is E value negative?
155 static bool isNegative(CheckerContext &C, const Expr *E) {
157  SVal EVal = State->getSVal(E, C.getLocationContext());
158  if (EVal.isUnknownOrUndef() || !EVal.getAs<NonLoc>())
159  return false;
160  DefinedSVal DefinedEVal = EVal.castAs<DefinedSVal>();
161 
162  SValBuilder &Bldr = C.getSValBuilder();
163  DefinedSVal V = Bldr.makeIntVal(0, false);
164 
165  SVal LT =
166  Bldr.evalBinOp(State, BO_LT, DefinedEVal, V, Bldr.getConditionType());
167 
168  // Is E value greater than MaxVal?
170  ProgramStateRef StNegative, StPositive;
171  std::tie(StNegative, StPositive) =
172  CM.assumeDual(State, LT.castAs<DefinedSVal>());
173 
174  return StNegative && !StPositive;
175 }
176 
177 bool ConversionChecker::isLossOfPrecision(const ImplicitCastExpr *Cast,
178  QualType DestType,
179  CheckerContext &C) const {
180  // Don't warn about explicit loss of precision.
181  if (Cast->isEvaluatable(C.getASTContext()))
182  return false;
183 
184  QualType SubType = Cast->IgnoreParenImpCasts()->getType();
185 
186  if (!DestType->isIntegerType() || !SubType->isIntegerType())
187  return false;
188 
189  if (C.getASTContext().getIntWidth(DestType) >=
190  C.getASTContext().getIntWidth(SubType))
191  return false;
192 
193  unsigned W = C.getASTContext().getIntWidth(DestType);
194  if (W == 1 || W >= 64U)
195  return false;
196 
197  unsigned long long MaxVal = 1ULL << W;
198  return isGreaterEqual(C, Cast->getSubExpr(), MaxVal);
199 }
200 
201 bool ConversionChecker::isLossOfSign(const ImplicitCastExpr *Cast,
202  CheckerContext &C) const {
203  QualType CastType = Cast->getType();
204  QualType SubType = Cast->IgnoreParenImpCasts()->getType();
205 
206  if (!CastType->isUnsignedIntegerType() || !SubType->isSignedIntegerType())
207  return false;
208 
209  return isNegative(C, Cast->getSubExpr());
210 }
211 
212 void ento::registerConversionChecker(CheckerManager &mgr) {
213  mgr.registerChecker<ConversionChecker>();
214 }
CanQualType LongLongTy
Definition: ASTContext.h:971
nonloc::ConcreteInt makeIntVal(const IntegerLiteral *integer)
Definition: SValBuilder.h:254
A (possibly-)qualified type.
Definition: Type.h:616
bool isMacroID() const
Stmt - This represents one statement.
Definition: Stmt.h:60
unsigned getIntWidth(QualType T) const
virtual SVal getBinding(Store store, Loc loc, QualType T=QualType())=0
Return the value bound to specified location in a given state.
LineState State
Expr * getSubExpr()
Definition: Expr.h:2753
BinaryOperatorKind
ProgramStateManager & getStateManager()
bool isUnknownOrUndef() const
Definition: SVals.h:136
bool isUnsignedIntegerType() const
Return true if this is an integer type that is unsigned, according to C99 6.2.5p6 [which returns true...
Definition: Type.cpp:1784
ProgramStatePair assumeDual(ProgramStateRef State, DefinedSVal Cond)
Returns a pair of states (StTrue, StFalse) where the given condition is assumed to be true or false...
Expr - This represents one expression.
Definition: Expr.h:105
const ProgramStateRef & getState() const
static bool isGreaterEqual(CheckerContext &C, const Expr *E, unsigned long long Val)
ParentMap & getParentMap() const
Optional< T > getAs() const
Convert to the specified SVal type, returning None if this SVal is not of the desired type...
Definition: SVals.h:100
SVal evalBinOp(ProgramStateRef state, BinaryOperator::Opcode op, SVal lhs, SVal rhs, QualType type)
ExplodedNode * generateNonFatalErrorNode(ProgramStateRef State=nullptr, const ProgramPointTag *Tag=nullptr)
Generate a transition to a node that will be used to report an error.
QualType getConditionType() const
Definition: SValBuilder.h:136
ConstraintManager & getConstraintManager()
void emitReport(std::unique_ptr< BugReport > R)
Emit the diagnostics report.
CHECKER * registerChecker()
Used to register checkers.
Stmt * getParent(Stmt *) const
Definition: ParentMap.cpp:122
SVal - This represents a symbolic expression, which can be either an L-value or an R-value...
Definition: SVals.h:63
ImplicitCastExpr - Allows us to explicitly represent implicit type conversions, which have no direct ...
Definition: Expr.h:2804
SourceLocation getExprLoc() const LLVM_READONLY
getExprLoc - Return the preferred location for the arrow when diagnosing a problem with a generic exp...
Definition: Expr.cpp:216
QualType getType() const
Definition: Expr.h:127
static bool isNegative(CheckerContext &C, const Expr *E)
detail::InMemoryDirectory::const_iterator E
Expr * IgnoreParenImpCasts() LLVM_READONLY
IgnoreParenImpCasts - Ignore parentheses and implicit casts.
Definition: Expr.cpp:2486
bool isEvaluatable(const ASTContext &Ctx, SideEffectsKind AllowSideEffects=SE_NoSideEffects) const
isEvaluatable - Call EvaluateAsRValue to see if this expression can be constant folded without side-e...
SValBuilder & getSValBuilder()
bool isSignedIntegerType() const
Return true if this is an integer type that is signed, according to C99 6.2.5p4 [char, signed char, short, int, long..], or an enum decl which has a signed representation.
Definition: Type.cpp:1744
T castAs() const
Convert to the specified SVal type, asserting that this SVal is of the desired type.
Definition: SVals.h:92
CastType
Definition: SemaCast.cpp:40
bool isIntegerType() const
isIntegerType() does not include complex integers (a GCC extension).
Definition: Type.h:5928
const LocationContext * getLocationContext() const
SVal getSVal(const Stmt *S) const
Get the value of arbitrary expressions at this point in the path.