]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/StaticAnalyzer/Checkers/ConversionChecker.cpp
Vendor import of clang trunk r290819:
[FreeBSD/FreeBSD.git] / lib / StaticAnalyzer / Checkers / ConversionChecker.cpp
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
32 using namespace clang;
33 using namespace ento;
34
35 namespace {
36 class ConversionChecker : public Checker<check::PreStmt<ImplicitCastExpr>> {
37 public:
38   void checkPreStmt(const ImplicitCastExpr *Cast, CheckerContext &C) const;
39
40 private:
41   mutable std::unique_ptr<BuiltinBug> BT;
42
43   // Is there loss of precision
44   bool isLossOfPrecision(const ImplicitCastExpr *Cast, CheckerContext &C) const;
45
46   // Is there loss of sign
47   bool isLossOfSign(const ImplicitCastExpr *Cast, CheckerContext &C) const;
48
49   void reportBug(ExplodedNode *N, CheckerContext &C, const char Msg[]) const;
50 };
51 }
52
53 void ConversionChecker::checkPreStmt(const ImplicitCastExpr *Cast,
54                                      CheckerContext &C) const {
55   // TODO: For now we only warn about DeclRefExpr, to avoid noise. Warn for
56   // calculations also.
57   if (!isa<DeclRefExpr>(Cast->IgnoreParenImpCasts()))
58     return;
59
60   // Don't warn for loss of sign/precision in macros.
61   if (Cast->getExprLoc().isMacroID())
62     return;
63
64   // Get Parent.
65   const ParentMap &PM = C.getLocationContext()->getParentMap();
66   const Stmt *Parent = PM.getParent(Cast);
67   if (!Parent)
68     return;
69
70   bool LossOfSign = false;
71   bool LossOfPrecision = false;
72
73   // Loss of sign/precision in binary operation.
74   if (const auto *B = dyn_cast<BinaryOperator>(Parent)) {
75     BinaryOperator::Opcode Opc = B->getOpcode();
76     if (Opc == BO_Assign || Opc == BO_AddAssign || Opc == BO_SubAssign ||
77         Opc == BO_MulAssign) {
78       LossOfSign = isLossOfSign(Cast, C);
79       LossOfPrecision = isLossOfPrecision(Cast, C);
80     } else if (B->isRelationalOp() || B->isMultiplicativeOp()) {
81       LossOfSign = isLossOfSign(Cast, C);
82     }
83   } else if (isa<DeclStmt>(Parent)) {
84     LossOfSign = isLossOfSign(Cast, C);
85     LossOfPrecision = isLossOfPrecision(Cast, C);
86   }
87
88   if (LossOfSign || LossOfPrecision) {
89     // Generate an error node.
90     ExplodedNode *N = C.generateNonFatalErrorNode(C.getState());
91     if (!N)
92       return;
93     if (LossOfSign)
94       reportBug(N, C, "Loss of sign in implicit conversion");
95     if (LossOfPrecision)
96       reportBug(N, C, "Loss of precision in implicit conversion");
97   }
98 }
99
100 void ConversionChecker::reportBug(ExplodedNode *N, CheckerContext &C,
101                                   const char Msg[]) const {
102   if (!BT)
103     BT.reset(
104         new BuiltinBug(this, "Conversion", "Possible loss of sign/precision."));
105
106   // Generate a report for this bug.
107   auto R = llvm::make_unique<BugReport>(*BT, Msg, N);
108   C.emitReport(std::move(R));
109 }
110
111 // Is E value greater or equal than Val?
112 static bool isGreaterEqual(CheckerContext &C, const Expr *E,
113                            unsigned long long Val) {
114   ProgramStateRef State = C.getState();
115   SVal EVal = C.getSVal(E);
116   if (EVal.isUnknownOrUndef() || !EVal.getAs<NonLoc>())
117     return false;
118
119   SValBuilder &Bldr = C.getSValBuilder();
120   DefinedSVal V = Bldr.makeIntVal(Val, C.getASTContext().LongLongTy);
121
122   // Is DefinedEVal greater or equal with V?
123   SVal GE = Bldr.evalBinOp(State, BO_GE, EVal, V, Bldr.getConditionType());
124   if (GE.isUnknownOrUndef())
125     return false;
126   ConstraintManager &CM = C.getConstraintManager();
127   ProgramStateRef StGE, StLT;
128   std::tie(StGE, StLT) = CM.assumeDual(State, GE.castAs<DefinedSVal>());
129   return StGE && !StLT;
130 }
131
132 // Is E value negative?
133 static bool isNegative(CheckerContext &C, const Expr *E) {
134   ProgramStateRef State = C.getState();
135   SVal EVal = State->getSVal(E, C.getLocationContext());
136   if (EVal.isUnknownOrUndef() || !EVal.getAs<NonLoc>())
137     return false;
138   DefinedSVal DefinedEVal = EVal.castAs<DefinedSVal>();
139
140   SValBuilder &Bldr = C.getSValBuilder();
141   DefinedSVal V = Bldr.makeIntVal(0, false);
142
143   SVal LT =
144       Bldr.evalBinOp(State, BO_LT, DefinedEVal, V, Bldr.getConditionType());
145
146   // Is E value greater than MaxVal?
147   ConstraintManager &CM = C.getConstraintManager();
148   ProgramStateRef StNegative, StPositive;
149   std::tie(StNegative, StPositive) =
150       CM.assumeDual(State, LT.castAs<DefinedSVal>());
151
152   return StNegative && !StPositive;
153 }
154
155 bool ConversionChecker::isLossOfPrecision(const ImplicitCastExpr *Cast,
156                                         CheckerContext &C) const {
157   // Don't warn about explicit loss of precision.
158   if (Cast->isEvaluatable(C.getASTContext()))
159     return false;
160
161   QualType CastType = Cast->getType();
162   QualType SubType = Cast->IgnoreParenImpCasts()->getType();
163
164   if (!CastType->isIntegerType() || !SubType->isIntegerType())
165     return false;
166
167   if (C.getASTContext().getIntWidth(CastType) >=
168       C.getASTContext().getIntWidth(SubType))
169     return false;
170
171   unsigned W = C.getASTContext().getIntWidth(CastType);
172   if (W == 1 || W >= 64U)
173     return false;
174
175   unsigned long long MaxVal = 1ULL << W;
176   return isGreaterEqual(C, Cast->getSubExpr(), MaxVal);
177 }
178
179 bool ConversionChecker::isLossOfSign(const ImplicitCastExpr *Cast,
180                                    CheckerContext &C) const {
181   QualType CastType = Cast->getType();
182   QualType SubType = Cast->IgnoreParenImpCasts()->getType();
183
184   if (!CastType->isUnsignedIntegerType() || !SubType->isSignedIntegerType())
185     return false;
186
187   return isNegative(C, Cast->getSubExpr());
188 }
189
190 void ento::registerConversionChecker(CheckerManager &mgr) {
191   mgr.registerChecker<ConversionChecker>();
192 }