Moved checks related to sizeof usage from checkother into new file

This commit is contained in:
PKEuS 2013-04-10 09:49:38 -07:00
parent 42fcb04d0c
commit 994c429b7d
10 changed files with 907 additions and 743 deletions

View File

@ -499,35 +499,6 @@ void CheckOther::invalidPointerCastError(const Token* tok, const std::string& fr
reportError(tok, Severity::warning, "invalidPointerCast", "Casting between " + from + "* and " + to + "* which have an incompatible binary data representation.");
}
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
void CheckOther::checkSizeofForNumericParameter()
{
if (!_settings->isEnabled("warning"))
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 CheckOther::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')'"
" and 'sizeof(char)' can return different results.");
}
//---------------------------------------------------------------------------
// This check detects errors on POSIX systems, when a pipe command called
// with a wrong dimensioned file descriptor array. The pipe command requires
@ -604,140 +575,6 @@ void CheckOther::checkSleepTimeError(const Token *tok, const std::string &strDim
"The argument of usleep must be less than 1000000, but " + strDim + " is provided.");
}
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
void CheckOther::checkSizeofForArrayParameter()
{
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();
}
if (varTok->varId() > 0) {
const Variable *var = varTok->variable();
if (var && var->isArray() && var->isArgument()) {
sizeofForArrayParameterError(tok);
}
}
}
}
}
}
void CheckOther::sizeofForArrayParameterError(const Token *tok)
{
reportError(tok, Severity::error,
"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 "
"size of the array in bytes)."
);
}
void CheckOther::checkSizeofForPointerSize()
{
if (!_settings->isEnabled("warning"))
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()) {
const Token *tokVar;
const Token *variable;
const Token *variable2 = 0;
// 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
if (Token::Match(tok, "[*;{}] %var% = malloc|alloca (")) {
variable = tok->next();
tokVar = tok->tokAt(5);
} else if (Token::Match(tok, "[*;{}] %var% = calloc (")) {
variable = tok->next();
tokVar = tok->tokAt(5)->nextArgument();
} else if (Token::simpleMatch(tok, "memset (")) {
variable = tok->tokAt(2);
tokVar = variable->tokAt(2)->nextArgument();
// The following tests can be inconclusive in case the variable in sizeof
// is constant string by intention
} else if (!_settings->inconclusive) {
continue;
} else if (Token::Match(tok, "memcpy|memcmp|memmove|strncpy|strncmp|strncat (")) {
variable = tok->tokAt(2);
variable2 = variable->nextArgument();
tokVar = variable2->nextArgument();
} else {
continue;
}
// 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()) {
variable = 0;
}
if (variable2) {
var = variable2->variable();
if (!var || !var->isPointer() || var->isArray()) {
variable2 = 0;
}
}
// If there are no pointer variable at this point, there is
// no need to continue
if (variable == 0 && variable2 == 0) {
continue;
}
// Jump to the next sizeof token in the function and in the parameter
// This is to allow generic operations with sizeof
for (; tokVar && tokVar->str() != ")" && tokVar->str() != "," && tokVar->str() != "sizeof"; tokVar = tokVar->next()) {}
// Now check for the sizeof usage. Once here, everything using sizeof(varid) or sizeof(&varid)
// looks suspicious
// Do it for first variable
if (variable && (Token::Match(tokVar, "sizeof ( &| %varid% )", variable->varId()) ||
Token::Match(tokVar, "sizeof &| %varid%", variable->varId()))) {
sizeofForPointerError(variable, variable->str());
} else if (variable2 && (Token::Match(tokVar, "sizeof ( &| %varid% )", variable2->varId()) ||
Token::Match(tokVar, "sizeof &| %varid%", variable2->varId()))) {
sizeofForPointerError(variable2, variable2->str());
}
}
}
}
void CheckOther::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 "
"write 'sizeof(*" + varname + ")'.", true);
}
//---------------------------------------------------------------------------
// Detect redundant assignments: x = 0; x = 4;
//---------------------------------------------------------------------------
@ -3265,59 +3102,6 @@ void CheckOther::moduloAlwaysTrueFalseError(const Token* tok, const std::string&
"Comparison of modulo result is predetermined, because it is always less than " + maxVal + ".");
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CheckOther::sizeofsizeof()
{
if (!_settings->isEnabled("warning"))
return;
for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) {
if (Token::Match(tok, "sizeof (| sizeof")) {
sizeofsizeofError(tok);
tok = tok->next();
}
}
}
void CheckOther::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 "
"code is equivalent to 'sizeof(size_t)'");
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CheckOther::sizeofCalculation()
{
if (!_settings->isEnabled("warning"))
return;
for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) {
if (Token::simpleMatch(tok, "sizeof (")) {
const Token* const end = tok->linkAt(1);
for (const Token *tok2 = tok->tokAt(2); tok2 != end; tok2 = tok2->next()) {
if (tok2->isConstOp() && (!tok2->isExpandedMacro() || _settings->inconclusive) && !Token::Match(tok2, ">|<|&") && (Token::Match(tok2->previous(), "%var%") || tok2->str() != "*")) {
if (!(Token::Match(tok2->previous(), "%type%") || Token::Match(tok2->next(), "%type%"))) {
sizeofCalculationError(tok2, tok2->isExpandedMacro());
break;
}
} else if (tok2->type() == Token::eIncDecOp)
sizeofCalculationError(tok2, tok2->isExpandedMacro());
}
}
}
}
void CheckOther::sizeofCalculationError(const Token *tok, bool inconclusive)
{
reportError(tok, Severity::warning,
"sizeofCalculation", "Found calculation inside sizeof().", inconclusive);
}
//-----------------------------------------------------------------------------
// Check for code like:
// seteuid(geteuid()) or setuid(getuid()), which first gets and then sets the
@ -3347,42 +3131,6 @@ void CheckOther::redundantGetAndSetUserIdError(const Token *tok)
"by get(e)uid() and then set with set(e)uid().", false);
}
//-----------------------------------------------------------------------------
// Check for code like sizeof()*sizeof() or sizeof(ptr)/value
//-----------------------------------------------------------------------------
void CheckOther::suspiciousSizeofCalculation()
{
if (!_settings->isEnabled("warning") || !_settings->inconclusive)
return;
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);
} else if (Token::simpleMatch(end, ") * sizeof"))
multiplySizeofError(tok);
}
}
}
void CheckOther::multiplySizeofError(const Token *tok)
{
reportError(tok, Severity::warning,
"multiplySizeof", "Multiplying sizeof() with sizeof() indicates a logic error.", true);
}
void CheckOther::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, "
"not the size of the memory area it points to.", true);
}
//---------------------------------------------------------------------------
// Check testing sign of unsigned variables and pointers.

View File

@ -57,16 +57,10 @@ public:
checkOther.checkUnsignedDivision();
checkOther.checkCharVariable();
checkOther.strPlusChar();
checkOther.sizeofsizeof();
checkOther.sizeofCalculation();
checkOther.suspiciousSizeofCalculation();
checkOther.checkRedundantAssignment();
checkOther.checkRedundantAssignmentInSwitch();
checkOther.checkSuspiciousCaseInSwitch();
checkOther.checkAssignmentInAssert();
checkOther.checkSizeofForArrayParameter();
checkOther.checkSizeofForPointerSize();
checkOther.checkSizeofForNumericParameter();
checkOther.checkSelfAssignment();
checkOther.checkDuplicateIf();
checkOther.checkDuplicateBranch();
@ -171,18 +165,9 @@ public:
/** @brief %Check for parameters given to cctype function that do make error*/
void checkCCTypeFunctions();
/** @brief %Check for 'sizeof sizeof ..' */
void sizeofsizeof();
/** @brief %Check for calculations inside sizeof */
void sizeofCalculation();
/** @brief % Check for seteuid(geteuid()) or setuid(getuid())*/
void redundantGetAndSetUserId();
/** @brief %Check for suspicious calculations with sizeof results */
void suspiciousSizeofCalculation();
/** @brief copying to memory or assigning to a variable twice */
void checkRedundantAssignment();
@ -213,15 +198,6 @@ public:
/** @brief %Check for filling zero bytes with memset() */
void checkMemsetZeroBytes();
/** @brief %Check for using sizeof with array given as function argument */
void checkSizeofForArrayParameter();
/** @brief %Check for using sizeof of a variable when allocating it */
void checkSizeofForPointerSize();
/** @brief %Check for using sizeof with numeric given as function argument */
void checkSizeofForNumericParameter();
/** @brief %Check for using bad usage of strncmp and substr */
void checkIncorrectStringCompare();
@ -293,11 +269,7 @@ private:
void clarifyCalculationError(const Token *tok, const std::string &op);
void clarifyConditionError(const Token *tok, bool assign, bool boolop);
void clarifyStatementError(const Token* tok);
void sizeofsizeofError(const Token *tok);
void sizeofCalculationError(const Token *tok, bool inconclusive);
void redundantGetAndSetUserIdError(const Token *tok);
void multiplySizeofError(const Token *tok);
void divideSizeofError(const Token *tok);
void cstyleCastError(const Token *tok);
void invalidPointerCastError(const Token* tok, const std::string& from, const std::string& to, bool inconclusive);
void dangerousUsageStrtolError(const Token *tok, const std::string& funcname);
@ -326,9 +298,6 @@ private:
void redundantConditionError(const Token *tok, const std::string &text);
void misusedScopeObjectError(const Token *tok, const std::string &varname);
void memsetZeroBytesError(const Token *tok, const std::string &varname);
void sizeofForArrayParameterError(const Token *tok);
void sizeofForPointerError(const Token *tok, const std::string &varname);
void sizeofForNumericParameterError(const Token *tok);
void incorrectStringCompareError(const Token *tok, const std::string& func, const std::string &string);
void incorrectStringBooleanError(const Token *tok, const std::string& string);
void duplicateIfError(const Token *tok1, const Token *tok2);
@ -360,9 +329,6 @@ private:
c.zerodivError(0);
c.mathfunctionCallError(0);
c.misusedScopeObjectError(NULL, "varname");
c.sizeofForArrayParameterError(0);
c.sizeofForPointerError(0, "varname");
c.sizeofForNumericParameterError(0);
c.doubleFreeError(0, "varname");
c.invalidPointerCastError(0, "float", "double", false);
c.negativeBitwiseShiftError(0);
@ -385,10 +351,6 @@ private:
c.charBitOpError(0);
c.variableScopeError(0, "varname");
c.strPlusCharError(0);
c.sizeofsizeofError(0);
c.sizeofCalculationError(0, false);
c.multiplySizeofError(0);
c.divideSizeofError(0);
c.redundantAssignmentInSwitchError(0, 0, "var");
c.redundantCopyInSwitchError(0, 0, "var");
c.switchCaseFallThrough(0);
@ -435,9 +397,6 @@ private:
"* division with zero\n"
"* scoped object destroyed immediately after construction\n"
"* assignment in an assert statement\n"
"* sizeof for array given as function argument\n"
"* sizeof for numeric given as function argument\n"
"* using sizeof(pointer) instead of the size of pointed data\n"
"* incorrect length arguments for 'substr' and 'strncmp'\n"
"* free() or delete of an invalid memory location\n"
"* double free() or double closedir()\n"
@ -467,9 +426,6 @@ private:
"* redundant pre/post operation in a switch statement\n"
"* redundant bitwise operation in a switch statement\n"
"* redundant strcpy in a switch statement\n"
"* look for 'sizeof sizeof ..'\n"
"* look for calculations inside sizeof()\n"
"* look for suspicious calculations with sizeof()\n"
"* assignment of a variable to itself\n"
"* Suspicious case labels in switch()\n"
"* Suspicious equality comparisons\n"

280
lib/checksizeof.cpp Normal file
View File

@ -0,0 +1,280 @@
/*
* Cppcheck - A tool for static C/C++ code analysis
* Copyright (C) 2007-2013 Daniel Marjamäki and 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 <http://www.gnu.org/licenses/>.
*/
//---------------------------------------------------------------------------
#include "checksizeof.h"
#include "symboldatabase.h"
//---------------------------------------------------------------------------
// Register this check class (by creating a static instance of it)
namespace {
CheckSizeof instance;
}
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
void CheckSizeof::checkSizeofForNumericParameter()
{
if (!_settings->isEnabled("warning"))
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')'"
" and 'sizeof(char)' can return different results.");
}
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
void CheckSizeof::checkSizeofForArrayParameter()
{
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();
}
if (varTok->varId() > 0) {
const Variable *var = varTok->variable();
if (var && var->isArray() && var->isArgument()) {
sizeofForArrayParameterError(tok);
}
}
}
}
}
}
void CheckSizeof::sizeofForArrayParameterError(const Token *tok)
{
reportError(tok, Severity::error,
"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 "
"size of the array in bytes)."
);
}
void CheckSizeof::checkSizeofForPointerSize()
{
if (!_settings->isEnabled("warning"))
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()) {
const Token *tokVar;
const Token *variable;
const Token *variable2 = 0;
// 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
if (Token::Match(tok, "[*;{}] %var% = malloc|alloca (")) {
variable = tok->next();
tokVar = tok->tokAt(5);
} else if (Token::Match(tok, "[*;{}] %var% = calloc (")) {
variable = tok->next();
tokVar = tok->tokAt(5)->nextArgument();
} else if (Token::simpleMatch(tok, "memset (")) {
variable = tok->tokAt(2);
tokVar = variable->tokAt(2)->nextArgument();
// The following tests can be inconclusive in case the variable in sizeof
// is constant string by intention
} else if (!_settings->inconclusive) {
continue;
} else if (Token::Match(tok, "memcpy|memcmp|memmove|strncpy|strncmp|strncat (")) {
variable = tok->tokAt(2);
variable2 = variable->nextArgument();
tokVar = variable2->nextArgument();
} else {
continue;
}
// 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()) {
variable = 0;
}
if (variable2) {
var = variable2->variable();
if (!var || !var->isPointer() || var->isArray()) {
variable2 = 0;
}
}
// If there are no pointer variable at this point, there is
// no need to continue
if (variable == 0 && variable2 == 0) {
continue;
}
// Jump to the next sizeof token in the function and in the parameter
// This is to allow generic operations with sizeof
for (; tokVar && tokVar->str() != ")" && tokVar->str() != "," && tokVar->str() != "sizeof"; tokVar = tokVar->next()) {}
// Now check for the sizeof usage. Once here, everything using sizeof(varid) or sizeof(&varid)
// looks suspicious
// Do it for first variable
if (variable && (Token::Match(tokVar, "sizeof ( &| %varid% )", variable->varId()) ||
Token::Match(tokVar, "sizeof &| %varid%", variable->varId()))) {
sizeofForPointerError(variable, variable->str());
} else if (variable2 && (Token::Match(tokVar, "sizeof ( &| %varid% )", variable2->varId()) ||
Token::Match(tokVar, "sizeof &| %varid%", variable2->varId()))) {
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 "
"write 'sizeof(*" + varname + ")'.", true);
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CheckSizeof::sizeofsizeof()
{
if (!_settings->isEnabled("warning"))
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 "
"code is equivalent to 'sizeof(size_t)'");
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CheckSizeof::sizeofCalculation()
{
if (!_settings->isEnabled("warning"))
return;
for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) {
if (Token::simpleMatch(tok, "sizeof (")) {
const Token* const end = tok->linkAt(1);
for (const Token *tok2 = tok->tokAt(2); tok2 != end; tok2 = tok2->next()) {
if (tok2->isConstOp() && (!tok2->isExpandedMacro() || _settings->inconclusive) && !Token::Match(tok2, ">|<|&") && (Token::Match(tok2->previous(), "%var%") || tok2->str() != "*")) {
if (!(Token::Match(tok2->previous(), "%type%") || Token::Match(tok2->next(), "%type%"))) {
sizeofCalculationError(tok2, tok2->isExpandedMacro());
break;
}
} else if (tok2->type() == Token::eIncDecOp)
sizeofCalculationError(tok2, tok2->isExpandedMacro());
}
}
}
}
void CheckSizeof::sizeofCalculationError(const Token *tok, bool inconclusive)
{
reportError(tok, Severity::warning,
"sizeofCalculation", "Found calculation inside sizeof().", inconclusive);
}
//-----------------------------------------------------------------------------
// Check for code like sizeof()*sizeof() or sizeof(ptr)/value
//-----------------------------------------------------------------------------
void CheckSizeof::suspiciousSizeofCalculation()
{
if (!_settings->isEnabled("warning") || !_settings->inconclusive)
return;
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);
} else if (Token::simpleMatch(end, ") * sizeof"))
multiplySizeofError(tok);
}
}
}
void CheckSizeof::multiplySizeofError(const Token *tok)
{
reportError(tok, Severity::warning,
"multiplySizeof", "Multiplying sizeof() with sizeof() indicates a logic error.", true);
}
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, "
"not the size of the memory area it points to.", true);
}

124
lib/checksizeof.h Normal file
View File

@ -0,0 +1,124 @@
/*
* Cppcheck - A tool for static C/C++ code analysis
* Copyright (C) 2007-2013 Daniel Marjamäki and 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 <http://www.gnu.org/licenses/>.
*/
//---------------------------------------------------------------------------
#ifndef CheckSizeofH
#define CheckSizeofH
//---------------------------------------------------------------------------
#include "config.h"
#include "check.h"
#include "settings.h"
class Token;
class Function;
class Variable;
/// @addtogroup Checks
/// @{
/** @brief checks on usage of sizeof() operator */
class CPPCHECKLIB CheckSizeof : public Check {
public:
/** @brief This constructor is used when registering the CheckClass */
CheckSizeof() : Check(myName())
{ }
/** @brief This constructor is used when running checks. */
CheckSizeof(const Tokenizer* tokenizer, const Settings* settings, ErrorLogger* errorLogger)
: Check(myName(), tokenizer, settings, errorLogger)
{ }
/** @brief Run checks against the normal token list */
void runChecks(const Tokenizer* tokenizer, const Settings* settings, ErrorLogger* errorLogger) {
CheckSizeof checkSizeof(tokenizer, settings, errorLogger);
// Checks
checkSizeof.sizeofsizeof();
checkSizeof.sizeofCalculation();
checkSizeof.suspiciousSizeofCalculation();
checkSizeof.checkSizeofForArrayParameter();
checkSizeof.checkSizeofForPointerSize();
checkSizeof.checkSizeofForNumericParameter();
}
/** @brief Run checks against the simplified token list */
void runSimplifiedChecks(const Tokenizer*, const Settings*, ErrorLogger*) {
}
/** @brief %Check for 'sizeof sizeof ..' */
void sizeofsizeof();
/** @brief %Check for calculations inside sizeof */
void sizeofCalculation();
/** @brief %Check for suspicious calculations with sizeof results */
void suspiciousSizeofCalculation();
/** @brief %Check for using sizeof with array given as function argument */
void checkSizeofForArrayParameter();
/** @brief %Check for using sizeof of a variable when allocating it */
void checkSizeofForPointerSize();
/** @brief %Check for using sizeof with numeric given as function argument */
void checkSizeofForNumericParameter();
private:
// Error messages..
void sizeofsizeofError(const Token* tok);
void sizeofCalculationError(const Token* tok, bool inconclusive);
void multiplySizeofError(const Token* tok);
void divideSizeofError(const Token* tok);
void sizeofForArrayParameterError(const Token* tok);
void sizeofForPointerError(const Token* tok, const std::string &varname);
void sizeofForNumericParameterError(const Token* tok);
void getErrorMessages(ErrorLogger* errorLogger, const Settings* settings) const {
CheckSizeof c(0, settings, errorLogger);
c.sizeofForArrayParameterError(0);
c.sizeofForPointerError(0, "varname");
c.sizeofForNumericParameterError(0);
c.sizeofsizeofError(0);
c.sizeofCalculationError(0, false);
c.multiplySizeofError(0);
c.divideSizeofError(0);
}
static std::string myName() {
return "Sizeof";
}
std::string classInfo() const {
return "sizeof() usage checks\n"
"* sizeof for array given as function argument\n"
"* sizeof for numeric given as function argument\n"
"* using sizeof(pointer) instead of the size of pointed data\n"
"* look for 'sizeof sizeof ..'\n"
"* look for calculations inside sizeof()\n"
"* look for suspicious calculations with sizeof()\n";
}
};
/// @}
//---------------------------------------------------------------------------
#endif

View File

@ -52,6 +52,7 @@
<ClCompile Include="checkobsoletefunctions.cpp" />
<ClCompile Include="checkother.cpp" />
<ClCompile Include="checkpostfixoperator.cpp" />
<ClCompile Include="checksizeof.cpp" />
<ClCompile Include="checkstl.cpp" />
<ClCompile Include="checkuninitvar.cpp" />
<ClCompile Include="checkunusedfunctions.cpp" />
@ -90,6 +91,7 @@
<ClInclude Include="checkobsoletefunctions.h" />
<ClInclude Include="checkother.h" />
<ClInclude Include="checkpostfixoperator.h" />
<ClInclude Include="checksizeof.h" />
<ClInclude Include="checkstl.h" />
<ClInclude Include="checkuninitvar.h" />
<ClInclude Include="checkunusedfunctions.h" />

View File

@ -116,6 +116,9 @@
<ClCompile Include="checkbool.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="checksizeof.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="checkbufferoverrun.h">
@ -232,6 +235,9 @@
<ClInclude Include="checkbool.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="checksizeof.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="version.rc" />

View File

@ -81,9 +81,6 @@ private:
TEST_CASE(mathfunctionCall1);
TEST_CASE(cctypefunctionCall);
TEST_CASE(sizeofsizeof);
TEST_CASE(sizeofCalculation);
TEST_CASE(switchRedundantAssignmentTest);
TEST_CASE(switchRedundantOperationTest);
TEST_CASE(switchRedundantBitwiseOperationTest);
@ -123,13 +120,8 @@ private:
TEST_CASE(memsetZeroBytes);
TEST_CASE(sizeofForArrayParameter);
TEST_CASE(sizeofForNumericParameter);
TEST_CASE(redundantGetAndSetUserId);
TEST_CASE(suspiciousSizeofCalculation);
TEST_CASE(clarifyCalculation);
TEST_CASE(clarifyStatement);
@ -156,7 +148,6 @@ private:
TEST_CASE(alwaysTrueFalseStringCompare);
TEST_CASE(suspiciousStringCompare);
TEST_CASE(checkPointerSizeof);
TEST_CASE(checkSignOfUnsignedVariable);
TEST_CASE(checkSignOfPointer);
@ -1533,49 +1524,6 @@ private:
ASSERT_EQUALS("[test.cpp:2]: (error) Passing value -10000 to isgraph() causes undefined behavior which may lead to a crash.\n", errout.str());
}
void sizeofsizeof() {
check("void foo()\n"
"{\n"
" int i = sizeof sizeof char;\n"
"}");
ASSERT_EQUALS("[test.cpp:3]: (warning) Calling 'sizeof' on 'sizeof'.\n", errout.str());
check("void foo()\n"
"{\n"
" int i = sizeof (sizeof long);\n"
"}");
ASSERT_EQUALS("[test.cpp:3]: (warning) Calling 'sizeof' on 'sizeof'.\n", errout.str());
check("void foo(long *p)\n"
"{\n"
" int i = sizeof (sizeof (p));\n"
"}");
ASSERT_EQUALS("[test.cpp:3]: (warning) Calling 'sizeof' on 'sizeof'.\n", errout.str());
}
void sizeofCalculation() {
check("int a, b; int a,sizeof(a+b)");
ASSERT_EQUALS("[test.cpp:1]: (warning) Found calculation inside sizeof().\n", errout.str());
check("int a, b; sizeof(a*b)");
ASSERT_EQUALS("[test.cpp:1]: (warning) Found calculation inside sizeof().\n", errout.str());
check("int a, b; sizeof(-a)");
ASSERT_EQUALS("[test.cpp:1]: (warning) Found calculation inside sizeof().\n", errout.str());
check("int a, b; sizeof(*a)");
ASSERT_EQUALS("", errout.str());
check("sizeof(void * const)");
ASSERT_EQUALS("", errout.str());
check("sizeof(foo++)");
ASSERT_EQUALS("[test.cpp:1]: (warning) Found calculation inside sizeof().\n", errout.str());
check("sizeof(--foo)");
ASSERT_EQUALS("[test.cpp:1]: (warning) Found calculation inside sizeof().\n", errout.str());
}
void switchRedundantAssignmentTest() {
check("void foo()\n"
@ -3891,168 +3839,6 @@ private:
ASSERT_EQUALS("", errout.str());
}
void sizeofForArrayParameter() {
check("void f() {\n"
" int a[10];\n"
" std::cout << sizeof(a) / sizeof(int) << std::endl;\n"
"}\n"
);
ASSERT_EQUALS("", errout.str());
check("void f() {\n"
" unsigned int a = 2;\n"
" unsigned int b = 2;\n"
" int c[(a+b)];\n"
" std::cout << sizeof(c) / sizeof(int) << std::endl;\n"
"}\n"
);
ASSERT_EQUALS("", errout.str());
check("void f() {\n"
" unsigned int a = { 2 };\n"
" unsigned int b[] = { 0 };\n"
" int c[a[b[0]]];\n"
" std::cout << sizeof(c) / sizeof(int) << std::endl;\n"
"}\n"
);
ASSERT_EQUALS("", errout.str());
check("void f() {\n"
" unsigned int a[] = { 1 };\n"
" unsigned int b = 2;\n"
" int c[(a[0]+b)];\n"
" std::cout << sizeof(c) / sizeof(int) << std::endl;\n"
"}\n"
);
ASSERT_EQUALS("", errout.str());
check("void f() {\n"
" int a[] = { 1, 2, 3 };\n"
" std::cout << sizeof(a) / sizeof(int) << std::endl;\n"
"}\n"
);
ASSERT_EQUALS("", errout.str());
check("void f() {\n"
" int a[3] = { 1, 2, 3 };\n"
" std::cout << sizeof(a) / sizeof(int) << std::endl;\n"
"}\n"
);
ASSERT_EQUALS("", errout.str());
check("void f( int a[]) {\n"
" std::cout << sizeof(a) / sizeof(int) << std::endl;\n"
"}\n"
);
ASSERT_EQUALS("[test.cpp:2]: (error) Using 'sizeof' on array given as "
"function argument returns size of a pointer.\n", errout.str());
check("void f( int a[]) {\n"
" std::cout << sizeof a / sizeof(int) << std::endl;\n"
"}\n"
);
ASSERT_EQUALS("[test.cpp:2]: (error) Using 'sizeof' on array given as "
"function argument returns size of a pointer.\n", errout.str());
check("void f( int a[3] ) {\n"
" std::cout << sizeof(a) / sizeof(int) << std::endl;\n"
"}\n"
);
ASSERT_EQUALS("[test.cpp:2]: (error) Using 'sizeof' on array given as "
"function argument returns size of a pointer.\n", errout.str());
check("void f(int *p) {\n"
" p[0] = 0;\n"
" int unused = sizeof(p);\n"
"}\n"
);
ASSERT_EQUALS("", errout.str());
check("void f() {\n"
" char p[] = \"test\";\n"
" int unused = sizeof(p);\n"
"}\n"
);
ASSERT_EQUALS("", errout.str());
// ticket #2495
check("void f() {\n"
" static float col[][3]={\n"
" {1,0,0},\n"
" {0,0,1},\n"
" {0,1,0},\n"
" {1,0,1},\n"
" {1,0,1},\n"
" {1,0,1},\n"
" };\n"
" const int COL_MAX=sizeof(col)/sizeof(col[0]);\n"
"}\n"
);
ASSERT_EQUALS("", errout.str());
// ticket #155
check("void f() {\n"
" char buff1[1024*64],buff2[sizeof(buff1)*2];\n"
"}\n"
);
ASSERT_EQUALS("", errout.str());
// ticket #2510
check("void f( int a[], int b) {\n"
" std::cout << sizeof(a) / sizeof(int) << std::endl;\n"
"}\n"
);
ASSERT_EQUALS("[test.cpp:2]: (error) Using 'sizeof' on array given as "
"function argument returns size of a pointer.\n", errout.str());
// ticket #2510
check("void f( int a[3] , int b[2] ) {\n"
" std::cout << sizeof(a) / sizeof(int) << std::endl;\n"
"}\n"
);
ASSERT_EQUALS("[test.cpp:2]: (error) Using 'sizeof' on array given as "
"function argument returns size of a pointer.\n", errout.str());
// ticket #2510
check("void f() {\n"
" char buff1[1024*64],buff2[sizeof(buff1)*(2+1)];\n"
"}\n"
);
ASSERT_EQUALS("", errout.str());
}
void sizeofForNumericParameter() {
check("void f() {\n"
" std::cout << sizeof(10) << std::endl;\n"
"}\n",
NULL, true
);
ASSERT_EQUALS("[test.cpp:2]: (warning) Suspicious usage of 'sizeof' with a numeric constant as parameter.\n", errout.str());
check("void f() {\n"
" std::cout << sizeof(-10) << std::endl;\n"
"}\n",
NULL, true
);
ASSERT_EQUALS("[test.cpp:2]: (warning) Suspicious usage of 'sizeof' with a numeric constant as parameter.\n", errout.str());
check("void f() {\n"
" std::cout << sizeof 10 << std::endl;\n"
"}\n",
NULL, true
);
ASSERT_EQUALS("[test.cpp:2]: (warning) Suspicious usage of 'sizeof' with a numeric constant as parameter.\n", errout.str());
check("void f() {\n"
" std::cout << sizeof -10 << std::endl;\n"
"}\n",
NULL, true
);
ASSERT_EQUALS("[test.cpp:2]: (warning) Suspicious usage of 'sizeof' with a numeric constant as parameter.\n", errout.str());
}
void redundantGetAndSetUserId() {
check("seteuid(geteuid());\n", NULL, false , false, true);
ASSERT_EQUALS("[test.cpp:1]: (warning) Redundant get and set of user id.\n", errout.str());
@ -4073,37 +3859,6 @@ private:
ASSERT_EQUALS("", errout.str());
}
void suspiciousSizeofCalculation() {
check("int* p;\n"
"return sizeof(p)/5;");
ASSERT_EQUALS("[test.cpp:2]: (warning, inconclusive) Division of result of sizeof() on pointer type.\n", errout.str());
check("unknown p;\n"
"return sizeof(p)/5;");
ASSERT_EQUALS("", errout.str());
check("return sizeof(unknown)/5;");
ASSERT_EQUALS("", errout.str());
check("int p;\n"
"return sizeof(p)/5;");
ASSERT_EQUALS("", errout.str());
check("int* p[5];\n"
"return sizeof(p)/5;");
ASSERT_EQUALS("", errout.str());
check("return sizeof(foo)*sizeof(bar);");
ASSERT_EQUALS("[test.cpp:1]: (warning, inconclusive) Multiplying sizeof() with sizeof() indicates a logic error.\n", errout.str());
check("return (foo)*sizeof(bar);");
ASSERT_EQUALS("", errout.str());
check("return sizeof(foo)*bar;");
ASSERT_EQUALS("", errout.str());
}
void clarifyCalculation() {
check("int f(char c) {\n"
" return 10 * (c == 0) ? 1 : 2;\n"
@ -4933,208 +4688,6 @@ private:
errout.str());
}
void checkPointerSizeof() {
check("void f() {\n"
" char *x = malloc(10);\n"
" free(x);\n"
"}");
ASSERT_EQUALS("", errout.str());
check("void f() {\n"
" int *x = malloc(sizeof(*x));\n"
" free(x);\n"
"}");
ASSERT_EQUALS("", errout.str());
check("void f() {\n"
" int *x = malloc(sizeof(int));\n"
" free(x);\n"
"}");
ASSERT_EQUALS("", errout.str());
check("void f() {\n"
" int *x = malloc(sizeof(x));\n"
" free(x);\n"
"}");
ASSERT_EQUALS("[test.cpp:2]: (warning, inconclusive) Size of pointer 'x' used instead of size of its data.\n", errout.str());
check("void f() {\n"
" int *x = malloc(sizeof(&x));\n"
" free(x);\n"
"}");
ASSERT_EQUALS("[test.cpp:2]: (warning, inconclusive) Size of pointer 'x' used instead of size of its data.\n", errout.str());
check("void f() {\n"
" int *x = malloc(100 * sizeof(x));\n"
" free(x);\n"
"}");
ASSERT_EQUALS("[test.cpp:2]: (warning, inconclusive) Size of pointer 'x' used instead of size of its data.\n", errout.str());
check("void f() {\n"
" int *x = malloc(sizeof(x) * 100);\n"
" free(x);\n"
"}");
ASSERT_EQUALS("[test.cpp:2]: (warning, inconclusive) Size of pointer 'x' used instead of size of its data.\n", errout.str());
check("void f() {\n"
" int *x = malloc(sizeof *x);\n"
" free(x);\n"
"}");
ASSERT_EQUALS("", errout.str());
check("void f() {\n"
" int *x = malloc(sizeof x);\n"
" free(x);\n"
"}");
ASSERT_EQUALS("[test.cpp:2]: (warning, inconclusive) Size of pointer 'x' used instead of size of its data.\n", errout.str());
check("void f() {\n"
" int *x = malloc(100 * sizeof x);\n"
" free(x);\n"
"}");
ASSERT_EQUALS("[test.cpp:2]: (warning, inconclusive) Size of pointer 'x' used instead of size of its data.\n", errout.str());
check("void f() {\n"
" int *x = calloc(1, sizeof(*x));\n"
" free(x);\n"
"}");
ASSERT_EQUALS("", errout.str());
check("void f() {\n"
" int *x = calloc(1, sizeof *x);\n"
" free(x);\n"
"}");
ASSERT_EQUALS("", errout.str());
check("void f() {\n"
" int *x = calloc(1, sizeof(x));\n"
" free(x);\n"
"}");
ASSERT_EQUALS("[test.cpp:2]: (warning, inconclusive) Size of pointer 'x' used instead of size of its data.\n", errout.str());
check("void f() {\n"
" int *x = calloc(1, sizeof x);\n"
" free(x);\n"
"}");
ASSERT_EQUALS("[test.cpp:2]: (warning, inconclusive) Size of pointer 'x' used instead of size of its data.\n", errout.str());
check("void f() {\n"
" int *x = calloc(1, sizeof(int));\n"
" free(x);\n"
"}");
ASSERT_EQUALS("", errout.str());
check("void f() {\n"
" char x[10];\n"
" memset(x, 0, sizeof(x));\n"
"}");
ASSERT_EQUALS("", errout.str());
check("void f() {\n"
" char* x[10];\n"
" memset(x, 0, sizeof(x));\n"
"}");
ASSERT_EQUALS("", errout.str());
check("void f() {\n"
" char x[10];\n"
" memset(x, 0, sizeof x);\n"
"}");
ASSERT_EQUALS("", errout.str());
check("void f() {\n"
" int *x = malloc(sizeof(int));\n"
" memset(x, 0, sizeof(int));\n"
" free(x);\n"
"}");
ASSERT_EQUALS("", errout.str());
check("void f() {\n"
" int *x = malloc(sizeof(int));\n"
" memset(x, 0, sizeof(*x));\n"
" free(x);\n"
"}");
ASSERT_EQUALS("", errout.str());
check("void f() {\n"
" int *x = malloc(sizeof(int));\n"
" memset(x, 0, sizeof *x);\n"
" free(x);\n"
"}");
ASSERT_EQUALS("", errout.str());
check("void f() {\n"
" int *x = malloc(sizeof(int));\n"
" memset(x, 0, sizeof x);\n"
" free(x);\n"
"}");
ASSERT_EQUALS("[test.cpp:3]: (warning, inconclusive) Size of pointer 'x' used instead of size of its data.\n", errout.str());
check("void f() {\n"
" int *x = malloc(sizeof(int));\n"
" memset(x, 0, sizeof(x));\n"
" free(x);\n"
"}");
ASSERT_EQUALS("[test.cpp:3]: (warning, inconclusive) Size of pointer 'x' used instead of size of its data.\n", errout.str());
check("void f() {\n"
" int *x = malloc(sizeof(int) * 10);\n"
" memset(x, 0, sizeof(x) * 10);\n"
" free(x);\n"
"}");
ASSERT_EQUALS("[test.cpp:3]: (warning, inconclusive) Size of pointer 'x' used instead of size of its data.\n", errout.str());
check("void f() {\n"
" int *x = malloc(sizeof(int) * 10);\n"
" memset(x, 0, sizeof x * 10);\n"
" free(x);\n"
"}");
ASSERT_EQUALS("[test.cpp:3]: (warning, inconclusive) Size of pointer 'x' used instead of size of its data.\n", errout.str());
check("void f() {\n"
" int *x = malloc(sizeof(int) * 10);\n"
" memset(x, 0, sizeof(*x) * 10);\n"
" free(x);\n"
"}");
ASSERT_EQUALS("", errout.str());
check("void f() {\n"
" int *x = malloc(sizeof(int) * 10);\n"
" memset(x, 0, sizeof *x * 10);\n"
" free(x);\n"
"}");
ASSERT_EQUALS("", errout.str());
check("void f() {\n"
" int *x = malloc(sizeof(int) * 10);\n"
" memset(x, 0, sizeof(int) * 10);\n"
" free(x);\n"
"}");
ASSERT_EQUALS("", errout.str());
check(
"int fun(const char *buf1)\n"
"{\n"
" const char *buf1_ex = \"foobarbaz\";\n"
" return strncmp(buf1, buf1_ex, sizeof(buf1_ex)) == 0;\n"
"}");
ASSERT_EQUALS("[test.cpp:4]: (warning, inconclusive) Size of pointer 'buf1_ex' used instead of size of its data.\n", errout.str());
check(
"int fun(const char *buf1) {\n"
" return strncmp(buf1, foo(buf2), sizeof(buf1)) == 0;\n"
"}");
ASSERT_EQUALS("[test.cpp:2]: (warning, inconclusive) Size of pointer 'buf1' used instead of size of its data.\n", errout.str());
// #ticket 3874
check("void f()\n"
"{\n"
" int * pIntArray[10];\n"
" memset(pIntArray, 0, sizeof(pIntArray));\n"
"}");
ASSERT_EQUALS("", errout.str());
}
void check_signOfUnsignedVariable(const char code[], bool inconclusive=false) {
// Clear the error buffer..
errout.str("");

View File

@ -62,6 +62,7 @@
<ClCompile Include="testpreprocessor.cpp" />
<ClCompile Include="testrunner.cpp" />
<ClCompile Include="testsimplifytokens.cpp" />
<ClCompile Include="testsizeof.cpp" />
<ClCompile Include="teststl.cpp" />
<ClCompile Include="testsuite.cpp" />
<ClCompile Include="testsuppressions.cpp" />

View File

@ -169,6 +169,9 @@
<ClCompile Include="testbool.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="testsizeof.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="options.h">

491
test/testsizeof.cpp Normal file
View File

@ -0,0 +1,491 @@
/*
* Cppcheck - A tool for static C/C++ code analysis
* Copyright (C) 2007-2013 Daniel Marjamäki and 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 <http://www.gnu.org/licenses/>.
*/
#include "tokenize.h"
#include "checksizeof.h"
#include "testsuite.h"
#include <sstream>
extern std::ostringstream errout;
class TestSizeof : public TestFixture {
public:
TestSizeof() : TestFixture("TestSizeof")
{ }
private:
void run() {
TEST_CASE(sizeofsizeof);
TEST_CASE(sizeofCalculation);
TEST_CASE(checkPointerSizeof);
TEST_CASE(sizeofForArrayParameter);
TEST_CASE(sizeofForNumericParameter);
TEST_CASE(suspiciousSizeofCalculation);
}
void check(const char code[]) {
// Clear the error buffer..
errout.str("");
Settings settings;
settings.addEnabled("warning");
settings.inconclusive = true;
// Tokenize..
Tokenizer tokenizer(&settings, this);
std::istringstream istr(code);
tokenizer.tokenize(istr, "test.cpp");
// Check...
CheckSizeof checkSizeof(&tokenizer, &settings, this);
checkSizeof.runChecks(&tokenizer, &settings, this);
}
void sizeofsizeof() {
check("void foo()\n"
"{\n"
" int i = sizeof sizeof char;\n"
"}");
ASSERT_EQUALS("[test.cpp:3]: (warning) Calling 'sizeof' on 'sizeof'.\n", errout.str());
check("void foo()\n"
"{\n"
" int i = sizeof (sizeof long);\n"
"}");
ASSERT_EQUALS("[test.cpp:3]: (warning) Calling 'sizeof' on 'sizeof'.\n", errout.str());
check("void foo(long *p)\n"
"{\n"
" int i = sizeof (sizeof (p));\n"
"}");
ASSERT_EQUALS("[test.cpp:3]: (warning) Calling 'sizeof' on 'sizeof'.\n", errout.str());
}
void sizeofCalculation() {
check("int a, b; int a,sizeof(a+b)");
ASSERT_EQUALS("[test.cpp:1]: (warning) Found calculation inside sizeof().\n", errout.str());
check("int a, b; sizeof(a*b)");
ASSERT_EQUALS("[test.cpp:1]: (warning) Found calculation inside sizeof().\n", errout.str());
check("int a, b; sizeof(-a)");
ASSERT_EQUALS("[test.cpp:1]: (warning) Found calculation inside sizeof().\n", errout.str());
check("int a, b; sizeof(*a)");
ASSERT_EQUALS("", errout.str());
check("sizeof(void * const)");
ASSERT_EQUALS("", errout.str());
check("sizeof(foo++)");
ASSERT_EQUALS("[test.cpp:1]: (warning) Found calculation inside sizeof().\n", errout.str());
check("sizeof(--foo)");
ASSERT_EQUALS("[test.cpp:1]: (warning) Found calculation inside sizeof().\n", errout.str());
}
void sizeofForArrayParameter() {
check("void f() {\n"
" int a[10];\n"
" std::cout << sizeof(a) / sizeof(int) << std::endl;\n"
"}\n"
);
ASSERT_EQUALS("", errout.str());
check("void f() {\n"
" unsigned int a = 2;\n"
" unsigned int b = 2;\n"
" int c[(a+b)];\n"
" std::cout << sizeof(c) / sizeof(int) << std::endl;\n"
"}\n"
);
ASSERT_EQUALS("", errout.str());
check("void f() {\n"
" unsigned int a = { 2 };\n"
" unsigned int b[] = { 0 };\n"
" int c[a[b[0]]];\n"
" std::cout << sizeof(c) / sizeof(int) << std::endl;\n"
"}\n"
);
ASSERT_EQUALS("", errout.str());
check("void f() {\n"
" unsigned int a[] = { 1 };\n"
" unsigned int b = 2;\n"
" int c[(a[0]+b)];\n"
" std::cout << sizeof(c) / sizeof(int) << std::endl;\n"
"}\n"
);
ASSERT_EQUALS("", errout.str());
check("void f() {\n"
" int a[] = { 1, 2, 3 };\n"
" std::cout << sizeof(a) / sizeof(int) << std::endl;\n"
"}\n"
);
ASSERT_EQUALS("", errout.str());
check("void f() {\n"
" int a[3] = { 1, 2, 3 };\n"
" std::cout << sizeof(a) / sizeof(int) << std::endl;\n"
"}\n"
);
ASSERT_EQUALS("", errout.str());
check("void f( int a[]) {\n"
" std::cout << sizeof(a) / sizeof(int) << std::endl;\n"
"}\n"
);
ASSERT_EQUALS("[test.cpp:2]: (error) Using 'sizeof' on array given as "
"function argument returns size of a pointer.\n", errout.str());
check("void f( int a[]) {\n"
" std::cout << sizeof a / sizeof(int) << std::endl;\n"
"}\n"
);
ASSERT_EQUALS("[test.cpp:2]: (error) Using 'sizeof' on array given as "
"function argument returns size of a pointer.\n", errout.str());
check("void f( int a[3] ) {\n"
" std::cout << sizeof(a) / sizeof(int) << std::endl;\n"
"}\n"
);
ASSERT_EQUALS("[test.cpp:2]: (error) Using 'sizeof' on array given as "
"function argument returns size of a pointer.\n", errout.str());
check("void f(int *p) {\n"
" p[0] = 0;\n"
" int unused = sizeof(p);\n"
"}\n"
);
ASSERT_EQUALS("", errout.str());
check("void f() {\n"
" char p[] = \"test\";\n"
" int unused = sizeof(p);\n"
"}\n"
);
ASSERT_EQUALS("", errout.str());
// ticket #2495
check("void f() {\n"
" static float col[][3]={\n"
" {1,0,0},\n"
" {0,0,1},\n"
" {0,1,0},\n"
" {1,0,1},\n"
" {1,0,1},\n"
" {1,0,1},\n"
" };\n"
" const int COL_MAX=sizeof(col)/sizeof(col[0]);\n"
"}\n"
);
ASSERT_EQUALS("", errout.str());
// ticket #155
check("void f() {\n"
" char buff1[1024*64],buff2[sizeof(buff1)*2];\n"
"}\n"
);
ASSERT_EQUALS("", errout.str());
// ticket #2510
check("void f( int a[], int b) {\n"
" std::cout << sizeof(a) / sizeof(int) << std::endl;\n"
"}\n"
);
ASSERT_EQUALS("[test.cpp:2]: (error) Using 'sizeof' on array given as "
"function argument returns size of a pointer.\n", errout.str());
// ticket #2510
check("void f( int a[3] , int b[2] ) {\n"
" std::cout << sizeof(a) / sizeof(int) << std::endl;\n"
"}\n"
);
ASSERT_EQUALS("[test.cpp:2]: (error) Using 'sizeof' on array given as "
"function argument returns size of a pointer.\n", errout.str());
// ticket #2510
check("void f() {\n"
" char buff1[1024*64],buff2[sizeof(buff1)*(2+1)];\n"
"}\n"
);
ASSERT_EQUALS("", errout.str());
}
void sizeofForNumericParameter() {
check("void f() {\n"
" std::cout << sizeof(10) << std::endl;\n"
"}\n");
ASSERT_EQUALS("[test.cpp:2]: (warning) Suspicious usage of 'sizeof' with a numeric constant as parameter.\n", errout.str());
check("void f() {\n"
" std::cout << sizeof(-10) << std::endl;\n"
"}\n");
ASSERT_EQUALS("[test.cpp:2]: (warning) Suspicious usage of 'sizeof' with a numeric constant as parameter.\n", errout.str());
check("void f() {\n"
" std::cout << sizeof 10 << std::endl;\n"
"}\n");
ASSERT_EQUALS("[test.cpp:2]: (warning) Suspicious usage of 'sizeof' with a numeric constant as parameter.\n", errout.str());
check("void f() {\n"
" std::cout << sizeof -10 << std::endl;\n"
"}\n");
ASSERT_EQUALS("[test.cpp:2]: (warning) Suspicious usage of 'sizeof' with a numeric constant as parameter.\n", errout.str());
}
void suspiciousSizeofCalculation() {
check("int* p;\n"
"return sizeof(p)/5;");
ASSERT_EQUALS("[test.cpp:2]: (warning, inconclusive) Division of result of sizeof() on pointer type.\n", errout.str());
check("unknown p;\n"
"return sizeof(p)/5;");
ASSERT_EQUALS("", errout.str());
check("return sizeof(unknown)/5;");
ASSERT_EQUALS("", errout.str());
check("int p;\n"
"return sizeof(p)/5;");
ASSERT_EQUALS("", errout.str());
check("int* p[5];\n"
"return sizeof(p)/5;");
ASSERT_EQUALS("", errout.str());
check("return sizeof(foo)*sizeof(bar);");
ASSERT_EQUALS("[test.cpp:1]: (warning, inconclusive) Multiplying sizeof() with sizeof() indicates a logic error.\n", errout.str());
check("return (foo)*sizeof(bar);");
ASSERT_EQUALS("", errout.str());
check("return sizeof(foo)*bar;");
ASSERT_EQUALS("", errout.str());
}
void checkPointerSizeof() {
check("void f() {\n"
" char *x = malloc(10);\n"
" free(x);\n"
"}");
ASSERT_EQUALS("", errout.str());
check("void f() {\n"
" int *x = malloc(sizeof(*x));\n"
" free(x);\n"
"}");
ASSERT_EQUALS("", errout.str());
check("void f() {\n"
" int *x = malloc(sizeof(int));\n"
" free(x);\n"
"}");
ASSERT_EQUALS("", errout.str());
check("void f() {\n"
" int *x = malloc(sizeof(x));\n"
" free(x);\n"
"}");
ASSERT_EQUALS("[test.cpp:2]: (warning, inconclusive) Size of pointer 'x' used instead of size of its data.\n", errout.str());
check("void f() {\n"
" int *x = malloc(sizeof(&x));\n"
" free(x);\n"
"}");
ASSERT_EQUALS("[test.cpp:2]: (warning, inconclusive) Size of pointer 'x' used instead of size of its data.\n", errout.str());
check("void f() {\n"
" int *x = malloc(100 * sizeof(x));\n"
" free(x);\n"
"}");
ASSERT_EQUALS("[test.cpp:2]: (warning, inconclusive) Size of pointer 'x' used instead of size of its data.\n", errout.str());
check("void f() {\n"
" int *x = malloc(sizeof(x) * 100);\n"
" free(x);\n"
"}");
ASSERT_EQUALS("[test.cpp:2]: (warning, inconclusive) Size of pointer 'x' used instead of size of its data.\n", errout.str());
check("void f() {\n"
" int *x = malloc(sizeof *x);\n"
" free(x);\n"
"}");
ASSERT_EQUALS("", errout.str());
check("void f() {\n"
" int *x = malloc(sizeof x);\n"
" free(x);\n"
"}");
ASSERT_EQUALS("[test.cpp:2]: (warning, inconclusive) Size of pointer 'x' used instead of size of its data.\n", errout.str());
check("void f() {\n"
" int *x = malloc(100 * sizeof x);\n"
" free(x);\n"
"}");
ASSERT_EQUALS("[test.cpp:2]: (warning, inconclusive) Size of pointer 'x' used instead of size of its data.\n", errout.str());
check("void f() {\n"
" int *x = calloc(1, sizeof(*x));\n"
" free(x);\n"
"}");
ASSERT_EQUALS("", errout.str());
check("void f() {\n"
" int *x = calloc(1, sizeof *x);\n"
" free(x);\n"
"}");
ASSERT_EQUALS("", errout.str());
check("void f() {\n"
" int *x = calloc(1, sizeof(x));\n"
" free(x);\n"
"}");
ASSERT_EQUALS("[test.cpp:2]: (warning, inconclusive) Size of pointer 'x' used instead of size of its data.\n", errout.str());
check("void f() {\n"
" int *x = calloc(1, sizeof x);\n"
" free(x);\n"
"}");
ASSERT_EQUALS("[test.cpp:2]: (warning, inconclusive) Size of pointer 'x' used instead of size of its data.\n", errout.str());
check("void f() {\n"
" int *x = calloc(1, sizeof(int));\n"
" free(x);\n"
"}");
ASSERT_EQUALS("", errout.str());
check("void f() {\n"
" char x[10];\n"
" memset(x, 0, sizeof(x));\n"
"}");
ASSERT_EQUALS("", errout.str());
check("void f() {\n"
" char* x[10];\n"
" memset(x, 0, sizeof(x));\n"
"}");
ASSERT_EQUALS("", errout.str());
check("void f() {\n"
" char x[10];\n"
" memset(x, 0, sizeof x);\n"
"}");
ASSERT_EQUALS("", errout.str());
check("void f() {\n"
" int *x = malloc(sizeof(int));\n"
" memset(x, 0, sizeof(int));\n"
" free(x);\n"
"}");
ASSERT_EQUALS("", errout.str());
check("void f() {\n"
" int *x = malloc(sizeof(int));\n"
" memset(x, 0, sizeof(*x));\n"
" free(x);\n"
"}");
ASSERT_EQUALS("", errout.str());
check("void f() {\n"
" int *x = malloc(sizeof(int));\n"
" memset(x, 0, sizeof *x);\n"
" free(x);\n"
"}");
ASSERT_EQUALS("", errout.str());
check("void f() {\n"
" int *x = malloc(sizeof(int));\n"
" memset(x, 0, sizeof x);\n"
" free(x);\n"
"}");
ASSERT_EQUALS("[test.cpp:3]: (warning, inconclusive) Size of pointer 'x' used instead of size of its data.\n", errout.str());
check("void f() {\n"
" int *x = malloc(sizeof(int));\n"
" memset(x, 0, sizeof(x));\n"
" free(x);\n"
"}");
ASSERT_EQUALS("[test.cpp:3]: (warning, inconclusive) Size of pointer 'x' used instead of size of its data.\n", errout.str());
check("void f() {\n"
" int *x = malloc(sizeof(int) * 10);\n"
" memset(x, 0, sizeof(x) * 10);\n"
" free(x);\n"
"}");
ASSERT_EQUALS("[test.cpp:3]: (warning, inconclusive) Size of pointer 'x' used instead of size of its data.\n", errout.str());
check("void f() {\n"
" int *x = malloc(sizeof(int) * 10);\n"
" memset(x, 0, sizeof x * 10);\n"
" free(x);\n"
"}");
ASSERT_EQUALS("[test.cpp:3]: (warning, inconclusive) Size of pointer 'x' used instead of size of its data.\n", errout.str());
check("void f() {\n"
" int *x = malloc(sizeof(int) * 10);\n"
" memset(x, 0, sizeof(*x) * 10);\n"
" free(x);\n"
"}");
ASSERT_EQUALS("", errout.str());
check("void f() {\n"
" int *x = malloc(sizeof(int) * 10);\n"
" memset(x, 0, sizeof *x * 10);\n"
" free(x);\n"
"}");
ASSERT_EQUALS("", errout.str());
check("void f() {\n"
" int *x = malloc(sizeof(int) * 10);\n"
" memset(x, 0, sizeof(int) * 10);\n"
" free(x);\n"
"}");
ASSERT_EQUALS("", errout.str());
check(
"int fun(const char *buf1)\n"
"{\n"
" const char *buf1_ex = \"foobarbaz\";\n"
" return strncmp(buf1, buf1_ex, sizeof(buf1_ex)) == 0;\n"
"}");
ASSERT_EQUALS("[test.cpp:4]: (warning, inconclusive) Size of pointer 'buf1_ex' used instead of size of its data.\n", errout.str());
check(
"int fun(const char *buf1) {\n"
" return strncmp(buf1, foo(buf2), sizeof(buf1)) == 0;\n"
"}");
ASSERT_EQUALS("[test.cpp:2]: (warning, inconclusive) Size of pointer 'buf1' used instead of size of its data.\n", errout.str());
// #ticket 3874
check("void f()\n"
"{\n"
" int * pIntArray[10];\n"
" memset(pIntArray, 0, sizeof(pIntArray));\n"
"}");
ASSERT_EQUALS("", errout.str());
}
};