2016-08-18 00:02:45 +08:00
|
|
|
//=== ConversionChecker.cpp -------------------------------------*- C++ -*-===//
|
|
|
|
//
|
2019-01-19 16:50:56 +08:00
|
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
2016-08-18 00:02:45 +08:00
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
//
|
|
|
|
// Check that there is no loss of sign/precision in assignments, comparisons
|
|
|
|
// and multiplications.
|
|
|
|
//
|
|
|
|
// ConversionChecker uses path sensitive analysis to determine possible values
|
|
|
|
// of expressions. A warning is reported when:
|
|
|
|
// * a negative value is implicitly converted to an unsigned value in an
|
|
|
|
// assignment, comparison or multiplication.
|
2018-11-16 09:00:55 +08:00
|
|
|
// * assignment / initialization when the source value is greater than the max
|
|
|
|
// value of the target integer type
|
|
|
|
// * assignment / initialization when the source integer is above the range
|
|
|
|
// where the target floating point type can represent all integers
|
2016-08-18 00:02:45 +08:00
|
|
|
//
|
|
|
|
// Many compilers and tools have similar checks that are based on semantic
|
|
|
|
// analysis. Those checks are sound but have poor precision. ConversionChecker
|
|
|
|
// is an alternative to those checks.
|
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
[analyzer][NFC] Move CheckerRegistry from the Core directory to Frontend
ClangCheckerRegistry is a very non-obvious, poorly documented, weird concept.
It derives from CheckerRegistry, and is placed in lib/StaticAnalyzer/Frontend,
whereas it's base is located in lib/StaticAnalyzer/Core. It was, from what I can
imagine, used to circumvent the problem that the registry functions of the
checkers are located in the clangStaticAnalyzerCheckers library, but that
library depends on clangStaticAnalyzerCore. However, clangStaticAnalyzerFrontend
depends on both of those libraries.
One can make the observation however, that CheckerRegistry has no place in Core,
it isn't used there at all! The only place where it is used is Frontend, which
is where it ultimately belongs.
This move implies that since
include/clang/StaticAnalyzer/Checkers/ClangCheckers.h only contained a single function:
class CheckerRegistry;
void registerBuiltinCheckers(CheckerRegistry ®istry);
it had to re purposed, as CheckerRegistry is no longer available to
clangStaticAnalyzerCheckers. It was renamed to BuiltinCheckerRegistration.h,
which actually describes it a lot better -- it does not contain the registration
functions for checkers, but only those generated by the tblgen files.
Differential Revision: https://reviews.llvm.org/D54436
llvm-svn: 349275
2018-12-16 00:23:51 +08:00
|
|
|
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
|
2016-08-18 00:02:45 +08:00
|
|
|
#include "clang/AST/ParentMap.h"
|
|
|
|
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
|
|
|
|
#include "clang/StaticAnalyzer/Core/Checker.h"
|
|
|
|
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
|
|
|
|
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
|
2018-11-16 09:00:55 +08:00
|
|
|
#include "llvm/ADT/APFloat.h"
|
|
|
|
|
|
|
|
#include <climits>
|
2016-08-18 00:02:45 +08:00
|
|
|
|
|
|
|
using namespace clang;
|
|
|
|
using namespace ento;
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
class ConversionChecker : public Checker<check::PreStmt<ImplicitCastExpr>> {
|
|
|
|
public:
|
|
|
|
void checkPreStmt(const ImplicitCastExpr *Cast, CheckerContext &C) const;
|
|
|
|
|
|
|
|
private:
|
|
|
|
mutable std::unique_ptr<BuiltinBug> BT;
|
|
|
|
|
2017-04-05 16:57:04 +08:00
|
|
|
bool isLossOfPrecision(const ImplicitCastExpr *Cast, QualType DestType,
|
|
|
|
CheckerContext &C) const;
|
2016-08-18 00:02:45 +08:00
|
|
|
|
|
|
|
bool isLossOfSign(const ImplicitCastExpr *Cast, CheckerContext &C) const;
|
|
|
|
|
|
|
|
void reportBug(ExplodedNode *N, CheckerContext &C, const char Msg[]) const;
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
void ConversionChecker::checkPreStmt(const ImplicitCastExpr *Cast,
|
|
|
|
CheckerContext &C) const {
|
|
|
|
// TODO: For now we only warn about DeclRefExpr, to avoid noise. Warn for
|
|
|
|
// calculations also.
|
|
|
|
if (!isa<DeclRefExpr>(Cast->IgnoreParenImpCasts()))
|
|
|
|
return;
|
|
|
|
|
|
|
|
// Don't warn for loss of sign/precision in macros.
|
|
|
|
if (Cast->getExprLoc().isMacroID())
|
|
|
|
return;
|
|
|
|
|
|
|
|
// Get Parent.
|
|
|
|
const ParentMap &PM = C.getLocationContext()->getParentMap();
|
|
|
|
const Stmt *Parent = PM.getParent(Cast);
|
|
|
|
if (!Parent)
|
|
|
|
return;
|
|
|
|
|
|
|
|
bool LossOfSign = false;
|
|
|
|
bool LossOfPrecision = false;
|
|
|
|
|
|
|
|
// Loss of sign/precision in binary operation.
|
|
|
|
if (const auto *B = dyn_cast<BinaryOperator>(Parent)) {
|
|
|
|
BinaryOperator::Opcode Opc = B->getOpcode();
|
2017-04-05 16:57:04 +08:00
|
|
|
if (Opc == BO_Assign) {
|
2016-08-18 00:02:45 +08:00
|
|
|
LossOfSign = isLossOfSign(Cast, C);
|
2017-04-05 16:57:04 +08:00
|
|
|
LossOfPrecision = isLossOfPrecision(Cast, Cast->getType(), C);
|
|
|
|
} else if (Opc == BO_AddAssign || Opc == BO_SubAssign) {
|
|
|
|
// No loss of sign.
|
|
|
|
LossOfPrecision = isLossOfPrecision(Cast, B->getLHS()->getType(), C);
|
|
|
|
} else if (Opc == BO_MulAssign) {
|
|
|
|
LossOfSign = isLossOfSign(Cast, C);
|
|
|
|
LossOfPrecision = isLossOfPrecision(Cast, B->getLHS()->getType(), C);
|
|
|
|
} else if (Opc == BO_DivAssign || Opc == BO_RemAssign) {
|
|
|
|
LossOfSign = isLossOfSign(Cast, C);
|
|
|
|
// No loss of precision.
|
|
|
|
} else if (Opc == BO_AndAssign) {
|
|
|
|
LossOfSign = isLossOfSign(Cast, C);
|
|
|
|
// No loss of precision.
|
|
|
|
} else if (Opc == BO_OrAssign || Opc == BO_XorAssign) {
|
|
|
|
LossOfSign = isLossOfSign(Cast, C);
|
|
|
|
LossOfPrecision = isLossOfPrecision(Cast, B->getLHS()->getType(), C);
|
2016-08-18 00:02:45 +08:00
|
|
|
} else if (B->isRelationalOp() || B->isMultiplicativeOp()) {
|
|
|
|
LossOfSign = isLossOfSign(Cast, C);
|
|
|
|
}
|
|
|
|
} else if (isa<DeclStmt>(Parent)) {
|
|
|
|
LossOfSign = isLossOfSign(Cast, C);
|
2017-04-05 16:57:04 +08:00
|
|
|
LossOfPrecision = isLossOfPrecision(Cast, Cast->getType(), C);
|
2016-08-18 00:02:45 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (LossOfSign || LossOfPrecision) {
|
|
|
|
// Generate an error node.
|
|
|
|
ExplodedNode *N = C.generateNonFatalErrorNode(C.getState());
|
|
|
|
if (!N)
|
|
|
|
return;
|
|
|
|
if (LossOfSign)
|
|
|
|
reportBug(N, C, "Loss of sign in implicit conversion");
|
|
|
|
if (LossOfPrecision)
|
|
|
|
reportBug(N, C, "Loss of precision in implicit conversion");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ConversionChecker::reportBug(ExplodedNode *N, CheckerContext &C,
|
|
|
|
const char Msg[]) const {
|
|
|
|
if (!BT)
|
|
|
|
BT.reset(
|
|
|
|
new BuiltinBug(this, "Conversion", "Possible loss of sign/precision."));
|
|
|
|
|
|
|
|
// Generate a report for this bug.
|
2019-09-10 04:34:40 +08:00
|
|
|
auto R = std::make_unique<PathSensitiveBugReport>(*BT, Msg, N);
|
2016-08-18 00:02:45 +08:00
|
|
|
C.emitReport(std::move(R));
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ConversionChecker::isLossOfPrecision(const ImplicitCastExpr *Cast,
|
2017-04-05 16:57:04 +08:00
|
|
|
QualType DestType,
|
|
|
|
CheckerContext &C) const {
|
2016-08-18 00:02:45 +08:00
|
|
|
// Don't warn about explicit loss of precision.
|
|
|
|
if (Cast->isEvaluatable(C.getASTContext()))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
QualType SubType = Cast->IgnoreParenImpCasts()->getType();
|
|
|
|
|
2018-11-16 09:00:55 +08:00
|
|
|
if (!DestType->isRealType() || !SubType->isIntegerType())
|
2016-08-18 00:02:45 +08:00
|
|
|
return false;
|
|
|
|
|
2018-11-16 09:00:55 +08:00
|
|
|
const bool isFloat = DestType->isFloatingType();
|
|
|
|
|
|
|
|
const auto &AC = C.getASTContext();
|
|
|
|
|
|
|
|
// We will find the largest RepresentsUntilExp value such that the DestType
|
|
|
|
// can exactly represent all nonnegative integers below 2^RepresentsUntilExp.
|
|
|
|
unsigned RepresentsUntilExp;
|
|
|
|
|
|
|
|
if (isFloat) {
|
|
|
|
const llvm::fltSemantics &Sema = AC.getFloatTypeSemantics(DestType);
|
|
|
|
RepresentsUntilExp = llvm::APFloat::semanticsPrecision(Sema);
|
|
|
|
} else {
|
|
|
|
RepresentsUntilExp = AC.getIntWidth(DestType);
|
|
|
|
if (RepresentsUntilExp == 1) {
|
|
|
|
// This is just casting a number to bool, probably not a bug.
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (DestType->isSignedIntegerType())
|
|
|
|
RepresentsUntilExp--;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (RepresentsUntilExp >= sizeof(unsigned long long) * CHAR_BIT) {
|
|
|
|
// Avoid overflow in our later calculations.
|
2016-08-18 00:02:45 +08:00
|
|
|
return false;
|
2018-11-16 09:00:55 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
unsigned CorrectedSrcWidth = AC.getIntWidth(SubType);
|
|
|
|
if (SubType->isSignedIntegerType())
|
|
|
|
CorrectedSrcWidth--;
|
2016-08-18 00:02:45 +08:00
|
|
|
|
2018-11-16 09:00:55 +08:00
|
|
|
if (RepresentsUntilExp >= CorrectedSrcWidth) {
|
|
|
|
// Simple case: the destination can store all values of the source type.
|
2016-08-18 00:02:45 +08:00
|
|
|
return false;
|
2018-11-16 09:00:55 +08:00
|
|
|
}
|
2016-08-18 00:02:45 +08:00
|
|
|
|
2018-11-16 09:00:55 +08:00
|
|
|
unsigned long long MaxVal = 1ULL << RepresentsUntilExp;
|
|
|
|
if (isFloat) {
|
|
|
|
// If this is a floating point type, it can also represent MaxVal exactly.
|
|
|
|
MaxVal++;
|
|
|
|
}
|
2017-10-11 22:49:35 +08:00
|
|
|
return C.isGreaterOrEqual(Cast->getSubExpr(), MaxVal);
|
2018-11-16 09:00:55 +08:00
|
|
|
// TODO: maybe also check negative values with too large magnitude.
|
2016-08-18 00:02:45 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
bool ConversionChecker::isLossOfSign(const ImplicitCastExpr *Cast,
|
2017-10-11 22:49:35 +08:00
|
|
|
CheckerContext &C) const {
|
2016-08-18 00:02:45 +08:00
|
|
|
QualType CastType = Cast->getType();
|
|
|
|
QualType SubType = Cast->IgnoreParenImpCasts()->getType();
|
|
|
|
|
|
|
|
if (!CastType->isUnsignedIntegerType() || !SubType->isSignedIntegerType())
|
|
|
|
return false;
|
|
|
|
|
2017-10-11 22:49:35 +08:00
|
|
|
return C.isNegative(Cast->getSubExpr());
|
2016-08-18 00:02:45 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void ento::registerConversionChecker(CheckerManager &mgr) {
|
|
|
|
mgr.registerChecker<ConversionChecker>();
|
|
|
|
}
|
2019-01-26 22:23:08 +08:00
|
|
|
|
2020-03-27 21:29:31 +08:00
|
|
|
bool ento::shouldRegisterConversionChecker(const CheckerManager &mgr) {
|
2019-01-26 22:23:08 +08:00
|
|
|
return true;
|
|
|
|
}
|