Library: Improved handling in CheckNullPointer::parseFunctionCall for Library data
This commit is contained in:
parent
66d8fa62d1
commit
7443883b9c
|
@ -36,10 +36,11 @@ namespace {
|
||||||
* @brief parse a function call and extract information about variable usage
|
* @brief parse a function call and extract information about variable usage
|
||||||
* @param tok first token
|
* @param tok first token
|
||||||
* @param var variables that the function read / write.
|
* @param var variables that the function read / write.
|
||||||
|
* @param library --library files data
|
||||||
* @param value 0 => invalid with null pointers as parameter.
|
* @param value 0 => invalid with null pointers as parameter.
|
||||||
* 1-.. => only invalid with uninitialized data.
|
* 1-.. => only invalid with uninitialized data.
|
||||||
*/
|
*/
|
||||||
void CheckNullPointer::parseFunctionCall(const Token &tok, std::list<const Token *> &var, unsigned char value)
|
void CheckNullPointer::parseFunctionCall(const Token &tok, std::list<const Token *> &var, const Library *library, unsigned char value)
|
||||||
{
|
{
|
||||||
// standard functions that dereference first parameter..
|
// standard functions that dereference first parameter..
|
||||||
static std::set<std::string> functionNames1_all; // used no matter what 'value' is
|
static std::set<std::string> functionNames1_all; // used no matter what 'value' is
|
||||||
|
@ -237,7 +238,11 @@ void CheckNullPointer::parseFunctionCall(const Token &tok, std::list<const Token
|
||||||
var.push_back(firstParam);
|
var.push_back(firstParam);
|
||||||
else if (value == 0 && Token::Match(&tok, "snprintf|vsnprintf|fnprintf|vfnprintf") && secondParam && secondParam->str() != "0") // Only if length (second parameter) is not zero
|
else if (value == 0 && Token::Match(&tok, "snprintf|vsnprintf|fnprintf|vfnprintf") && secondParam && secondParam->str() != "0") // Only if length (second parameter) is not zero
|
||||||
var.push_back(firstParam);
|
var.push_back(firstParam);
|
||||||
}
|
else if (value == 0 && library != NULL && library->isnullargbad(tok.str(),1))
|
||||||
|
var.push_back(firstParam);
|
||||||
|
else if (value == 1 && library != NULL && library->isuninitargbad(tok.str(),1))
|
||||||
|
var.push_back(firstParam);
|
||||||
|
}
|
||||||
|
|
||||||
// 2nd parameter..
|
// 2nd parameter..
|
||||||
if ((value == 0 && Token::Match(secondParam, "0|NULL ,|)")) || (secondParam && secondParam->varId() > 0)) {
|
if ((value == 0 && Token::Match(secondParam, "0|NULL ,|)")) || (secondParam && secondParam->varId() > 0)) {
|
||||||
|
@ -245,6 +250,10 @@ void CheckNullPointer::parseFunctionCall(const Token &tok, std::list<const Token
|
||||||
var.push_back(secondParam);
|
var.push_back(secondParam);
|
||||||
else if (value == 0 && functionNames2_nullptr.find(tok.str()) != functionNames2_nullptr.end())
|
else if (value == 0 && functionNames2_nullptr.find(tok.str()) != functionNames2_nullptr.end())
|
||||||
var.push_back(secondParam);
|
var.push_back(secondParam);
|
||||||
|
else if (value == 0 && library != NULL && library->isnullargbad(tok.str(),2))
|
||||||
|
var.push_back(secondParam);
|
||||||
|
else if (value == 1 && library != NULL && library->isuninitargbad(tok.str(),2))
|
||||||
|
var.push_back(secondParam);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Token::Match(&tok, "printf|sprintf|snprintf|fprintf|fnprintf|scanf|sscanf|fscanf|wprintf|swprintf|fwprintf|wscanf|swscanf|fwscanf")) {
|
if (Token::Match(&tok, "printf|sprintf|snprintf|fprintf|fnprintf|scanf|sscanf|fscanf|wprintf|swprintf|fwprintf|wscanf|swscanf|fwscanf")) {
|
||||||
|
@ -808,7 +817,7 @@ void CheckNullPointer::nullPointerByDeRefAndChec()
|
||||||
|
|
||||||
if (Token::Match(tok2->next(), "%var% ( %varid% ,", varid)) {
|
if (Token::Match(tok2->next(), "%var% ( %varid% ,", varid)) {
|
||||||
std::list<const Token *> varlist;
|
std::list<const Token *> varlist;
|
||||||
parseFunctionCall(*(tok2->next()), varlist, 0);
|
parseFunctionCall(*(tok2->next()), varlist, &_settings->library, 0);
|
||||||
if (!varlist.empty() && varlist.front() == tok2->tokAt(3)) {
|
if (!varlist.empty() && varlist.front() == tok2->tokAt(3)) {
|
||||||
nullPointerError(tok2->tokAt(3), varname, tok, inconclusive);
|
nullPointerError(tok2->tokAt(3), varname, tok, inconclusive);
|
||||||
break;
|
break;
|
||||||
|
@ -1069,7 +1078,7 @@ void CheckNullPointer::nullPointerByCheckAndDeRef()
|
||||||
// function call, check if pointer is dereferenced
|
// function call, check if pointer is dereferenced
|
||||||
if (Token::Match(tok2, "%var% (") && !Token::Match(tok2, "if|while")) {
|
if (Token::Match(tok2, "%var% (") && !Token::Match(tok2, "if|while")) {
|
||||||
std::list<const Token *> vars;
|
std::list<const Token *> vars;
|
||||||
parseFunctionCall(*tok2, vars, 0);
|
parseFunctionCall(*tok2, vars, &_settings->library, 0);
|
||||||
for (std::list<const Token *>::const_iterator it = vars.begin(); it != vars.end(); ++it) {
|
for (std::list<const Token *>::const_iterator it = vars.begin(); it != vars.end(); ++it) {
|
||||||
if (Token::Match(*it, "%varid% [,)]", varid)) {
|
if (Token::Match(*it, "%varid% [,)]", varid)) {
|
||||||
nullPointerError(*it, pointerName, vartok, inconclusive);
|
nullPointerError(*it, pointerName, vartok, inconclusive);
|
||||||
|
@ -1167,7 +1176,7 @@ void CheckNullPointer::nullConstantDereference()
|
||||||
nullPointerError(tok);
|
nullPointerError(tok);
|
||||||
} else { // function call
|
} else { // function call
|
||||||
std::list<const Token *> var;
|
std::list<const Token *> var;
|
||||||
parseFunctionCall(*tok, var, 0);
|
parseFunctionCall(*tok, var, &_settings->library, 0);
|
||||||
|
|
||||||
// is one of the var items a NULL pointer?
|
// is one of the var items a NULL pointer?
|
||||||
for (std::list<const Token *>::const_iterator it = var.begin(); it != var.end(); ++it) {
|
for (std::list<const Token *>::const_iterator it = var.begin(); it != var.end(); ++it) {
|
||||||
|
@ -1351,16 +1360,18 @@ void CheckNullPointer::nullPointerDefaultArgument()
|
||||||
class Nullpointer : public ExecutionPath {
|
class Nullpointer : public ExecutionPath {
|
||||||
public:
|
public:
|
||||||
/** Startup constructor */
|
/** Startup constructor */
|
||||||
Nullpointer(Check *c, const SymbolDatabase* symbolDatabase_) : ExecutionPath(c, 0), symbolDatabase(symbolDatabase_), null(false) {
|
Nullpointer(Check *c, const SymbolDatabase* symbolDatabase_, const Library *lib) : ExecutionPath(c, 0), symbolDatabase(symbolDatabase_), library(lib), null(false) {
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const SymbolDatabase* symbolDatabase;
|
const SymbolDatabase* symbolDatabase;
|
||||||
|
const Library *library;
|
||||||
|
|
||||||
/** Create checking of specific variable: */
|
/** Create checking of specific variable: */
|
||||||
Nullpointer(Check *c, const unsigned int id, const std::string &name, const SymbolDatabase* symbolDatabase_)
|
Nullpointer(Check *c, const unsigned int id, const std::string &name, const SymbolDatabase* symbolDatabase_, const Library *lib)
|
||||||
: ExecutionPath(c, id),
|
: ExecutionPath(c, id),
|
||||||
symbolDatabase(symbolDatabase_),
|
symbolDatabase(symbolDatabase_),
|
||||||
|
library(lib),
|
||||||
varname(name),
|
varname(name),
|
||||||
null(false) {
|
null(false) {
|
||||||
}
|
}
|
||||||
|
@ -1430,7 +1441,7 @@ private:
|
||||||
// Pointer declaration declaration?
|
// Pointer declaration declaration?
|
||||||
const Variable *var = tok.variable();
|
const Variable *var = tok.variable();
|
||||||
if (var && var->isPointer() && var->nameToken() == &tok)
|
if (var && var->isPointer() && var->nameToken() == &tok)
|
||||||
checks.push_back(new Nullpointer(owner, var->varId(), var->name(), symbolDatabase));
|
checks.push_back(new Nullpointer(owner, var->varId(), var->name(), symbolDatabase, library));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Token::simpleMatch(&tok, "try {")) {
|
if (Token::simpleMatch(&tok, "try {")) {
|
||||||
|
@ -1450,7 +1461,7 @@ private:
|
||||||
|
|
||||||
// parse usage..
|
// parse usage..
|
||||||
std::list<const Token *> var;
|
std::list<const Token *> var;
|
||||||
CheckNullPointer::parseFunctionCall(tok, var, 0);
|
CheckNullPointer::parseFunctionCall(tok, var, library, 0);
|
||||||
for (std::list<const Token *>::const_iterator it = var.begin(); it != var.end(); ++it)
|
for (std::list<const Token *>::const_iterator it = var.begin(); it != var.end(); ++it)
|
||||||
dereference(checks, *it);
|
dereference(checks, *it);
|
||||||
}
|
}
|
||||||
|
@ -1518,7 +1529,7 @@ private:
|
||||||
|
|
||||||
if (Token::Match(&tok, "!| %var% (")) {
|
if (Token::Match(&tok, "!| %var% (")) {
|
||||||
std::list<const Token *> var;
|
std::list<const Token *> var;
|
||||||
CheckNullPointer::parseFunctionCall(tok.str() == "!" ? *tok.next() : tok, var, 0);
|
CheckNullPointer::parseFunctionCall(tok.str() == "!" ? *tok.next() : tok, var, library, 0);
|
||||||
for (std::list<const Token *>::const_iterator it = var.begin(); it != var.end(); ++it)
|
for (std::list<const Token *>::const_iterator it = var.begin(); it != var.end(); ++it)
|
||||||
dereference(checks, *it);
|
dereference(checks, *it);
|
||||||
}
|
}
|
||||||
|
@ -1544,7 +1555,7 @@ private:
|
||||||
void CheckNullPointer::executionPaths()
|
void CheckNullPointer::executionPaths()
|
||||||
{
|
{
|
||||||
// Check for null pointer errors..
|
// Check for null pointer errors..
|
||||||
Nullpointer c(this, _tokenizer->getSymbolDatabase());
|
Nullpointer c(this, _tokenizer->getSymbolDatabase(), &_settings->library);
|
||||||
checkExecutionPaths(_tokenizer->getSymbolDatabase(), &c);
|
checkExecutionPaths(_tokenizer->getSymbolDatabase(), &c);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -63,11 +63,13 @@ public:
|
||||||
* @brief parse a function call and extract information about variable usage
|
* @brief parse a function call and extract information about variable usage
|
||||||
* @param tok first token
|
* @param tok first token
|
||||||
* @param var variables that the function read / write.
|
* @param var variables that the function read / write.
|
||||||
|
* @param library --library files data
|
||||||
* @param value 0 => invalid with null pointers as parameter.
|
* @param value 0 => invalid with null pointers as parameter.
|
||||||
* non-zero => invalid with uninitialized data.
|
* non-zero => invalid with uninitialized data.
|
||||||
*/
|
*/
|
||||||
static void parseFunctionCall(const Token &tok,
|
static void parseFunctionCall(const Token &tok,
|
||||||
std::list<const Token *> &var,
|
std::list<const Token *> &var,
|
||||||
|
const Library *library,
|
||||||
unsigned char value);
|
unsigned char value);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -622,7 +622,7 @@ private:
|
||||||
// parse usage..
|
// parse usage..
|
||||||
{
|
{
|
||||||
std::list<const Token *> var1;
|
std::list<const Token *> var1;
|
||||||
CheckNullPointer::parseFunctionCall(tok, var1, 1);
|
CheckNullPointer::parseFunctionCall(tok, var1, library, 1);
|
||||||
for (std::list<const Token *>::const_iterator it = var1.begin(); it != var1.end(); ++it) {
|
for (std::list<const Token *>::const_iterator it = var1.begin(); it != var1.end(); ++it) {
|
||||||
// does iterator point at first function parameter?
|
// does iterator point at first function parameter?
|
||||||
const bool firstPar(*it == tok.tokAt(2));
|
const bool firstPar(*it == tok.tokAt(2));
|
||||||
|
@ -643,7 +643,7 @@ private:
|
||||||
|
|
||||||
// Using uninitialized pointer is bad if using null pointer is bad
|
// Using uninitialized pointer is bad if using null pointer is bad
|
||||||
std::list<const Token *> var2;
|
std::list<const Token *> var2;
|
||||||
CheckNullPointer::parseFunctionCall(tok, var2, 0);
|
CheckNullPointer::parseFunctionCall(tok, var2, library, 0);
|
||||||
for (std::list<const Token *>::const_iterator it = var2.begin(); it != var2.end(); ++it) {
|
for (std::list<const Token *>::const_iterator it = var2.begin(); it != var2.end(); ++it) {
|
||||||
if (std::find(var1.begin(), var1.end(), *it) == var1.end())
|
if (std::find(var1.begin(), var1.end(), *it) == var1.end())
|
||||||
use_dead_pointer(checks, *it);
|
use_dead_pointer(checks, *it);
|
||||||
|
@ -886,7 +886,7 @@ private:
|
||||||
else if (Token::Match(&tok, "!| %var% (")) {
|
else if (Token::Match(&tok, "!| %var% (")) {
|
||||||
const Token * const ftok = (tok.str() == "!") ? tok.next() : &tok;
|
const Token * const ftok = (tok.str() == "!") ? tok.next() : &tok;
|
||||||
std::list<const Token *> var1;
|
std::list<const Token *> var1;
|
||||||
CheckNullPointer::parseFunctionCall(*ftok, var1, 1);
|
CheckNullPointer::parseFunctionCall(*ftok, var1, library, 1);
|
||||||
for (std::list<const Token *>::const_iterator it = var1.begin(); it != var1.end(); ++it) {
|
for (std::list<const Token *>::const_iterator it = var1.begin(); it != var1.end(); ++it) {
|
||||||
// is function memset/memcpy/etc?
|
// is function memset/memcpy/etc?
|
||||||
if (ftok->str().compare(0,3,"mem") == 0)
|
if (ftok->str().compare(0,3,"mem") == 0)
|
||||||
|
|
|
@ -80,6 +80,16 @@ public:
|
||||||
// function name, argument nr => argument data
|
// function name, argument nr => argument data
|
||||||
std::map<std::string, std::map<int, Argument> > functionArgument;
|
std::map<std::string, std::map<int, Argument> > functionArgument;
|
||||||
|
|
||||||
|
bool isnullargbad(const std::string &functionName, int argnr) const {
|
||||||
|
const Argument *arg = getarg(functionName,argnr);
|
||||||
|
return arg && arg->nullpointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isuninitargbad(const std::string &functionName, int argnr) const {
|
||||||
|
const Argument *arg = getarg(functionName,argnr);
|
||||||
|
return arg && arg->uninitdata;
|
||||||
|
}
|
||||||
|
|
||||||
std::set<std::string> returnuninitdata;
|
std::set<std::string> returnuninitdata;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -88,6 +98,17 @@ private:
|
||||||
std::map<std::string, int> _dealloc; // deallocation functions
|
std::map<std::string, int> _dealloc; // deallocation functions
|
||||||
std::map<std::string, bool> _noreturn; // is function noreturn?
|
std::map<std::string, bool> _noreturn; // is function noreturn?
|
||||||
|
|
||||||
|
const Argument * getarg(const std::string &functionName, int argnr) const {
|
||||||
|
std::map<std::string, std::map<int, Argument> >::const_iterator it1;
|
||||||
|
it1 = functionArgument.find(functionName);
|
||||||
|
if (it1 != functionArgument.end()) {
|
||||||
|
const std::map<int,Argument>::const_iterator it2 = it1->second.find(argnr);
|
||||||
|
if (it2 != it1->second.end())
|
||||||
|
return &it2->second;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
int getid(const std::map<std::string,int> &data, const std::string &name) const {
|
int getid(const std::map<std::string,int> &data, const std::string &name) const {
|
||||||
const std::map<std::string,int>::const_iterator it = data.find(name);
|
const std::map<std::string,int>::const_iterator it = data.find(name);
|
||||||
return (it == data.end()) ? 0 : it->second;
|
return (it == data.end()) ? 0 : it->second;
|
||||||
|
|
|
@ -72,6 +72,7 @@ private:
|
||||||
TEST_CASE(nullpointerStdString);
|
TEST_CASE(nullpointerStdString);
|
||||||
TEST_CASE(nullpointerStdStream);
|
TEST_CASE(nullpointerStdStream);
|
||||||
TEST_CASE(functioncall);
|
TEST_CASE(functioncall);
|
||||||
|
TEST_CASE(functioncalllibrary); // use Library to parse function call
|
||||||
TEST_CASE(crash1);
|
TEST_CASE(crash1);
|
||||||
TEST_CASE(functioncallDefaultArguments);
|
TEST_CASE(functioncallDefaultArguments);
|
||||||
}
|
}
|
||||||
|
@ -2105,6 +2106,59 @@ private:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void functioncalllibrary() {
|
||||||
|
Settings settings;
|
||||||
|
Tokenizer tokenizer(&settings,this);
|
||||||
|
std::istringstream code("void f() { int a,b; x(a,b); }");
|
||||||
|
tokenizer.tokenize(code,"test.c");
|
||||||
|
const Token *xtok = Token::findsimplematch(tokenizer.tokens(), "x");
|
||||||
|
|
||||||
|
// nothing bad..
|
||||||
|
{
|
||||||
|
Library library;
|
||||||
|
Library::Argument arg = {0};
|
||||||
|
library.functionArgument["x"][1] = arg;
|
||||||
|
library.functionArgument["x"][2] = arg;
|
||||||
|
|
||||||
|
std::list<const Token *> null, uninit;
|
||||||
|
CheckNullPointer::parseFunctionCall(*xtok, null, &library, 0U);
|
||||||
|
CheckNullPointer::parseFunctionCall(*xtok, uninit, &library, 1U);
|
||||||
|
ASSERT_EQUALS(0U, null.size());
|
||||||
|
ASSERT_EQUALS(0U, uninit.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
// for 1st parameter null pointer is not ok..
|
||||||
|
{
|
||||||
|
Library library;
|
||||||
|
Library::Argument arg = {0};
|
||||||
|
library.functionArgument["x"][1] = arg;
|
||||||
|
library.functionArgument["x"][2] = arg;
|
||||||
|
library.functionArgument["x"][1].nullpointer = true;
|
||||||
|
|
||||||
|
std::list<const Token *> null,uninit;
|
||||||
|
CheckNullPointer::parseFunctionCall(*xtok, null, &library, 0U);
|
||||||
|
CheckNullPointer::parseFunctionCall(*xtok, uninit, &library, 1U);
|
||||||
|
ASSERT_EQUALS(1U, null.size());
|
||||||
|
ASSERT_EQUALS("a", null.front()->str());
|
||||||
|
ASSERT_EQUALS(0U, uninit.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
// for 2nd parameter uninit data is not ok..
|
||||||
|
{
|
||||||
|
Library library;
|
||||||
|
Library::Argument arg = {0};
|
||||||
|
library.functionArgument["x"][1] = arg;
|
||||||
|
library.functionArgument["x"][2] = arg;
|
||||||
|
library.functionArgument["x"][2].uninitdata = true;
|
||||||
|
|
||||||
|
std::list<const Token *> null,uninit;
|
||||||
|
CheckNullPointer::parseFunctionCall(*xtok, null, &library, 0U);
|
||||||
|
CheckNullPointer::parseFunctionCall(*xtok, uninit, &library, 1U);
|
||||||
|
ASSERT_EQUALS(0U, null.size());
|
||||||
|
ASSERT_EQUALS(1U, uninit.size());
|
||||||
|
ASSERT_EQUALS("b", uninit.front()->str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void functioncallDefaultArguments() {
|
void functioncallDefaultArguments() {
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue