fix #3094 (Buffer access out-of-bounds in struct variable)

This commit is contained in:
Robert Reif 2011-09-09 07:16:39 -04:00
parent 812a17f294
commit 16924c7c7a
2 changed files with 112 additions and 109 deletions

View File

@ -1500,128 +1500,119 @@ void CheckBufferOverrun::checkStructVariable()
if (!scope->isClassOrStruct()) if (!scope->isClassOrStruct())
continue; continue;
// check all variables // check all variables to see if they are arrays
std::list<Variable>::const_iterator var; std::list<Variable>::const_iterator var;
for (var = scope->varlist.begin(); var != scope->varlist.end(); ++var) for (var = scope->varlist.begin(); var != scope->varlist.end(); ++var)
{ {
// find all array variables // find all array variables
if (var->isArray()) if (var->isArray())
{ {
// create ArrayInfo from the array variable
ArrayInfo arrayInfo(&*var, _tokenizer); ArrayInfo arrayInfo(&*var, _tokenizer);
// check each function for array variable usage std::list<Scope>::const_iterator func_scope;
std::list<Function>::const_iterator func;
for (func = scope->functionList.begin(); func != scope->functionList.end(); ++func) // find every function
for (func_scope = symbolDatabase->scopeList.begin(); func_scope != symbolDatabase->scopeList.end(); ++func_scope)
{ {
// check existing and non-empty function // only check functions
if (func->hasBody && func->start->next() != func->start->link()) if (func_scope->type != Scope::eFunction)
continue;
// is this a member function of this class/struct?
if (func_scope->functionOf == &*scope)
{ {
const Token *tok = func->start->next(); // only check non-empty function
checkScope(tok, arrayInfo); if (func_scope->function->start->next() != func_scope->function->start->link())
{
// start checking after the {
const Token *tok = func_scope->function->start->next();
checkScope(tok, arrayInfo);
}
}
// not a member function of this class/struct
else
{
// skip inner scopes..
/** @todo false negatives: handle inner scopes someday */
if (scope->nestedIn->isClassOrStruct())
continue;
// Only handling 1-dimensional arrays yet..
/** @todo false negatives: handle multi-dimension arrays someday */
if (arrayInfo.num().size() > 1)
continue;
// Skip array with only 0/1 elements because those are
// often overrun intentionally
/** @todo false negatives: only true if last member of struct and
* dynamically allocated with size larger that struct */
/** @todo false negatives: calculate real array size based on allocated size */
if (arrayInfo.num(0) <= 1)
continue;
std::vector<std::string> varname;
varname.push_back("");
varname.push_back(arrayInfo.varname());
// search the function and it's parameters
for (const Token *tok3 = func_scope->classDef; tok3 && tok3 != func_scope->classEnd; tok3 = tok3->next())
{
// search for the class/struct name
if (tok3->str() != scope->className)
continue;
// Declare variable: Fred fred1;
if (Token::Match(tok3->next(), "%var% ;"))
varname[0] = tok3->strAt(1);
// Declare pointer or reference: Fred *fred1
else if (Token::Match(tok3->next(), "*|& %var% [,);=]"))
varname[0] = tok3->strAt(2);
else
continue;
// Goto end of statement.
const Token *CheckTok = NULL;
while (tok3 && tok3 != func_scope->classEnd)
{
// End of statement.
if (tok3->str() == ";")
{
CheckTok = tok3;
break;
}
// End of function declaration..
if (Token::simpleMatch(tok3, ") ;"))
break;
// Function implementation..
if (Token::simpleMatch(tok3, ") {"))
{
CheckTok = tok3->tokAt(2);
break;
}
tok3 = tok3->next();
}
if (!tok3)
break;
if (!CheckTok)
continue;
// Check variable usage..
checkScope(CheckTok, varname, static_cast<int>(arrayInfo.num(0)), static_cast<int>(arrayInfo.num(0) * arrayInfo.element_size()), 0);
}
} }
} }
} }
} }
} }
for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next())
{
if (tok->str() == "{")
{
tok = tok->link();
}
if (!Token::Match(tok, "struct|class %var% {|:"))
continue;
const std::string &structname = tok->next()->str();
const Token *tok2 = tok;
while (tok2 && tok2->str() != "{")
tok2 = tok2->next();
// Found a struct declaration. Search for arrays..
for (; tok2; tok2 = tok2->next())
{
// skip inner scopes..
if (tok2->next() && tok2->next()->str() == "{")
{
tok2 = tok2->next()->link();
continue;
}
if (tok2->str() == "}")
break;
ArrayInfo arrayInfo;
if (!arrayInfo.declare(tok2->next(), *_tokenizer))
continue;
// Only handling 1-dimensional arrays yet..
if (arrayInfo.num().size() > 1)
continue;
// Skip array with only 0/1 elements because those are
// often overrun intentionally
if (arrayInfo.num(0) <= 1)
continue;
std::vector<std::string> varname;
varname.push_back("");
varname.push_back(arrayInfo.varname());
for (const Token *tok3 = _tokenizer->tokens(); tok3; tok3 = tok3->next())
{
if (tok3->str() != structname)
continue;
// Declare variable: Fred fred1;
if (Token::Match(tok3->next(), "%var% ;"))
varname[0] = tok3->strAt(1);
// Declare pointer or reference: Fred *fred1
else if (Token::Match(tok3->next(), "*|& %var% [,);=]"))
varname[0] = tok3->strAt(2);
else
continue;
// Goto end of statement.
const Token *CheckTok = NULL;
while (tok3)
{
// End of statement.
if (tok3->str() == ";")
{
CheckTok = tok3;
break;
}
// End of function declaration..
if (Token::simpleMatch(tok3, ") ;"))
break;
// Function implementation..
if (Token::simpleMatch(tok3, ") {"))
{
CheckTok = tok3->tokAt(2);
break;
}
tok3 = tok3->next();
}
if (!tok3)
break;
if (!CheckTok)
continue;
// Check variable usage..
checkScope(CheckTok, varname, static_cast<int>(arrayInfo.num(0)), static_cast<int>(arrayInfo.num(0) * arrayInfo.element_size()), 0);
}
}
}
} }
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------

View File

@ -1779,7 +1779,7 @@ private:
" } abc;\n" " } abc;\n"
" strcpy( abc.str, \"abcdef\" );\n" " strcpy( abc.str, \"abcdef\" );\n"
"}\n"); "}\n");
TODO_ASSERT_EQUALS("[test.cpp:7]: (error) Buffer access out-of-bounds: abc.str\n", "", errout.str()); ASSERT_EQUALS("[test.cpp:7]: (error) Buffer access out-of-bounds: abc.str\n", errout.str());
check("static void f()\n" check("static void f()\n"
"{\n" "{\n"
@ -1791,7 +1791,7 @@ private:
" strcpy( abc->str, \"abcdef\" );\n" " strcpy( abc->str, \"abcdef\" );\n"
" free(abc);\n" " free(abc);\n"
"}\n"); "}\n");
TODO_ASSERT_EQUALS("[test.cpp:8]: (error) Buffer access out-of-bounds: abc.str\n", "", errout.str()); ASSERT_EQUALS("[test.cpp:8]: (error) Buffer access out-of-bounds: abc.str\n", errout.str());
} }
@ -3322,6 +3322,18 @@ private:
" x.buf[10] = 0;\n" " x.buf[10] = 0;\n"
"}\n"); "}\n");
ASSERT_EQUALS("", errout.str()); ASSERT_EQUALS("", errout.str());
check("class A {\n"
"public:\n"
" struct X { char buf[10]; };\n"
"}\n"
"\n"
"void f()\n"
"{\n"
" A::X x;\n"
" x.buf[10] = 0;\n"
"}\n");
TODO_ASSERT_EQUALS("[test.cpp:9]: (error) Array 'x.buf[10]' index 10 out of bounds\n", "", errout.str());
} }
void getErrorMessages() void getErrorMessages()