1 //=== ConversionChecker.cpp -------------------------------------*- C++ -*-===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // Check that there is no loss of sign/precision in assignments, comparisons 10 // and multiplications. 11 // 12 // ConversionChecker uses path sensitive analysis to determine possible values 13 // of expressions. A warning is reported when: 14 // * a negative value is implicitly converted to an unsigned value in an 15 // assignment, comparison or multiplication. 16 // * assignment / initialization when the source value is greater than the max 17 // value of the target integer type 18 // * assignment / initialization when the source integer is above the range 19 // where the target floating point type can represent all integers 20 // 21 // Many compilers and tools have similar checks that are based on semantic 22 // analysis. Those checks are sound but have poor precision. ConversionChecker 23 // is an alternative to those checks. 24 // 25 //===----------------------------------------------------------------------===// 26 #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" 27 #include "clang/AST/ParentMap.h" 28 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 29 #include "clang/StaticAnalyzer/Core/Checker.h" 30 #include "clang/StaticAnalyzer/Core/CheckerManager.h" 31 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 32 #include "llvm/ADT/APFloat.h" 33 34 #include <climits> 35 36 using namespace clang; 37 using namespace ento; 38 39 namespace { 40 class ConversionChecker : public Checker<check::PreStmt<ImplicitCastExpr>> { 41 public: 42 void checkPreStmt(const ImplicitCastExpr *Cast, CheckerContext &C) const; 43 44 private: 45 mutable std::unique_ptr<BuiltinBug> BT; 46 47 bool isLossOfPrecision(const ImplicitCastExpr *Cast, QualType DestType, 48 CheckerContext &C) const; 49 50 bool isLossOfSign(const ImplicitCastExpr *Cast, CheckerContext &C) const; 51 52 void reportBug(ExplodedNode *N, const Expr *E, CheckerContext &C, 53 const char Msg[]) const; 54 }; 55 } 56 57 void ConversionChecker::checkPreStmt(const ImplicitCastExpr *Cast, 58 CheckerContext &C) const { 59 // TODO: For now we only warn about DeclRefExpr, to avoid noise. Warn for 60 // calculations also. 61 if (!isa<DeclRefExpr>(Cast->IgnoreParenImpCasts())) 62 return; 63 64 // Don't warn for loss of sign/precision in macros. 65 if (Cast->getExprLoc().isMacroID()) 66 return; 67 68 // Get Parent. 69 const ParentMap &PM = C.getLocationContext()->getParentMap(); 70 const Stmt *Parent = PM.getParent(Cast); 71 if (!Parent) 72 return; 73 74 bool LossOfSign = false; 75 bool LossOfPrecision = false; 76 77 // Loss of sign/precision in binary operation. 78 if (const auto *B = dyn_cast<BinaryOperator>(Parent)) { 79 BinaryOperator::Opcode Opc = B->getOpcode(); 80 if (Opc == BO_Assign) { 81 LossOfSign = isLossOfSign(Cast, C); 82 LossOfPrecision = isLossOfPrecision(Cast, Cast->getType(), C); 83 } else if (Opc == BO_AddAssign || Opc == BO_SubAssign) { 84 // No loss of sign. 85 LossOfPrecision = isLossOfPrecision(Cast, B->getLHS()->getType(), C); 86 } else if (Opc == BO_MulAssign) { 87 LossOfSign = isLossOfSign(Cast, C); 88 LossOfPrecision = isLossOfPrecision(Cast, B->getLHS()->getType(), C); 89 } else if (Opc == BO_DivAssign || Opc == BO_RemAssign) { 90 LossOfSign = isLossOfSign(Cast, C); 91 // No loss of precision. 92 } else if (Opc == BO_AndAssign) { 93 LossOfSign = isLossOfSign(Cast, C); 94 // No loss of precision. 95 } else if (Opc == BO_OrAssign || Opc == BO_XorAssign) { 96 LossOfSign = isLossOfSign(Cast, C); 97 LossOfPrecision = isLossOfPrecision(Cast, B->getLHS()->getType(), C); 98 } else if (B->isRelationalOp() || B->isMultiplicativeOp()) { 99 LossOfSign = isLossOfSign(Cast, C); 100 } 101 } else if (isa<DeclStmt>(Parent)) { 102 LossOfSign = isLossOfSign(Cast, C); 103 LossOfPrecision = isLossOfPrecision(Cast, Cast->getType(), C); 104 } 105 106 if (LossOfSign || LossOfPrecision) { 107 // Generate an error node. 108 ExplodedNode *N = C.generateNonFatalErrorNode(C.getState()); 109 if (!N) 110 return; 111 if (LossOfSign) 112 reportBug(N, Cast, C, "Loss of sign in implicit conversion"); 113 if (LossOfPrecision) 114 reportBug(N, Cast, C, "Loss of precision in implicit conversion"); 115 } 116 } 117 118 void ConversionChecker::reportBug(ExplodedNode *N, const Expr *E, 119 CheckerContext &C, const char Msg[]) const { 120 if (!BT) 121 BT.reset( 122 new BuiltinBug(this, "Conversion", "Possible loss of sign/precision.")); 123 124 // Generate a report for this bug. 125 auto R = std::make_unique<PathSensitiveBugReport>(*BT, Msg, N); 126 bugreporter::trackExpressionValue(N, E, *R); 127 C.emitReport(std::move(R)); 128 } 129 130 bool ConversionChecker::isLossOfPrecision(const ImplicitCastExpr *Cast, 131 QualType DestType, 132 CheckerContext &C) const { 133 // Don't warn about explicit loss of precision. 134 if (Cast->isEvaluatable(C.getASTContext())) 135 return false; 136 137 QualType SubType = Cast->IgnoreParenImpCasts()->getType(); 138 139 if (!DestType->isRealType() || !SubType->isIntegerType()) 140 return false; 141 142 const bool isFloat = DestType->isFloatingType(); 143 144 const auto &AC = C.getASTContext(); 145 146 // We will find the largest RepresentsUntilExp value such that the DestType 147 // can exactly represent all nonnegative integers below 2^RepresentsUntilExp. 148 unsigned RepresentsUntilExp; 149 150 if (isFloat) { 151 const llvm::fltSemantics &Sema = AC.getFloatTypeSemantics(DestType); 152 RepresentsUntilExp = llvm::APFloat::semanticsPrecision(Sema); 153 } else { 154 RepresentsUntilExp = AC.getIntWidth(DestType); 155 if (RepresentsUntilExp == 1) { 156 // This is just casting a number to bool, probably not a bug. 157 return false; 158 } 159 if (DestType->isSignedIntegerType()) 160 RepresentsUntilExp--; 161 } 162 163 if (RepresentsUntilExp >= sizeof(unsigned long long) * CHAR_BIT) { 164 // Avoid overflow in our later calculations. 165 return false; 166 } 167 168 unsigned CorrectedSrcWidth = AC.getIntWidth(SubType); 169 if (SubType->isSignedIntegerType()) 170 CorrectedSrcWidth--; 171 172 if (RepresentsUntilExp >= CorrectedSrcWidth) { 173 // Simple case: the destination can store all values of the source type. 174 return false; 175 } 176 177 unsigned long long MaxVal = 1ULL << RepresentsUntilExp; 178 if (isFloat) { 179 // If this is a floating point type, it can also represent MaxVal exactly. 180 MaxVal++; 181 } 182 return C.isGreaterOrEqual(Cast->getSubExpr(), MaxVal); 183 // TODO: maybe also check negative values with too large magnitude. 184 } 185 186 bool ConversionChecker::isLossOfSign(const ImplicitCastExpr *Cast, 187 CheckerContext &C) const { 188 QualType CastType = Cast->getType(); 189 QualType SubType = Cast->IgnoreParenImpCasts()->getType(); 190 191 if (!CastType->isUnsignedIntegerType() || !SubType->isSignedIntegerType()) 192 return false; 193 194 return C.isNegative(Cast->getSubExpr()); 195 } 196 197 void ento::registerConversionChecker(CheckerManager &mgr) { 198 mgr.registerChecker<ConversionChecker>(); 199 } 200 201 bool ento::shouldRegisterConversionChecker(const CheckerManager &mgr) { 202 return true; 203 } 204