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 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 115 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 126 // Is E value greater or equal than Val? 127 static bool isGreaterEqual(CheckerContext &C, const Expr *E, 128 unsigned long long Val) { 129 ProgramStateRef State = C.getState(); 130 SVal EVal = C.getSVal(E); 131 if (EVal.isUnknownOrUndef()) 132 return false; 133 if (!EVal.getAs<NonLoc>() && EVal.getAs<Loc>()) { 134 ProgramStateManager &Mgr = C.getStateManager(); 135 EVal = 136 Mgr.getStoreManager().getBinding(State->getStore(), EVal.castAs<Loc>()); 137 } 138 if (EVal.isUnknownOrUndef() || !EVal.getAs<NonLoc>()) 139 return false; 140 141 SValBuilder &Bldr = C.getSValBuilder(); 142 DefinedSVal V = Bldr.makeIntVal(Val, C.getASTContext().LongLongTy); 143 144 // Is DefinedEVal greater or equal with V? 145 SVal GE = Bldr.evalBinOp(State, BO_GE, EVal, V, Bldr.getConditionType()); 146 if (GE.isUnknownOrUndef()) 147 return false; 148 ConstraintManager &CM = C.getConstraintManager(); 149 ProgramStateRef StGE, StLT; 150 std::tie(StGE, StLT) = CM.assumeDual(State, GE.castAs<DefinedSVal>()); 151 return StGE && !StLT; 152 } 153 154 // Is E value negative? 155 static bool isNegative(CheckerContext &C, const Expr *E) { 156 ProgramStateRef State = C.getState(); 157 SVal EVal = State->getSVal(E, C.getLocationContext()); 158 if (EVal.isUnknownOrUndef() || !EVal.getAs<NonLoc>()) 159 return false; 160 DefinedSVal DefinedEVal = EVal.castAs<DefinedSVal>(); 161 162 SValBuilder &Bldr = C.getSValBuilder(); 163 DefinedSVal V = Bldr.makeIntVal(0, false); 164 165 SVal LT = 166 Bldr.evalBinOp(State, BO_LT, DefinedEVal, V, Bldr.getConditionType()); 167 168 // Is E value greater than MaxVal? 169 ConstraintManager &CM = C.getConstraintManager(); 170 ProgramStateRef StNegative, StPositive; 171 std::tie(StNegative, StPositive) = 172 CM.assumeDual(State, LT.castAs<DefinedSVal>()); 173 174 return StNegative && !StPositive; 175 } 176 177 bool ConversionChecker::isLossOfPrecision(const ImplicitCastExpr *Cast, 178 QualType DestType, 179 CheckerContext &C) const { 180 // Don't warn about explicit loss of precision. 181 if (Cast->isEvaluatable(C.getASTContext())) 182 return false; 183 184 QualType SubType = Cast->IgnoreParenImpCasts()->getType(); 185 186 if (!DestType->isIntegerType() || !SubType->isIntegerType()) 187 return false; 188 189 if (C.getASTContext().getIntWidth(DestType) >= 190 C.getASTContext().getIntWidth(SubType)) 191 return false; 192 193 unsigned W = C.getASTContext().getIntWidth(DestType); 194 if (W == 1 || W >= 64U) 195 return false; 196 197 unsigned long long MaxVal = 1ULL << W; 198 return isGreaterEqual(C, Cast->getSubExpr(), MaxVal); 199 } 200 201 bool ConversionChecker::isLossOfSign(const ImplicitCastExpr *Cast, 202 CheckerContext &C) const { 203 QualType CastType = Cast->getType(); 204 QualType SubType = Cast->IgnoreParenImpCasts()->getType(); 205 206 if (!CastType->isUnsignedIntegerType() || !SubType->isSignedIntegerType()) 207 return false; 208 209 return isNegative(C, Cast->getSubExpr()); 210 } 211 212 void ento::registerConversionChecker(CheckerManager &mgr) { 213 mgr.registerChecker<ConversionChecker>(); 214 } 215