ConversionChecker.cpp revision 344779
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 the source value is greater than the max
18//   value of the target integer type
19// * assignment / initialization when the source integer is above the range
20//   where the target floating point type can represent all integers
21//
22// Many compilers and tools have similar checks that are based on semantic
23// analysis. Those checks are sound but have poor precision. ConversionChecker
24// is an alternative to those checks.
25//
26//===----------------------------------------------------------------------===//
27#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
28#include "clang/AST/ParentMap.h"
29#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
30#include "clang/StaticAnalyzer/Core/Checker.h"
31#include "clang/StaticAnalyzer/Core/CheckerManager.h"
32#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
33#include "llvm/ADT/APFloat.h"
34
35#include <climits>
36
37using namespace clang;
38using namespace ento;
39
40namespace {
41class ConversionChecker : public Checker<check::PreStmt<ImplicitCastExpr>> {
42public:
43  void checkPreStmt(const ImplicitCastExpr *Cast, CheckerContext &C) const;
44
45private:
46  mutable std::unique_ptr<BuiltinBug> BT;
47
48  bool isLossOfPrecision(const ImplicitCastExpr *Cast, QualType DestType,
49                         CheckerContext &C) const;
50
51  bool isLossOfSign(const ImplicitCastExpr *Cast, CheckerContext &C) const;
52
53  void reportBug(ExplodedNode *N, CheckerContext &C, const char Msg[]) const;
54};
55}
56
57void ConversionChecker::checkPreStmt(const ImplicitCastExpr *Cast,
58                                     CheckerContext &C) const {
59  // TODO: For now we only warn about DeclRefExpr, to avoid noise. Warn for
60  // calculations also.
61  if (!isa<DeclRefExpr>(Cast->IgnoreParenImpCasts()))
62    return;
63
64  // Don't warn for loss of sign/precision in macros.
65  if (Cast->getExprLoc().isMacroID())
66    return;
67
68  // Get Parent.
69  const ParentMap &PM = C.getLocationContext()->getParentMap();
70  const Stmt *Parent = PM.getParent(Cast);
71  if (!Parent)
72    return;
73
74  bool LossOfSign = false;
75  bool LossOfPrecision = false;
76
77  // Loss of sign/precision in binary operation.
78  if (const auto *B = dyn_cast<BinaryOperator>(Parent)) {
79    BinaryOperator::Opcode Opc = B->getOpcode();
80    if (Opc == BO_Assign) {
81      LossOfSign = isLossOfSign(Cast, C);
82      LossOfPrecision = isLossOfPrecision(Cast, Cast->getType(), C);
83    } else if (Opc == BO_AddAssign || Opc == BO_SubAssign) {
84      // No loss of sign.
85      LossOfPrecision = isLossOfPrecision(Cast, B->getLHS()->getType(), C);
86    } else if (Opc == BO_MulAssign) {
87      LossOfSign = isLossOfSign(Cast, C);
88      LossOfPrecision = isLossOfPrecision(Cast, B->getLHS()->getType(), C);
89    } else if (Opc == BO_DivAssign || Opc == BO_RemAssign) {
90      LossOfSign = isLossOfSign(Cast, C);
91      // No loss of precision.
92    } else if (Opc == BO_AndAssign) {
93      LossOfSign = isLossOfSign(Cast, C);
94      // No loss of precision.
95    } else if (Opc == BO_OrAssign || Opc == BO_XorAssign) {
96      LossOfSign = isLossOfSign(Cast, C);
97      LossOfPrecision = isLossOfPrecision(Cast, B->getLHS()->getType(), C);
98    } else if (B->isRelationalOp() || B->isMultiplicativeOp()) {
99      LossOfSign = isLossOfSign(Cast, C);
100    }
101  } else if (isa<DeclStmt>(Parent)) {
102    LossOfSign = isLossOfSign(Cast, C);
103    LossOfPrecision = isLossOfPrecision(Cast, Cast->getType(), C);
104  }
105
106  if (LossOfSign || LossOfPrecision) {
107    // Generate an error node.
108    ExplodedNode *N = C.generateNonFatalErrorNode(C.getState());
109    if (!N)
110      return;
111    if (LossOfSign)
112      reportBug(N, C, "Loss of sign in implicit conversion");
113    if (LossOfPrecision)
114      reportBug(N, C, "Loss of precision in implicit conversion");
115  }
116}
117
118void ConversionChecker::reportBug(ExplodedNode *N, CheckerContext &C,
119                                  const char Msg[]) const {
120  if (!BT)
121    BT.reset(
122        new BuiltinBug(this, "Conversion", "Possible loss of sign/precision."));
123
124  // Generate a report for this bug.
125  auto R = llvm::make_unique<BugReport>(*BT, Msg, N);
126  C.emitReport(std::move(R));
127}
128
129bool ConversionChecker::isLossOfPrecision(const ImplicitCastExpr *Cast,
130                                          QualType DestType,
131                                          CheckerContext &C) const {
132  // Don't warn about explicit loss of precision.
133  if (Cast->isEvaluatable(C.getASTContext()))
134    return false;
135
136  QualType SubType = Cast->IgnoreParenImpCasts()->getType();
137
138  if (!DestType->isRealType() || !SubType->isIntegerType())
139    return false;
140
141  const bool isFloat = DestType->isFloatingType();
142
143  const auto &AC = C.getASTContext();
144
145  // We will find the largest RepresentsUntilExp value such that the DestType
146  // can exactly represent all nonnegative integers below 2^RepresentsUntilExp.
147  unsigned RepresentsUntilExp;
148
149  if (isFloat) {
150    const llvm::fltSemantics &Sema = AC.getFloatTypeSemantics(DestType);
151    RepresentsUntilExp = llvm::APFloat::semanticsPrecision(Sema);
152  } else {
153    RepresentsUntilExp = AC.getIntWidth(DestType);
154    if (RepresentsUntilExp == 1) {
155      // This is just casting a number to bool, probably not a bug.
156      return false;
157    }
158    if (DestType->isSignedIntegerType())
159      RepresentsUntilExp--;
160  }
161
162  if (RepresentsUntilExp >= sizeof(unsigned long long) * CHAR_BIT) {
163    // Avoid overflow in our later calculations.
164    return false;
165  }
166
167  unsigned CorrectedSrcWidth = AC.getIntWidth(SubType);
168  if (SubType->isSignedIntegerType())
169    CorrectedSrcWidth--;
170
171  if (RepresentsUntilExp >= CorrectedSrcWidth) {
172    // Simple case: the destination can store all values of the source type.
173    return false;
174  }
175
176  unsigned long long MaxVal = 1ULL << RepresentsUntilExp;
177  if (isFloat) {
178    // If this is a floating point type, it can also represent MaxVal exactly.
179    MaxVal++;
180  }
181  return C.isGreaterOrEqual(Cast->getSubExpr(), MaxVal);
182  // TODO: maybe also check negative values with too large magnitude.
183}
184
185bool ConversionChecker::isLossOfSign(const ImplicitCastExpr *Cast,
186                                     CheckerContext &C) const {
187  QualType CastType = Cast->getType();
188  QualType SubType = Cast->IgnoreParenImpCasts()->getType();
189
190  if (!CastType->isUnsignedIntegerType() || !SubType->isSignedIntegerType())
191    return false;
192
193  return C.isNegative(Cast->getSubExpr());
194}
195
196void ento::registerConversionChecker(CheckerManager &mgr) {
197  mgr.registerChecker<ConversionChecker>();
198}
199