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