Fixed #5252 (Improve check: use Library to validate function arguments in invalidFunctionUsage)
This commit is contained in:
parent
be03d4718a
commit
76b907fe65
|
@ -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>
|
||||
|
24
cfg/std.cfg
24
cfg/std.cfg
|
@ -25,10 +25,11 @@
|
|||
<function name="islower"> <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="memcpy"> <noreturn>false</noreturn> <leak-ignore/> </function>
|
||||
<function name="memmove"> <noreturn>false</noreturn> <leak-ignore/> </function>
|
||||
<function name="memset"> <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="memcmp"> <noreturn>false</noreturn> <leak-ignore/> <arg nr="3"><not-bool/><valid>0-</valid></arg> </function>
|
||||
<function name="memcpy"> <noreturn>false</noreturn> <leak-ignore/> <arg nr="3"><not-bool/><valid>0-</valid></arg> </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="strchr"> <noreturn>false</noreturn> <leak-ignore/> </function>
|
||||
|
@ -36,9 +37,18 @@
|
|||
<function name="strcmp"> <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="strncat"> <noreturn>false</noreturn> <leak-ignore/> </function>
|
||||
<function name="strncpy"> <noreturn>false</noreturn> <leak-ignore/> </function>
|
||||
<function name="strncmp"> <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/> <arg nr="3"><not-bool/><valid>0-</valid></arg> </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="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>
|
||||
|
||||
|
|
|
@ -157,6 +157,9 @@ int CppCheckExecutor::check(int argc, const char* const argv[])
|
|||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (settings.standards.posix)
|
||||
settings.library.load(argv[0], "posix");
|
||||
|
||||
if (settings.reportProgress)
|
||||
time1 = std::time(0);
|
||||
|
||||
|
|
|
@ -516,9 +516,6 @@ Settings MainWindow::GetCppcheckSettings()
|
|||
{
|
||||
Settings result;
|
||||
|
||||
const QString applicationFilePath = QCoreApplication::applicationFilePath();
|
||||
result.library.load(applicationFilePath.toLatin1(), "std");
|
||||
|
||||
// If project file loaded, read settings from it
|
||||
if (mProject) {
|
||||
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.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) {
|
||||
result._jobs = 1;
|
||||
}
|
||||
|
|
|
@ -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.");
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// 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;
|
||||
//---------------------------------------------------------------------------
|
||||
|
@ -1472,24 +1438,40 @@ void CheckOther::redundantConditionError(const Token *tok, const std::string &te
|
|||
//---------------------------------------------------------------------------
|
||||
void CheckOther::invalidFunctionUsage()
|
||||
{
|
||||
// strtol and strtoul..
|
||||
for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) {
|
||||
if (!Token::Match(tok, "strtol|strtoul|strtoll|strtoull|wcstol|wcstoul|wcstoll|wcstoull ("))
|
||||
continue;
|
||||
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, "%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();
|
||||
tok = tok->tokAt(2);
|
||||
// Locate the third parameter of the function call..
|
||||
for (int i = 0; i < 2 && tok; i++)
|
||||
tok = tok->nextArgument();
|
||||
|
||||
if (Token::Match(tok, "%num% )")) {
|
||||
const MathLib::bigint radix = MathLib::toLongNumber(tok->str());
|
||||
if (!(radix == 0 || (radix >= 2 && radix <= 36))) {
|
||||
dangerousUsageStrtolError(tok, funcname);
|
||||
// Are the values 0 and 1 valid?
|
||||
else if (!_settings->library.isargvalid(functionName, argnr, 0))
|
||||
invalidFunctionArgError(top, functionName, argnr, _settings->library.validarg(functionName,argnr));
|
||||
else if (!_settings->library.isargvalid(functionName, argnr, 1))
|
||||
invalidFunctionArgError(top, functionName, argnr, _settings->library.validarg(functionName,argnr));
|
||||
}
|
||||
}
|
||||
argnr++;
|
||||
argtok = argtok->nextArgument();
|
||||
}
|
||||
} else
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 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)
|
||||
{
|
||||
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.\"");
|
||||
}
|
||||
|
||||
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.:
|
||||
// break; break;
|
||||
|
|
|
@ -109,7 +109,6 @@ public:
|
|||
checkOther.checkRedundantCopy();
|
||||
checkOther.checkNegativeBitwiseShift();
|
||||
checkOther.checkSuspiciousEqualityComparison();
|
||||
checkOther.checkSleepTimeInterval();
|
||||
checkOther.checkComparisonFunctionIsAlwaysTrueOrFalse();
|
||||
}
|
||||
|
||||
|
@ -132,11 +131,12 @@ public:
|
|||
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
|
||||
* - wrong radix given for strtol/strtoul
|
||||
* - overlapping data when using sprintf/snprintf
|
||||
* - wrong input value according to library
|
||||
*/
|
||||
void invalidFunctionUsage();
|
||||
|
||||
|
@ -263,9 +263,6 @@ public:
|
|||
/** @brief %Check to avoid casting a return value to unsigned char and then back to integer type. */
|
||||
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. */
|
||||
void checkComparisonFunctionIsAlwaysTrueOrFalse(void);
|
||||
|
||||
|
@ -275,7 +272,6 @@ private:
|
|||
|
||||
// Error messages..
|
||||
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 checkPipeParameterSizeError(const Token *tok, const std::string &strVarName, const std::string &strDim);
|
||||
void oppositeInnerConditionError(const Token *tok);
|
||||
|
@ -285,8 +281,9 @@ private:
|
|||
void redundantGetAndSetUserIdError(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);
|
||||
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 passedByValueError(const Token *tok, const std::string &parname);
|
||||
void constStatementError(const Token *tok, const std::string &type);
|
||||
|
@ -340,6 +337,8 @@ private:
|
|||
|
||||
// error
|
||||
c.sprintfOverlappingDataError(0, "varname");
|
||||
c.invalidFunctionArgError(0, "func_name", 1, "1-4");
|
||||
c.invalidFunctionArgBoolError(0, "func_name", 1);
|
||||
c.udivError(0, false);
|
||||
c.zerodivError(0);
|
||||
c.zerodivcondError(0,0);
|
||||
|
@ -349,7 +348,6 @@ private:
|
|||
c.invalidPointerCastError(0, "float", "double", false);
|
||||
c.negativeBitwiseShiftError(0);
|
||||
c.checkPipeParameterSizeError(0, "varname", "dimension");
|
||||
c.checkSleepTimeError(0,"dimension");
|
||||
|
||||
//performance
|
||||
c.redundantCopyError(0, "varname");
|
||||
|
@ -361,7 +359,6 @@ private:
|
|||
c.checkCastIntToCharAndBackError(0,"func_name");
|
||||
c.oppositeInnerConditionError(0);
|
||||
c.cstyleCastError(0);
|
||||
c.dangerousUsageStrtolError(0, "strtol");
|
||||
c.passedByValueError(0, "parametername");
|
||||
c.constStatementError(0, "type");
|
||||
c.charArrayIndexError(0);
|
||||
|
@ -421,7 +418,7 @@ private:
|
|||
"* bitwise operation with negative right operand\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"
|
||||
"* provide too big sleep times on POSIX systems\n"
|
||||
"* invalid input values for functions\n"
|
||||
|
||||
// warning
|
||||
"* either division by zero or useless condition\n"
|
||||
|
@ -435,7 +432,6 @@ private:
|
|||
"* C-style pointer cast in cpp file\n"
|
||||
"* casting between incompatible pointer types\n"
|
||||
"* redundant if\n"
|
||||
"* bad usage of the function 'strtol'\n"
|
||||
"* [[CheckUnsignedDivision|unsigned division]]\n"
|
||||
"* passing parameter by value\n"
|
||||
"* [[IncompleteStatement|Incomplete statement]]\n"
|
||||
|
|
|
@ -19,6 +19,9 @@
|
|||
#include "library.h"
|
||||
#include "path.h"
|
||||
#include "tinyxml2.h"
|
||||
#include "tokenlist.h"
|
||||
#include "mathlib.h"
|
||||
#include "token.h"
|
||||
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
|
@ -108,13 +111,16 @@ bool Library::load(const tinyxml2::XMLDocument &doc)
|
|||
leakignore.insert(name);
|
||||
else if (strcmp(functionnode->Name(), "arg") == 0 && functionnode->Attribute("nr") != NULL) {
|
||||
const int nr = atoi(functionnode->Attribute("nr"));
|
||||
bool notbool = false;
|
||||
bool notnull = false;
|
||||
bool notuninit = false;
|
||||
bool formatstr = false;
|
||||
bool strz = false;
|
||||
std::string valid;
|
||||
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;
|
||||
else if (strcmp(argnode->Name(), "not-uninit") == 0)
|
||||
notuninit = true;
|
||||
|
@ -144,6 +150,7 @@ bool Library::load(const tinyxml2::XMLDocument &doc)
|
|||
else
|
||||
return false;
|
||||
}
|
||||
argumentChecks[name][nr].notbool = notbool;
|
||||
argumentChecks[name][nr].notnull = notnull;
|
||||
argumentChecks[name][nr].notuninit = notuninit;
|
||||
argumentChecks[name][nr].formatstr = formatstr;
|
||||
|
@ -249,3 +256,24 @@ bool Library::load(const tinyxml2::XMLDocument &doc)
|
|||
}
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
|
||||
#include "config.h"
|
||||
#include "path.h"
|
||||
#include "mathlib.h"
|
||||
|
||||
#include <tinyxml2.h>
|
||||
#include <map>
|
||||
|
@ -31,6 +32,8 @@
|
|||
#include <list>
|
||||
#include <algorithm>
|
||||
|
||||
class TokenList;
|
||||
|
||||
/// @addtogroup Core
|
||||
/// @{
|
||||
|
||||
|
@ -91,11 +94,13 @@ public:
|
|||
return (it != _noreturn.end() && !it->second);
|
||||
}
|
||||
|
||||
struct ArgumentChecks {
|
||||
class ArgumentChecks {
|
||||
public:
|
||||
ArgumentChecks() {
|
||||
notnull = notuninit = formatstr = strz = false;
|
||||
notbool = notnull = notuninit = formatstr = strz = false;
|
||||
}
|
||||
|
||||
bool notbool;
|
||||
bool notnull;
|
||||
bool notuninit;
|
||||
bool formatstr;
|
||||
|
@ -106,6 +111,11 @@ public:
|
|||
// function name, argument nr => argument data
|
||||
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 {
|
||||
const ArgumentChecks *arg = getarg(functionName, argnr);
|
||||
return arg && arg->notnull;
|
||||
|
@ -126,6 +136,13 @@ public:
|
|||
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 {
|
||||
return _markupExtensions.find(Path::getFilenameExtensionInLowerCase(path)) != _markupExtensions.end();
|
||||
}
|
||||
|
|
|
@ -17,6 +17,8 @@
|
|||
*/
|
||||
|
||||
#include "library.h"
|
||||
#include "token.h"
|
||||
#include "tokenlist.h"
|
||||
#include "testsuite.h"
|
||||
|
||||
class TestLibrary : public TestFixture {
|
||||
|
@ -67,21 +69,12 @@ private:
|
|||
const char xmldata[] = "<?xml version=\"1.0\"?>\n"
|
||||
"<def>\n"
|
||||
" <function name=\"foo\">\n"
|
||||
" <arg nr=\"1\">\n"
|
||||
" <not-uninit/>\n"
|
||||
" </arg>\n"
|
||||
" <arg nr=\"2\">\n"
|
||||
" <not-null/>\n"
|
||||
" </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"
|
||||
" <arg nr=\"1\"><not-uninit/></arg>\n"
|
||||
" <arg nr=\"2\"><not-null/></arg>\n"
|
||||
" <arg nr=\"3\"><formatstr/></arg>\n"
|
||||
" <arg nr=\"4\"><strz/></arg>\n"
|
||||
" <arg nr=\"5\"><valid>1-</valid></arg>\n"
|
||||
" <arg nr=\"6\"><not-bool/></arg>\n"
|
||||
" </function>\n"
|
||||
"</def>";
|
||||
tinyxml2::XMLDocument doc;
|
||||
|
@ -94,6 +87,7 @@ private:
|
|||
ASSERT_EQUALS(true, library.argumentChecks["foo"][3].formatstr);
|
||||
ASSERT_EQUALS(true, library.argumentChecks["foo"][4].strz);
|
||||
ASSERT_EQUALS("1-", library.argumentChecks["foo"][5].valid);
|
||||
ASSERT_EQUALS(true, library.argumentChecks["foo"][6].notbool);
|
||||
}
|
||||
|
||||
void memory() {
|
||||
|
|
|
@ -49,6 +49,8 @@ private:
|
|||
|
||||
TEST_CASE(nanInArithmeticExpression);
|
||||
|
||||
TEST_CASE(invalidFunctionUsage1);
|
||||
|
||||
TEST_CASE(sprintf1); // Dangerous usage of sprintf
|
||||
TEST_CASE(sprintf2);
|
||||
TEST_CASE(sprintf3);
|
||||
|
@ -82,8 +84,6 @@ private:
|
|||
TEST_CASE(oldStylePointerCast);
|
||||
TEST_CASE(invalidPointerCast);
|
||||
|
||||
TEST_CASE(dangerousStrolUsage);
|
||||
|
||||
TEST_CASE(passedByValue);
|
||||
|
||||
TEST_CASE(mathfunctionCall_fmod);
|
||||
|
@ -211,6 +211,16 @@ private:
|
|||
settings->experimental = experimental;
|
||||
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..
|
||||
Tokenizer tokenizer(settings, this);
|
||||
std::istringstream istr(code);
|
||||
|
@ -651,64 +661,88 @@ private:
|
|||
|
||||
}
|
||||
|
||||
void sprintfUsage(const char code[]) {
|
||||
void invalidFunctionUsage(const char code[]) {
|
||||
// Clear the error buffer..
|
||||
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.library.load(xmldoc);
|
||||
|
||||
// Tokenize..
|
||||
Tokenizer tokenizer(&settings, this);
|
||||
std::istringstream istr(code);
|
||||
tokenizer.tokenize(istr, "test.cpp");
|
||||
|
||||
//tokenizer.tokens()->printOut( "tokens" );
|
||||
|
||||
// Check for redundant code..
|
||||
CheckOther checkOther(&tokenizer, &settings, this);
|
||||
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() {
|
||||
sprintfUsage("void foo()\n"
|
||||
"{\n"
|
||||
" char buf[100];\n"
|
||||
" sprintf(buf,\"%s\",buf);\n"
|
||||
"}");
|
||||
invalidFunctionUsage("void foo()\n"
|
||||
"{\n"
|
||||
" char buf[100];\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());
|
||||
}
|
||||
|
||||
void sprintf2() {
|
||||
sprintfUsage("void foo()\n"
|
||||
"{\n"
|
||||
" char buf[100];\n"
|
||||
" sprintf(buf,\"%i\",sizeof(buf));\n"
|
||||
"}");
|
||||
invalidFunctionUsage("void foo()\n"
|
||||
"{\n"
|
||||
" char buf[100];\n"
|
||||
" sprintf(buf,\"%i\",sizeof(buf));\n"
|
||||
"}");
|
||||
ASSERT_EQUALS("", errout.str());
|
||||
}
|
||||
|
||||
void sprintf3() {
|
||||
sprintfUsage("void foo()\n"
|
||||
"{\n"
|
||||
" char buf[100];\n"
|
||||
" sprintf(buf,\"%i\",sizeof(buf));\n"
|
||||
" if (buf[0]);\n"
|
||||
"}");
|
||||
invalidFunctionUsage("void foo()\n"
|
||||
"{\n"
|
||||
" char buf[100];\n"
|
||||
" sprintf(buf,\"%i\",sizeof(buf));\n"
|
||||
" if (buf[0]);\n"
|
||||
"}");
|
||||
ASSERT_EQUALS("", errout.str());
|
||||
}
|
||||
|
||||
void sprintf4() {
|
||||
sprintfUsage("struct A\n"
|
||||
"{\n"
|
||||
" char filename[128];\n"
|
||||
"};\n"
|
||||
"\n"
|
||||
"void foo()\n"
|
||||
"{\n"
|
||||
" const char* filename = \"hello\";\n"
|
||||
" struct A a;\n"
|
||||
" snprintf(a.filename, 128, \"%s\", filename);\n"
|
||||
"}");
|
||||
invalidFunctionUsage("struct A\n"
|
||||
"{\n"
|
||||
" char filename[128];\n"
|
||||
"};\n"
|
||||
"\n"
|
||||
"void foo()\n"
|
||||
"{\n"
|
||||
" const char* filename = \"hello\";\n"
|
||||
" struct A a;\n"
|
||||
" snprintf(a.filename, 128, \"%s\", filename);\n"
|
||||
"}");
|
||||
ASSERT_EQUALS("", errout.str());
|
||||
}
|
||||
|
||||
|
@ -1374,26 +1408,6 @@ private:
|
|||
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[]) {
|
||||
// Clear the error buffer..
|
||||
errout.str("");
|
||||
|
@ -6893,12 +6907,12 @@ private:
|
|||
check("void f(){\n"
|
||||
"usleep(1000000);\n"
|
||||
"}",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"
|
||||
"usleep(1000001);\n"
|
||||
"}",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() {
|
||||
|
|
Loading…
Reference in New Issue