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="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>

View File

@ -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);

View File

@ -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;
}

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.");
}
//-----------------------------------------------------------------------------
// 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;

View File

@ -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"

View File

@ -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;
}

View File

@ -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();
}

View File

@ -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() {

View File

@ -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() {