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