ConversionChecker.cpp revision 311143
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 32using namespace clang; 33using namespace ento; 34 35namespace { 36class ConversionChecker : public Checker<check::PreStmt<ImplicitCastExpr>> { 37public: 38 void checkPreStmt(const ImplicitCastExpr *Cast, CheckerContext &C) const; 39 40private: 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 53void 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 100void 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? 112static 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? 133static 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 155bool 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 179bool 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 190void ento::registerConversionChecker(CheckerManager &mgr) { 191 mgr.registerChecker<ConversionChecker>(); 192} 193