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 // Don't warn for implicit conversions to bool 60 if (Cast->getType()->isBooleanType()) 61 return; 62 63 // Don't warn for loss of sign/precision in macros. 64 if (Cast->getExprLoc().isMacroID()) 65 return; 66 67 // Get Parent. 68 const ParentMap &PM = C.getLocationContext()->getParentMap(); 69 const Stmt *Parent = PM.getParent(Cast); 70 if (!Parent) 71 return; 72 // Dont warn if this is part of an explicit cast 73 if (isa<ExplicitCastExpr>(Parent)) 74 return; 75 76 bool LossOfSign = false; 77 bool LossOfPrecision = false; 78 79 // Loss of sign/precision in binary operation. 80 if (const auto *B = dyn_cast<BinaryOperator>(Parent)) { 81 BinaryOperator::Opcode Opc = B->getOpcode(); 82 if (Opc == BO_Assign) { 83 if (!Cast->IgnoreParenImpCasts()->isEvaluatable(C.getASTContext())) { 84 LossOfSign = isLossOfSign(Cast, C); 85 LossOfPrecision = isLossOfPrecision(Cast, Cast->getType(), C); 86 } 87 } else if (Opc == BO_AddAssign || Opc == BO_SubAssign) { 88 // No loss of sign. 89 LossOfPrecision = isLossOfPrecision(Cast, B->getLHS()->getType(), C); 90 } else if (Opc == BO_MulAssign) { 91 LossOfSign = isLossOfSign(Cast, C); 92 LossOfPrecision = isLossOfPrecision(Cast, B->getLHS()->getType(), C); 93 } else if (Opc == BO_DivAssign || Opc == BO_RemAssign) { 94 LossOfSign = isLossOfSign(Cast, C); 95 // No loss of precision. 96 } else if (Opc == BO_AndAssign) { 97 LossOfSign = isLossOfSign(Cast, C); 98 // No loss of precision. 99 } else if (Opc == BO_OrAssign || Opc == BO_XorAssign) { 100 LossOfSign = isLossOfSign(Cast, C); 101 LossOfPrecision = isLossOfPrecision(Cast, B->getLHS()->getType(), C); 102 } else if (B->isRelationalOp() || B->isMultiplicativeOp()) { 103 LossOfSign = isLossOfSign(Cast, C); 104 } 105 } else if (isa<DeclStmt, ReturnStmt>(Parent)) { 106 if (!Cast->IgnoreParenImpCasts()->isEvaluatable(C.getASTContext())) { 107 LossOfSign = isLossOfSign(Cast, C); 108 LossOfPrecision = isLossOfPrecision(Cast, Cast->getType(), C); 109 } 110 } else { 111 LossOfSign = isLossOfSign(Cast, C); 112 LossOfPrecision = isLossOfPrecision(Cast, Cast->getType(), C); 113 } 114 115 if (LossOfSign || LossOfPrecision) { 116 // Generate an error node. 117 ExplodedNode *N = C.generateNonFatalErrorNode(C.getState()); 118 if (!N) 119 return; 120 if (LossOfSign) 121 reportBug(N, Cast, C, "Loss of sign in implicit conversion"); 122 if (LossOfPrecision) 123 reportBug(N, Cast, C, "Loss of precision in implicit conversion"); 124 } 125 } 126 127 void ConversionChecker::reportBug(ExplodedNode *N, const Expr *E, 128 CheckerContext &C, const char Msg[]) const { 129 if (!BT) 130 BT.reset( 131 new BuiltinBug(this, "Conversion", "Possible loss of sign/precision.")); 132 133 // Generate a report for this bug. 134 auto R = std::make_unique<PathSensitiveBugReport>(*BT, Msg, N); 135 bugreporter::trackExpressionValue(N, E, *R); 136 C.emitReport(std::move(R)); 137 } 138 139 bool ConversionChecker::isLossOfPrecision(const ImplicitCastExpr *Cast, 140 QualType DestType, 141 CheckerContext &C) const { 142 // Don't warn about explicit loss of precision. 143 if (Cast->isEvaluatable(C.getASTContext())) 144 return false; 145 146 QualType SubType = Cast->IgnoreParenImpCasts()->getType(); 147 148 if (!DestType->isRealType() || !SubType->isIntegerType()) 149 return false; 150 151 const bool isFloat = DestType->isFloatingType(); 152 153 const auto &AC = C.getASTContext(); 154 155 // We will find the largest RepresentsUntilExp value such that the DestType 156 // can exactly represent all nonnegative integers below 2^RepresentsUntilExp. 157 unsigned RepresentsUntilExp; 158 159 if (isFloat) { 160 const llvm::fltSemantics &Sema = AC.getFloatTypeSemantics(DestType); 161 RepresentsUntilExp = llvm::APFloat::semanticsPrecision(Sema); 162 } else { 163 RepresentsUntilExp = AC.getIntWidth(DestType); 164 if (RepresentsUntilExp == 1) { 165 // This is just casting a number to bool, probably not a bug. 166 return false; 167 } 168 if (DestType->isSignedIntegerType()) 169 RepresentsUntilExp--; 170 } 171 172 if (RepresentsUntilExp >= sizeof(unsigned long long) * CHAR_BIT) { 173 // Avoid overflow in our later calculations. 174 return false; 175 } 176 177 unsigned CorrectedSrcWidth = AC.getIntWidth(SubType); 178 if (SubType->isSignedIntegerType()) 179 CorrectedSrcWidth--; 180 181 if (RepresentsUntilExp >= CorrectedSrcWidth) { 182 // Simple case: the destination can store all values of the source type. 183 return false; 184 } 185 186 unsigned long long MaxVal = 1ULL << RepresentsUntilExp; 187 if (isFloat) { 188 // If this is a floating point type, it can also represent MaxVal exactly. 189 MaxVal++; 190 } 191 return C.isGreaterOrEqual(Cast->getSubExpr(), MaxVal); 192 // TODO: maybe also check negative values with too large magnitude. 193 } 194 195 bool ConversionChecker::isLossOfSign(const ImplicitCastExpr *Cast, 196 CheckerContext &C) const { 197 QualType CastType = Cast->getType(); 198 QualType SubType = Cast->IgnoreParenImpCasts()->getType(); 199 200 if (!CastType->isUnsignedIntegerType() || !SubType->isSignedIntegerType()) 201 return false; 202 203 return C.isNegative(Cast->getSubExpr()); 204 } 205 206 void ento::registerConversionChecker(CheckerManager &mgr) { 207 mgr.registerChecker<ConversionChecker>(); 208 } 209 210 bool ento::shouldRegisterConversionChecker(const CheckerManager &mgr) { 211 return true; 212 } 213