Path: blob/main/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ConversionChecker.cpp
35266 views
//=== ConversionChecker.cpp -------------------------------------*- C++ -*-===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// Check that there is no loss of sign/precision in assignments, comparisons9// and multiplications.10//11// ConversionChecker uses path sensitive analysis to determine possible values12// of expressions. A warning is reported when:13// * a negative value is implicitly converted to an unsigned value in an14// assignment, comparison or multiplication.15// * assignment / initialization when the source value is greater than the max16// value of the target integer type17// * assignment / initialization when the source integer is above the range18// where the target floating point type can represent all integers19//20// Many compilers and tools have similar checks that are based on semantic21// analysis. Those checks are sound but have poor precision. ConversionChecker22// is an alternative to those checks.23//24//===----------------------------------------------------------------------===//25#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"26#include "clang/AST/ParentMap.h"27#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"28#include "clang/StaticAnalyzer/Core/Checker.h"29#include "clang/StaticAnalyzer/Core/CheckerManager.h"30#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"31#include "llvm/ADT/APFloat.h"3233#include <climits>3435using namespace clang;36using namespace ento;3738namespace {39class ConversionChecker : public Checker<check::PreStmt<ImplicitCastExpr>> {40public:41void checkPreStmt(const ImplicitCastExpr *Cast, CheckerContext &C) const;4243private:44const BugType BT{this, "Conversion"};4546bool isLossOfPrecision(const ImplicitCastExpr *Cast, QualType DestType,47CheckerContext &C) const;4849bool isLossOfSign(const ImplicitCastExpr *Cast, CheckerContext &C) const;5051void reportBug(ExplodedNode *N, const Expr *E, CheckerContext &C,52const char Msg[]) const;53};54}5556void ConversionChecker::checkPreStmt(const ImplicitCastExpr *Cast,57CheckerContext &C) const {58// Don't warn for implicit conversions to bool59if (Cast->getType()->isBooleanType())60return;6162// Don't warn for loss of sign/precision in macros.63if (Cast->getExprLoc().isMacroID())64return;6566// Get Parent.67const ParentMap &PM = C.getLocationContext()->getParentMap();68const Stmt *Parent = PM.getParent(Cast);69if (!Parent)70return;71// Dont warn if this is part of an explicit cast72if (isa<ExplicitCastExpr>(Parent))73return;7475bool LossOfSign = false;76bool LossOfPrecision = false;7778// Loss of sign/precision in binary operation.79if (const auto *B = dyn_cast<BinaryOperator>(Parent)) {80BinaryOperator::Opcode Opc = B->getOpcode();81if (Opc == BO_Assign) {82if (!Cast->IgnoreParenImpCasts()->isEvaluatable(C.getASTContext())) {83LossOfSign = isLossOfSign(Cast, C);84LossOfPrecision = isLossOfPrecision(Cast, Cast->getType(), C);85}86} else if (Opc == BO_AddAssign || Opc == BO_SubAssign) {87// No loss of sign.88LossOfPrecision = isLossOfPrecision(Cast, B->getLHS()->getType(), C);89} else if (Opc == BO_MulAssign) {90LossOfSign = isLossOfSign(Cast, C);91LossOfPrecision = isLossOfPrecision(Cast, B->getLHS()->getType(), C);92} else if (Opc == BO_DivAssign || Opc == BO_RemAssign) {93LossOfSign = isLossOfSign(Cast, C);94// No loss of precision.95} else if (Opc == BO_AndAssign) {96LossOfSign = isLossOfSign(Cast, C);97// No loss of precision.98} else if (Opc == BO_OrAssign || Opc == BO_XorAssign) {99LossOfSign = isLossOfSign(Cast, C);100LossOfPrecision = isLossOfPrecision(Cast, B->getLHS()->getType(), C);101} else if (B->isRelationalOp() || B->isMultiplicativeOp()) {102LossOfSign = isLossOfSign(Cast, C);103}104} else if (isa<DeclStmt, ReturnStmt>(Parent)) {105if (!Cast->IgnoreParenImpCasts()->isEvaluatable(C.getASTContext())) {106LossOfSign = isLossOfSign(Cast, C);107LossOfPrecision = isLossOfPrecision(Cast, Cast->getType(), C);108}109} else {110LossOfSign = isLossOfSign(Cast, C);111LossOfPrecision = isLossOfPrecision(Cast, Cast->getType(), C);112}113114if (LossOfSign || LossOfPrecision) {115// Generate an error node.116ExplodedNode *N = C.generateNonFatalErrorNode(C.getState());117if (!N)118return;119if (LossOfSign)120reportBug(N, Cast, C, "Loss of sign in implicit conversion");121if (LossOfPrecision)122reportBug(N, Cast, C, "Loss of precision in implicit conversion");123}124}125126void ConversionChecker::reportBug(ExplodedNode *N, const Expr *E,127CheckerContext &C, const char Msg[]) const {128// Generate a report for this bug.129auto R = std::make_unique<PathSensitiveBugReport>(BT, Msg, N);130bugreporter::trackExpressionValue(N, E, *R);131C.emitReport(std::move(R));132}133134bool ConversionChecker::isLossOfPrecision(const ImplicitCastExpr *Cast,135QualType DestType,136CheckerContext &C) const {137// Don't warn about explicit loss of precision.138if (Cast->isEvaluatable(C.getASTContext()))139return false;140141QualType SubType = Cast->IgnoreParenImpCasts()->getType();142143if (!DestType->isRealType() || !SubType->isIntegerType())144return false;145146const bool isFloat = DestType->isFloatingType();147148const auto &AC = C.getASTContext();149150// We will find the largest RepresentsUntilExp value such that the DestType151// can exactly represent all nonnegative integers below 2^RepresentsUntilExp.152unsigned RepresentsUntilExp;153154if (isFloat) {155const llvm::fltSemantics &Sema = AC.getFloatTypeSemantics(DestType);156RepresentsUntilExp = llvm::APFloat::semanticsPrecision(Sema);157} else {158RepresentsUntilExp = AC.getIntWidth(DestType);159if (RepresentsUntilExp == 1) {160// This is just casting a number to bool, probably not a bug.161return false;162}163if (DestType->isSignedIntegerType())164RepresentsUntilExp--;165}166167if (RepresentsUntilExp >= sizeof(unsigned long long) * CHAR_BIT) {168// Avoid overflow in our later calculations.169return false;170}171172unsigned CorrectedSrcWidth = AC.getIntWidth(SubType);173if (SubType->isSignedIntegerType())174CorrectedSrcWidth--;175176if (RepresentsUntilExp >= CorrectedSrcWidth) {177// Simple case: the destination can store all values of the source type.178return false;179}180181unsigned long long MaxVal = 1ULL << RepresentsUntilExp;182if (isFloat) {183// If this is a floating point type, it can also represent MaxVal exactly.184MaxVal++;185}186return C.isGreaterOrEqual(Cast->getSubExpr(), MaxVal);187// TODO: maybe also check negative values with too large magnitude.188}189190bool ConversionChecker::isLossOfSign(const ImplicitCastExpr *Cast,191CheckerContext &C) const {192QualType CastType = Cast->getType();193QualType SubType = Cast->IgnoreParenImpCasts()->getType();194195if (!CastType->isUnsignedIntegerType() || !SubType->isSignedIntegerType())196return false;197198return C.isNegative(Cast->getSubExpr());199}200201void ento::registerConversionChecker(CheckerManager &mgr) {202mgr.registerChecker<ConversionChecker>();203}204205bool ento::shouldRegisterConversionChecker(const CheckerManager &mgr) {206return true;207}208209210