Refactoring: Moved some code into a new function.
Renamed count->countSprintfLength. Added code to collect sprintf parameters. Added a few TODO test cases.
This commit is contained in:
parent
8fc21dda27
commit
fcd269dbf7
|
@ -465,78 +465,7 @@ void CheckBufferOverrun::checkScope(const Token *tok, const char *varname[], con
|
|||
// sprintf..
|
||||
if (varid > 0 && Token::Match(tok, "sprintf ( %varid% , %str% [,)]", varid))
|
||||
{
|
||||
int len = -2;
|
||||
|
||||
// check format string
|
||||
const char *fmt = tok->strAt(4);
|
||||
while (*fmt)
|
||||
{
|
||||
if (*fmt == '\\')
|
||||
{
|
||||
++fmt;
|
||||
}
|
||||
else if (*fmt == '%')
|
||||
{
|
||||
++fmt;
|
||||
|
||||
// skip field width
|
||||
while (std::isdigit(*fmt))
|
||||
{
|
||||
++fmt;
|
||||
}
|
||||
|
||||
// FIXME: better handling for format specifiers
|
||||
++fmt;
|
||||
continue;
|
||||
}
|
||||
++fmt;
|
||||
++len;
|
||||
}
|
||||
|
||||
if (len >= (int)size)
|
||||
{
|
||||
bufferOverrun(tok);
|
||||
}
|
||||
|
||||
// check arguments (if they exists)
|
||||
if (tok->tokAt(5)->str() == ",")
|
||||
{
|
||||
len = 0;
|
||||
const Token *end = tok->next()->link();
|
||||
bool argumentAlreadyChecked = false;
|
||||
int lastCheckedArgumentMaxSize = 0;
|
||||
for (const Token *tok2 = tok->tokAt(6); tok2 && tok2 != end; tok2 = tok2->next())
|
||||
{
|
||||
if (tok2->str() == ",")
|
||||
{
|
||||
argumentAlreadyChecked = false;
|
||||
lastCheckedArgumentMaxSize = 0;
|
||||
}
|
||||
else if (Token::Match(tok2, "%str%"))
|
||||
{
|
||||
int argumentSize = static_cast<int>(Token::getStrLength(tok2));
|
||||
if (argumentAlreadyChecked == false)
|
||||
{
|
||||
lastCheckedArgumentMaxSize = argumentSize;
|
||||
len += argumentSize;
|
||||
argumentAlreadyChecked = true;
|
||||
}
|
||||
else if (argumentSize > lastCheckedArgumentMaxSize)
|
||||
{
|
||||
// when sprintf() argument is ternary
|
||||
// operation we may have two and more
|
||||
// strings as argument. In this case we
|
||||
// use length of longest string.
|
||||
len += (argumentSize - lastCheckedArgumentMaxSize);
|
||||
lastCheckedArgumentMaxSize = argumentSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (len >= (int)size)
|
||||
{
|
||||
bufferOverrun(tok);
|
||||
}
|
||||
}
|
||||
checkSprintfCall(tok, size);
|
||||
}
|
||||
|
||||
// snprintf..
|
||||
|
@ -842,7 +771,7 @@ void CheckBufferOverrun::bufferOverrun()
|
|||
//---------------------------------------------------------------------------
|
||||
|
||||
|
||||
int CheckBufferOverrun::count(const std::string &input_string)
|
||||
int CheckBufferOverrun::countSprintfLength(const std::string &input_string, const std::list<const Token*> &/*parameters*/)
|
||||
{
|
||||
|
||||
|
||||
|
@ -926,3 +855,139 @@ int CheckBufferOverrun::count(const std::string &input_string)
|
|||
}
|
||||
|
||||
|
||||
void CheckBufferOverrun::checkSprintfCall(const Token *tok, int size)
|
||||
{
|
||||
if (0)
|
||||
{
|
||||
std::list<const Token*> parameters;
|
||||
if (tok->tokAt(5)->str() == ",")
|
||||
{
|
||||
const Token *end = tok->next()->link();
|
||||
for (const Token *tok2 = tok->tokAt(6); tok2 && tok2 != end; tok2 = tok2->next())
|
||||
{
|
||||
if (Token::Match(tok2, ", %any% [,)]"))
|
||||
{
|
||||
if (Token::Match(tok2->next(), "%str%"))
|
||||
parameters.push_back(tok2->next());
|
||||
|
||||
else if (Token::Match(tok2->next(), "%num%"))
|
||||
parameters.push_back(tok2->next());
|
||||
|
||||
// TODO, get value of the variable if possible and use that instead of 0
|
||||
else
|
||||
parameters.push_back(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Parameter is more complex, than just a value or variable. Ignore it for now
|
||||
// and skip to next token.
|
||||
parameters.push_back(0);
|
||||
int ind = 0;
|
||||
for (const Token *tok3 = tok2->next(); tok3; tok3 = tok3->next())
|
||||
{
|
||||
if (tok3->str() == "(")
|
||||
++ind;
|
||||
|
||||
else if (tok3->str() == ")")
|
||||
{
|
||||
--ind;
|
||||
if (ind < 0)
|
||||
break;
|
||||
}
|
||||
|
||||
else if (ind == 0 && tok3->str() == ",")
|
||||
{
|
||||
tok2 = tok3->previous();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (ind < 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int len = countSprintfLength(tok->tokAt(4)->strValue(), parameters);
|
||||
if (len > size)
|
||||
{
|
||||
bufferOverrun(tok);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int len = -2;
|
||||
|
||||
// check format string
|
||||
const char *fmt = tok->strAt(4);
|
||||
while (*fmt)
|
||||
{
|
||||
if (*fmt == '\\')
|
||||
{
|
||||
++fmt;
|
||||
}
|
||||
else if (*fmt == '%')
|
||||
{
|
||||
++fmt;
|
||||
|
||||
// skip field width
|
||||
while (std::isdigit(*fmt))
|
||||
{
|
||||
++fmt;
|
||||
}
|
||||
|
||||
// FIXME: better handling for format specifiers
|
||||
++fmt;
|
||||
continue;
|
||||
}
|
||||
++fmt;
|
||||
++len;
|
||||
}
|
||||
|
||||
if (len >= (int)size)
|
||||
{
|
||||
bufferOverrun(tok);
|
||||
}
|
||||
|
||||
// check arguments (if they exists)
|
||||
if (tok->tokAt(5)->str() == ",")
|
||||
{
|
||||
len = 0;
|
||||
const Token *end = tok->next()->link();
|
||||
bool argumentAlreadyChecked = false;
|
||||
int lastCheckedArgumentMaxSize = 0;
|
||||
for (const Token *tok2 = tok->tokAt(6); tok2 && tok2 != end; tok2 = tok2->next())
|
||||
{
|
||||
if (tok2->str() == ",")
|
||||
{
|
||||
argumentAlreadyChecked = false;
|
||||
lastCheckedArgumentMaxSize = 0;
|
||||
}
|
||||
else if (Token::Match(tok2, "%str%"))
|
||||
{
|
||||
int argumentSize = static_cast<int>(Token::getStrLength(tok2));
|
||||
if (argumentAlreadyChecked == false)
|
||||
{
|
||||
lastCheckedArgumentMaxSize = argumentSize;
|
||||
len += argumentSize;
|
||||
argumentAlreadyChecked = true;
|
||||
}
|
||||
else if (argumentSize > lastCheckedArgumentMaxSize)
|
||||
{
|
||||
// when sprintf() argument is ternary
|
||||
// operation we may have two and more
|
||||
// strings as argument. In this case we
|
||||
// use length of longest string.
|
||||
len += (argumentSize - lastCheckedArgumentMaxSize);
|
||||
lastCheckedArgumentMaxSize = argumentSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (len >= (int)size)
|
||||
{
|
||||
bufferOverrun(tok);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -56,10 +56,18 @@ public:
|
|||
|
||||
/** Check for buffer overruns */
|
||||
void bufferOverrun();
|
||||
static int count(const std::string &input_string);
|
||||
static int countSprintfLength(const std::string &input_string, const std::list<const Token*> ¶meters);
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
* Check code that matches: "sprintf ( %varid% , %str% [,)]" when varid is not 0,
|
||||
* and report found errors.
|
||||
* @param tok The "sprintf" token.
|
||||
* @param size The size of the buffer where sprintf is writing.
|
||||
*/
|
||||
void checkSprintfCall(const Token *tok, int size);
|
||||
|
||||
/** Check for buffer overruns - locate struct variables and check them with the .._CheckScope function */
|
||||
void checkStructVariable();
|
||||
|
||||
|
|
|
@ -826,7 +826,7 @@ private:
|
|||
check("void f()\n"
|
||||
"{\n"
|
||||
" char str[5];\n"
|
||||
" sprintf(str, \"test%s\", "");\n"
|
||||
" sprintf(str, \"test%s\", \"\");\n"
|
||||
"}\n");
|
||||
ASSERT_EQUALS("", errout.str());
|
||||
}
|
||||
|
@ -1013,17 +1013,38 @@ private:
|
|||
|
||||
void counter_test()
|
||||
{
|
||||
ASSERT_EQUALS(6, CheckBufferOverrun::count("Hello"));
|
||||
ASSERT_EQUALS(2, CheckBufferOverrun::count("s"));
|
||||
ASSERT_EQUALS(2, CheckBufferOverrun::count("i"));
|
||||
ASSERT_EQUALS(2, CheckBufferOverrun::count("%d"));
|
||||
ASSERT_EQUALS(2, CheckBufferOverrun::count("%1d"));
|
||||
ASSERT_EQUALS(3, CheckBufferOverrun::count("%2.2d"));
|
||||
ASSERT_EQUALS(1, CheckBufferOverrun::count("%s"));
|
||||
ASSERT_EQUALS(6, CheckBufferOverrun::count("%-5s"));
|
||||
ASSERT_EQUALS(2, CheckBufferOverrun::count("\\\""));
|
||||
TODO_ASSERT_EQUALS(6, CheckBufferOverrun::count("Hello\\0Text"));
|
||||
TODO_ASSERT_EQUALS(2, CheckBufferOverrun::count("%%"));
|
||||
std::list<const Token*> unknownParameter;
|
||||
unknownParameter.push_back(0);
|
||||
|
||||
ASSERT_EQUALS(6, CheckBufferOverrun::countSprintfLength("Hello", unknownParameter));
|
||||
ASSERT_EQUALS(2, CheckBufferOverrun::countSprintfLength("s", unknownParameter));
|
||||
ASSERT_EQUALS(2, CheckBufferOverrun::countSprintfLength("i", unknownParameter));
|
||||
ASSERT_EQUALS(2, CheckBufferOverrun::countSprintfLength("%d", unknownParameter));
|
||||
ASSERT_EQUALS(2, CheckBufferOverrun::countSprintfLength("%1d", unknownParameter));
|
||||
ASSERT_EQUALS(3, CheckBufferOverrun::countSprintfLength("%2.2d", unknownParameter));
|
||||
ASSERT_EQUALS(1, CheckBufferOverrun::countSprintfLength("%s", unknownParameter));
|
||||
ASSERT_EQUALS(6, CheckBufferOverrun::countSprintfLength("%-5s", unknownParameter));
|
||||
ASSERT_EQUALS(2, CheckBufferOverrun::countSprintfLength("\\\"", unknownParameter));
|
||||
TODO_ASSERT_EQUALS(6, CheckBufferOverrun::countSprintfLength("Hello\\0Text", unknownParameter));
|
||||
TODO_ASSERT_EQUALS(2, CheckBufferOverrun::countSprintfLength("%%", unknownParameter));
|
||||
|
||||
std::list<const Token*> stringAsParameter;
|
||||
{
|
||||
Token tok;
|
||||
tok.str("\"12345\"");
|
||||
stringAsParameter.push_back(&tok);
|
||||
}
|
||||
|
||||
TODO_ASSERT_EQUALS(9, CheckBufferOverrun::countSprintfLength("str%s", stringAsParameter));
|
||||
|
||||
std::list<const Token*> intAsParameter;
|
||||
{
|
||||
Token tok;
|
||||
tok.str("12345");
|
||||
stringAsParameter.push_back(&tok);
|
||||
}
|
||||
|
||||
TODO_ASSERT_EQUALS(6, CheckBufferOverrun::countSprintfLength("%02ld", intAsParameter));
|
||||
}
|
||||
|
||||
void strncpy1()
|
||||
|
|
Loading…
Reference in New Issue