1 //=== ConversionChecker.cpp -------------------------------------*- C++ -*-===//
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
10 // Check that there is no loss of sign/precision in assignments, comparisons
11 // and multiplications.
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
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.
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"
32 using namespace clang;
36 class ConversionChecker : public Checker<check::PreStmt<ImplicitCastExpr>> {
38 void checkPreStmt(const ImplicitCastExpr *Cast, CheckerContext &C) const;
41 mutable std::unique_ptr<BuiltinBug> BT;
43 // Is there loss of precision
44 bool isLossOfPrecision(const ImplicitCastExpr *Cast, CheckerContext &C) const;
46 // Is there loss of sign
47 bool isLossOfSign(const ImplicitCastExpr *Cast, CheckerContext &C) const;
49 void reportBug(ExplodedNode *N, CheckerContext &C, const char Msg[]) const;
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
57 if (!isa<DeclRefExpr>(Cast->IgnoreParenImpCasts()))
60 // Don't warn for loss of sign/precision in macros.
61 if (Cast->getExprLoc().isMacroID())
65 const ParentMap &PM = C.getLocationContext()->getParentMap();
66 const Stmt *Parent = PM.getParent(Cast);
70 bool LossOfSign = false;
71 bool LossOfPrecision = false;
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);
83 } else if (isa<DeclStmt>(Parent)) {
84 LossOfSign = isLossOfSign(Cast, C);
85 LossOfPrecision = isLossOfPrecision(Cast, C);
88 if (LossOfSign || LossOfPrecision) {
89 // Generate an error node.
90 ExplodedNode *N = C.generateNonFatalErrorNode(C.getState());
94 reportBug(N, C, "Loss of sign in implicit conversion");
96 reportBug(N, C, "Loss of precision in implicit conversion");
100 void ConversionChecker::reportBug(ExplodedNode *N, CheckerContext &C,
101 const char Msg[]) const {
104 new BuiltinBug(this, "Conversion", "Possible loss of sign/precision."));
106 // Generate a report for this bug.
107 auto R = llvm::make_unique<BugReport>(*BT, Msg, N);
108 C.emitReport(std::move(R));
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>())
119 SValBuilder &Bldr = C.getSValBuilder();
120 DefinedSVal V = Bldr.makeIntVal(Val, C.getASTContext().LongLongTy);
122 // Is DefinedEVal greater or equal with V?
123 SVal GE = Bldr.evalBinOp(State, BO_GE, EVal, V, Bldr.getConditionType());
124 if (GE.isUnknownOrUndef())
126 ConstraintManager &CM = C.getConstraintManager();
127 ProgramStateRef StGE, StLT;
128 std::tie(StGE, StLT) = CM.assumeDual(State, GE.castAs<DefinedSVal>());
129 return StGE && !StLT;
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>())
138 DefinedSVal DefinedEVal = EVal.castAs<DefinedSVal>();
140 SValBuilder &Bldr = C.getSValBuilder();
141 DefinedSVal V = Bldr.makeIntVal(0, false);
144 Bldr.evalBinOp(State, BO_LT, DefinedEVal, V, Bldr.getConditionType());
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>());
152 return StNegative && !StPositive;
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()))
161 QualType CastType = Cast->getType();
162 QualType SubType = Cast->IgnoreParenImpCasts()->getType();
164 if (!CastType->isIntegerType() || !SubType->isIntegerType())
167 if (C.getASTContext().getIntWidth(CastType) >=
168 C.getASTContext().getIntWidth(SubType))
171 unsigned W = C.getASTContext().getIntWidth(CastType);
172 if (W == 1 || W >= 64U)
175 unsigned long long MaxVal = 1ULL << W;
176 return isGreaterEqual(C, Cast->getSubExpr(), MaxVal);
179 bool ConversionChecker::isLossOfSign(const ImplicitCastExpr *Cast,
180 CheckerContext &C) const {
181 QualType CastType = Cast->getType();
182 QualType SubType = Cast->IgnoreParenImpCasts()->getType();
184 if (!CastType->isUnsignedIntegerType() || !SubType->isSignedIntegerType())
187 return isNegative(C, Cast->getSubExpr());
190 void ento::registerConversionChecker(CheckerManager &mgr) {
191 mgr.registerChecker<ConversionChecker>();