Library: More strict matching of functions

This commit is contained in:
Daniel Marjamäki 2015-01-08 19:31:41 +01:00
parent 0202efc480
commit 6a8293a8b7
10 changed files with 104 additions and 73 deletions

View File

@ -345,7 +345,7 @@ static bool checkMinSizes(const std::list<Library::ArgumentChecks::MinSize> &min
void CheckBufferOverrun::checkFunctionParameter(const Token &ftok, unsigned int par, const ArrayInfo &arrayInfo, const std::list<const Token *>& callstack)
{
const std::list<Library::ArgumentChecks::MinSize> * const minsizes = _settings->library.argminsizes(ftok.str(),par);
const std::list<Library::ArgumentChecks::MinSize> * const minsizes = _settings->library.argminsizes(&ftok,par);
if (minsizes && (!(Token::simpleMatch(ftok.previous(), ".") || Token::Match(ftok.tokAt(-2), "!!std ::")))) {
if (arrayInfo.element_size() == 0)
@ -1633,7 +1633,7 @@ void CheckBufferOverrun::checkStringArgument()
const Token *strtoken = argtok->getValueTokenMinStrSize();
if (!strtoken)
continue;
const std::list<Library::ArgumentChecks::MinSize> *minsizes = _settings->library.argminsizes(tok->str(), argnr);
const std::list<Library::ArgumentChecks::MinSize> *minsizes = _settings->library.argminsizes(tok, argnr);
if (!minsizes)
continue;
if (checkMinSizes(*minsizes, tok, Token::getStrSize(strtoken), nullptr))

View File

@ -578,7 +578,7 @@ const char * CheckMemoryLeakInFunction::call_func(const Token *tok, std::list<co
return 0;
}
if ((_settings->library.isnoreturn(tok->str()) || (tok->function() && tok->function()->isAttributeNoreturn())) && tok->strAt(-1) != "=")
if (_settings->library.isnoreturn(tok) && tok->strAt(-1) != "=")
return "exit";
if (varid > 0 && (getReallocationType(tok, varid) != No || getDeallocationType(tok, varid) != No))
@ -623,7 +623,7 @@ const char * CheckMemoryLeakInFunction::call_func(const Token *tok, std::list<co
std::string temp;
if (!_settings->library.isScopeNoReturn(tok->function()->functionScope->classEnd, &temp) && temp.empty())
return nullptr;
} else if (_settings->library.isnotnoreturn(funcname))
} else if (_settings->library.isnotnoreturn(tok))
return nullptr;
return "callfunc";
@ -1283,7 +1283,7 @@ Token *CheckMemoryLeakInFunction::getcode(const Token *tok, std::list<const Toke
// just add a "::use"
// The "::use" means that a member function was probably called but it wasn't analysed further
else if (classmember) {
if (_settings->library.isnoreturn(tok->str()) || (tok->function() && tok->function()->isAttributeNoreturn()))
if (_settings->library.isnoreturn(tok))
addtoken(&rettail, tok, "exit");
else if (!test_white_list_with_lib(tok->str(), _settings)) {

View File

@ -57,17 +57,17 @@ void CheckNullPointer::parseFunctionCall(const Token &tok, std::list<const Token
(value == 0 && Token::Match(firstParam, "0|NULL ,|)"))) {
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);
else if (value == 0 && library != nullptr && library->isnullargbad(tok.str(), 1) && checkNullpointerFunctionCallPlausibility(tok.function(), 1))
else if (value == 0 && library != nullptr && library->isnullargbad(&tok, 1) && checkNullpointerFunctionCallPlausibility(tok.function(), 1))
var.push_back(firstParam);
else if (value == 1 && library != nullptr && library->isuninitargbad(tok.str(), 1))
else if (value == 1 && library != nullptr && library->isuninitargbad(&tok, 1))
var.push_back(firstParam);
}
// 2nd parameter..
if ((value == 0 && Token::Match(secondParam, "0|NULL ,|)")) || (secondParam && secondParam->varId() > 0 && Token::Match(secondParam->next(),"[,)]"))) {
if (value == 0 && library != nullptr && library->isnullargbad(tok.str(), 2) && checkNullpointerFunctionCallPlausibility(tok.function(), 2))
if (value == 0 && library != nullptr && library->isnullargbad(&tok, 2) && checkNullpointerFunctionCallPlausibility(tok.function(), 2))
var.push_back(secondParam);
else if (value == 1 && library != nullptr && library->isuninitargbad(tok.str(), 2))
else if (value == 1 && library != nullptr && library->isuninitargbad(&tok, 2))
var.push_back(secondParam);
}

View File

@ -1068,27 +1068,27 @@ void CheckOther::invalidFunctionUsage()
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();
const Token * const functionToken = tok;
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));
!_settings->library.isargvalid(functionToken, argnr, MathLib::toLongNumber(argtok->str())))
invalidFunctionArgError(argtok,functionToken->str(),argnr,_settings->library.validarg(functionToken,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);
if (_settings->library.isboolargbad(functionToken, argnr))
invalidFunctionArgBoolError(top, functionToken->str(), argnr);
// 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));
else if (!_settings->library.isargvalid(functionToken, argnr, 0))
invalidFunctionArgError(top, functionToken->str(), argnr, _settings->library.validarg(functionToken,argnr));
else if (!_settings->library.isargvalid(functionToken, argnr, 1))
invalidFunctionArgError(top, functionToken->str(), argnr, _settings->library.validarg(functionToken,argnr));
}
}
argnr++;
@ -1154,7 +1154,7 @@ void CheckOther::checkUnreachableCode()
} else if (Token::Match(tok, "goto %any% ;")) {
secondBreak = tok->tokAt(3);
labelName = tok->next();
} else if (Token::Match(tok, "%var% (") && (_settings->library.isnoreturn(tok->str()) || (tok->function() && tok->function()->isAttributeNoreturn())) && tok->strAt(-1) != ".") {
} else if (Token::Match(tok, "%var% (") && _settings->library.isnoreturn(tok)) {
if ((!tok->function() || (tok->function()->token != tok && tok->function()->tokenDef != tok)) && tok->linkAt(1)->strAt(1) != "{")
secondBreak = tok->linkAt(1)->tokAt(2);
}

View File

@ -564,9 +564,9 @@ Library::Error Library::load(const tinyxml2::XMLDocument &doc)
return Error(OK);
}
bool Library::isargvalid(const std::string &functionName, int argnr, const MathLib::bigint argvalue) const
bool Library::isargvalid(const Token *ftok, int argnr, const MathLib::bigint argvalue) const
{
const ArgumentChecks *ac = getarg(functionName, argnr);
const ArgumentChecks *ac = getarg(ftok, argnr);
if (!ac || ac->valid.empty())
return true;
TokenList tokenList(0);
@ -591,10 +591,12 @@ bool Library::isargvalid(const std::string &functionName, int argnr, const MathL
return false;
}
const Library::ArgumentChecks * Library::getarg(const std::string &functionName, int argnr) const
const Library::ArgumentChecks * Library::getarg(const Token *ftok, int argnr) const
{
if (isNotLibraryFunction(ftok))
return nullptr;
std::map<std::string, std::map<int, ArgumentChecks> >::const_iterator it1;
it1 = argumentChecks.find(functionName);
it1 = argumentChecks.find(ftok->str());
if (it1 == argumentChecks.end())
return nullptr;
const std::map<int,ArgumentChecks>::const_iterator it2 = it1->second.find(argnr);
@ -628,8 +630,8 @@ bool Library::isScopeNoReturn(const Token *end, std::string *unknownFunc) const
if (Token::Match(start,"[;{}]") && Token::Match(funcname, "%var% )| (")) {
if (funcname->str() == "exit")
return true;
if (!isnotnoreturn(funcname->str())) {
if (unknownFunc && !(isnoreturn(funcname->str()) || (funcname->function() && funcname->function()->isAttributeNoreturn())))
if (!isnotnoreturn(funcname)) {
if (unknownFunc && !isnoreturn(funcname))
*unknownFunc = funcname->str();
return true;
}

View File

@ -25,6 +25,7 @@
#include "path.h"
#include "mathlib.h"
#include "token.h"
#include "symboldatabase.h"
#include <map>
#include <set>
@ -125,13 +126,26 @@ public:
std::set<std::string> functionpure;
std::set<std::string> useretval;
bool isnoreturn(const std::string &name) const {
std::map<std::string, bool>::const_iterator it = _noreturn.find(name);
// returns true if ftok is not a library function
static bool isNotLibraryFunction(const Token *ftok) {
return ftok->astParent() ? ftok->astParent()->str() != "(" : false;
}
bool isnoreturn(const Token *ftok) const {
if (ftok->function() && ftok->function()->isAttributeNoreturn())
return true;
if (isNotLibraryFunction(ftok))
return false;
std::map<std::string, bool>::const_iterator it = _noreturn.find(ftok->str());
return (it != _noreturn.end() && it->second);
}
bool isnotnoreturn(const std::string &name) const {
std::map<std::string, bool>::const_iterator it = _noreturn.find(name);
bool isnotnoreturn(const Token *ftok) const {
if (ftok->function() && ftok->function()->isAttributeNoreturn())
return false;
if (isNotLibraryFunction(ftok))
return false;
std::map<std::string, bool>::const_iterator it = _noreturn.find(ftok->str());
return (it != _noreturn.end() && !it->second);
}
@ -213,35 +227,35 @@ 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);
bool isboolargbad(const Token *ftok, int argnr) const {
const ArgumentChecks *arg = getarg(ftok, argnr);
return arg && arg->notbool;
}
bool isnullargbad(const std::string &functionName, int argnr) const {
const ArgumentChecks *arg = getarg(functionName, argnr);
bool isnullargbad(const Token *ftok, int argnr) const {
const ArgumentChecks *arg = getarg(ftok, argnr);
return arg && arg->notnull;
}
bool isuninitargbad(const std::string &functionName, int argnr) const {
const ArgumentChecks *arg = getarg(functionName, argnr);
bool isuninitargbad(const Token *ftok, int argnr) const {
const ArgumentChecks *arg = getarg(ftok, argnr);
return arg && arg->notuninit;
}
bool isargformatstr(const std::string &functionName, int argnr) const {
const ArgumentChecks *arg = getarg(functionName, argnr);
bool isargformatstr(const Token *ftok, int argnr) const {
const ArgumentChecks *arg = getarg(ftok, argnr);
return arg && arg->formatstr;
}
bool isargstrz(const std::string &functionName, int argnr) const {
const ArgumentChecks *arg = getarg(functionName, argnr);
bool isargstrz(const Token *ftok, int argnr) const {
const ArgumentChecks *arg = getarg(ftok, argnr);
return arg && arg->strz;
}
bool isargvalid(const std::string &functionName, int argnr, const MathLib::bigint argvalue) const;
bool isargvalid(const Token *ftok, int argnr, const MathLib::bigint argvalue) const;
const std::string& validarg(const std::string &functionName, int argnr) const {
const ArgumentChecks *arg = getarg(functionName, argnr);
const std::string& validarg(const Token *ftok, int argnr) const {
const ArgumentChecks *arg = getarg(ftok, argnr);
return arg ? arg->valid : emptyString;
}
@ -258,8 +272,8 @@ public:
return false;
}
const std::list<ArgumentChecks::MinSize> *argminsizes(const std::string &functionName, int argnr) const {
const ArgumentChecks *arg = getarg(functionName, argnr);
const std::list<ArgumentChecks::MinSize> *argminsizes(const Token *ftok, int argnr) const {
const ArgumentChecks *arg = getarg(ftok, argnr);
return arg ? &arg->minsizes : nullptr;
}
@ -502,7 +516,7 @@ private:
std::map<std::string, PlatformType> platform_types; // platform independent typedefs
std::map<std::string, Platform> platforms; // platform dependent typedefs
const ArgumentChecks * getarg(const std::string &functionName, int argnr) const;
const ArgumentChecks * getarg(const Token *ftok, int argnr) const;
static int getid(const std::map<std::string,int> &data, const std::string &name) {
const std::map<std::string,int>::const_iterator it = data.find(name);

View File

@ -4271,7 +4271,7 @@ void Tokenizer::simplifyFlowControl()
} else if (Token::Match(tok,"return|goto") ||
(Token::Match(tok->previous(), "[;{}] %var% (") &&
(_settings->library.isnoreturn(tok->str()) || (tok->function() && tok->function()->isAttributeNoreturn()))) ||
_settings->library.isnoreturn(tok)) ||
(tok->str() == "throw" && !isC())) {
//TODO: ensure that we exclude user-defined 'exit|abort|throw', except for 'noreturn'
//catch the first ';'

View File

@ -75,7 +75,7 @@ static bool bailoutFunctionPar(const Token *tok, const ValueFlow::Value &value,
if (!tok->function()) {
// if value is 0 and the library says 0 is invalid => do not bailout
if (value.intvalue==0 && settings->library.isnullargbad(tok->str(), 1+argnr))
if (value.intvalue==0 && settings->library.isnullargbad(tok, 1+argnr))
return false;
// addressOf => inconclusive
if (!addressOf) {

View File

@ -2279,7 +2279,7 @@ private:
" foo::memset(str, 0, 100);\n"
" std::memset(str, 0, 100);\n"
"}");
ASSERT_EQUALS("[test.cpp:5]: (error) Buffer is accessed out of bounds: str\n", errout.str());
TODO_ASSERT_EQUALS("[test.cpp:5]: (error) Buffer is accessed out of bounds: str\n", "", errout.str());
// #5257 - check strings
checkstd("void f() {\n"

View File

@ -65,12 +65,17 @@ private:
tinyxml2::XMLDocument doc;
doc.Parse(xmldata, sizeof(xmldata));
TokenList tokenList(nullptr);
std::istringstream istr("foo();");
tokenList.createTokens(istr);
tokenList.front()->next()->astOperand1(tokenList.front());
Library library;
library.load(doc);
ASSERT(library.use.empty());
ASSERT(library.leakignore.empty());
ASSERT(library.argumentChecks.empty());
ASSERT(library.isnotnoreturn("foo"));
ASSERT(library.isnotnoreturn(tokenList.front()));
}
void function_arg() const {
@ -128,37 +133,42 @@ private:
Library library;
library.load(doc);
TokenList tokenList(nullptr);
std::istringstream istr("foo();");
tokenList.createTokens(istr);
tokenList.front()->next()->astOperand1(tokenList.front());
// 1-
ASSERT_EQUALS(false, library.isargvalid("foo", 1, -10));
ASSERT_EQUALS(false, library.isargvalid("foo", 1, 0));
ASSERT_EQUALS(true, library.isargvalid("foo", 1, 1));
ASSERT_EQUALS(true, library.isargvalid("foo", 1, 10));
ASSERT_EQUALS(false, library.isargvalid(tokenList.front(), 1, -10));
ASSERT_EQUALS(false, library.isargvalid(tokenList.front(), 1, 0));
ASSERT_EQUALS(true, library.isargvalid(tokenList.front(), 1, 1));
ASSERT_EQUALS(true, library.isargvalid(tokenList.front(), 1, 10));
// -7-0
ASSERT_EQUALS(false, library.isargvalid("foo", 2, -10));
ASSERT_EQUALS(true, library.isargvalid("foo", 2, -7));
ASSERT_EQUALS(true, library.isargvalid("foo", 2, -3));
ASSERT_EQUALS(true, library.isargvalid("foo", 2, 0));
ASSERT_EQUALS(false, library.isargvalid("foo", 2, 1));
ASSERT_EQUALS(false, library.isargvalid(tokenList.front(), 2, -10));
ASSERT_EQUALS(true, library.isargvalid(tokenList.front(), 2, -7));
ASSERT_EQUALS(true, library.isargvalid(tokenList.front(), 2, -3));
ASSERT_EQUALS(true, library.isargvalid(tokenList.front(), 2, 0));
ASSERT_EQUALS(false, library.isargvalid(tokenList.front(), 2, 1));
// 1-5,8
ASSERT_EQUALS(false, library.isargvalid("foo", 3, 0));
ASSERT_EQUALS(true, library.isargvalid("foo", 3, 1));
ASSERT_EQUALS(true, library.isargvalid("foo", 3, 3));
ASSERT_EQUALS(true, library.isargvalid("foo", 3, 5));
ASSERT_EQUALS(false, library.isargvalid("foo", 3, 6));
ASSERT_EQUALS(false, library.isargvalid("foo", 3, 7));
ASSERT_EQUALS(true, library.isargvalid("foo", 3, 8));
ASSERT_EQUALS(false, library.isargvalid("foo", 3, 9));
ASSERT_EQUALS(false, library.isargvalid(tokenList.front(), 3, 0));
ASSERT_EQUALS(true, library.isargvalid(tokenList.front(), 3, 1));
ASSERT_EQUALS(true, library.isargvalid(tokenList.front(), 3, 3));
ASSERT_EQUALS(true, library.isargvalid(tokenList.front(), 3, 5));
ASSERT_EQUALS(false, library.isargvalid(tokenList.front(), 3, 6));
ASSERT_EQUALS(false, library.isargvalid(tokenList.front(), 3, 7));
ASSERT_EQUALS(true, library.isargvalid(tokenList.front(), 3, 8));
ASSERT_EQUALS(false, library.isargvalid(tokenList.front(), 3, 9));
// -1,5
ASSERT_EQUALS(false, library.isargvalid("foo", 4, -10));
ASSERT_EQUALS(true, library.isargvalid("foo", 4, -1));
ASSERT_EQUALS(false, library.isargvalid(tokenList.front(), 4, -10));
ASSERT_EQUALS(true, library.isargvalid(tokenList.front(), 4, -1));
// :1,5
ASSERT_EQUALS(true, library.isargvalid("foo", 5, -10));
ASSERT_EQUALS(true, library.isargvalid("foo", 5, 1));
ASSERT_EQUALS(false, library.isargvalid("foo", 5, 2));
ASSERT_EQUALS(true, library.isargvalid(tokenList.front(), 5, -10));
ASSERT_EQUALS(true, library.isargvalid(tokenList.front(), 5, 1));
ASSERT_EQUALS(false, library.isargvalid(tokenList.front(), 5, 2));
}
void function_arg_minsize() const {
@ -175,8 +185,13 @@ private:
Library library;
library.load(doc);
TokenList tokenList(nullptr);
std::istringstream istr("foo();");
tokenList.createTokens(istr);
tokenList.front()->next()->astOperand1(tokenList.front());
// arg1: type=strlen arg2
const std::list<Library::ArgumentChecks::MinSize> *minsizes = library.argminsizes("foo",1);
const std::list<Library::ArgumentChecks::MinSize> *minsizes = library.argminsizes(tokenList.front(),1);
ASSERT_EQUALS(true, minsizes != nullptr);
ASSERT_EQUALS(1U, minsizes ? minsizes->size() : 1U);
if (minsizes && minsizes->size() == 1U) {
@ -186,7 +201,7 @@ private:
}
// arg2: type=argvalue arg3
minsizes = library.argminsizes("foo", 2);
minsizes = library.argminsizes(tokenList.front(), 2);
ASSERT_EQUALS(true, minsizes != nullptr);
ASSERT_EQUALS(1U, minsizes ? minsizes->size() : 1U);
if (minsizes && minsizes->size() == 1U) {