ConversionChecker.cpp revision 344779
1//=== ConversionChecker.cpp -------------------------------------*- C++ -*-===// 2// 3// The LLVM Compiler Infrastructure 4// 5// This file is distributed under the University of Illinois Open Source 6// License. See LICENSE.TXT for details. 7// 8//===----------------------------------------------------------------------===// 9// 10// Check that there is no loss of sign/precision in assignments, comparisons 11// and multiplications. 12// 13// ConversionChecker uses path sensitive analysis to determine possible values 14// of expressions. A warning is reported when: 15// * a negative value is implicitly converted to an unsigned value in an 16// assignment, comparison or multiplication. 17// * assignment / initialization when the source value is greater than the max 18// value of the target integer type 19// * assignment / initialization when the source integer is above the range 20// where the target floating point type can represent all integers 21// 22// Many compilers and tools have similar checks that are based on semantic 23// analysis. Those checks are sound but have poor precision. ConversionChecker 24// is an alternative to those checks. 25// 26//===----------------------------------------------------------------------===// 27#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" 28#include "clang/AST/ParentMap.h" 29#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 30#include "clang/StaticAnalyzer/Core/Checker.h" 31#include "clang/StaticAnalyzer/Core/CheckerManager.h" 32#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 33#include "llvm/ADT/APFloat.h" 34 35#include <climits> 36 37using namespace clang; 38using namespace ento; 39 40namespace { 41class ConversionChecker : public Checker<check::PreStmt<ImplicitCastExpr>> { 42public: 43 void checkPreStmt(const ImplicitCastExpr *Cast, CheckerContext &C) const; 44 45private: 46 mutable std::unique_ptr<BuiltinBug> BT; 47 48 bool isLossOfPrecision(const ImplicitCastExpr *Cast, QualType DestType, 49 CheckerContext &C) const; 50 51 bool isLossOfSign(const ImplicitCastExpr *Cast, CheckerContext &C) const; 52 53 void reportBug(ExplodedNode *N, CheckerContext &C, const char Msg[]) const; 54}; 55} 56 57void 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, C, "Loss of sign in implicit conversion"); 113 if (LossOfPrecision) 114 reportBug(N, C, "Loss of precision in implicit conversion"); 115 } 116} 117 118void ConversionChecker::reportBug(ExplodedNode *N, CheckerContext &C, 119 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 = llvm::make_unique<BugReport>(*BT, Msg, N); 126 C.emitReport(std::move(R)); 127} 128 129bool ConversionChecker::isLossOfPrecision(const ImplicitCastExpr *Cast, 130 QualType DestType, 131 CheckerContext &C) const { 132 // Don't warn about explicit loss of precision. 133 if (Cast->isEvaluatable(C.getASTContext())) 134 return false; 135 136 QualType SubType = Cast->IgnoreParenImpCasts()->getType(); 137 138 if (!DestType->isRealType() || !SubType->isIntegerType()) 139 return false; 140 141 const bool isFloat = DestType->isFloatingType(); 142 143 const auto &AC = C.getASTContext(); 144 145 // We will find the largest RepresentsUntilExp value such that the DestType 146 // can exactly represent all nonnegative integers below 2^RepresentsUntilExp. 147 unsigned RepresentsUntilExp; 148 149 if (isFloat) { 150 const llvm::fltSemantics &Sema = AC.getFloatTypeSemantics(DestType); 151 RepresentsUntilExp = llvm::APFloat::semanticsPrecision(Sema); 152 } else { 153 RepresentsUntilExp = AC.getIntWidth(DestType); 154 if (RepresentsUntilExp == 1) { 155 // This is just casting a number to bool, probably not a bug. 156 return false; 157 } 158 if (DestType->isSignedIntegerType()) 159 RepresentsUntilExp--; 160 } 161 162 if (RepresentsUntilExp >= sizeof(unsigned long long) * CHAR_BIT) { 163 // Avoid overflow in our later calculations. 164 return false; 165 } 166 167 unsigned CorrectedSrcWidth = AC.getIntWidth(SubType); 168 if (SubType->isSignedIntegerType()) 169 CorrectedSrcWidth--; 170 171 if (RepresentsUntilExp >= CorrectedSrcWidth) { 172 // Simple case: the destination can store all values of the source type. 173 return false; 174 } 175 176 unsigned long long MaxVal = 1ULL << RepresentsUntilExp; 177 if (isFloat) { 178 // If this is a floating point type, it can also represent MaxVal exactly. 179 MaxVal++; 180 } 181 return C.isGreaterOrEqual(Cast->getSubExpr(), MaxVal); 182 // TODO: maybe also check negative values with too large magnitude. 183} 184 185bool ConversionChecker::isLossOfSign(const ImplicitCastExpr *Cast, 186 CheckerContext &C) const { 187 QualType CastType = Cast->getType(); 188 QualType SubType = Cast->IgnoreParenImpCasts()->getType(); 189 190 if (!CastType->isUnsignedIntegerType() || !SubType->isSignedIntegerType()) 191 return false; 192 193 return C.isNegative(Cast->getSubExpr()); 194} 195 196void ento::registerConversionChecker(CheckerManager &mgr) { 197 mgr.registerChecker<ConversionChecker>(); 198} 199