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  const BugType BT{this, "Conversion"};
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, const Expr *E, CheckerContext &C,
53                 const char Msg[]) const;
54};
55}
56
57void ConversionChecker::checkPreStmt(const ImplicitCastExpr *Cast,
58                                     CheckerContext &C) const {
59  // Don't warn for implicit conversions to bool
60  if (Cast->getType()->isBooleanType())
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  // Dont warn if this is part of an explicit cast
73  if (isa<ExplicitCastExpr>(Parent))
74    return;
75
76  bool LossOfSign = false;
77  bool LossOfPrecision = false;
78
79  // Loss of sign/precision in binary operation.
80  if (const auto *B = dyn_cast<BinaryOperator>(Parent)) {
81    BinaryOperator::Opcode Opc = B->getOpcode();
82    if (Opc == BO_Assign) {
83      if (!Cast->IgnoreParenImpCasts()->isEvaluatable(C.getASTContext())) {
84        LossOfSign = isLossOfSign(Cast, C);
85        LossOfPrecision = isLossOfPrecision(Cast, Cast->getType(), C);
86      }
87    } else if (Opc == BO_AddAssign || Opc == BO_SubAssign) {
88      // No loss of sign.
89      LossOfPrecision = isLossOfPrecision(Cast, B->getLHS()->getType(), C);
90    } else if (Opc == BO_MulAssign) {
91      LossOfSign = isLossOfSign(Cast, C);
92      LossOfPrecision = isLossOfPrecision(Cast, B->getLHS()->getType(), C);
93    } else if (Opc == BO_DivAssign || Opc == BO_RemAssign) {
94      LossOfSign = isLossOfSign(Cast, C);
95      // No loss of precision.
96    } else if (Opc == BO_AndAssign) {
97      LossOfSign = isLossOfSign(Cast, C);
98      // No loss of precision.
99    } else if (Opc == BO_OrAssign || Opc == BO_XorAssign) {
100      LossOfSign = isLossOfSign(Cast, C);
101      LossOfPrecision = isLossOfPrecision(Cast, B->getLHS()->getType(), C);
102    } else if (B->isRelationalOp() || B->isMultiplicativeOp()) {
103      LossOfSign = isLossOfSign(Cast, C);
104    }
105  } else if (isa<DeclStmt, ReturnStmt>(Parent)) {
106    if (!Cast->IgnoreParenImpCasts()->isEvaluatable(C.getASTContext())) {
107      LossOfSign = isLossOfSign(Cast, C);
108      LossOfPrecision = isLossOfPrecision(Cast, Cast->getType(), C);
109    }
110  } else {
111    LossOfSign = isLossOfSign(Cast, C);
112    LossOfPrecision = isLossOfPrecision(Cast, Cast->getType(), C);
113  }
114
115  if (LossOfSign || LossOfPrecision) {
116    // Generate an error node.
117    ExplodedNode *N = C.generateNonFatalErrorNode(C.getState());
118    if (!N)
119      return;
120    if (LossOfSign)
121      reportBug(N, Cast, C, "Loss of sign in implicit conversion");
122    if (LossOfPrecision)
123      reportBug(N, Cast, C, "Loss of precision in implicit conversion");
124  }
125}
126
127void ConversionChecker::reportBug(ExplodedNode *N, const Expr *E,
128                                  CheckerContext &C, const char Msg[]) const {
129  // Generate a report for this bug.
130  auto R = std::make_unique<PathSensitiveBugReport>(BT, Msg, N);
131  bugreporter::trackExpressionValue(N, E, *R);
132  C.emitReport(std::move(R));
133}
134
135bool ConversionChecker::isLossOfPrecision(const ImplicitCastExpr *Cast,
136                                          QualType DestType,
137                                          CheckerContext &C) const {
138  // Don't warn about explicit loss of precision.
139  if (Cast->isEvaluatable(C.getASTContext()))
140    return false;
141
142  QualType SubType = Cast->IgnoreParenImpCasts()->getType();
143
144  if (!DestType->isRealType() || !SubType->isIntegerType())
145    return false;
146
147  const bool isFloat = DestType->isFloatingType();
148
149  const auto &AC = C.getASTContext();
150
151  // We will find the largest RepresentsUntilExp value such that the DestType
152  // can exactly represent all nonnegative integers below 2^RepresentsUntilExp.
153  unsigned RepresentsUntilExp;
154
155  if (isFloat) {
156    const llvm::fltSemantics &Sema = AC.getFloatTypeSemantics(DestType);
157    RepresentsUntilExp = llvm::APFloat::semanticsPrecision(Sema);
158  } else {
159    RepresentsUntilExp = AC.getIntWidth(DestType);
160    if (RepresentsUntilExp == 1) {
161      // This is just casting a number to bool, probably not a bug.
162      return false;
163    }
164    if (DestType->isSignedIntegerType())
165      RepresentsUntilExp--;
166  }
167
168  if (RepresentsUntilExp >= sizeof(unsigned long long) * CHAR_BIT) {
169    // Avoid overflow in our later calculations.
170    return false;
171  }
172
173  unsigned CorrectedSrcWidth = AC.getIntWidth(SubType);
174  if (SubType->isSignedIntegerType())
175    CorrectedSrcWidth--;
176
177  if (RepresentsUntilExp >= CorrectedSrcWidth) {
178    // Simple case: the destination can store all values of the source type.
179    return false;
180  }
181
182  unsigned long long MaxVal = 1ULL << RepresentsUntilExp;
183  if (isFloat) {
184    // If this is a floating point type, it can also represent MaxVal exactly.
185    MaxVal++;
186  }
187  return C.isGreaterOrEqual(Cast->getSubExpr(), MaxVal);
188  // TODO: maybe also check negative values with too large magnitude.
189}
190
191bool ConversionChecker::isLossOfSign(const ImplicitCastExpr *Cast,
192                                     CheckerContext &C) const {
193  QualType CastType = Cast->getType();
194  QualType SubType = Cast->IgnoreParenImpCasts()->getType();
195
196  if (!CastType->isUnsignedIntegerType() || !SubType->isSignedIntegerType())
197    return false;
198
199  return C.isNegative(Cast->getSubExpr());
200}
201
202void ento::registerConversionChecker(CheckerManager &mgr) {
203  mgr.registerChecker<ConversionChecker>();
204}
205
206bool ento::shouldRegisterConversionChecker(const CheckerManager &mgr) {
207  return true;
208}
209