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
checkPreStmt(const ImplicitCastExpr * Cast,CheckerContext & C) const54 void ConversionChecker::checkPreStmt(const ImplicitCastExpr *Cast,
55 CheckerContext &C) const {
56 // TODO: For now we only warn about DeclRefExpr, to avoid noise. Warn for
57 // calculations also.
58 if (!isa<DeclRefExpr>(Cast->IgnoreParenImpCasts()))
59 return;
60
61 // Don't warn for loss of sign/precision in macros.
62 if (Cast->getExprLoc().isMacroID())
63 return;
64
65 // Get Parent.
66 const ParentMap &PM = C.getLocationContext()->getParentMap();
67 const Stmt *Parent = PM.getParent(Cast);
68 if (!Parent)
69 return;
70
71 bool LossOfSign = false;
72 bool LossOfPrecision = false;
73
74 // Loss of sign/precision in binary operation.
75 if (const auto *B = dyn_cast<BinaryOperator>(Parent)) {
76 BinaryOperator::Opcode Opc = B->getOpcode();
77 if (Opc == BO_Assign) {
78 LossOfSign = isLossOfSign(Cast, C);
79 LossOfPrecision = isLossOfPrecision(Cast, Cast->getType(), C);
80 } else if (Opc == BO_AddAssign || Opc == BO_SubAssign) {
81 // No loss of sign.
82 LossOfPrecision = isLossOfPrecision(Cast, B->getLHS()->getType(), C);
83 } else if (Opc == BO_MulAssign) {
84 LossOfSign = isLossOfSign(Cast, C);
85 LossOfPrecision = isLossOfPrecision(Cast, B->getLHS()->getType(), C);
86 } else if (Opc == BO_DivAssign || Opc == BO_RemAssign) {
87 LossOfSign = isLossOfSign(Cast, C);
88 // No loss of precision.
89 } else if (Opc == BO_AndAssign) {
90 LossOfSign = isLossOfSign(Cast, C);
91 // No loss of precision.
92 } else if (Opc == BO_OrAssign || Opc == BO_XorAssign) {
93 LossOfSign = isLossOfSign(Cast, C);
94 LossOfPrecision = isLossOfPrecision(Cast, B->getLHS()->getType(), C);
95 } else if (B->isRelationalOp() || B->isMultiplicativeOp()) {
96 LossOfSign = isLossOfSign(Cast, C);
97 }
98 } else if (isa<DeclStmt>(Parent)) {
99 LossOfSign = isLossOfSign(Cast, C);
100 LossOfPrecision = isLossOfPrecision(Cast, Cast->getType(), C);
101 }
102
103 if (LossOfSign || LossOfPrecision) {
104 // Generate an error node.
105 ExplodedNode *N = C.generateNonFatalErrorNode(C.getState());
106 if (!N)
107 return;
108 if (LossOfSign)
109 reportBug(N, C, "Loss of sign in implicit conversion");
110 if (LossOfPrecision)
111 reportBug(N, C, "Loss of precision in implicit conversion");
112 }
113 }
114
reportBug(ExplodedNode * N,CheckerContext & C,const char Msg[]) const115 void ConversionChecker::reportBug(ExplodedNode *N, CheckerContext &C,
116 const char Msg[]) const {
117 if (!BT)
118 BT.reset(
119 new BuiltinBug(this, "Conversion", "Possible loss of sign/precision."));
120
121 // Generate a report for this bug.
122 auto R = llvm::make_unique<BugReport>(*BT, Msg, N);
123 C.emitReport(std::move(R));
124 }
125
isLossOfPrecision(const ImplicitCastExpr * Cast,QualType DestType,CheckerContext & C) const126 bool ConversionChecker::isLossOfPrecision(const ImplicitCastExpr *Cast,
127 QualType DestType,
128 CheckerContext &C) const {
129 // Don't warn about explicit loss of precision.
130 if (Cast->isEvaluatable(C.getASTContext()))
131 return false;
132
133 QualType SubType = Cast->IgnoreParenImpCasts()->getType();
134
135 if (!DestType->isIntegerType() || !SubType->isIntegerType())
136 return false;
137
138 if (C.getASTContext().getIntWidth(DestType) >=
139 C.getASTContext().getIntWidth(SubType))
140 return false;
141
142 unsigned W = C.getASTContext().getIntWidth(DestType);
143 if (W == 1 || W >= 64U)
144 return false;
145
146 unsigned long long MaxVal = 1ULL << W;
147 return C.isGreaterOrEqual(Cast->getSubExpr(), MaxVal);
148 }
149
isLossOfSign(const ImplicitCastExpr * Cast,CheckerContext & C) const150 bool ConversionChecker::isLossOfSign(const ImplicitCastExpr *Cast,
151 CheckerContext &C) const {
152 QualType CastType = Cast->getType();
153 QualType SubType = Cast->IgnoreParenImpCasts()->getType();
154
155 if (!CastType->isUnsignedIntegerType() || !SubType->isSignedIntegerType())
156 return false;
157
158 return C.isNegative(Cast->getSubExpr());
159 }
160
registerConversionChecker(CheckerManager & mgr)161 void ento::registerConversionChecker(CheckerManager &mgr) {
162 mgr.registerChecker<ConversionChecker>();
163 }
164