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 const BugType BT{this, "Conversion"}; 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 // Generate a report for this bug. 130 auto R = std::make_unique<PathSensitiveBugReport>(BT, Msg, N); 131 bugreporter::trackExpressionValue(N, E, *R); 132 C.emitReport(std::move(R)); 133 } 134 135 bool ConversionChecker::isLossOfPrecision(const ImplicitCastExpr *Cast, 136 QualType DestType, 137 CheckerContext &C) const { 138 // Don't warn about explicit loss of precision. 139 if (Cast->isEvaluatable(C.getASTContext())) 140 return false; 141 142 QualType SubType = Cast->IgnoreParenImpCasts()->getType(); 143 144 if (!DestType->isRealType() || !SubType->isIntegerType()) 145 return false; 146 147 const bool isFloat = DestType->isFloatingType(); 148 149 const auto &AC = C.getASTContext(); 150 151 // We will find the largest RepresentsUntilExp value such that the DestType 152 // can exactly represent all nonnegative integers below 2^RepresentsUntilExp. 153 unsigned RepresentsUntilExp; 154 155 if (isFloat) { 156 const llvm::fltSemantics &Sema = AC.getFloatTypeSemantics(DestType); 157 RepresentsUntilExp = llvm::APFloat::semanticsPrecision(Sema); 158 } else { 159 RepresentsUntilExp = AC.getIntWidth(DestType); 160 if (RepresentsUntilExp == 1) { 161 // This is just casting a number to bool, probably not a bug. 162 return false; 163 } 164 if (DestType->isSignedIntegerType()) 165 RepresentsUntilExp--; 166 } 167 168 if (RepresentsUntilExp >= sizeof(unsigned long long) * CHAR_BIT) { 169 // Avoid overflow in our later calculations. 170 return false; 171 } 172 173 unsigned CorrectedSrcWidth = AC.getIntWidth(SubType); 174 if (SubType->isSignedIntegerType()) 175 CorrectedSrcWidth--; 176 177 if (RepresentsUntilExp >= CorrectedSrcWidth) { 178 // Simple case: the destination can store all values of the source type. 179 return false; 180 } 181 182 unsigned long long MaxVal = 1ULL << RepresentsUntilExp; 183 if (isFloat) { 184 // If this is a floating point type, it can also represent MaxVal exactly. 185 MaxVal++; 186 } 187 return C.isGreaterOrEqual(Cast->getSubExpr(), MaxVal); 188 // TODO: maybe also check negative values with too large magnitude. 189 } 190 191 bool ConversionChecker::isLossOfSign(const ImplicitCastExpr *Cast, 192 CheckerContext &C) const { 193 QualType CastType = Cast->getType(); 194 QualType SubType = Cast->IgnoreParenImpCasts()->getType(); 195 196 if (!CastType->isUnsignedIntegerType() || !SubType->isSignedIntegerType()) 197 return false; 198 199 return C.isNegative(Cast->getSubExpr()); 200 } 201 202 void ento::registerConversionChecker(CheckerManager &mgr) { 203 mgr.registerChecker<ConversionChecker>(); 204 } 205 206 bool ento::shouldRegisterConversionChecker(const CheckerManager &mgr) { 207 return true; 208 } 209