/Users/buildslave/jenkins/sharedspace/clang-stage2-coverage-R@2/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ConversionChecker.cpp
Line | Count | Source (jump to first uncovered line) |
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, QualType DestType, |
45 | | CheckerContext &C) const; |
46 | | |
47 | | // Is there loss of sign |
48 | | bool isLossOfSign(const ImplicitCastExpr *Cast, CheckerContext &C) const; |
49 | | |
50 | | void reportBug(ExplodedNode *N, CheckerContext &C, const char Msg[]) const; |
51 | | }; |
52 | | } |
53 | | |
54 | | void ConversionChecker::checkPreStmt(const ImplicitCastExpr *Cast, |
55 | 10.5k | CheckerContext &C) const { |
56 | 10.5k | // TODO: For now we only warn about DeclRefExpr, to avoid noise. Warn for |
57 | 10.5k | // calculations also. |
58 | 10.5k | if (!isa<DeclRefExpr>(Cast->IgnoreParenImpCasts())) |
59 | 2.75k | return; |
60 | 7.78k | |
61 | 7.78k | // Don't warn for loss of sign/precision in macros. |
62 | 7.78k | if (7.78k Cast->getExprLoc().isMacroID()7.78k ) |
63 | 70 | return; |
64 | 7.71k | |
65 | 7.71k | // Get Parent. |
66 | 7.71k | const ParentMap &PM = C.getLocationContext()->getParentMap(); |
67 | 7.71k | const Stmt *Parent = PM.getParent(Cast); |
68 | 7.71k | if (!Parent) |
69 | 4 | return; |
70 | 7.70k | |
71 | 7.70k | bool LossOfSign = false; |
72 | 7.70k | bool LossOfPrecision = false; |
73 | 7.70k | |
74 | 7.70k | // Loss of sign/precision in binary operation. |
75 | 7.70k | if (const auto *B7.70k = dyn_cast<BinaryOperator>(Parent)) { |
76 | 2.87k | BinaryOperator::Opcode Opc = B->getOpcode(); |
77 | 2.87k | if (Opc == BO_Assign2.87k ) { |
78 | 588 | LossOfSign = isLossOfSign(Cast, C); |
79 | 588 | LossOfPrecision = isLossOfPrecision(Cast, Cast->getType(), C); |
80 | 2.87k | } else if (2.28k Opc == BO_AddAssign || 2.28k Opc == BO_SubAssign2.28k ) { |
81 | 6 | // No loss of sign. |
82 | 6 | LossOfPrecision = isLossOfPrecision(Cast, B->getLHS()->getType(), C); |
83 | 2.28k | } else if (2.28k Opc == BO_MulAssign2.28k ) { |
84 | 23 | LossOfSign = isLossOfSign(Cast, C); |
85 | 23 | LossOfPrecision = isLossOfPrecision(Cast, B->getLHS()->getType(), C); |
86 | 2.28k | } else if (2.25k Opc == BO_DivAssign || 2.25k Opc == BO_RemAssign2.25k ) { |
87 | 6 | LossOfSign = isLossOfSign(Cast, C); |
88 | 6 | // No loss of precision. |
89 | 2.25k | } else if (2.25k Opc == BO_AndAssign2.25k ) { |
90 | 5 | LossOfSign = isLossOfSign(Cast, C); |
91 | 5 | // No loss of precision. |
92 | 2.25k | } else if (2.24k Opc == BO_OrAssign || 2.24k Opc == BO_XorAssign2.24k ) { |
93 | 4 | LossOfSign = isLossOfSign(Cast, C); |
94 | 4 | LossOfPrecision = isLossOfPrecision(Cast, B->getLHS()->getType(), C); |
95 | 2.24k | } else if (2.24k B->isRelationalOp() || 2.24k B->isMultiplicativeOp()1.47k ) { |
96 | 909 | LossOfSign = isLossOfSign(Cast, C); |
97 | 909 | } |
98 | 7.70k | } else if (4.83k isa<DeclStmt>(Parent)4.83k ) { |
99 | 46 | LossOfSign = isLossOfSign(Cast, C); |
100 | 46 | LossOfPrecision = isLossOfPrecision(Cast, Cast->getType(), C); |
101 | 46 | } |
102 | 7.70k | |
103 | 7.70k | if (LossOfSign || 7.70k LossOfPrecision7.69k ) { |
104 | 48 | // Generate an error node. |
105 | 48 | ExplodedNode *N = C.generateNonFatalErrorNode(C.getState()); |
106 | 48 | if (!N) |
107 | 0 | return; |
108 | 48 | if (48 LossOfSign48 ) |
109 | 12 | reportBug(N, C, "Loss of sign in implicit conversion"); |
110 | 48 | if (LossOfPrecision) |
111 | 36 | reportBug(N, C, "Loss of precision in implicit conversion"); |
112 | 48 | } |
113 | 10.5k | } |
114 | | |
115 | | void ConversionChecker::reportBug(ExplodedNode *N, CheckerContext &C, |
116 | 48 | const char Msg[]) const { |
117 | 48 | if (!BT) |
118 | 1 | BT.reset( |
119 | 1 | new BuiltinBug(this, "Conversion", "Possible loss of sign/precision.")); |
120 | 48 | |
121 | 48 | // Generate a report for this bug. |
122 | 48 | auto R = llvm::make_unique<BugReport>(*BT, Msg, N); |
123 | 48 | C.emitReport(std::move(R)); |
124 | 48 | } |
125 | | |
126 | | // Is E value greater or equal than Val? |
127 | | static bool isGreaterEqual(CheckerContext &C, const Expr *E, |
128 | 331 | unsigned long long Val) { |
129 | 331 | ProgramStateRef State = C.getState(); |
130 | 331 | SVal EVal = C.getSVal(E); |
131 | 331 | if (EVal.isUnknownOrUndef()) |
132 | 0 | return false; |
133 | 331 | if (331 !EVal.getAs<NonLoc>() && 331 EVal.getAs<Loc>()5 ) { |
134 | 5 | ProgramStateManager &Mgr = C.getStateManager(); |
135 | 5 | EVal = |
136 | 5 | Mgr.getStoreManager().getBinding(State->getStore(), EVal.castAs<Loc>()); |
137 | 5 | } |
138 | 331 | if (EVal.isUnknownOrUndef() || 331 !EVal.getAs<NonLoc>()331 ) |
139 | 0 | return false; |
140 | 331 | |
141 | 331 | SValBuilder &Bldr = C.getSValBuilder(); |
142 | 331 | DefinedSVal V = Bldr.makeIntVal(Val, C.getASTContext().LongLongTy); |
143 | 331 | |
144 | 331 | // Is DefinedEVal greater or equal with V? |
145 | 331 | SVal GE = Bldr.evalBinOp(State, BO_GE, EVal, V, Bldr.getConditionType()); |
146 | 331 | if (GE.isUnknownOrUndef()) |
147 | 0 | return false; |
148 | 331 | ConstraintManager &CM = C.getConstraintManager(); |
149 | 331 | ProgramStateRef StGE, StLT; |
150 | 331 | std::tie(StGE, StLT) = CM.assumeDual(State, GE.castAs<DefinedSVal>()); |
151 | 327 | return StGE && !StLT; |
152 | 331 | } |
153 | | |
154 | | // Is E value negative? |
155 | 19 | static bool isNegative(CheckerContext &C, const Expr *E) { |
156 | 19 | ProgramStateRef State = C.getState(); |
157 | 19 | SVal EVal = State->getSVal(E, C.getLocationContext()); |
158 | 19 | if (EVal.isUnknownOrUndef() || 19 !EVal.getAs<NonLoc>()19 ) |
159 | 0 | return false; |
160 | 19 | DefinedSVal DefinedEVal = EVal.castAs<DefinedSVal>(); |
161 | 19 | |
162 | 19 | SValBuilder &Bldr = C.getSValBuilder(); |
163 | 19 | DefinedSVal V = Bldr.makeIntVal(0, false); |
164 | 19 | |
165 | 19 | SVal LT = |
166 | 19 | Bldr.evalBinOp(State, BO_LT, DefinedEVal, V, Bldr.getConditionType()); |
167 | 19 | |
168 | 19 | // Is E value greater than MaxVal? |
169 | 19 | ConstraintManager &CM = C.getConstraintManager(); |
170 | 19 | ProgramStateRef StNegative, StPositive; |
171 | 19 | std::tie(StNegative, StPositive) = |
172 | 19 | CM.assumeDual(State, LT.castAs<DefinedSVal>()); |
173 | 19 | |
174 | 13 | return StNegative && !StPositive; |
175 | 19 | } |
176 | | |
177 | | bool ConversionChecker::isLossOfPrecision(const ImplicitCastExpr *Cast, |
178 | | QualType DestType, |
179 | 667 | CheckerContext &C) const { |
180 | 667 | // Don't warn about explicit loss of precision. |
181 | 667 | if (Cast->isEvaluatable(C.getASTContext())) |
182 | 64 | return false; |
183 | 603 | |
184 | 603 | QualType SubType = Cast->IgnoreParenImpCasts()->getType(); |
185 | 603 | |
186 | 603 | if (!DestType->isIntegerType() || 603 !SubType->isIntegerType()512 ) |
187 | 93 | return false; |
188 | 510 | |
189 | 510 | if (510 C.getASTContext().getIntWidth(DestType) >= |
190 | 510 | C.getASTContext().getIntWidth(SubType)) |
191 | 179 | return false; |
192 | 331 | |
193 | 331 | unsigned W = C.getASTContext().getIntWidth(DestType); |
194 | 331 | if (W == 1 || 331 W >= 64U331 ) |
195 | 0 | return false; |
196 | 331 | |
197 | 331 | unsigned long long MaxVal = 1ULL << W; |
198 | 331 | return isGreaterEqual(C, Cast->getSubExpr(), MaxVal); |
199 | 331 | } |
200 | | |
201 | | bool ConversionChecker::isLossOfSign(const ImplicitCastExpr *Cast, |
202 | 1.58k | CheckerContext &C) const { |
203 | 1.58k | QualType CastType = Cast->getType(); |
204 | 1.58k | QualType SubType = Cast->IgnoreParenImpCasts()->getType(); |
205 | 1.58k | |
206 | 1.58k | if (!CastType->isUnsignedIntegerType() || 1.58k !SubType->isSignedIntegerType()254 ) |
207 | 1.56k | return false; |
208 | 19 | |
209 | 19 | return isNegative(C, Cast->getSubExpr()); |
210 | 19 | } |
211 | | |
212 | 65 | void ento::registerConversionChecker(CheckerManager &mgr) { |
213 | 65 | mgr.registerChecker<ConversionChecker>(); |
214 | 65 | } |