ConversionChecker.cpp revision 314564
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"
27#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
28#include "clang/StaticAnalyzer/Core/Checker.h"
29#include "clang/StaticAnalyzer/Core/CheckerManager.h"
30#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
31
32using namespace clang;
33using namespace ento;
34
35namespace {
36class ConversionChecker : public Checker<check::PreStmt<ImplicitCastExpr>> {
37public:
38  void checkPreStmt(const ImplicitCastExpr *Cast, CheckerContext &C) const;
39
40private:
41  mutable std::unique_ptr<BuiltinBug> BT;
42
43  // Is there loss of precision
44  bool isLossOfPrecision(const ImplicitCastExpr *Cast, CheckerContext &C) const;
45
46  // Is there loss of sign
47  bool isLossOfSign(const ImplicitCastExpr *Cast, CheckerContext &C) const;
48
49  void reportBug(ExplodedNode *N, CheckerContext &C, const char Msg[]) const;
50};
51}
52
53void ConversionChecker::checkPreStmt(const ImplicitCastExpr *Cast,
54                                     CheckerContext &C) const {
55  // TODO: For now we only warn about DeclRefExpr, to avoid noise. Warn for
56  // calculations also.
57  if (!isa<DeclRefExpr>(Cast->IgnoreParenImpCasts()))
58    return;
59
60  // Don't warn for loss of sign/precision in macros.
61  if (Cast->getExprLoc().isMacroID())
62    return;
63
64  // Get Parent.
65  const ParentMap &PM = C.getLocationContext()->getParentMap();
66  const Stmt *Parent = PM.getParent(Cast);
67  if (!Parent)
68    return;
69
70  bool LossOfSign = false;
71  bool LossOfPrecision = false;
72
73  // Loss of sign/precision in binary operation.
74  if (const auto *B = dyn_cast<BinaryOperator>(Parent)) {
75    BinaryOperator::Opcode Opc = B->getOpcode();
76    if (Opc == BO_Assign || Opc == BO_AddAssign || Opc == BO_SubAssign ||
77        Opc == BO_MulAssign) {
78      LossOfSign = isLossOfSign(Cast, C);
79      LossOfPrecision = isLossOfPrecision(Cast, C);
80    } else if (B->isRelationalOp() || B->isMultiplicativeOp()) {
81      LossOfSign = isLossOfSign(Cast, C);
82    }
83  } else if (isa<DeclStmt>(Parent)) {
84    LossOfSign = isLossOfSign(Cast, C);
85    LossOfPrecision = isLossOfPrecision(Cast, C);
86  }
87
88  if (LossOfSign || LossOfPrecision) {
89    // Generate an error node.
90    ExplodedNode *N = C.generateNonFatalErrorNode(C.getState());
91    if (!N)
92      return;
93    if (LossOfSign)
94      reportBug(N, C, "Loss of sign in implicit conversion");
95    if (LossOfPrecision)
96      reportBug(N, C, "Loss of precision in implicit conversion");
97  }
98}
99
100void ConversionChecker::reportBug(ExplodedNode *N, CheckerContext &C,
101                                  const char Msg[]) const {
102  if (!BT)
103    BT.reset(
104        new BuiltinBug(this, "Conversion", "Possible loss of sign/precision."));
105
106  // Generate a report for this bug.
107  auto R = llvm::make_unique<BugReport>(*BT, Msg, N);
108  C.emitReport(std::move(R));
109}
110
111// Is E value greater or equal than Val?
112static bool isGreaterEqual(CheckerContext &C, const Expr *E,
113                           unsigned long long Val) {
114  ProgramStateRef State = C.getState();
115  SVal EVal = C.getSVal(E);
116  if (EVal.isUnknownOrUndef() || !EVal.getAs<NonLoc>())
117    return false;
118
119  SValBuilder &Bldr = C.getSValBuilder();
120  DefinedSVal V = Bldr.makeIntVal(Val, C.getASTContext().LongLongTy);
121
122  // Is DefinedEVal greater or equal with V?
123  SVal GE = Bldr.evalBinOp(State, BO_GE, EVal, V, Bldr.getConditionType());
124  if (GE.isUnknownOrUndef())
125    return false;
126  ConstraintManager &CM = C.getConstraintManager();
127  ProgramStateRef StGE, StLT;
128  std::tie(StGE, StLT) = CM.assumeDual(State, GE.castAs<DefinedSVal>());
129  return StGE && !StLT;
130}
131
132// Is E value negative?
133static bool isNegative(CheckerContext &C, const Expr *E) {
134  ProgramStateRef State = C.getState();
135  SVal EVal = State->getSVal(E, C.getLocationContext());
136  if (EVal.isUnknownOrUndef() || !EVal.getAs<NonLoc>())
137    return false;
138  DefinedSVal DefinedEVal = EVal.castAs<DefinedSVal>();
139
140  SValBuilder &Bldr = C.getSValBuilder();
141  DefinedSVal V = Bldr.makeIntVal(0, false);
142
143  SVal LT =
144      Bldr.evalBinOp(State, BO_LT, DefinedEVal, V, Bldr.getConditionType());
145
146  // Is E value greater than MaxVal?
147  ConstraintManager &CM = C.getConstraintManager();
148  ProgramStateRef StNegative, StPositive;
149  std::tie(StNegative, StPositive) =
150      CM.assumeDual(State, LT.castAs<DefinedSVal>());
151
152  return StNegative && !StPositive;
153}
154
155bool ConversionChecker::isLossOfPrecision(const ImplicitCastExpr *Cast,
156                                        CheckerContext &C) const {
157  // Don't warn about explicit loss of precision.
158  if (Cast->isEvaluatable(C.getASTContext()))
159    return false;
160
161  QualType CastType = Cast->getType();
162  QualType SubType = Cast->IgnoreParenImpCasts()->getType();
163
164  if (!CastType->isIntegerType() || !SubType->isIntegerType())
165    return false;
166
167  if (C.getASTContext().getIntWidth(CastType) >=
168      C.getASTContext().getIntWidth(SubType))
169    return false;
170
171  unsigned W = C.getASTContext().getIntWidth(CastType);
172  if (W == 1 || W >= 64U)
173    return false;
174
175  unsigned long long MaxVal = 1ULL << W;
176  return isGreaterEqual(C, Cast->getSubExpr(), MaxVal);
177}
178
179bool ConversionChecker::isLossOfSign(const ImplicitCastExpr *Cast,
180                                   CheckerContext &C) const {
181  QualType CastType = Cast->getType();
182  QualType SubType = Cast->IgnoreParenImpCasts()->getType();
183
184  if (!CastType->isUnsignedIntegerType() || !SubType->isSignedIntegerType())
185    return false;
186
187  return isNegative(C, Cast->getSubExpr());
188}
189
190void ento::registerConversionChecker(CheckerManager &mgr) {
191  mgr.registerChecker<ConversionChecker>();
192}
193