ConversionChecker.cpp revision 327952
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, 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
54void 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.
105    ExplodedNode *N = C.generateNonFatalErrorNode(C.getState());
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
115void 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
126bool ConversionChecker::isLossOfPrecision(const ImplicitCastExpr *Cast,
127                                          QualType DestType,
128                                          CheckerContext &C) const {
129  // Don't warn about explicit loss of precision.
130  if (Cast->isEvaluatable(C.getASTContext()))
131    return false;
132
133  QualType SubType = Cast->IgnoreParenImpCasts()->getType();
134
135  if (!DestType->isIntegerType() || !SubType->isIntegerType())
136    return false;
137
138  if (C.getASTContext().getIntWidth(DestType) >=
139      C.getASTContext().getIntWidth(SubType))
140    return false;
141
142  unsigned W = C.getASTContext().getIntWidth(DestType);
143  if (W == 1 || W >= 64U)
144    return false;
145
146  unsigned long long MaxVal = 1ULL << W;
147  return C.isGreaterOrEqual(Cast->getSubExpr(), MaxVal);
148}
149
150bool ConversionChecker::isLossOfSign(const ImplicitCastExpr *Cast,
151                                     CheckerContext &C) const {
152  QualType CastType = Cast->getType();
153  QualType SubType = Cast->IgnoreParenImpCasts()->getType();
154
155  if (!CastType->isUnsignedIntegerType() || !SubType->isSignedIntegerType())
156    return false;
157
158  return C.isNegative(Cast->getSubExpr());
159}
160
161void ento::registerConversionChecker(CheckerManager &mgr) {
162  mgr.registerChecker<ConversionChecker>();
163}
164