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