Fixed #5252 (Improve check: use Library to validate function arguments in invalidFunctionUsage)

This commit is contained in:
Daniel Marjamäki 2013-12-23 10:06:45 +01:00
parent be03d4718a
commit 76b907fe65
10 changed files with 214 additions and 148 deletions

5
cfg/posix.cfg Normal file
View File

@ -0,0 +1,5 @@
<?xml version="1.0"?>
<def>
<function name="usleep"> <noreturn>false</noreturn> <arg nr="1"><not-bool/><valid>0-999999</valid></arg> </function>
</def>

View File

@ -25,10 +25,11 @@
<function name="islower"> <noreturn>false</noreturn> <leak-ignore/> </function> <function name="islower"> <noreturn>false</noreturn> <leak-ignore/> </function>
<function name="isupper"> <noreturn>false</noreturn> <leak-ignore/> </function> <function name="isupper"> <noreturn>false</noreturn> <leak-ignore/> </function>
<function name="memchr"> <noreturn>false</noreturn> <leak-ignore/> </function> <function name="memchr"> <noreturn>false</noreturn> <leak-ignore/> <arg nr="3"><not-bool/><valid>0-</valid></arg> </function>
<function name="memcpy"> <noreturn>false</noreturn> <leak-ignore/> </function> <function name="memcmp"> <noreturn>false</noreturn> <leak-ignore/> <arg nr="3"><not-bool/><valid>0-</valid></arg> </function>
<function name="memmove"> <noreturn>false</noreturn> <leak-ignore/> </function> <function name="memcpy"> <noreturn>false</noreturn> <leak-ignore/> <arg nr="3"><not-bool/><valid>0-</valid></arg> </function>
<function name="memset"> <noreturn>false</noreturn> <leak-ignore/> </function> <function name="memmove"> <noreturn>false</noreturn> <leak-ignore/> <arg nr="3"><not-bool/><valid>0-</valid></arg> </function>
<function name="memset"> <noreturn>false</noreturn> <leak-ignore/> <arg nr="3"><not-bool/><valid>0-</valid></arg> </function>
<function name="strcat"> <noreturn>false</noreturn> <leak-ignore/> </function> <function name="strcat"> <noreturn>false</noreturn> <leak-ignore/> </function>
<function name="strchr"> <noreturn>false</noreturn> <leak-ignore/> </function> <function name="strchr"> <noreturn>false</noreturn> <leak-ignore/> </function>
@ -36,9 +37,18 @@
<function name="strcmp"> <noreturn>false</noreturn> <leak-ignore/> </function> <function name="strcmp"> <noreturn>false</noreturn> <leak-ignore/> </function>
<function name="strdup"> <noreturn>false</noreturn> <leak-ignore/> </function> <function name="strdup"> <noreturn>false</noreturn> <leak-ignore/> </function>
<function name="strlen"> <noreturn>false</noreturn> <leak-ignore/> </function> <function name="strlen"> <noreturn>false</noreturn> <leak-ignore/> </function>
<function name="strncat"> <noreturn>false</noreturn> <leak-ignore/> </function> <function name="strncat"> <noreturn>false</noreturn> <leak-ignore/> <arg nr="3"><not-bool/><valid>0-</valid></arg> </function>
<function name="strncpy"> <noreturn>false</noreturn> <leak-ignore/> </function> <function name="strncpy"> <noreturn>false</noreturn> <leak-ignore/> <arg nr="3"><not-bool/><valid>0-</valid></arg> </function>
<function name="strncmp"> <noreturn>false</noreturn> <leak-ignore/> </function> <function name="strncmp"> <noreturn>false</noreturn> <leak-ignore/> <arg nr="3"><not-bool/><valid>0-</valid></arg> </function>
<function name="strstr"> <noreturn>false</noreturn> <leak-ignore/> </function> <function name="strstr"> <noreturn>false</noreturn> <leak-ignore/> </function>
<function name="strtol"> <leak-ignore/> <arg nr="3"><valid>0,2-36</valid></arg> </function>
<function name="strtoll"> <leak-ignore/> <arg nr="3"><valid>0,2-36</valid></arg> </function>
<function name="strtoul"> <leak-ignore/> <arg nr="3"><valid>0,2-36</valid></arg> </function>
<function name="strtoull"> <leak-ignore/> <arg nr="3"><valid>0,2-36</valid></arg> </function>
<function name="wcstol"> <leak-ignore/> <arg nr="3"><valid>0,2-36</valid></arg> </function>
<function name="wcstoll"> <leak-ignore/> <arg nr="3"><valid>0,2-36</valid></arg> </function>
<function name="wcstoul"> <leak-ignore/> <arg nr="3"><valid>0,2-36</valid></arg> </function>
<function name="wcstoull"> <leak-ignore/> <arg nr="3"><valid>0,2-36</valid></arg> </function>
</def> </def>

View File

@ -157,6 +157,9 @@ int CppCheckExecutor::check(int argc, const char* const argv[])
return EXIT_FAILURE; return EXIT_FAILURE;
} }
if (settings.standards.posix)
settings.library.load(argv[0], "posix");
if (settings.reportProgress) if (settings.reportProgress)
time1 = std::time(0); time1 = std::time(0);

View File

@ -516,9 +516,6 @@ Settings MainWindow::GetCppcheckSettings()
{ {
Settings result; Settings result;
const QString applicationFilePath = QCoreApplication::applicationFilePath();
result.library.load(applicationFilePath.toLatin1(), "std");
// If project file loaded, read settings from it // If project file loaded, read settings from it
if (mProject) { if (mProject) {
ProjectFile *pfile = mProject->GetProjectFile(); ProjectFile *pfile = mProject->GetProjectFile();
@ -567,6 +564,11 @@ Settings MainWindow::GetCppcheckSettings()
result.standards.c = mSettings->value(SETTINGS_STD_C99, true).toBool() ? Standards::C99 : (mSettings->value(SETTINGS_STD_C11, false).toBool() ? Standards::C11 : Standards::C89); result.standards.c = mSettings->value(SETTINGS_STD_C99, true).toBool() ? Standards::C99 : (mSettings->value(SETTINGS_STD_C11, false).toBool() ? Standards::C11 : Standards::C89);
result.standards.posix = mSettings->value(SETTINGS_STD_POSIX, false).toBool(); result.standards.posix = mSettings->value(SETTINGS_STD_POSIX, false).toBool();
const QString applicationFilePath = QCoreApplication::applicationFilePath();
result.library.load(applicationFilePath.toLatin1(), "std");
if (result.standards.posix)
result.library.load(applicationFilePath.toLatin1(), "posix");
if (result._jobs <= 1) { if (result._jobs <= 1) {
result._jobs = 1; result._jobs = 1;
} }

View File

@ -579,40 +579,6 @@ void CheckOther::checkPipeParameterSizeError(const Token *tok, const std::string
"The variable '" + strVarName + "' is an array of size " + strDim + ", which does not match."); "The variable '" + strVarName + "' is an array of size " + strDim + ", which does not match.");
} }
//-----------------------------------------------------------------------------
// check usleep(), which is allowed to be called with in a range of [0,999999]
//
// Reference:
// - http://man7.org/linux/man-pages/man3/usleep.3.html
//-----------------------------------------------------------------------------
void CheckOther::checkSleepTimeInterval()
{
if (!_settings->standards.posix)
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, "usleep ( %num% )")) {
const Token * const numTok = tok->tokAt(2);
MathLib::bigint value = MathLib::toLongNumber(numTok->str());
if (value > 999999) { // less than 1 million
checkSleepTimeError(numTok, numTok->str());
}
}
}
}
}
void CheckOther::checkSleepTimeError(const Token *tok, const std::string &strDim)
{
reportError(tok, Severity::error,
"tooBigSleepTime", "The argument of usleep must be less than 1000000.\n"
"The argument of usleep must be less than 1000000, but " + strDim + " is provided.");
}
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
// Detect redundant assignments: x = 0; x = 4; // Detect redundant assignments: x = 0; x = 4;
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
@ -1472,24 +1438,40 @@ void CheckOther::redundantConditionError(const Token *tok, const std::string &te
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
void CheckOther::invalidFunctionUsage() void CheckOther::invalidFunctionUsage()
{ {
// strtol and strtoul.. const SymbolDatabase* symbolDatabase = _tokenizer->getSymbolDatabase();
for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { const std::size_t functions = symbolDatabase->functionScopes.size();
if (!Token::Match(tok, "strtol|strtoul|strtoll|strtoull|wcstol|wcstoul|wcstoll|wcstoull (")) for (std::size_t i = 0; i < functions; ++i) {
continue; const Scope * scope = symbolDatabase->functionScopes[i];
for (const Token* tok = scope->classStart->next(); tok != scope->classEnd; tok = tok->next()) {
if (!Token::Match(tok, "%var% ( !!)"))
continue;
const std::string functionName = tok->str();
int argnr = 1;
const Token *argtok = tok->tokAt(2);
while (argtok && argtok->str() != ")") {
if (Token::Match(argtok,"%num% [,)]")) {
if (MathLib::isInt(argtok->str()) &&
!_settings->library.isargvalid(functionName, argnr, MathLib::toLongNumber(argtok->str())))
invalidFunctionArgError(argtok,functionName,argnr,_settings->library.validarg(functionName,argnr));
} else {
const Token *top = argtok;
while (top->astParent() && top->astParent()->str() != "," && top->astParent() != tok->next())
top = top->astParent();
if (top->isComparisonOp() || Token::Match(top, "%oror%|&&")) {
if (_settings->library.isboolargbad(functionName, argnr))
invalidFunctionArgBoolError(top, functionName, argnr);
const std::string& funcname = tok->str(); // Are the values 0 and 1 valid?
tok = tok->tokAt(2); else if (!_settings->library.isargvalid(functionName, argnr, 0))
// Locate the third parameter of the function call.. invalidFunctionArgError(top, functionName, argnr, _settings->library.validarg(functionName,argnr));
for (int i = 0; i < 2 && tok; i++) else if (!_settings->library.isargvalid(functionName, argnr, 1))
tok = tok->nextArgument(); invalidFunctionArgError(top, functionName, argnr, _settings->library.validarg(functionName,argnr));
}
if (Token::Match(tok, "%num% )")) { }
const MathLib::bigint radix = MathLib::toLongNumber(tok->str()); argnr++;
if (!(radix == 0 || (radix >= 2 && radix <= 36))) { argtok = argtok->nextArgument();
dangerousUsageStrtolError(tok, funcname);
} }
} else }
break;
} }
// sprintf|snprintf overlapping data // sprintf|snprintf overlapping data
@ -1529,11 +1511,6 @@ void CheckOther::invalidFunctionUsage()
} }
} }
void CheckOther::dangerousUsageStrtolError(const Token *tok, const std::string& funcname)
{
reportError(tok, Severity::error, "dangerousUsageStrtol", "Invalid radix in call to " + funcname + "(). It must be 0 or 2-36.");
}
void CheckOther::sprintfOverlappingDataError(const Token *tok, const std::string &varname) void CheckOther::sprintfOverlappingDataError(const Token *tok, const std::string &varname)
{ {
reportError(tok, Severity::error, "sprintfOverlappingData", reportError(tok, Severity::error, "sprintfOverlappingData",
@ -1545,6 +1522,26 @@ void CheckOther::sprintfOverlappingDataError(const Token *tok, const std::string
"to sprintf() or snprintf(), the results are undefined.\""); "to sprintf() or snprintf(), the results are undefined.\"");
} }
void CheckOther::invalidFunctionArgError(const Token *tok, const std::string &functionName, int argnr, const std::string &validstr)
{
std::ostringstream errmsg;
errmsg << "Invalid " << functionName << "() argument nr " << argnr;
if (!tok)
;
else if (tok->isNumber())
errmsg << ". The value is " << tok->str() << " but the valid values are '" << validstr << "'.";
else if (tok->isComparisonOp())
errmsg << ". The value is 0 or 1 (comparison result) but the valid values are '" << validstr << "'.";
reportError(tok, Severity::error, "invalidFunctionArg", errmsg.str());
}
void CheckOther::invalidFunctionArgBoolError(const Token *tok, const std::string &functionName, int argnr)
{
std::ostringstream errmsg;
errmsg << "Invalid " << functionName << "() argument nr " << argnr << ". A non-boolean value is required.";
reportError(tok, Severity::error, "invalidFunctionArgBool", errmsg.str());
}
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
// Find consecutive return, break, continue, goto or throw statements. e.g.: // Find consecutive return, break, continue, goto or throw statements. e.g.:
// break; break; // break; break;

View File

@ -109,7 +109,6 @@ public:
checkOther.checkRedundantCopy(); checkOther.checkRedundantCopy();
checkOther.checkNegativeBitwiseShift(); checkOther.checkNegativeBitwiseShift();
checkOther.checkSuspiciousEqualityComparison(); checkOther.checkSuspiciousEqualityComparison();
checkOther.checkSleepTimeInterval();
checkOther.checkComparisonFunctionIsAlwaysTrueOrFalse(); checkOther.checkComparisonFunctionIsAlwaysTrueOrFalse();
} }
@ -132,11 +131,12 @@ public:
void invalidPointerCast(); void invalidPointerCast();
/** /**
* @brief Invalid function usage (invalid radix / overlapping data) * @brief Invalid function usage (invalid input value / overlapping data)
* *
* %Check that given function parameters are valid according to the standard * %Check that given function parameters are valid according to the standard
* - wrong radix given for strtol/strtoul * - wrong radix given for strtol/strtoul
* - overlapping data when using sprintf/snprintf * - overlapping data when using sprintf/snprintf
* - wrong input value according to library
*/ */
void invalidFunctionUsage(); void invalidFunctionUsage();
@ -263,9 +263,6 @@ public:
/** @brief %Check to avoid casting a return value to unsigned char and then back to integer type. */ /** @brief %Check to avoid casting a return value to unsigned char and then back to integer type. */
void checkCastIntToCharAndBack(); void checkCastIntToCharAndBack();
/** @brief %Check providing too big sleep time intervals on POSIX systems. */
void checkSleepTimeInterval();
/** @brief %Check for using of comparison functions evaluating always to true or false. */ /** @brief %Check for using of comparison functions evaluating always to true or false. */
void checkComparisonFunctionIsAlwaysTrueOrFalse(void); void checkComparisonFunctionIsAlwaysTrueOrFalse(void);
@ -275,7 +272,6 @@ private:
// Error messages.. // Error messages..
void checkComparisonFunctionIsAlwaysTrueOrFalseError(const Token* tok, const std::string &strFunctionName, const std::string &varName, const bool result); void checkComparisonFunctionIsAlwaysTrueOrFalseError(const Token* tok, const std::string &strFunctionName, const std::string &varName, const bool result);
void checkSleepTimeError(const Token *tok, const std::string &strDim);
void checkCastIntToCharAndBackError(const Token *tok, const std::string &strFunctionName); void checkCastIntToCharAndBackError(const Token *tok, const std::string &strFunctionName);
void checkPipeParameterSizeError(const Token *tok, const std::string &strVarName, const std::string &strDim); void checkPipeParameterSizeError(const Token *tok, const std::string &strVarName, const std::string &strDim);
void oppositeInnerConditionError(const Token *tok); void oppositeInnerConditionError(const Token *tok);
@ -285,8 +281,9 @@ private:
void redundantGetAndSetUserIdError(const Token *tok); void redundantGetAndSetUserIdError(const Token *tok);
void cstyleCastError(const Token *tok); void cstyleCastError(const Token *tok);
void invalidPointerCastError(const Token* tok, const std::string& from, const std::string& to, bool inconclusive); void invalidPointerCastError(const Token* tok, const std::string& from, const std::string& to, bool inconclusive);
void dangerousUsageStrtolError(const Token *tok, const std::string& funcname);
void sprintfOverlappingDataError(const Token *tok, const std::string &varname); void sprintfOverlappingDataError(const Token *tok, const std::string &varname);
void invalidFunctionArgError(const Token *tok, const std::string &functionName, int argnr, const std::string &validstr);
void invalidFunctionArgBoolError(const Token *tok, const std::string &functionName, int argnr);
void udivError(const Token *tok, bool inconclusive); void udivError(const Token *tok, bool inconclusive);
void passedByValueError(const Token *tok, const std::string &parname); void passedByValueError(const Token *tok, const std::string &parname);
void constStatementError(const Token *tok, const std::string &type); void constStatementError(const Token *tok, const std::string &type);
@ -340,6 +337,8 @@ private:
// error // error
c.sprintfOverlappingDataError(0, "varname"); c.sprintfOverlappingDataError(0, "varname");
c.invalidFunctionArgError(0, "func_name", 1, "1-4");
c.invalidFunctionArgBoolError(0, "func_name", 1);
c.udivError(0, false); c.udivError(0, false);
c.zerodivError(0); c.zerodivError(0);
c.zerodivcondError(0,0); c.zerodivcondError(0,0);
@ -349,7 +348,6 @@ private:
c.invalidPointerCastError(0, "float", "double", false); c.invalidPointerCastError(0, "float", "double", false);
c.negativeBitwiseShiftError(0); c.negativeBitwiseShiftError(0);
c.checkPipeParameterSizeError(0, "varname", "dimension"); c.checkPipeParameterSizeError(0, "varname", "dimension");
c.checkSleepTimeError(0,"dimension");
//performance //performance
c.redundantCopyError(0, "varname"); c.redundantCopyError(0, "varname");
@ -361,7 +359,6 @@ private:
c.checkCastIntToCharAndBackError(0,"func_name"); c.checkCastIntToCharAndBackError(0,"func_name");
c.oppositeInnerConditionError(0); c.oppositeInnerConditionError(0);
c.cstyleCastError(0); c.cstyleCastError(0);
c.dangerousUsageStrtolError(0, "strtol");
c.passedByValueError(0, "parametername"); c.passedByValueError(0, "parametername");
c.constStatementError(0, "type"); c.constStatementError(0, "type");
c.charArrayIndexError(0); c.charArrayIndexError(0);
@ -421,7 +418,7 @@ private:
"* bitwise operation with negative right operand\n" "* bitwise operation with negative right operand\n"
"* provide wrong dimensioned array to pipe() system command (--std=posix)\n" "* provide wrong dimensioned array to pipe() system command (--std=posix)\n"
"* cast the return values of getc(),fgetc() and getchar() to character and compare it to EOF\n" "* cast the return values of getc(),fgetc() and getchar() to character and compare it to EOF\n"
"* provide too big sleep times on POSIX systems\n" "* invalid input values for functions\n"
// warning // warning
"* either division by zero or useless condition\n" "* either division by zero or useless condition\n"
@ -435,7 +432,6 @@ private:
"* C-style pointer cast in cpp file\n" "* C-style pointer cast in cpp file\n"
"* casting between incompatible pointer types\n" "* casting between incompatible pointer types\n"
"* redundant if\n" "* redundant if\n"
"* bad usage of the function 'strtol'\n"
"* [[CheckUnsignedDivision|unsigned division]]\n" "* [[CheckUnsignedDivision|unsigned division]]\n"
"* passing parameter by value\n" "* passing parameter by value\n"
"* [[IncompleteStatement|Incomplete statement]]\n" "* [[IncompleteStatement|Incomplete statement]]\n"

View File

@ -19,6 +19,9 @@
#include "library.h" #include "library.h"
#include "path.h" #include "path.h"
#include "tinyxml2.h" #include "tinyxml2.h"
#include "tokenlist.h"
#include "mathlib.h"
#include "token.h"
#include <string> #include <string>
#include <algorithm> #include <algorithm>
@ -108,13 +111,16 @@ bool Library::load(const tinyxml2::XMLDocument &doc)
leakignore.insert(name); leakignore.insert(name);
else if (strcmp(functionnode->Name(), "arg") == 0 && functionnode->Attribute("nr") != NULL) { else if (strcmp(functionnode->Name(), "arg") == 0 && functionnode->Attribute("nr") != NULL) {
const int nr = atoi(functionnode->Attribute("nr")); const int nr = atoi(functionnode->Attribute("nr"));
bool notbool = false;
bool notnull = false; bool notnull = false;
bool notuninit = false; bool notuninit = false;
bool formatstr = false; bool formatstr = false;
bool strz = false; bool strz = false;
std::string valid; std::string valid;
for (const tinyxml2::XMLElement *argnode = functionnode->FirstChildElement(); argnode; argnode = argnode->NextSiblingElement()) { for (const tinyxml2::XMLElement *argnode = functionnode->FirstChildElement(); argnode; argnode = argnode->NextSiblingElement()) {
if (strcmp(argnode->Name(), "not-null") == 0) if (strcmp(argnode->Name(), "not-bool") == 0)
notbool = true;
else if (strcmp(argnode->Name(), "not-null") == 0)
notnull = true; notnull = true;
else if (strcmp(argnode->Name(), "not-uninit") == 0) else if (strcmp(argnode->Name(), "not-uninit") == 0)
notuninit = true; notuninit = true;
@ -144,6 +150,7 @@ bool Library::load(const tinyxml2::XMLDocument &doc)
else else
return false; return false;
} }
argumentChecks[name][nr].notbool = notbool;
argumentChecks[name][nr].notnull = notnull; argumentChecks[name][nr].notnull = notnull;
argumentChecks[name][nr].notuninit = notuninit; argumentChecks[name][nr].notuninit = notuninit;
argumentChecks[name][nr].formatstr = formatstr; argumentChecks[name][nr].formatstr = formatstr;
@ -249,3 +256,24 @@ bool Library::load(const tinyxml2::XMLDocument &doc)
} }
return true; return true;
} }
bool Library::isargvalid(const std::string &functionName, int argnr, const MathLib::bigint argvalue) const
{
const ArgumentChecks *ac = getarg(functionName, argnr);
if (!ac || ac->valid.empty())
return true;
TokenList tokenList(0);
std::istringstream istr(ac->valid + ',');
tokenList.createTokens(istr,"");
for (const Token *tok = tokenList.front(); tok; tok = tok->next()) {
if (tok->isNumber() && argvalue == MathLib::toLongNumber(tok->str()))
return true;
if (Token::Match(tok, "%num% - %num%") && argvalue >= MathLib::toLongNumber(tok->str()) && argvalue <= MathLib::toLongNumber(tok->strAt(2)))
return true;
if (Token::Match(tok, "%num% - ,") && argvalue >= MathLib::toLongNumber(tok->str()))
return true;
if ((!tok->previous() || tok->previous()->str() == ",") && Token::Match(tok,"- %num%") && argvalue <= MathLib::toLongNumber(tok->strAt(1)))
return true;
}
return false;
}

View File

@ -23,6 +23,7 @@
#include "config.h" #include "config.h"
#include "path.h" #include "path.h"
#include "mathlib.h"
#include <tinyxml2.h> #include <tinyxml2.h>
#include <map> #include <map>
@ -31,6 +32,8 @@
#include <list> #include <list>
#include <algorithm> #include <algorithm>
class TokenList;
/// @addtogroup Core /// @addtogroup Core
/// @{ /// @{
@ -91,11 +94,13 @@ public:
return (it != _noreturn.end() && !it->second); return (it != _noreturn.end() && !it->second);
} }
struct ArgumentChecks { class ArgumentChecks {
public:
ArgumentChecks() { ArgumentChecks() {
notnull = notuninit = formatstr = strz = false; notbool = notnull = notuninit = formatstr = strz = false;
} }
bool notbool;
bool notnull; bool notnull;
bool notuninit; bool notuninit;
bool formatstr; bool formatstr;
@ -106,6 +111,11 @@ public:
// function name, argument nr => argument data // function name, argument nr => argument data
std::map<std::string, std::map<int, ArgumentChecks> > argumentChecks; std::map<std::string, std::map<int, ArgumentChecks> > argumentChecks;
bool isboolargbad(const std::string &functionName, int argnr) const {
const ArgumentChecks *arg = getarg(functionName, argnr);
return arg && arg->notbool;
}
bool isnullargbad(const std::string &functionName, int argnr) const { bool isnullargbad(const std::string &functionName, int argnr) const {
const ArgumentChecks *arg = getarg(functionName, argnr); const ArgumentChecks *arg = getarg(functionName, argnr);
return arg && arg->notnull; return arg && arg->notnull;
@ -126,6 +136,13 @@ public:
return arg && arg->strz; return arg && arg->strz;
} }
bool isargvalid(const std::string &functionName, int argnr, const MathLib::bigint argvalue) const;
std::string validarg(const std::string &functionName, int argnr) const {
const ArgumentChecks *arg = getarg(functionName, argnr);
return arg ? arg->valid : std::string("");
}
bool markupFile(const std::string &path) const { bool markupFile(const std::string &path) const {
return _markupExtensions.find(Path::getFilenameExtensionInLowerCase(path)) != _markupExtensions.end(); return _markupExtensions.find(Path::getFilenameExtensionInLowerCase(path)) != _markupExtensions.end();
} }

View File

@ -17,6 +17,8 @@
*/ */
#include "library.h" #include "library.h"
#include "token.h"
#include "tokenlist.h"
#include "testsuite.h" #include "testsuite.h"
class TestLibrary : public TestFixture { class TestLibrary : public TestFixture {
@ -67,21 +69,12 @@ private:
const char xmldata[] = "<?xml version=\"1.0\"?>\n" const char xmldata[] = "<?xml version=\"1.0\"?>\n"
"<def>\n" "<def>\n"
" <function name=\"foo\">\n" " <function name=\"foo\">\n"
" <arg nr=\"1\">\n" " <arg nr=\"1\"><not-uninit/></arg>\n"
" <not-uninit/>\n" " <arg nr=\"2\"><not-null/></arg>\n"
" </arg>\n" " <arg nr=\"3\"><formatstr/></arg>\n"
" <arg nr=\"2\">\n" " <arg nr=\"4\"><strz/></arg>\n"
" <not-null/>\n" " <arg nr=\"5\"><valid>1-</valid></arg>\n"
" </arg>\n" " <arg nr=\"6\"><not-bool/></arg>\n"
" <arg nr=\"3\">\n"
" <formatstr/>\n"
" </arg>\n"
" <arg nr=\"4\">\n"
" <strz/>\n"
" </arg>\n"
" <arg nr=\"5\">\n"
" <valid>1-</valid>\n"
" </arg>\n"
" </function>\n" " </function>\n"
"</def>"; "</def>";
tinyxml2::XMLDocument doc; tinyxml2::XMLDocument doc;
@ -94,6 +87,7 @@ private:
ASSERT_EQUALS(true, library.argumentChecks["foo"][3].formatstr); ASSERT_EQUALS(true, library.argumentChecks["foo"][3].formatstr);
ASSERT_EQUALS(true, library.argumentChecks["foo"][4].strz); ASSERT_EQUALS(true, library.argumentChecks["foo"][4].strz);
ASSERT_EQUALS("1-", library.argumentChecks["foo"][5].valid); ASSERT_EQUALS("1-", library.argumentChecks["foo"][5].valid);
ASSERT_EQUALS(true, library.argumentChecks["foo"][6].notbool);
} }
void memory() { void memory() {

View File

@ -49,6 +49,8 @@ private:
TEST_CASE(nanInArithmeticExpression); TEST_CASE(nanInArithmeticExpression);
TEST_CASE(invalidFunctionUsage1);
TEST_CASE(sprintf1); // Dangerous usage of sprintf TEST_CASE(sprintf1); // Dangerous usage of sprintf
TEST_CASE(sprintf2); TEST_CASE(sprintf2);
TEST_CASE(sprintf3); TEST_CASE(sprintf3);
@ -82,8 +84,6 @@ private:
TEST_CASE(oldStylePointerCast); TEST_CASE(oldStylePointerCast);
TEST_CASE(invalidPointerCast); TEST_CASE(invalidPointerCast);
TEST_CASE(dangerousStrolUsage);
TEST_CASE(passedByValue); TEST_CASE(passedByValue);
TEST_CASE(mathfunctionCall_fmod); TEST_CASE(mathfunctionCall_fmod);
@ -211,6 +211,16 @@ private:
settings->experimental = experimental; settings->experimental = experimental;
settings->standards.posix = posix; settings->standards.posix = posix;
if (posix) {
const char cfg[] = "<?xml version=\"1.0\"?>\n"
"<def>\n"
" <function name=\"usleep\"> <arg nr=\"1\"><valid>0-999999</valid></arg> </function>\n"
"</def>";
tinyxml2::XMLDocument xmldoc;
xmldoc.Parse(cfg, sizeof(cfg));
settings->library.load(xmldoc);
}
// Tokenize.. // Tokenize..
Tokenizer tokenizer(settings, this); Tokenizer tokenizer(settings, this);
std::istringstream istr(code); std::istringstream istr(code);
@ -651,64 +661,88 @@ private:
} }
void sprintfUsage(const char code[]) { void invalidFunctionUsage(const char code[]) {
// Clear the error buffer.. // Clear the error buffer..
errout.str(""); errout.str("");
const char cfg[] = "<?xml version=\"1.0\"?>\n"
"<def>\n"
" <function name=\"memset\"> <arg nr=\"3\"><not-bool/></arg> </function>\n"
" <function name=\"strtol\"> <arg nr=\"3\"><valid>0,2-36</valid></arg> </function>\n"
"</def>";
tinyxml2::XMLDocument xmldoc;
xmldoc.Parse(cfg, sizeof(cfg));
Settings settings; Settings settings;
settings.library.load(xmldoc);
// Tokenize.. // Tokenize..
Tokenizer tokenizer(&settings, this); Tokenizer tokenizer(&settings, this);
std::istringstream istr(code); std::istringstream istr(code);
tokenizer.tokenize(istr, "test.cpp"); tokenizer.tokenize(istr, "test.cpp");
//tokenizer.tokens()->printOut( "tokens" );
// Check for redundant code.. // Check for redundant code..
CheckOther checkOther(&tokenizer, &settings, this); CheckOther checkOther(&tokenizer, &settings, this);
checkOther.invalidFunctionUsage(); checkOther.invalidFunctionUsage();
} }
void invalidFunctionUsage1() {
invalidFunctionUsage("int f() { memset(a,b,sizeof(a)!=12); }");
ASSERT_EQUALS("[test.cpp:1]: (error) Invalid memset() argument nr 3. A non-boolean value is required.\n", errout.str());
invalidFunctionUsage("int f() { memset(a,b,sizeof(a)!=0); }");
TODO_ASSERT_EQUALS("error", "", errout.str());
invalidFunctionUsage("int f() { strtol(a,b,sizeof(a)!=12); }");
ASSERT_EQUALS("[test.cpp:1]: (error) Invalid strtol() argument nr 3. The value is 0 or 1 (comparison result) but the valid values are '0,2-36'.\n", errout.str());
invalidFunctionUsage("int f() { strtol(a,b,1); }");
ASSERT_EQUALS("[test.cpp:1]: (error) Invalid strtol() argument nr 3. The value is 1 but the valid values are '0,2-36'.\n", errout.str());
invalidFunctionUsage("int f() { strtol(a,b,10); }");
ASSERT_EQUALS("", errout.str());
}
void sprintf1() { void sprintf1() {
sprintfUsage("void foo()\n" invalidFunctionUsage("void foo()\n"
"{\n" "{\n"
" char buf[100];\n" " char buf[100];\n"
" sprintf(buf,\"%s\",buf);\n" " sprintf(buf,\"%s\",buf);\n"
"}"); "}");
ASSERT_EQUALS("[test.cpp:4]: (error) Undefined behavior: Variable 'buf' is used as parameter and destination in s[n]printf().\n", errout.str()); ASSERT_EQUALS("[test.cpp:4]: (error) Undefined behavior: Variable 'buf' is used as parameter and destination in s[n]printf().\n", errout.str());
} }
void sprintf2() { void sprintf2() {
sprintfUsage("void foo()\n" invalidFunctionUsage("void foo()\n"
"{\n" "{\n"
" char buf[100];\n" " char buf[100];\n"
" sprintf(buf,\"%i\",sizeof(buf));\n" " sprintf(buf,\"%i\",sizeof(buf));\n"
"}"); "}");
ASSERT_EQUALS("", errout.str()); ASSERT_EQUALS("", errout.str());
} }
void sprintf3() { void sprintf3() {
sprintfUsage("void foo()\n" invalidFunctionUsage("void foo()\n"
"{\n" "{\n"
" char buf[100];\n" " char buf[100];\n"
" sprintf(buf,\"%i\",sizeof(buf));\n" " sprintf(buf,\"%i\",sizeof(buf));\n"
" if (buf[0]);\n" " if (buf[0]);\n"
"}"); "}");
ASSERT_EQUALS("", errout.str()); ASSERT_EQUALS("", errout.str());
} }
void sprintf4() { void sprintf4() {
sprintfUsage("struct A\n" invalidFunctionUsage("struct A\n"
"{\n" "{\n"
" char filename[128];\n" " char filename[128];\n"
"};\n" "};\n"
"\n" "\n"
"void foo()\n" "void foo()\n"
"{\n" "{\n"
" const char* filename = \"hello\";\n" " const char* filename = \"hello\";\n"
" struct A a;\n" " struct A a;\n"
" snprintf(a.filename, 128, \"%s\", filename);\n" " snprintf(a.filename, 128, \"%s\", filename);\n"
"}"); "}");
ASSERT_EQUALS("", errout.str()); ASSERT_EQUALS("", errout.str());
} }
@ -1374,26 +1408,6 @@ private:
checkInvalidPointerCast("Q_DECLARE_METATYPE(int*)"); // #4135 - don't crash checkInvalidPointerCast("Q_DECLARE_METATYPE(int*)"); // #4135 - don't crash
} }
void dangerousStrolUsage() {
{
sprintfUsage("int f(const char *num)\n"
"{\n"
" return strtol(num, NULL, 1);\n"
"}");
ASSERT_EQUALS("[test.cpp:3]: (error) Invalid radix in call to strtol(). It must be 0 or 2-36.\n", errout.str());
}
{
sprintfUsage("int f(const char *num)\n"
"{\n"
" return strtol(num, NULL, 10);\n"
"}");
ASSERT_EQUALS("", errout.str());
}
}
void testPassedByValue(const char code[]) { void testPassedByValue(const char code[]) {
// Clear the error buffer.. // Clear the error buffer..
errout.str(""); errout.str("");
@ -6893,12 +6907,12 @@ private:
check("void f(){\n" check("void f(){\n"
"usleep(1000000);\n" "usleep(1000000);\n"
"}",NULL,false,false,true); "}",NULL,false,false,true);
ASSERT_EQUALS("[test.cpp:2]: (error) The argument of usleep must be less than 1000000.\n", errout.str()); ASSERT_EQUALS("[test.cpp:2]: (error) Invalid usleep() argument nr 1. The value is 1000000 but the valid values are '0-999999'.\n", errout.str());
check("void f(){\n" check("void f(){\n"
"usleep(1000001);\n" "usleep(1000001);\n"
"}",NULL,false,false,true); "}",NULL,false,false,true);
ASSERT_EQUALS("[test.cpp:2]: (error) The argument of usleep must be less than 1000000.\n", errout.str()); ASSERT_EQUALS("[test.cpp:2]: (error) Invalid usleep() argument nr 1. The value is 1000001 but the valid values are '0-999999'.\n", errout.str());
} }
void checkCommaSeparatedReturn() { void checkCommaSeparatedReturn() {