xref: /freebsd/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ConversionChecker.cpp (revision 5e801ac66d24704442eba426ed13c3effb8a34e7)
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 
36 using namespace clang;
37 using namespace ento;
38 
39 namespace {
40 class ConversionChecker : public Checker<check::PreStmt<ImplicitCastExpr>> {
41 public:
42   void checkPreStmt(const ImplicitCastExpr *Cast, CheckerContext &C) const;
43 
44 private:
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, const Expr *E, CheckerContext &C,
53                  const char Msg[]) const;
54 };
55 }
56 
57 void 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, Cast, C, "Loss of sign in implicit conversion");
113     if (LossOfPrecision)
114       reportBug(N, Cast, C, "Loss of precision in implicit conversion");
115   }
116 }
117 
118 void ConversionChecker::reportBug(ExplodedNode *N, const Expr *E,
119                                   CheckerContext &C, 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 = std::make_unique<PathSensitiveBugReport>(*BT, Msg, N);
126   bugreporter::trackExpressionValue(N, E, *R);
127   C.emitReport(std::move(R));
128 }
129 
130 bool ConversionChecker::isLossOfPrecision(const ImplicitCastExpr *Cast,
131                                           QualType DestType,
132                                           CheckerContext &C) const {
133   // Don't warn about explicit loss of precision.
134   if (Cast->isEvaluatable(C.getASTContext()))
135     return false;
136 
137   QualType SubType = Cast->IgnoreParenImpCasts()->getType();
138 
139   if (!DestType->isRealType() || !SubType->isIntegerType())
140     return false;
141 
142   const bool isFloat = DestType->isFloatingType();
143 
144   const auto &AC = C.getASTContext();
145 
146   // We will find the largest RepresentsUntilExp value such that the DestType
147   // can exactly represent all nonnegative integers below 2^RepresentsUntilExp.
148   unsigned RepresentsUntilExp;
149 
150   if (isFloat) {
151     const llvm::fltSemantics &Sema = AC.getFloatTypeSemantics(DestType);
152     RepresentsUntilExp = llvm::APFloat::semanticsPrecision(Sema);
153   } else {
154     RepresentsUntilExp = AC.getIntWidth(DestType);
155     if (RepresentsUntilExp == 1) {
156       // This is just casting a number to bool, probably not a bug.
157       return false;
158     }
159     if (DestType->isSignedIntegerType())
160       RepresentsUntilExp--;
161   }
162 
163   if (RepresentsUntilExp >= sizeof(unsigned long long) * CHAR_BIT) {
164     // Avoid overflow in our later calculations.
165     return false;
166   }
167 
168   unsigned CorrectedSrcWidth = AC.getIntWidth(SubType);
169   if (SubType->isSignedIntegerType())
170     CorrectedSrcWidth--;
171 
172   if (RepresentsUntilExp >= CorrectedSrcWidth) {
173     // Simple case: the destination can store all values of the source type.
174     return false;
175   }
176 
177   unsigned long long MaxVal = 1ULL << RepresentsUntilExp;
178   if (isFloat) {
179     // If this is a floating point type, it can also represent MaxVal exactly.
180     MaxVal++;
181   }
182   return C.isGreaterOrEqual(Cast->getSubExpr(), MaxVal);
183   // TODO: maybe also check negative values with too large magnitude.
184 }
185 
186 bool ConversionChecker::isLossOfSign(const ImplicitCastExpr *Cast,
187                                      CheckerContext &C) const {
188   QualType CastType = Cast->getType();
189   QualType SubType = Cast->IgnoreParenImpCasts()->getType();
190 
191   if (!CastType->isUnsignedIntegerType() || !SubType->isSignedIntegerType())
192     return false;
193 
194   return C.isNegative(Cast->getSubExpr());
195 }
196 
197 void ento::registerConversionChecker(CheckerManager &mgr) {
198   mgr.registerChecker<ConversionChecker>();
199 }
200 
201 bool ento::shouldRegisterConversionChecker(const CheckerManager &mgr) {
202   return true;
203 }
204