2013-04-10 18:49:38 +02:00
|
|
|
/*
|
|
|
|
* Cppcheck - A tool for static C/C++ code analysis
|
2023-01-28 10:16:34 +01:00
|
|
|
* Copyright (C) 2007-2023 Cppcheck team.
|
2013-04-10 18:49:38 +02:00
|
|
|
*
|
|
|
|
* 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 <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
#include "checksizeof.h"
|
2016-06-05 18:24:06 +02:00
|
|
|
|
2022-01-27 19:03:20 +01:00
|
|
|
#include "errortypes.h"
|
2023-08-02 10:36:17 +02:00
|
|
|
#include "library.h"
|
2017-05-27 04:33:47 +02:00
|
|
|
#include "settings.h"
|
|
|
|
#include "symboldatabase.h"
|
|
|
|
#include "token.h"
|
|
|
|
#include "tokenize.h"
|
2016-06-05 18:24:06 +02:00
|
|
|
|
2023-03-02 21:50:14 +01:00
|
|
|
#include <algorithm>
|
|
|
|
#include <iterator>
|
2022-01-27 19:03:20 +01:00
|
|
|
#include <map>
|
|
|
|
#include <vector>
|
|
|
|
|
2013-04-10 18:49:38 +02:00
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
|
|
// Register this check class (by creating a static instance of it)
|
|
|
|
namespace {
|
|
|
|
CheckSizeof instance;
|
|
|
|
}
|
|
|
|
|
2016-06-05 18:24:06 +02:00
|
|
|
// CWE IDs used:
|
2023-10-08 09:10:17 +02:00
|
|
|
static const CWE CWE398(398U); // Indicator of Poor Code Quality
|
|
|
|
static const CWE CWE467(467U); // Use of sizeof() on a Pointer Type
|
|
|
|
static const CWE CWE682(682U); // Incorrect Calculation
|
2013-04-10 18:49:38 +02:00
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
void CheckSizeof::checkSizeofForNumericParameter()
|
|
|
|
{
|
2021-02-24 22:00:06 +01:00
|
|
|
if (!mSettings->severity.isEnabled(Severity::warning))
|
2013-04-10 18:49:38 +02:00
|
|
|
return;
|
|
|
|
|
2023-08-29 12:00:52 +02:00
|
|
|
logChecker("CheckSizeof::checkSizeofForNumericParameter"); // warning
|
|
|
|
|
2018-06-16 16:10:28 +02:00
|
|
|
const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();
|
2018-07-13 16:40:15 +02:00
|
|
|
for (const Scope * scope : symbolDatabase->functionScopes) {
|
2018-04-27 22:36:30 +02:00
|
|
|
for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) {
|
2013-04-10 18:49:38 +02:00
|
|
|
if (Token::Match(tok, "sizeof ( %num% )") ||
|
|
|
|
Token::Match(tok, "sizeof %num%")) {
|
|
|
|
sizeofForNumericParameterError(tok);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CheckSizeof::sizeofForNumericParameterError(const Token *tok)
|
|
|
|
{
|
|
|
|
reportError(tok, Severity::warning,
|
|
|
|
"sizeofwithnumericparameter", "Suspicious usage of 'sizeof' with a numeric constant as parameter.\n"
|
|
|
|
"It is unusual to use a constant value with sizeof. For example, 'sizeof(10)'"
|
|
|
|
" returns 4 (in 32-bit systems) or 8 (in 64-bit systems) instead of 10. 'sizeof('A')'"
|
2021-02-24 22:00:06 +01:00
|
|
|
" and 'sizeof(char)' can return different results.", CWE682, Certainty::normal);
|
2013-04-10 18:49:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
void CheckSizeof::checkSizeofForArrayParameter()
|
|
|
|
{
|
2021-02-24 22:00:06 +01:00
|
|
|
if (!mSettings->severity.isEnabled(Severity::warning))
|
2015-01-06 15:20:42 +01:00
|
|
|
return;
|
2023-08-29 12:00:52 +02:00
|
|
|
|
|
|
|
logChecker("CheckSizeof::checkSizeofForArrayParameter"); // warning
|
|
|
|
|
2018-06-16 16:10:28 +02:00
|
|
|
const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();
|
2018-07-13 16:40:15 +02:00
|
|
|
for (const Scope * scope : symbolDatabase->functionScopes) {
|
2018-04-27 22:36:30 +02:00
|
|
|
for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) {
|
2013-04-10 18:49:38 +02:00
|
|
|
if (Token::Match(tok, "sizeof ( %var% )") ||
|
|
|
|
Token::Match(tok, "sizeof %var% !![")) {
|
|
|
|
const Token* varTok = tok->next();
|
|
|
|
if (varTok->str() == "(") {
|
|
|
|
varTok = varTok->next();
|
|
|
|
}
|
2015-01-31 10:50:39 +01:00
|
|
|
|
|
|
|
const Variable *var = varTok->variable();
|
2015-05-06 07:38:26 +02:00
|
|
|
if (var && var->isArray() && var->isArgument() && !var->isReference())
|
2015-01-31 10:50:39 +01:00
|
|
|
sizeofForArrayParameterError(tok);
|
2013-04-10 18:49:38 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CheckSizeof::sizeofForArrayParameterError(const Token *tok)
|
|
|
|
{
|
2014-08-08 09:23:07 +02:00
|
|
|
reportError(tok, Severity::warning,
|
2013-04-10 18:49:38 +02:00
|
|
|
"sizeofwithsilentarraypointer", "Using 'sizeof' on array given as function argument "
|
|
|
|
"returns size of a pointer.\n"
|
|
|
|
"Using 'sizeof' for array given as function argument returns the size of a pointer. "
|
|
|
|
"It does not return the size of the whole array in bytes as might be "
|
|
|
|
"expected. For example, this code:\n"
|
|
|
|
" int f(char a[100]) {\n"
|
|
|
|
" return sizeof(a);\n"
|
|
|
|
" }\n"
|
|
|
|
"returns 4 (in 32-bit systems) or 8 (in 64-bit systems) instead of 100 (the "
|
2021-02-24 22:00:06 +01:00
|
|
|
"size of the array in bytes).", CWE467, Certainty::normal
|
2021-08-07 20:51:18 +02:00
|
|
|
);
|
2013-04-10 18:49:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void CheckSizeof::checkSizeofForPointerSize()
|
|
|
|
{
|
2021-02-24 22:00:06 +01:00
|
|
|
if (!mSettings->severity.isEnabled(Severity::warning))
|
2013-04-10 18:49:38 +02:00
|
|
|
return;
|
|
|
|
|
2023-08-29 12:00:52 +02:00
|
|
|
logChecker("CheckSizeof::checkSizeofForPointerSize"); // warning
|
|
|
|
|
2018-06-16 16:10:28 +02:00
|
|
|
const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();
|
2018-07-13 16:40:15 +02:00
|
|
|
for (const Scope * scope : symbolDatabase->functionScopes) {
|
2018-04-27 22:36:30 +02:00
|
|
|
for (const Token* tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) {
|
2014-08-08 09:23:07 +02:00
|
|
|
const Token* tokSize;
|
|
|
|
const Token* tokFunc;
|
|
|
|
const Token *variable = nullptr;
|
2014-02-16 11:47:52 +01:00
|
|
|
const Token *variable2 = nullptr;
|
2013-04-10 18:49:38 +02:00
|
|
|
|
|
|
|
// Find any function that may use sizeof on a pointer
|
|
|
|
// Once leaving those tests, it is mandatory to have:
|
|
|
|
// - variable matching the used pointer
|
|
|
|
// - tokVar pointing on the argument where sizeof may be used
|
2023-06-21 17:35:15 +02:00
|
|
|
if (Token::Match(tok->tokAt(2), "%name% (") && mSettings->library.getAllocFuncInfo(tok->tokAt(2))) {
|
2016-05-22 22:29:21 +02:00
|
|
|
if (Token::Match(tok, "%var% ="))
|
|
|
|
variable = tok;
|
|
|
|
else if (tok->strAt(1) == ")" && Token::Match(tok->linkAt(1)->tokAt(-2), "%var% ="))
|
|
|
|
variable = tok->linkAt(1)->tokAt(-2);
|
2023-06-21 17:35:15 +02:00
|
|
|
else if (tok->link() && Token::Match(tok, "> ( %name% (") && mSettings->library.getAllocFuncInfo(tok->tokAt(2)) && Token::Match(tok->link()->tokAt(-3), "%var% ="))
|
2016-05-22 22:29:21 +02:00
|
|
|
variable = tok->link()->tokAt(-3);
|
2015-11-06 12:35:49 +01:00
|
|
|
tokSize = tok->tokAt(4);
|
|
|
|
tokFunc = tok->tokAt(2);
|
2015-11-07 09:35:30 +01:00
|
|
|
} else if (Token::simpleMatch(tok, "memset (") && tok->strAt(-1) != ".") {
|
2013-04-10 18:49:38 +02:00
|
|
|
variable = tok->tokAt(2);
|
2014-08-08 09:23:07 +02:00
|
|
|
tokSize = variable->nextArgument();
|
|
|
|
if (tokSize)
|
|
|
|
tokSize = tokSize->nextArgument();
|
|
|
|
tokFunc = tok;
|
2015-11-07 09:35:30 +01:00
|
|
|
} else if (Token::Match(tok, "memcpy|memcmp|memmove|strncpy|strncmp|strncat (") && tok->strAt(-1) != ".") {
|
2013-04-10 18:49:38 +02:00
|
|
|
variable = tok->tokAt(2);
|
|
|
|
variable2 = variable->nextArgument();
|
2014-06-01 22:18:17 +02:00
|
|
|
if (!variable2)
|
|
|
|
continue;
|
2014-08-08 09:23:07 +02:00
|
|
|
tokSize = variable2->nextArgument();
|
|
|
|
tokFunc = tok;
|
2013-04-10 18:49:38 +02:00
|
|
|
} else {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2017-10-14 18:09:42 +02:00
|
|
|
if (tokSize && tokFunc->str() == "calloc")
|
2016-05-22 22:29:21 +02:00
|
|
|
tokSize = tokSize->nextArgument();
|
|
|
|
|
2017-10-14 18:09:42 +02:00
|
|
|
if (tokSize) {
|
2017-10-14 17:57:27 +02:00
|
|
|
const Token * const paramsListEndTok = tokFunc->linkAt(1);
|
|
|
|
for (const Token* tok2 = tokSize; tok2 != paramsListEndTok; tok2 = tok2->next()) {
|
2016-07-17 19:19:05 +02:00
|
|
|
if (Token::simpleMatch(tok2, "/ sizeof")) {
|
|
|
|
// Allow division with sizeof(char)
|
|
|
|
if (Token::simpleMatch(tok2->next(), "sizeof (")) {
|
|
|
|
const Token *sztok = tok2->tokAt(2)->astOperand2();
|
|
|
|
const ValueType *vt = ((sztok != nullptr) ? sztok->valueType() : nullptr);
|
|
|
|
if (vt && vt->type == ValueType::CHAR && vt->pointer == 0)
|
|
|
|
continue;
|
|
|
|
}
|
2022-04-13 18:21:56 +02:00
|
|
|
auto hasMultiplication = [](const Token* parTok) -> bool {
|
|
|
|
while (parTok) { // Allow division if followed by multiplication
|
|
|
|
if (parTok->isArithmeticalOp() && parTok->str() == "*") {
|
2022-10-16 13:46:26 +02:00
|
|
|
const Token* szToks[] = { parTok->astOperand1(), parTok->astOperand2() };
|
|
|
|
if (std::any_of(std::begin(szToks), std::end(szToks), [](const Token* szTok) {
|
|
|
|
return Token::simpleMatch(szTok, "(") && Token::simpleMatch(szTok->previous(), "sizeof");
|
|
|
|
}))
|
|
|
|
return true;
|
2022-04-13 18:21:56 +02:00
|
|
|
}
|
|
|
|
parTok = parTok->astParent();
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
};
|
|
|
|
if (hasMultiplication(tok2->astParent()))
|
|
|
|
continue;
|
2016-07-17 19:19:05 +02:00
|
|
|
|
2014-08-08 09:23:07 +02:00
|
|
|
divideBySizeofError(tok2, tokFunc->str());
|
2016-07-17 19:19:05 +02:00
|
|
|
}
|
2014-08-08 09:23:07 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-11-07 09:35:30 +01:00
|
|
|
if (!variable || !tokSize)
|
2014-08-08 09:23:07 +02:00
|
|
|
continue;
|
|
|
|
|
2015-11-06 12:35:49 +01:00
|
|
|
while (Token::Match(variable, "%var% ::|."))
|
|
|
|
variable = variable->tokAt(2);
|
|
|
|
|
2016-05-26 21:25:29 +02:00
|
|
|
while (Token::Match(variable2, "%var% ::|."))
|
|
|
|
variable2 = variable2->tokAt(2);
|
|
|
|
|
2020-11-10 16:00:55 +01:00
|
|
|
if (!variable)
|
|
|
|
continue;
|
|
|
|
|
2013-04-10 18:49:38 +02:00
|
|
|
// Ensure the variables are in the symbol database
|
|
|
|
// Also ensure the variables are pointers
|
|
|
|
// Only keep variables which are pointers
|
|
|
|
const Variable *var = variable->variable();
|
|
|
|
if (!var || !var->isPointer() || var->isArray()) {
|
2017-08-09 20:00:26 +02:00
|
|
|
variable = nullptr;
|
2013-04-10 18:49:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (variable2) {
|
|
|
|
var = variable2->variable();
|
|
|
|
if (!var || !var->isPointer() || var->isArray()) {
|
2017-08-09 20:00:26 +02:00
|
|
|
variable2 = nullptr;
|
2013-04-10 18:49:38 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// If there are no pointer variable at this point, there is
|
|
|
|
// no need to continue
|
2017-08-09 20:00:26 +02:00
|
|
|
if (variable == nullptr && variable2 == nullptr) {
|
2013-04-10 18:49:38 +02:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Jump to the next sizeof token in the function and in the parameter
|
|
|
|
// This is to allow generic operations with sizeof
|
2014-08-08 09:23:07 +02:00
|
|
|
for (; tokSize && tokSize->str() != ")" && tokSize->str() != "," && tokSize->str() != "sizeof"; tokSize = tokSize->next()) {}
|
2013-04-10 18:49:38 +02:00
|
|
|
|
2015-11-07 13:18:50 +01:00
|
|
|
if (tokSize->str() != "sizeof")
|
|
|
|
continue;
|
|
|
|
|
2016-05-22 22:29:21 +02:00
|
|
|
// Now check for the sizeof usage: Does the level of pointer indirection match?
|
|
|
|
if (tokSize->linkAt(1)->strAt(-1) == "*") {
|
2016-05-26 21:25:29 +02:00
|
|
|
if (variable && variable->valueType() && variable->valueType()->pointer == 1 && variable->valueType()->type != ValueType::VOID)
|
2016-05-22 22:29:21 +02:00
|
|
|
sizeofForPointerError(variable, variable->str());
|
2016-05-26 21:25:29 +02:00
|
|
|
else if (variable2 && variable2->valueType() && variable2->valueType()->pointer == 1 && variable2->valueType()->type != ValueType::VOID)
|
2016-05-22 22:29:21 +02:00
|
|
|
sizeofForPointerError(variable2, variable2->str());
|
|
|
|
}
|
|
|
|
|
2015-11-06 22:21:39 +01:00
|
|
|
if (Token::simpleMatch(tokSize, "sizeof ( &"))
|
2015-11-06 12:35:49 +01:00
|
|
|
tokSize = tokSize->tokAt(3);
|
|
|
|
else if (Token::Match(tokSize, "sizeof (|&"))
|
|
|
|
tokSize = tokSize->tokAt(2);
|
|
|
|
else
|
|
|
|
tokSize = tokSize->next();
|
|
|
|
|
|
|
|
while (Token::Match(tokSize, "%var% ::|."))
|
|
|
|
tokSize = tokSize->tokAt(2);
|
|
|
|
|
2015-11-07 09:35:30 +01:00
|
|
|
if (Token::Match(tokSize, "%var% [|("))
|
|
|
|
continue;
|
|
|
|
|
2016-05-22 22:29:21 +02:00
|
|
|
// Now check for the sizeof usage again. Once here, everything using sizeof(varid) or sizeof(&varid)
|
2013-04-10 18:49:38 +02:00
|
|
|
// looks suspicious
|
2015-11-06 12:35:49 +01:00
|
|
|
if (variable && tokSize->varId() == variable->varId())
|
2013-04-10 18:49:38 +02:00
|
|
|
sizeofForPointerError(variable, variable->str());
|
2015-11-06 12:35:49 +01:00
|
|
|
if (variable2 && tokSize->varId() == variable2->varId())
|
2013-04-10 18:49:38 +02:00
|
|
|
sizeofForPointerError(variable2, variable2->str());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CheckSizeof::sizeofForPointerError(const Token *tok, const std::string &varname)
|
|
|
|
{
|
|
|
|
reportError(tok, Severity::warning, "pointerSize",
|
|
|
|
"Size of pointer '" + varname + "' used instead of size of its data.\n"
|
|
|
|
"Size of pointer '" + varname + "' used instead of size of its data. "
|
|
|
|
"This is likely to lead to a buffer overflow. You probably intend to "
|
2021-02-24 22:00:06 +01:00
|
|
|
"write 'sizeof(*" + varname + ")'.", CWE467, Certainty::normal);
|
2014-08-08 09:23:07 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void CheckSizeof::divideBySizeofError(const Token *tok, const std::string &memfunc)
|
|
|
|
{
|
|
|
|
reportError(tok, Severity::warning, "sizeofDivisionMemfunc",
|
2021-02-24 22:00:06 +01:00
|
|
|
"Division by result of sizeof(). " + memfunc + "() expects a size in bytes, did you intend to multiply instead?", CWE682, Certainty::normal);
|
2013-04-10 18:49:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void CheckSizeof::sizeofsizeof()
|
|
|
|
{
|
2021-02-24 22:00:06 +01:00
|
|
|
if (!mSettings->severity.isEnabled(Severity::warning))
|
2013-04-10 18:49:38 +02:00
|
|
|
return;
|
|
|
|
|
2023-08-29 12:00:52 +02:00
|
|
|
logChecker("CheckSizeof::sizeofsizeof"); // warning
|
|
|
|
|
2018-06-16 16:10:28 +02:00
|
|
|
for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) {
|
2013-04-10 18:49:38 +02:00
|
|
|
if (Token::Match(tok, "sizeof (| sizeof")) {
|
|
|
|
sizeofsizeofError(tok);
|
|
|
|
tok = tok->next();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CheckSizeof::sizeofsizeofError(const Token *tok)
|
|
|
|
{
|
|
|
|
reportError(tok, Severity::warning,
|
|
|
|
"sizeofsizeof", "Calling 'sizeof' on 'sizeof'.\n"
|
|
|
|
"Calling sizeof for 'sizeof looks like a suspicious code and "
|
|
|
|
"most likely there should be just one 'sizeof'. The current "
|
2021-02-24 22:00:06 +01:00
|
|
|
"code is equivalent to 'sizeof(size_t)'", CWE682, Certainty::normal);
|
2013-04-10 18:49:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
2013-12-25 12:35:41 +01:00
|
|
|
|
2013-04-10 18:49:38 +02:00
|
|
|
void CheckSizeof::sizeofCalculation()
|
|
|
|
{
|
2021-02-24 22:00:06 +01:00
|
|
|
if (!mSettings->severity.isEnabled(Severity::warning))
|
2013-04-10 18:49:38 +02:00
|
|
|
return;
|
|
|
|
|
2023-08-29 12:00:52 +02:00
|
|
|
logChecker("CheckSizeof::sizeofCalculation"); // warning
|
|
|
|
|
2021-02-24 22:00:06 +01:00
|
|
|
const bool printInconclusive = mSettings->certainty.isEnabled(Certainty::inconclusive);
|
2015-04-10 14:18:52 +02:00
|
|
|
|
2018-06-16 16:10:28 +02:00
|
|
|
for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) {
|
2018-03-12 12:50:33 +01:00
|
|
|
if (!Token::simpleMatch(tok, "sizeof ("))
|
2018-03-12 13:43:04 +01:00
|
|
|
continue;
|
2018-03-12 12:50:33 +01:00
|
|
|
|
|
|
|
// ignore if the `sizeof` result is cast to void inside a macro, i.e. the calculation is
|
|
|
|
// expected to be parsed but skipped, such as in a disabled custom ASSERT() macro
|
|
|
|
if (tok->isExpandedMacro() && tok->previous()) {
|
|
|
|
const Token *cast_end = (tok->previous()->str() == "(") ? tok->previous() : tok;
|
|
|
|
if (Token::simpleMatch(cast_end->tokAt(-3), "( void )") ||
|
2020-12-31 09:33:23 +01:00
|
|
|
Token::simpleMatch(cast_end->tokAt(-4), "static_cast < void >")) {
|
2018-03-12 12:50:33 +01:00
|
|
|
continue;
|
2015-09-21 13:35:07 +02:00
|
|
|
}
|
2013-04-10 18:49:38 +02:00
|
|
|
}
|
2018-03-12 12:50:33 +01:00
|
|
|
|
|
|
|
const Token *argument = tok->next()->astOperand2();
|
2019-02-26 21:06:44 +01:00
|
|
|
if (!argument || !argument->isCalculation())
|
|
|
|
continue;
|
|
|
|
|
|
|
|
bool inconclusive = false;
|
|
|
|
if (argument->isExpandedMacro())
|
|
|
|
inconclusive = true;
|
|
|
|
else if (tok->next()->isExpandedMacro())
|
|
|
|
inconclusive = true;
|
|
|
|
|
|
|
|
if (!inconclusive || printInconclusive)
|
|
|
|
sizeofCalculationError(argument, inconclusive);
|
2013-04-10 18:49:38 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CheckSizeof::sizeofCalculationError(const Token *tok, bool inconclusive)
|
|
|
|
{
|
|
|
|
reportError(tok, Severity::warning,
|
2021-02-24 22:00:06 +01:00
|
|
|
"sizeofCalculation", "Found calculation inside sizeof().", CWE682, inconclusive ? Certainty::inconclusive : Certainty::normal);
|
2013-04-10 18:49:38 +02:00
|
|
|
}
|
|
|
|
|
2018-03-15 10:24:17 +01:00
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
void CheckSizeof::sizeofFunction()
|
|
|
|
{
|
2021-02-24 22:00:06 +01:00
|
|
|
if (!mSettings->severity.isEnabled(Severity::warning))
|
2018-03-15 10:24:17 +01:00
|
|
|
return;
|
|
|
|
|
2023-08-29 12:00:52 +02:00
|
|
|
logChecker("CheckSizeof::sizeofFunction"); // warning
|
|
|
|
|
2018-06-16 16:10:28 +02:00
|
|
|
for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) {
|
2018-03-15 10:24:17 +01:00
|
|
|
if (Token::simpleMatch(tok, "sizeof (")) {
|
|
|
|
|
|
|
|
// ignore if the `sizeof` result is cast to void inside a macro, i.e. the calculation is
|
|
|
|
// expected to be parsed but skipped, such as in a disabled custom ASSERT() macro
|
|
|
|
if (tok->isExpandedMacro() && tok->previous()) {
|
|
|
|
const Token *cast_end = (tok->previous()->str() == "(") ? tok->previous() : tok;
|
|
|
|
if (Token::simpleMatch(cast_end->tokAt(-3), "( void )") ||
|
2020-12-31 09:33:23 +01:00
|
|
|
Token::simpleMatch(cast_end->tokAt(-4), "static_cast < void >")) {
|
2018-03-15 10:24:17 +01:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (const Token *argument = tok->next()->astOperand2()) {
|
|
|
|
const Token *checkToken = argument->previous();
|
|
|
|
if (checkToken->tokType() == Token::eName)
|
|
|
|
break;
|
|
|
|
const Function * fun = checkToken->function();
|
2018-08-30 19:51:39 +02:00
|
|
|
// Don't report error if the function is overloaded
|
2018-03-15 10:24:17 +01:00
|
|
|
if (fun && fun->nestedIn->functionMap.count(checkToken->str()) == 1) {
|
|
|
|
sizeofFunctionError(tok);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CheckSizeof::sizeofFunctionError(const Token *tok)
|
|
|
|
{
|
|
|
|
reportError(tok, Severity::warning,
|
2021-02-24 22:00:06 +01:00
|
|
|
"sizeofFunctionCall", "Found function call inside sizeof().", CWE682, Certainty::normal);
|
2018-03-15 10:24:17 +01:00
|
|
|
}
|
|
|
|
|
2013-04-10 18:49:38 +02:00
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// Check for code like sizeof()*sizeof() or sizeof(ptr)/value
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void CheckSizeof::suspiciousSizeofCalculation()
|
|
|
|
{
|
2021-02-24 22:00:06 +01:00
|
|
|
if (!mSettings->severity.isEnabled(Severity::warning) || !mSettings->certainty.isEnabled(Certainty::inconclusive))
|
2013-04-10 18:49:38 +02:00
|
|
|
return;
|
|
|
|
|
2023-08-29 12:00:52 +02:00
|
|
|
logChecker("CheckSizeof::suspiciousSizeofCalculation"); // warning,inconclusive
|
|
|
|
|
2018-06-16 16:10:28 +02:00
|
|
|
for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) {
|
2013-04-10 18:49:38 +02:00
|
|
|
if (Token::simpleMatch(tok, "sizeof (")) {
|
2022-04-10 22:47:27 +02:00
|
|
|
const Token* lPar = tok->astParent();
|
|
|
|
if (lPar && lPar->str() == "(") {
|
|
|
|
const Token* const rPar = lPar->link();
|
2022-04-13 12:24:35 +02:00
|
|
|
const Token* varTok = lPar->astOperand2();
|
|
|
|
int derefCount = 0;
|
|
|
|
while (Token::Match(varTok, "[|*")) {
|
|
|
|
++derefCount;
|
|
|
|
varTok = varTok->astOperand1();
|
|
|
|
}
|
2022-04-10 22:47:27 +02:00
|
|
|
if (lPar->astParent() && lPar->astParent()->str() == "/") {
|
|
|
|
const Variable* var = varTok ? varTok->variable() : nullptr;
|
2022-04-13 12:24:35 +02:00
|
|
|
if (var && var->isPointer() && !var->isArray() && !(var->valueType() && var->valueType()->pointer <= derefCount))
|
2022-04-10 22:47:27 +02:00
|
|
|
divideSizeofError(tok);
|
|
|
|
}
|
|
|
|
else if (Token::simpleMatch(rPar, ") * sizeof") && rPar->next()->astOperand1() == tok->next())
|
|
|
|
multiplySizeofError(tok);
|
|
|
|
}
|
2013-04-10 18:49:38 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CheckSizeof::multiplySizeofError(const Token *tok)
|
|
|
|
{
|
|
|
|
reportError(tok, Severity::warning,
|
2021-02-24 22:00:06 +01:00
|
|
|
"multiplySizeof", "Multiplying sizeof() with sizeof() indicates a logic error.", CWE682, Certainty::inconclusive);
|
2013-04-10 18:49:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void CheckSizeof::divideSizeofError(const Token *tok)
|
|
|
|
{
|
|
|
|
reportError(tok, Severity::warning,
|
|
|
|
"divideSizeof", "Division of result of sizeof() on pointer type.\n"
|
|
|
|
"Division of result of sizeof() on pointer type. sizeof() returns the size of the pointer, "
|
2021-02-24 22:00:06 +01:00
|
|
|
"not the size of the memory area it points to.", CWE682, Certainty::inconclusive);
|
2013-04-10 18:49:38 +02:00
|
|
|
}
|
2013-07-05 21:07:07 +02:00
|
|
|
|
|
|
|
void CheckSizeof::sizeofVoid()
|
|
|
|
{
|
2021-02-24 22:00:06 +01:00
|
|
|
if (!mSettings->severity.isEnabled(Severity::portability))
|
2013-07-05 21:07:07 +02:00
|
|
|
return;
|
|
|
|
|
2023-08-29 12:00:52 +02:00
|
|
|
logChecker("CheckSizeof::sizeofVoid"); // portability
|
|
|
|
|
2018-06-16 16:10:28 +02:00
|
|
|
for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) {
|
2021-03-25 08:25:43 +01:00
|
|
|
if (Token::simpleMatch(tok, "sizeof ( void )")) {
|
2013-07-05 21:07:07 +02:00
|
|
|
sizeofVoidError(tok);
|
2015-12-30 12:28:55 +01:00
|
|
|
} else if (Token::simpleMatch(tok, "sizeof (") && tok->next()->astOperand2()) {
|
2015-12-30 11:48:20 +01:00
|
|
|
const ValueType *vt = tok->next()->astOperand2()->valueType();
|
|
|
|
if (vt && vt->type == ValueType::Type::VOID && vt->pointer == 0U)
|
|
|
|
sizeofDereferencedVoidPointerError(tok, tok->strAt(3));
|
|
|
|
} else if (tok->str() == "-") {
|
|
|
|
// only warn for: 'void *' - 'integral'
|
|
|
|
const ValueType *vt1 = tok->astOperand1() ? tok->astOperand1()->valueType() : nullptr;
|
|
|
|
const ValueType *vt2 = tok->astOperand2() ? tok->astOperand2()->valueType() : nullptr;
|
2018-04-04 21:51:31 +02:00
|
|
|
const bool op1IsvoidPointer = (vt1 && vt1->type == ValueType::Type::VOID && vt1->pointer == 1U);
|
|
|
|
const bool op2IsIntegral = (vt2 && vt2->isIntegral() && vt2->pointer == 0U);
|
2015-12-30 11:48:20 +01:00
|
|
|
if (op1IsvoidPointer && op2IsIntegral)
|
|
|
|
arithOperationsOnVoidPointerError(tok, tok->astOperand1()->expressionString(), vt1->str());
|
|
|
|
} else if (Token::Match(tok, "+|++|--|+=|-=")) { // Arithmetic operations on variable of type "void*"
|
|
|
|
const ValueType *vt1 = tok->astOperand1() ? tok->astOperand1()->valueType() : nullptr;
|
|
|
|
const ValueType *vt2 = tok->astOperand2() ? tok->astOperand2()->valueType() : nullptr;
|
|
|
|
|
2018-04-04 21:51:31 +02:00
|
|
|
const bool voidpointer1 = (vt1 && vt1->type == ValueType::Type::VOID && vt1->pointer == 1U);
|
|
|
|
const bool voidpointer2 = (vt2 && vt2->type == ValueType::Type::VOID && vt2->pointer == 1U);
|
2015-12-30 11:48:20 +01:00
|
|
|
|
|
|
|
if (voidpointer1)
|
|
|
|
arithOperationsOnVoidPointerError(tok, tok->astOperand1()->expressionString(), vt1->str());
|
|
|
|
|
|
|
|
if (!tok->isAssignmentOp() && voidpointer2)
|
|
|
|
arithOperationsOnVoidPointerError(tok, tok->astOperand2()->expressionString(), vt2->str());
|
2013-07-05 21:07:07 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CheckSizeof::sizeofVoidError(const Token *tok)
|
|
|
|
{
|
|
|
|
const std::string message = "Behaviour of 'sizeof(void)' is not covered by the ISO C standard.";
|
|
|
|
const std::string verbose = message + " A value for 'sizeof(void)' is defined only as part of a GNU C extension, which defines 'sizeof(void)' to be 1.";
|
2021-02-24 22:00:06 +01:00
|
|
|
reportError(tok, Severity::portability, "sizeofVoid", message + "\n" + verbose, CWE682, Certainty::normal);
|
2013-07-05 21:07:07 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void CheckSizeof::sizeofDereferencedVoidPointerError(const Token *tok, const std::string &varname)
|
|
|
|
{
|
|
|
|
const std::string message = "'*" + varname + "' is of type 'void', the behaviour of 'sizeof(void)' is not covered by the ISO C standard.";
|
|
|
|
const std::string verbose = message + " A value for 'sizeof(void)' is defined only as part of a GNU C extension, which defines 'sizeof(void)' to be 1.";
|
2021-02-24 22:00:06 +01:00
|
|
|
reportError(tok, Severity::portability, "sizeofDereferencedVoidPointer", message + "\n" + verbose, CWE682, Certainty::normal);
|
2013-07-05 21:07:07 +02:00
|
|
|
}
|
|
|
|
|
2013-07-10 06:40:09 +02:00
|
|
|
void CheckSizeof::arithOperationsOnVoidPointerError(const Token* tok, const std::string &varname, const std::string &vartype)
|
2013-07-05 21:07:07 +02:00
|
|
|
{
|
2018-09-26 17:58:22 +02:00
|
|
|
const std::string message = "'$symbol' is of type '" + vartype + "'. When using void pointers in calculations, the behaviour is undefined.";
|
2013-07-05 21:07:07 +02:00
|
|
|
const std::string verbose = message + " Arithmetic operations on 'void *' is a GNU C extension, which defines the 'sizeof(void)' to be 1.";
|
2021-02-24 22:00:06 +01:00
|
|
|
reportError(tok, Severity::portability, "arithOperationsOnVoidPointer", "$symbol:" + varname + '\n' + message + '\n' + verbose, CWE467, Certainty::normal);
|
2013-07-05 21:07:07 +02:00
|
|
|
}
|