209 lines · cpp
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-exception6//7//===----------------------------------------------------------------------===//8//9// Check that there is no loss of sign/precision in assignments, comparisons10// and multiplications.11//12// ConversionChecker uses path sensitive analysis to determine possible values13// of expressions. A warning is reported when:14// * a negative value is implicitly converted to an unsigned value in an15// assignment, comparison or multiplication.16// * assignment / initialization when the source value is greater than the max17// value of the target integer type18// * assignment / initialization when the source integer is above the range19// where the target floating point type can represent all integers20//21// Many compilers and tools have similar checks that are based on semantic22// analysis. Those checks are sound but have poor precision. ConversionChecker23// 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 36using namespace clang;37using namespace ento;38 39namespace {40class ConversionChecker : public Checker<check::PreStmt<ImplicitCastExpr>> {41public:42 void checkPreStmt(const ImplicitCastExpr *Cast, CheckerContext &C) const;43 44private: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 57void ConversionChecker::checkPreStmt(const ImplicitCastExpr *Cast,58 CheckerContext &C) const {59 // Don't warn for implicit conversions to bool60 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 cast73 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 127void 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 135bool 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 DestType152 // 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 191bool 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 202void ento::registerConversionChecker(CheckerManager &mgr) {203 mgr.registerChecker<ConversionChecker>();204}205 206bool ento::shouldRegisterConversionChecker(const CheckerManager &mgr) {207 return true;208}209