2013-04-10 18:49:38 +02:00
|
|
|
/*
|
|
|
|
* Cppcheck - A tool for static C/C++ code analysis
|
2018-03-31 20:59:09 +02:00
|
|
|
* Copyright (C) 2007-2018 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
|
|
|
|
2017-05-27 04:33:47 +02:00
|
|
|
#include "errorlogger.h"
|
|
|
|
#include "settings.h"
|
|
|
|
#include "symboldatabase.h"
|
|
|
|
#include "token.h"
|
|
|
|
#include "tokenize.h"
|
2016-06-05 18:24:06 +02:00
|
|
|
|
2017-05-27 04:33:47 +02:00
|
|
|
#include <cstddef>
|
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:
|
2016-06-07 19:28:32 +02:00
|
|
|
static const struct CWE CWE398(398U); // Indicator of Poor Code Quality
|
2016-07-08 20:10:33 +02:00
|
|
|
static const struct CWE CWE467(467U); // Use of sizeof() on a Pointer Type
|
|
|
|
static const struct CWE CWE682(682U); // Incorrect Calculation
|
2013-04-10 18:49:38 +02:00
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
void CheckSizeof::checkSizeofForNumericParameter()
|
|
|
|
{
|
2017-04-11 11:49:09 +02:00
|
|
|
if (!_settings->isEnabled(Settings::WARNING))
|
2013-04-10 18:49:38 +02:00
|
|
|
return;
|
|
|
|
|
|
|
|
const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase();
|
|
|
|
const std::size_t functions = symbolDatabase->functionScopes.size();
|
|
|
|
for (std::size_t i = 0; i < functions; ++i) {
|
|
|
|
const Scope * scope = symbolDatabase->functionScopes[i];
|
|
|
|
for (const Token* tok = scope->classStart->next(); tok != scope->classEnd; tok = tok->next()) {
|
|
|
|
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')'"
|
2016-07-08 09:06:55 +02:00
|
|
|
" and 'sizeof(char)' can return different results.", CWE682, false);
|
2013-04-10 18:49:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
void CheckSizeof::checkSizeofForArrayParameter()
|
|
|
|
{
|
2017-04-11 11:49:09 +02:00
|
|
|
if (!_settings->isEnabled(Settings::WARNING))
|
2015-01-06 15:20:42 +01:00
|
|
|
return;
|
2013-04-10 18:49:38 +02:00
|
|
|
const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase();
|
|
|
|
const std::size_t functions = symbolDatabase->functionScopes.size();
|
|
|
|
for (std::size_t i = 0; i < functions; ++i) {
|
|
|
|
const Scope * scope = symbolDatabase->functionScopes[i];
|
|
|
|
for (const Token* tok = scope->classStart->next(); tok != scope->classEnd; tok = tok->next()) {
|
|
|
|
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 "
|
2016-07-08 09:06:55 +02:00
|
|
|
"size of the array in bytes).", CWE467, false
|
2013-04-10 18:49:38 +02:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
void CheckSizeof::checkSizeofForPointerSize()
|
|
|
|
{
|
2017-04-11 11:49:09 +02:00
|
|
|
if (!_settings->isEnabled(Settings::WARNING))
|
2013-04-10 18:49:38 +02:00
|
|
|
return;
|
|
|
|
|
|
|
|
const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase();
|
|
|
|
const std::size_t functions = symbolDatabase->functionScopes.size();
|
|
|
|
for (std::size_t i = 0; i < functions; ++i) {
|
|
|
|
const Scope * scope = symbolDatabase->functionScopes[i];
|
|
|
|
for (const Token* tok = scope->classStart; tok != scope->classEnd; 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
|
2016-05-22 22:29:21 +02:00
|
|
|
if (Token::Match(tok->tokAt(2), "malloc|alloca|calloc (")) {
|
|
|
|
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);
|
|
|
|
else if (tok->link() && Token::Match(tok, "> ( malloc|alloca|calloc (") && Token::Match(tok->link()->tokAt(-3), "%var% ="))
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
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);
|
|
|
|
|
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 "
|
2016-07-08 09:06:55 +02:00
|
|
|
"write 'sizeof(*" + varname + ")'.", CWE467, false);
|
2014-08-08 09:23:07 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void CheckSizeof::divideBySizeofError(const Token *tok, const std::string &memfunc)
|
|
|
|
{
|
|
|
|
reportError(tok, Severity::warning, "sizeofDivisionMemfunc",
|
2016-07-08 09:06:55 +02:00
|
|
|
"Division by result of sizeof(). " + memfunc + "() expects a size in bytes, did you intend to multiply instead?", CWE682, false);
|
2013-04-10 18:49:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void CheckSizeof::sizeofsizeof()
|
|
|
|
{
|
2017-04-11 11:49:09 +02:00
|
|
|
if (!_settings->isEnabled(Settings::WARNING))
|
2013-04-10 18:49:38 +02:00
|
|
|
return;
|
|
|
|
|
|
|
|
for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) {
|
|
|
|
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 "
|
2016-07-08 09:06:55 +02:00
|
|
|
"code is equivalent to 'sizeof(size_t)'", CWE682, false);
|
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()
|
|
|
|
{
|
2017-04-11 11:49:09 +02:00
|
|
|
if (!_settings->isEnabled(Settings::WARNING))
|
2013-04-10 18:49:38 +02:00
|
|
|
return;
|
|
|
|
|
2015-04-10 14:18:52 +02:00
|
|
|
const bool printInconclusive = _settings->inconclusive;
|
|
|
|
|
2013-04-10 18:49:38 +02:00
|
|
|
for (const Token *tok = _tokenizer->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 )") ||
|
|
|
|
Token::simpleMatch(cast_end->previous(), "static_cast<void>")) {
|
|
|
|
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();
|
|
|
|
if (argument && argument->isCalculation() && (!argument->isExpandedMacro() || printInconclusive))
|
|
|
|
sizeofCalculationError(argument, argument->isExpandedMacro());
|
2013-04-10 18:49:38 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CheckSizeof::sizeofCalculationError(const Token *tok, bool inconclusive)
|
|
|
|
{
|
|
|
|
reportError(tok, Severity::warning,
|
2016-07-08 09:06:55 +02:00
|
|
|
"sizeofCalculation", "Found calculation inside sizeof().", CWE682, inconclusive);
|
2013-04-10 18:49:38 +02:00
|
|
|
}
|
|
|
|
|
2018-03-15 10:24:17 +01:00
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
void CheckSizeof::sizeofFunction()
|
|
|
|
{
|
|
|
|
if (!_settings->isEnabled(Settings::WARNING))
|
|
|
|
return;
|
|
|
|
|
|
|
|
for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) {
|
|
|
|
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 )") ||
|
|
|
|
Token::simpleMatch(cast_end->previous(), "static_cast<void>")) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (const Token *argument = tok->next()->astOperand2()) {
|
|
|
|
const Token *checkToken = argument->previous();
|
|
|
|
if (checkToken->tokType() == Token::eName)
|
|
|
|
break;
|
|
|
|
const Function * fun = checkToken->function();
|
|
|
|
// Dont report error if the function is overloaded
|
|
|
|
if (fun && fun->nestedIn->functionMap.count(checkToken->str()) == 1) {
|
|
|
|
sizeofFunctionError(tok);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CheckSizeof::sizeofFunctionError(const Token *tok)
|
|
|
|
{
|
|
|
|
reportError(tok, Severity::warning,
|
|
|
|
"sizeofFunctionCall", "Found function call inside sizeof().", CWE682, false);
|
|
|
|
}
|
|
|
|
|
2013-04-10 18:49:38 +02:00
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// Check for code like sizeof()*sizeof() or sizeof(ptr)/value
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void CheckSizeof::suspiciousSizeofCalculation()
|
|
|
|
{
|
2017-04-11 11:49:09 +02:00
|
|
|
if (!_settings->isEnabled(Settings::WARNING) || !_settings->inconclusive)
|
2013-04-10 18:49:38 +02:00
|
|
|
return;
|
|
|
|
|
2014-05-19 20:57:13 +02:00
|
|
|
// TODO: Use AST here. This should be possible as soon as sizeof without brackets is correctly parsed
|
2013-04-10 18:49:38 +02:00
|
|
|
for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) {
|
|
|
|
if (Token::simpleMatch(tok, "sizeof (")) {
|
|
|
|
const Token* const end = tok->linkAt(1);
|
|
|
|
const Variable* var = end->previous()->variable();
|
|
|
|
if (end->strAt(-1) == "*" || (var && var->isPointer() && !var->isArray())) {
|
|
|
|
if (end->strAt(1) == "/")
|
|
|
|
divideSizeofError(tok);
|
2014-05-19 20:57:13 +02:00
|
|
|
} else if (Token::simpleMatch(end, ") * sizeof") && end->next()->astOperand1() == tok->next())
|
2013-04-10 18:49:38 +02:00
|
|
|
multiplySizeofError(tok);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CheckSizeof::multiplySizeofError(const Token *tok)
|
|
|
|
{
|
|
|
|
reportError(tok, Severity::warning,
|
CWE mapping of useAutoPointerMalloc, uselessCallsCompare, uselessCallsSwap, uselessCallsSubstr, uselessCallsEmpty, uselessCallsRemove, derefInvalidIterator, reademptycontainer, multiplySizeof, divideSizeof, stringLiteralWrite, incorrectStringCompare, literalWithCharPtrCompare, charLiteralWithCharPtrCompare, incorrectStringBooleanError, staticStringCompare, stringCompare, signConversion, truncLongCastAssignment, truncLongCastReturn, unusedFunction, unusedVariable, unusedAllocatedMemory, unreadVariable, unassignedVariable, unusedStructMember, postfixOperator, va_start_wrongParameter (#824)
Add an optional extended description…
2016-09-03 00:31:35 +02:00
|
|
|
"multiplySizeof", "Multiplying sizeof() with sizeof() indicates a logic error.", CWE682, true);
|
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, "
|
CWE mapping of useAutoPointerMalloc, uselessCallsCompare, uselessCallsSwap, uselessCallsSubstr, uselessCallsEmpty, uselessCallsRemove, derefInvalidIterator, reademptycontainer, multiplySizeof, divideSizeof, stringLiteralWrite, incorrectStringCompare, literalWithCharPtrCompare, charLiteralWithCharPtrCompare, incorrectStringBooleanError, staticStringCompare, stringCompare, signConversion, truncLongCastAssignment, truncLongCastReturn, unusedFunction, unusedVariable, unusedAllocatedMemory, unreadVariable, unassignedVariable, unusedStructMember, postfixOperator, va_start_wrongParameter (#824)
Add an optional extended description…
2016-09-03 00:31:35 +02:00
|
|
|
"not the size of the memory area it points to.", CWE682, true);
|
2013-04-10 18:49:38 +02:00
|
|
|
}
|
2013-07-05 21:07:07 +02:00
|
|
|
|
|
|
|
void CheckSizeof::sizeofVoid()
|
|
|
|
{
|
2017-04-11 11:49:09 +02:00
|
|
|
if (!_settings->isEnabled(Settings::PORTABILITY))
|
2013-07-05 21:07:07 +02:00
|
|
|
return;
|
|
|
|
|
|
|
|
for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) {
|
|
|
|
if (Token::simpleMatch(tok, "sizeof ( )")) { // "sizeof(void)" gets simplified to sizeof ( )
|
|
|
|
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;
|
|
|
|
bool op1IsvoidPointer = (vt1 && vt1->type == ValueType::Type::VOID && vt1->pointer == 1U);
|
|
|
|
bool op2IsIntegral = (vt2 && vt2->isIntegral() && vt2->pointer == 0U);
|
|
|
|
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;
|
|
|
|
|
|
|
|
bool voidpointer1 = (vt1 && vt1->type == ValueType::Type::VOID && vt1->pointer == 1U);
|
|
|
|
bool voidpointer2 = (vt2 && vt2->type == ValueType::Type::VOID && vt2->pointer == 1U);
|
|
|
|
|
|
|
|
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.";
|
2016-07-08 09:06:55 +02:00
|
|
|
reportError(tok, Severity::portability, "sizeofVoid", message + "\n" + verbose, CWE682, false);
|
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.";
|
2016-07-08 09:06:55 +02:00
|
|
|
reportError(tok, Severity::portability, "sizeofDereferencedVoidPointer", message + "\n" + verbose, CWE682, false);
|
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
|
|
|
{
|
2013-07-10 06:40:09 +02:00
|
|
|
const std::string message = "'" + varname + "' 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.";
|
2016-07-08 09:06:55 +02:00
|
|
|
reportError(tok, Severity::portability, "arithOperationsOnVoidPointer", message + "\n" + verbose, CWE467, false);
|
2013-07-05 21:07:07 +02:00
|
|
|
}
|