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