speed up checks by caching commonly looked up stuff in the symbol database (CheckBufferOverrun, CheckBoost)
This commit is contained in:
parent
d7c7f8c9af
commit
0f8db28d30
|
@ -17,6 +17,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "checkboost.h"
|
#include "checkboost.h"
|
||||||
|
#include "symboldatabase.h"
|
||||||
|
|
||||||
// Register this check class (by creating a static instance of it)
|
// Register this check class (by creating a static instance of it)
|
||||||
namespace {
|
namespace {
|
||||||
|
@ -25,24 +26,29 @@ namespace {
|
||||||
|
|
||||||
void CheckBoost::checkBoostForeachModification()
|
void CheckBoost::checkBoostForeachModification()
|
||||||
{
|
{
|
||||||
for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) {
|
const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase();
|
||||||
if (!Token::simpleMatch(tok, "BOOST_FOREACH ("))
|
const std::size_t functions = symbolDatabase->functionScopes.size();
|
||||||
continue;
|
for (std::size_t i = 0; i < functions; ++i) {
|
||||||
|
const Scope * scope = symbolDatabase->functionScopes[i];
|
||||||
|
for (const Token *tok = scope->classStart->next(); tok && tok != scope->classEnd; tok = tok->next()) {
|
||||||
|
if (!Token::simpleMatch(tok, "BOOST_FOREACH ("))
|
||||||
|
continue;
|
||||||
|
|
||||||
const Token *container_tok = tok->next()->link()->previous();
|
const Token *container_tok = tok->next()->link()->previous();
|
||||||
if (!Token::Match(container_tok, "%var% ) {"))
|
if (!Token::Match(container_tok, "%var% ) {"))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
const unsigned int container_id = container_tok->varId();
|
const unsigned int container_id = container_tok->varId();
|
||||||
if (container_id == 0)
|
if (container_id == 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
const Token *tok2 = container_tok->tokAt(2);
|
const Token *tok2 = container_tok->tokAt(2);
|
||||||
const Token *end = tok2->link();
|
const Token *end = tok2->link();
|
||||||
for (; tok2 != end; tok2 = tok2->next()) {
|
for (; tok2 != end; tok2 = tok2->next()) {
|
||||||
if (Token::Match(tok2, "%varid% . insert|erase|push_back|push_front|pop_front|pop_back|clear|swap|resize|assign|merge|remove|remove_if|reverse|sort|splice|unique|pop|push", container_id)) {
|
if (Token::Match(tok2, "%varid% . insert|erase|push_back|push_front|pop_front|pop_back|clear|swap|resize|assign|merge|remove|remove_if|reverse|sort|splice|unique|pop|push", container_id)) {
|
||||||
boostForeachError(tok2);
|
boostForeachError(tok2);
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1326,11 +1326,10 @@ void CheckBufferOverrun::checkGlobalAndLocalVariable()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// find all dynamically allocated arrays next by parsing the token stream
|
// find all dynamically allocated arrays next
|
||||||
// Count { and } when parsing all tokens
|
const std::size_t functions = symbolDatabase->functionScopes.size();
|
||||||
for (std::list<Scope>::const_iterator scope = symbolDatabase->scopeList.begin(); scope != symbolDatabase->scopeList.end(); ++scope) {
|
for (std::size_t i = 0; i < functions; ++i) {
|
||||||
if (scope->type != Scope::eFunction)
|
const Scope * scope = symbolDatabase->functionScopes[i];
|
||||||
continue;
|
|
||||||
|
|
||||||
for (const Token *tok = scope->classStart; tok != scope->classEnd; tok = tok->next()) {
|
for (const Token *tok = scope->classStart; tok != scope->classEnd; tok = tok->next()) {
|
||||||
// if the previous token exists, it must be either a variable name or "[;{}]"
|
// if the previous token exists, it must be either a variable name or "[;{}]"
|
||||||
|
@ -1423,13 +1422,10 @@ void CheckBufferOverrun::checkStructVariable()
|
||||||
{
|
{
|
||||||
const SymbolDatabase * symbolDatabase = _tokenizer->getSymbolDatabase();
|
const SymbolDatabase * symbolDatabase = _tokenizer->getSymbolDatabase();
|
||||||
|
|
||||||
std::list<Scope>::const_iterator scope;
|
|
||||||
|
|
||||||
// find every class and struct
|
// find every class and struct
|
||||||
for (scope = symbolDatabase->scopeList.begin(); scope != symbolDatabase->scopeList.end(); ++scope) {
|
const std::size_t classes = symbolDatabase->classAndStructScopes.size();
|
||||||
// only check classes and structures
|
for (std::size_t i = 0; i < classes; ++i) {
|
||||||
if (!scope->isClassOrStruct())
|
const Scope * scope = symbolDatabase->classAndStructScopes[i];
|
||||||
continue;
|
|
||||||
|
|
||||||
// check all variables to see if they are arrays
|
// check all variables to see if they are arrays
|
||||||
std::list<Variable>::const_iterator var;
|
std::list<Variable>::const_iterator var;
|
||||||
|
@ -1439,13 +1435,10 @@ void CheckBufferOverrun::checkStructVariable()
|
||||||
// create ArrayInfo from the array variable
|
// create ArrayInfo from the array variable
|
||||||
ArrayInfo arrayInfo(&*var, _tokenizer);
|
ArrayInfo arrayInfo(&*var, _tokenizer);
|
||||||
|
|
||||||
std::list<Scope>::const_iterator func_scope;
|
|
||||||
|
|
||||||
// find every function
|
// find every function
|
||||||
for (func_scope = symbolDatabase->scopeList.begin(); func_scope != symbolDatabase->scopeList.end(); ++func_scope) {
|
const std::size_t functions = symbolDatabase->functionScopes.size();
|
||||||
// only check functions
|
for (std::size_t j = 0; j < functions; ++j) {
|
||||||
if (func_scope->type != Scope::eFunction)
|
const Scope * func_scope = symbolDatabase->functionScopes[j];
|
||||||
continue;
|
|
||||||
|
|
||||||
// If struct is declared in a function then check
|
// If struct is declared in a function then check
|
||||||
// if scope_func matches
|
// if scope_func matches
|
||||||
|
@ -1570,8 +1563,8 @@ void CheckBufferOverrun::checkStructVariable()
|
||||||
ArrayInfo temp = arrayInfo;
|
ArrayInfo temp = arrayInfo;
|
||||||
temp.varid(0); // do variable lookup by variable and member names rather than varid
|
temp.varid(0); // do variable lookup by variable and member names rather than varid
|
||||||
std::string varnames; // use class and member name for messages
|
std::string varnames; // use class and member name for messages
|
||||||
for (unsigned int i = 0; i < varname.size(); ++i)
|
for (unsigned int k = 0; k < varname.size(); ++k)
|
||||||
varnames += (i == 0 ? "" : ".") + varname[i];
|
varnames += (k == 0 ? "" : ".") + varname[k];
|
||||||
temp.varname(varnames);
|
temp.varname(varnames);
|
||||||
checkScope(CheckTok, varname, temp);
|
checkScope(CheckTok, varname, temp);
|
||||||
}
|
}
|
||||||
|
@ -1734,44 +1727,50 @@ void CheckBufferOverrun::checkSprintfCall(const Token *tok, const MathLib::bigin
|
||||||
//---------------------------------------------------------------------------
|
//---------------------------------------------------------------------------
|
||||||
void CheckBufferOverrun::checkBufferAllocatedWithStrlen()
|
void CheckBufferOverrun::checkBufferAllocatedWithStrlen()
|
||||||
{
|
{
|
||||||
for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) {
|
const SymbolDatabase* const symbolDatabase = _tokenizer->getSymbolDatabase();
|
||||||
unsigned int dstVarId;
|
|
||||||
unsigned int srcVarId;
|
|
||||||
|
|
||||||
// Look for allocation of a buffer based on the size of a string
|
const std::size_t functions = symbolDatabase->functionScopes.size();
|
||||||
if (Token::Match(tok, "%var% = malloc|g_malloc|g_try_malloc ( strlen ( %var% ) )")) {
|
for (std::size_t i = 0; i < functions; ++i) {
|
||||||
dstVarId = tok->varId();
|
const Scope * scope = symbolDatabase->functionScopes[i];
|
||||||
srcVarId = tok->tokAt(6)->varId();
|
for (const Token *tok = scope->classStart->next(); tok && tok != scope->classEnd; tok = tok->next()) {
|
||||||
tok = tok->tokAt(8);
|
unsigned int dstVarId;
|
||||||
} else if (Token::Match(tok, "%var% = new char [ strlen ( %var% ) ]")) {
|
unsigned int srcVarId;
|
||||||
dstVarId = tok->varId();
|
|
||||||
srcVarId = tok->tokAt(7)->varId();
|
|
||||||
tok = tok->tokAt(9);
|
|
||||||
} else if (Token::Match(tok, "%var% = realloc|g_realloc|g_try_realloc ( %var% , strlen ( %var% ) )")) {
|
|
||||||
dstVarId = tok->varId();
|
|
||||||
srcVarId = tok->tokAt(8)->varId();
|
|
||||||
tok = tok->tokAt(10);
|
|
||||||
} else
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// To avoid false positives and added complexity, we will only look for
|
// Look for allocation of a buffer based on the size of a string
|
||||||
// improper usage of the buffer within the block that it was allocated
|
if (Token::Match(tok, "%var% = malloc|g_malloc|g_try_malloc ( strlen ( %var% ) )")) {
|
||||||
for (const Token* const end = tok->scope()->classEnd; tok && tok->next() && tok != end; tok = tok->next()) {
|
dstVarId = tok->varId();
|
||||||
// If the buffers are modified, we can't be sure of their sizes
|
srcVarId = tok->tokAt(6)->varId();
|
||||||
if (tok->varId() == srcVarId || tok->varId() == dstVarId)
|
tok = tok->tokAt(8);
|
||||||
break;
|
} else if (Token::Match(tok, "%var% = new char [ strlen ( %var% ) ]")) {
|
||||||
|
dstVarId = tok->varId();
|
||||||
|
srcVarId = tok->tokAt(7)->varId();
|
||||||
|
tok = tok->tokAt(9);
|
||||||
|
} else if (Token::Match(tok, "%var% = realloc|g_realloc|g_try_realloc ( %var% , strlen ( %var% ) )")) {
|
||||||
|
dstVarId = tok->varId();
|
||||||
|
srcVarId = tok->tokAt(8)->varId();
|
||||||
|
tok = tok->tokAt(10);
|
||||||
|
} else
|
||||||
|
continue;
|
||||||
|
|
||||||
if (Token::Match(tok, "strcpy ( %varid% , %var% )", dstVarId) &&
|
// To avoid false positives and added complexity, we will only look for
|
||||||
tok->tokAt(4)->varId() == srcVarId) {
|
// improper usage of the buffer within the block that it was allocated
|
||||||
bufferOverrunError(tok);
|
for (const Token* const end = tok->scope()->classEnd; tok && tok->next() && tok != end; tok = tok->next()) {
|
||||||
} else if (Token::Match(tok, "sprintf ( %varid% , %str% , %var% )", dstVarId) &&
|
// If the buffers are modified, we can't be sure of their sizes
|
||||||
tok->tokAt(6)->varId() == srcVarId &&
|
if (tok->varId() == srcVarId || tok->varId() == dstVarId)
|
||||||
tok->strAt(4).find("%s") != std::string::npos) {
|
break;
|
||||||
bufferOverrunError(tok);
|
|
||||||
|
if (Token::Match(tok, "strcpy ( %varid% , %var% )", dstVarId) &&
|
||||||
|
tok->tokAt(4)->varId() == srcVarId) {
|
||||||
|
bufferOverrunError(tok);
|
||||||
|
} else if (Token::Match(tok, "sprintf ( %varid% , %str% , %var% )", dstVarId) &&
|
||||||
|
tok->tokAt(6)->varId() == srcVarId &&
|
||||||
|
tok->strAt(4).find("%s") != std::string::npos) {
|
||||||
|
bufferOverrunError(tok);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
if (!tok)
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
if (!tok)
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1790,8 +1789,11 @@ void CheckBufferOverrun::checkInsecureCmdLineArgs()
|
||||||
{
|
{
|
||||||
const SymbolDatabase* const symbolDatabase = _tokenizer->getSymbolDatabase();
|
const SymbolDatabase* const symbolDatabase = _tokenizer->getSymbolDatabase();
|
||||||
|
|
||||||
for (std::list<Scope>::const_iterator i = symbolDatabase->scopeList.begin(); i != symbolDatabase->scopeList.end(); ++i) {
|
std::size_t functions = symbolDatabase->functionScopes.size();
|
||||||
for (std::list<Function>::const_iterator j = i->functionList.begin(); j != i->functionList.end(); ++j) {
|
for (std::size_t i = 0; i < functions; ++i) {
|
||||||
|
const Scope * scope = symbolDatabase->functionScopes[i];
|
||||||
|
Function * j = scope->function;
|
||||||
|
if (j) {
|
||||||
const Token* tok = j->token;
|
const Token* tok = j->token;
|
||||||
|
|
||||||
// Get the name of the argv variable
|
// Get the name of the argv variable
|
||||||
|
@ -1829,7 +1831,6 @@ void CheckBufferOverrun::checkInsecureCmdLineArgs()
|
||||||
tok->strAt(4).find("%s") != std::string::npos) {
|
tok->strAt(4).find("%s") != std::string::npos) {
|
||||||
cmdLineArgsError(tok);
|
cmdLineArgsError(tok);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2087,26 +2088,32 @@ void CheckBufferOverrun::arrayIndexThenCheck()
|
||||||
{
|
{
|
||||||
if (!_settings->isEnabled("style"))
|
if (!_settings->isEnabled("style"))
|
||||||
return;
|
return;
|
||||||
for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) {
|
|
||||||
if (Token::Match(tok, "%var% [ %var% ]")) {
|
|
||||||
const std::string& indexName(tok->strAt(2));
|
|
||||||
|
|
||||||
// skip array index..
|
const SymbolDatabase* symbolDatabase = _tokenizer->getSymbolDatabase();
|
||||||
tok = tok->tokAt(4);
|
const std::size_t functions = symbolDatabase->functionScopes.size();
|
||||||
while (tok && tok->str() == "[")
|
for (std::size_t i = 0; i < functions; ++i) {
|
||||||
tok = tok->link()->next();
|
const Scope * scope = symbolDatabase->functionScopes[i];
|
||||||
|
for (const Token *tok = scope->classStart; tok && tok != scope->classEnd; tok = tok->next()) {
|
||||||
|
if (Token::Match(tok, "%var% [ %var% ]")) {
|
||||||
|
const std::string& indexName(tok->strAt(2));
|
||||||
|
|
||||||
// syntax error
|
// skip array index..
|
||||||
if (!tok)
|
tok = tok->tokAt(4);
|
||||||
return;
|
while (tok && tok->str() == "[")
|
||||||
|
tok = tok->link()->next();
|
||||||
|
|
||||||
// skip comparison
|
// syntax error
|
||||||
if (tok->type() == Token::eComparisonOp && tok->strAt(2) == "&&")
|
if (!tok)
|
||||||
tok = tok->tokAt(2);
|
return;
|
||||||
|
|
||||||
// check if array index is ok
|
// skip comparison
|
||||||
if (Token::Match(tok, ("&& " + indexName + " <|<=").c_str()))
|
if (tok->type() == Token::eComparisonOp && tok->strAt(2) == "&&")
|
||||||
arrayIndexThenCheckError(tok, indexName);
|
tok = tok->tokAt(2);
|
||||||
|
|
||||||
|
// check if array index is ok
|
||||||
|
if (Token::Match(tok, ("&& " + indexName + " <|<=").c_str()))
|
||||||
|
arrayIndexThenCheckError(tok, indexName);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue