brintos

brintos / llvm-project-archived public Read only

0
0
Text · 7.4 KiB · eca8d3c Raw
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