/*
* Cppcheck - A tool for static C/C++ code analysis
* Copyright (C) 2007-2026 Cppcheck team.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
//---------------------------------------------------------------------------
#include "checktype.h"
#include "astutils.h"
#include "errortypes.h"
#include "mathlib.h"
#include "platform.h"
#include "settings.h"
#include "standards.h"
#include "symboldatabase.h"
#include "token.h"
#include "tokenize.h"
#include "valueflow.h"
#include "vfvalue.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
// Checking for shift by too many bits
//---------------------------------------------------------------------------
//
// CWE ids used:
static const CWE CWE195(195U); // Signed to Unsigned Conversion Error
static const CWE CWE197(197U); // Numeric Truncation Error
static const CWE CWE758(758U); // Reliance on Undefined, Unspecified, or Implementation-Defined Behavior
static const CWE CWE190(190U); // Integer Overflow or Wraparound
void CheckTypeImpl::checkTooBigBitwiseShift()
{
// unknown sizeof(int) => can't run this checker
if (mSettings.platform.type == Platform::Type::Unspecified)
return;
logChecker("CheckType::checkTooBigBitwiseShift"); // platform
for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) {
// C++ and macro: OUT(xlinkAt(2), ") ;") && tok->next()->isUpperCaseName() && !tok->next()->function())
tok = tok->linkAt(2);
tok = skipUnreachableBranch(tok);
if (!tok->astOperand1() || !tok->astOperand2())
continue;
if (!Token::Match(tok, "|="))
continue;
// get number of bits of lhs
const ValueType * const lhstype = tok->astOperand1()->valueType();
if (!lhstype || !lhstype->isIntegral() || lhstype->pointer >= 1)
continue;
// C11 Standard, section 6.5.7 Bitwise shift operators, states:
// The integer promotions are performed on each of the operands.
// The type of the result is that of the promoted left operand.
std::uint8_t lhsbits;
if ((lhstype->type == ValueType::Type::CHAR) ||
(lhstype->type == ValueType::Type::SHORT) ||
(lhstype->type == ValueType::Type::WCHAR_T) ||
(lhstype->type == ValueType::Type::BOOL) ||
(lhstype->type == ValueType::Type::INT))
lhsbits = mSettings.platform.int_bit;
else if (lhstype->type == ValueType::Type::LONG)
lhsbits = mSettings.platform.long_bit;
else if (lhstype->type == ValueType::Type::LONGLONG)
lhsbits = mSettings.platform.long_long_bit;
else
continue;
// Get biggest rhs value. preferably a value which doesn't have 'condition'.
const ValueFlow::Value * value = tok->astOperand2()->getValueGE(lhsbits, mSettings);
if (value && mSettings.isEnabled(value, false))
tooBigBitwiseShiftError(tok, lhsbits, *value);
else if (lhstype->sign == ValueType::Sign::SIGNED) {
value = tok->astOperand2()->getValueGE(lhsbits-1, mSettings);
if (value && mSettings.isEnabled(value, false))
tooBigSignedBitwiseShiftError(tok, lhsbits, *value);
}
}
}
void CheckTypeImpl::tooBigBitwiseShiftError(const Token *tok, int lhsbits, const ValueFlow::Value &rhsbits)
{
constexpr char id[] = "shiftTooManyBits";
if (!tok) {
reportError(tok, Severity::error, id, "Shifting 32-bit value by 40 bits is undefined behaviour", CWE758, Certainty::normal);
return;
}
ErrorPath errorPath = getErrorPath(tok, &rhsbits, "Shift");
std::ostringstream errmsg;
errmsg str() == " 'l = (" + tgtStr + ")a * b;'.", CWE197, Certainty::normal);
}
void CheckTypeImpl::longCastReturnError(const Token *tok, const ValueType* src, const ValueType* tgt)
{
std::string srcStr = src ? src->str() : "int";
makeBaseTypeString(srcStr);
std::string tgtStr = tgt ? tgt->str() : "long";
makeBaseTypeString(tgtStr);
reportError(tok,
Severity::style,
"truncLongCastReturn",
srcStr +" result is returned as " + tgtStr + " value. If the return value is " + tgtStr + " to avoid loss of information, then you have loss of information.\n" +
srcStr +" result is returned as " + tgtStr + " value. If the return value is " + tgtStr + " to avoid loss of information, then there is loss of information. To avoid loss of information you must cast a calculation operand to long, for example 'return a*b;' => 'return (long)a*b'.", CWE197, Certainty::normal);
}
//---------------------------------------------------------------------------
// Checking for float to integer overflow
//---------------------------------------------------------------------------
void CheckTypeImpl::checkFloatToIntegerOverflow()
{
logChecker("CheckType::checkFloatToIntegerOverflow");
for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) {
const ValueType *vtint, *vtfloat;
// Explicit cast
if (Token::Match(tok, "( %name%") && tok->astOperand1() && !tok->astOperand2()) {
if (isUnreachableOperand(tok))
continue;
vtint = tok->valueType();
vtfloat = tok->astOperand1()->valueType();
checkFloatToIntegerOverflow(tok, vtint, vtfloat, tok->astOperand1()->values());
}
// Assignment
else if (tok->str() == "=" && tok->astOperand1() && tok->astOperand2()) {
if (isUnreachableOperand(tok))
continue;
vtint = tok->astOperand1()->valueType();
vtfloat = tok->astOperand2()->valueType();
checkFloatToIntegerOverflow(tok, vtint, vtfloat, tok->astOperand2()->values());
}
else if (tok->str() == "return" && tok->astOperand1() && tok->astOperand1()->valueType() && tok->astOperand1()->valueType()->isFloat()) {
if (isUnreachableOperand(tok))
continue;
const Scope *scope = tok->scope();
while (scope && scope->type != ScopeType::eLambda && scope->type != ScopeType::eFunction)
scope = scope->nestedIn;
if (scope && scope->type == ScopeType::eFunction && scope->function && scope->function->retDef) {
const ValueType &valueType = ValueType::parseDecl(scope->function->retDef, mSettings);
vtfloat = tok->astOperand1()->valueType();
checkFloatToIntegerOverflow(tok, &valueType, vtfloat, tok->astOperand1()->values());
}
}
}
}
void CheckTypeImpl::checkFloatToIntegerOverflow(const Token *tok, const ValueType *vtint, const ValueType *vtfloat, const std::list &floatValues)
{
// Conversion of float to integer?
if (!vtint || !vtint->isIntegral())
return;
if (!vtfloat || !vtfloat->isFloat())
return;
for (const ValueFlow::Value &f : floatValues) {
if (f.valueType != ValueFlow::Value::ValueType::FLOAT)
continue;
if (!mSettings.isEnabled(&f, false))
continue;
if (f.floatValue >= std::exp2(mSettings.platform.long_long_bit))
floatToIntegerOverflowError(tok, f);
else if ((-f.floatValue) > std::exp2(mSettings.platform.long_long_bit - 1))
floatToIntegerOverflowError(tok, f);
else if (mSettings.platform.type != Platform::Type::Unspecified) {
int bits = 0;
if (vtint->type == ValueType::Type::CHAR)
bits = mSettings.platform.char_bit;
else if (vtint->type == ValueType::Type::SHORT)
bits = mSettings.platform.short_bit;
else if (vtint->type == ValueType::Type::INT)
bits = mSettings.platform.int_bit;
else if (vtint->type == ValueType::Type::LONG)
bits = mSettings.platform.long_bit;
else if (vtint->type == ValueType::Type::LONGLONG)
bits = mSettings.platform.long_long_bit;
else
continue;
if (bits < MathLib::bigint_bits && f.floatValue >= (1ULL