Added new attribute "arg" to <alloc> and <dealloc> to specify the argument that is allocated/deallocated.

This fixes several issues with allocation functions in windows.cfg, such as HeapAlloc() and VirtualAllocEx() (#7503)
This commit is contained in:
PKEuS 2016-05-22 17:18:50 +02:00
parent fbc499d033
commit 64d2fd2f57
9 changed files with 182 additions and 89 deletions

View File

@ -1001,7 +1001,7 @@
<memory>
<alloc>HeapAlloc</alloc>
<alloc>HeapReAlloc</alloc>
<dealloc>HeapFree</dealloc>
<dealloc arg="3">HeapFree</dealloc>
</memory>
<memory>
<alloc>IoAllocateErrorLogEntry</alloc>
@ -1062,7 +1062,7 @@
<memory>
<alloc>VirtualAllocEx</alloc>
<alloc>VirtualAllocExNuma</alloc>
<dealloc>VirtualFreeEx</dealloc>
<dealloc arg="2">VirtualFreeEx</dealloc>
</memory>
<memory>
<alloc>LocalAlloc</alloc>

View File

@ -207,7 +207,7 @@ void CheckLeakAutoVar::checkScope(const Token * const startToken,
if (tok->str() == "(" && tok->previous()->isName()) {
VarInfo::AllocInfo allocation(0, VarInfo::NOALLOC);
functionCall(tok->previous(), varInfo, allocation);
functionCall(tok->previous(), varInfo, allocation, nullptr);
tok = tok->link();
continue;
}
@ -274,9 +274,9 @@ void CheckLeakAutoVar::checkScope(const Token * const startToken,
// allocation?
if (varTok->next()->astOperand2() && Token::Match(varTok->next()->astOperand2()->previous(), "%type% (")) {
int i = _settings->library.alloc(varTok->next()->astOperand2()->previous());
if (i > 0) {
alloctype[varTok->varId()].type = i;
const Library::AllocFunc* f = _settings->library.alloc(varTok->next()->astOperand2()->previous());
if (f && f->arg == -1) {
alloctype[varTok->varId()].type = f->groupId;
alloctype[varTok->varId()].status = VarInfo::ALLOC;
}
} else if (_tokenizer->isCPP() && varTok->strAt(2) == "new") {
@ -301,10 +301,8 @@ void CheckLeakAutoVar::checkScope(const Token * const startToken,
if (innerTok->str() == ")")
break;
if (innerTok->str() == "(" && innerTok->previous()->isName()) {
VarInfo::AllocInfo allocation(_settings->library.dealloc(tok), VarInfo::DEALLOC);
if (allocation.type == 0)
allocation.status = VarInfo::NOALLOC;
functionCall(innerTok->previous(), varInfo, allocation);
VarInfo::AllocInfo allocation(0, VarInfo::NOALLOC);
functionCall(innerTok->previous(), varInfo, allocation, nullptr);
innerTok = innerTok->link();
}
}
@ -440,10 +438,11 @@ void CheckLeakAutoVar::checkScope(const Token * const startToken,
// Function call..
else if (Token::Match(tok, "%type% (")) {
VarInfo::AllocInfo allocation(_settings->library.dealloc(tok), VarInfo::DEALLOC);
const Library::AllocFunc* af = _settings->library.dealloc(tok);
VarInfo::AllocInfo allocation(af ? af->groupId : 0, VarInfo::DEALLOC);
if (allocation.type == 0)
allocation.status = VarInfo::NOALLOC;
functionCall(tok, varInfo, allocation);
functionCall(tok, varInfo, allocation, af);
tok = tok->next()->link();
@ -516,13 +515,14 @@ void CheckLeakAutoVar::changeAllocStatus(VarInfo *varInfo, const VarInfo::AllocI
}
}
void CheckLeakAutoVar::functionCall(const Token *tok, VarInfo *varInfo, const VarInfo::AllocInfo& allocation)
void CheckLeakAutoVar::functionCall(const Token *tok, VarInfo *varInfo, const VarInfo::AllocInfo& allocation, const Library::AllocFunc* af)
{
// Ignore function call?
const bool ignore = bool(_settings->library.leakignore.find(tok->str()) != _settings->library.leakignore.end());
if (ignore)
return;
int argNr = 1;
for (const Token *arg = tok->tokAt(2); arg; arg = arg->nextArgument()) {
if (_tokenizer->isCPP() && arg->str() == "new") {
arg = arg->next();
@ -537,10 +537,12 @@ void CheckLeakAutoVar::functionCall(const Token *tok, VarInfo *varInfo, const Va
arg = arg->next();
// Is variable allocated?
changeAllocStatus(varInfo, allocation, tok, arg);
if (!af || af->arg == argNr)
changeAllocStatus(varInfo, allocation, tok, arg);
} else if (Token::Match(arg, "%name% (")) {
functionCall(arg, varInfo, allocation);
functionCall(arg, varInfo, allocation, af);
}
argNr++;
}
}

View File

@ -105,7 +105,7 @@ private:
std::set<unsigned int> notzero);
/** parse function call */
void functionCall(const Token *tok, VarInfo *varInfo, const VarInfo::AllocInfo& allocation);
void functionCall(const Token *tok, VarInfo *varInfo, const VarInfo::AllocInfo& allocation, const Library::AllocFunc* af);
/** parse changes in allocation status */
void changeAllocStatus(VarInfo *varInfo, const VarInfo::AllocInfo& allocation, const Token* tok, const Token* arg);

View File

@ -154,11 +154,11 @@ CheckMemoryLeak::AllocType CheckMemoryLeak::getAllocationType(const Token *tok2,
}
// Does tok2 point on a Library allocation function?
const int alloctype = settings1->library.alloc(tok2);
const int alloctype = settings1->library.alloc(tok2, -1);
if (alloctype > 0) {
if (alloctype == settings1->library.dealloc("free"))
if (alloctype == settings1->library.deallocId("free"))
return Malloc;
if (alloctype == settings1->library.dealloc("fclose"))
if (alloctype == settings1->library.deallocId("fclose"))
return File;
return Library::ismemory(alloctype) ? OtherMem : OtherRes;
}
@ -227,30 +227,34 @@ CheckMemoryLeak::AllocType CheckMemoryLeak::getDeallocationType(const Token *tok
if (Token::simpleMatch(tok, "fcloseall ( )"))
return File;
const Token* vartok = tok->tokAt(2);
while (Token::Match(vartok, "%name% .|::"))
vartok = vartok->tokAt(2);
int argNr = 1;
for (const Token* tok2 = tok->tokAt(2); tok2; tok2 = tok2->nextArgument()) {
const Token* vartok = tok2;
while (Token::Match(vartok, "%name% .|::"))
vartok = vartok->tokAt(2);
if (Token::Match(vartok, "%varid% )|,|-", varid)) {
if (tok->str() == "realloc" && Token::simpleMatch(vartok->next(), ", 0 )"))
return Malloc;
if (settings1->standards.posix) {
if (tok->str() == "close")
return Fd;
if (tok->str() == "pclose")
return Pipe;
}
// Does tok point on a Library deallocation function?
const int dealloctype = settings1->library.dealloc(tok);
if (dealloctype > 0) {
if (dealloctype == settings1->library.dealloc("free"))
if (Token::Match(vartok, "%varid% )|,|-", varid)) {
if (tok->str() == "realloc" && Token::simpleMatch(vartok->next(), ", 0 )"))
return Malloc;
if (dealloctype == settings1->library.dealloc("fclose"))
return File;
return Library::ismemory(dealloctype) ? OtherMem : OtherRes;
if (settings1->standards.posix) {
if (tok->str() == "close")
return Fd;
if (tok->str() == "pclose")
return Pipe;
}
// Does tok point on a Library deallocation function?
const int dealloctype = settings1->library.dealloc(tok, argNr);
if (dealloctype > 0) {
if (dealloctype == settings1->library.deallocId("free"))
return Malloc;
if (dealloctype == settings1->library.deallocId("fclose"))
return File;
return Library::ismemory(dealloctype) ? OtherMem : OtherRes;
}
}
argNr++;
}
}

View File

@ -1137,7 +1137,7 @@ void CheckStl::checkAutoPointer()
std::set<unsigned int> autoPtrVarId;
std::map<unsigned int, const std::string> mallocVarId; // variables allocated by the malloc-like function
const char STL_CONTAINER_LIST[] = "array|bitset|deque|list|forward_list|map|multimap|multiset|priority_queue|queue|set|stack|vector|hash_map|hash_multimap|hash_set|unordered_map|unordered_multimap|unordered_set|unordered_multiset|basic_string";
const int malloc = _settings->library.alloc("malloc"); // allocation function, which are not compatible with auto_ptr
const int malloc = _settings->library.allocId("malloc"); // allocation function, which are not compatible with auto_ptr
const bool printStyle = _settings->isEnabled("style");
for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) {
@ -1153,7 +1153,7 @@ void CheckStl::checkAutoPointer()
if (Token::Match(tok3, "( new %type%") && hasArrayEndParen(tok3)) {
autoPointerArrayError(tok2->next());
}
if (Token::Match(tok3, "( %name% (") && malloc && _settings->library.alloc(tok3->next()) == malloc) {
if (Token::Match(tok3, "( %name% (") && malloc && _settings->library.alloc(tok3->next(), -1) == malloc) {
// malloc-like function allocated memory passed to the auto_ptr constructor -> error
autoPointerMallocError(tok2->next(), tok3->next()->str());
}
@ -1197,7 +1197,7 @@ void CheckStl::checkAutoPointer()
if (iter != autoPtrVarId.end()) {
autoPointerArrayError(tok);
}
} else if (Token::Match(tok, "%var% = %name% (") && malloc && _settings->library.alloc(tok->tokAt(2)) == malloc) {
} else if (Token::Match(tok, "%var% = %name% (") && malloc && _settings->library.alloc(tok->tokAt(2), -1) == malloc) {
// C library function like 'malloc' used together with auto pointer -> error
std::set<unsigned int>::const_iterator iter = autoPtrVarId.find(tok->varId());
if (iter != autoPtrVarId.end()) {
@ -1206,7 +1206,7 @@ void CheckStl::checkAutoPointer()
// it is not an auto pointer variable and it is allocated by malloc like function.
mallocVarId.insert(std::make_pair(tok->varId(), tok->strAt(2)));
}
} else if (Token::Match(tok, "%var% . reset ( %name% (") && malloc && _settings->library.alloc(tok->tokAt(4)) == malloc) {
} else if (Token::Match(tok, "%var% . reset ( %name% (") && malloc && _settings->library.alloc(tok->tokAt(4), -1) == malloc) {
// C library function like 'malloc' used when resetting auto pointer -> error
std::set<unsigned int>::const_iterator iter = autoPtrVarId.find(tok->varId());
if (iter != autoPtrVarId.end()) {

View File

@ -139,9 +139,9 @@ Library::Error Library::load(const tinyxml2::XMLDocument &doc)
int allocationId = 0;
for (const tinyxml2::XMLElement *memorynode = node->FirstChildElement(); memorynode; memorynode = memorynode->NextSiblingElement()) {
if (strcmp(memorynode->Name(),"dealloc")==0) {
const std::map<std::string,int>::const_iterator it = _dealloc.find(memorynode->GetText());
const std::map<std::string, AllocFunc>::const_iterator it = _dealloc.find(memorynode->GetText());
if (it != _dealloc.end()) {
allocationId = it->second;
allocationId = it->second.groupId;
break;
}
}
@ -158,14 +158,29 @@ Library::Error Library::load(const tinyxml2::XMLDocument &doc)
for (const tinyxml2::XMLElement *memorynode = node->FirstChildElement(); memorynode; memorynode = memorynode->NextSiblingElement()) {
const std::string memorynodename = memorynode->Name();
if (memorynodename == "alloc") {
_alloc[memorynode->GetText()] = allocationId;
AllocFunc temp;
temp.groupId = allocationId;
const char *init = memorynode->Attribute("init");
if (init && strcmp(init,"false")==0) {
returnuninitdata.insert(memorynode->GetText());
}
} else if (memorynodename == "dealloc")
_dealloc[memorynode->GetText()] = allocationId;
else if (memorynodename == "use")
const char *arg = memorynode->Attribute("arg");
if (arg)
temp.arg = atoi(arg);
else
temp.arg = -1;
_alloc[memorynode->GetText()] = temp;
} else if (memorynodename == "dealloc") {
AllocFunc temp;
temp.groupId = allocationId;
const char *arg = memorynode->Attribute("arg");
if (arg)
temp.arg = atoi(arg);
else
temp.arg = 1;
_dealloc[memorynode->GetText()] = temp;
} else if (memorynodename == "use")
use.insert(memorynode->GetText());
else
unknown_elements.insert(memorynodename);
@ -772,18 +787,32 @@ bool Library::isuninitargbad(const Token *ftok, int argnr) const
}
/** get allocation id for function */
int Library::alloc(const Token *tok) const
/** get allocation info for function */
const Library::AllocFunc* Library::alloc(const Token *tok) const
{
const std::string funcname = functionName(tok);
return isNotLibraryFunction(tok) && argumentChecks.find(funcname) != argumentChecks.end() ? 0 : getid(_alloc, funcname);
return isNotLibraryFunction(tok) && argumentChecks.find(funcname) != argumentChecks.end() ? 0 : getAllocDealloc(_alloc, funcname);
}
/** get deallocation info for function */
const Library::AllocFunc* Library::dealloc(const Token *tok) const
{
const std::string funcname = functionName(tok);
return isNotLibraryFunction(tok) && argumentChecks.find(funcname) != argumentChecks.end() ? 0 : getAllocDealloc(_dealloc, funcname);
}
/** get allocation id for function */
int Library::alloc(const Token *tok, int arg) const
{
const Library::AllocFunc* af = alloc(tok);
return (af && af->arg == arg) ? af->groupId : 0;
}
/** get deallocation id for function */
int Library::dealloc(const Token *tok) const
int Library::dealloc(const Token *tok, int arg) const
{
const std::string funcname = functionName(tok);
return isNotLibraryFunction(tok) && argumentChecks.find(funcname) != argumentChecks.end() ? 0 : getid(_dealloc, funcname);
const Library::AllocFunc* af = dealloc(tok);
return (af && af->arg == arg) ? af->groupId : 0;
}

View File

@ -64,29 +64,54 @@ public:
/** this is primarily meant for unit tests. it only returns true/false */
bool loadxmldata(const char xmldata[], std::size_t len);
/** get allocation id for function by name (deprecated, use other alloc) */
int alloc(const char name[]) const {
return getid(_alloc, name);
}
struct AllocFunc {
int groupId;
int arg;
};
/** get allocation info for function */
const AllocFunc* alloc(const Token *tok) const;
/** get deallocation info for function */
const AllocFunc* dealloc(const Token *tok) const;
/** get allocation id for function */
int alloc(const Token *tok) const;
int alloc(const Token *tok, int arg) const;
/** get deallocation id for function */
int dealloc(const Token *tok) const;
int dealloc(const Token *tok, int arg) const;
/** get allocation info for function by name (deprecated, use other alloc) */
const AllocFunc* alloc(const char name[]) const {
return getAllocDealloc(_alloc, name);
}
/** get deallocation info for function by name (deprecated, use other alloc) */
const AllocFunc* dealloc(const char name[]) const {
return getAllocDealloc(_dealloc, name);
}
/** get allocation id for function by name (deprecated, use other alloc) */
int allocId(const char name[]) const {
const AllocFunc* af = getAllocDealloc(_alloc, name);
return af ? af->groupId : 0;
}
/** get deallocation id for function by name (deprecated, use other alloc) */
int dealloc(const char name[]) const {
return getid(_dealloc, name);
int deallocId(const char name[]) const {
const AllocFunc* af = getAllocDealloc(_dealloc, name);
return af ? af->groupId : 0;
}
/** set allocation id for function */
void setalloc(const std::string &functionname, int id) {
_alloc[functionname] = id;
void setalloc(const std::string &functionname, int id, int arg) {
_alloc[functionname].groupId = id;
_alloc[functionname].arg = arg;
}
void setdealloc(const std::string &functionname, int id) {
_dealloc[functionname] = id;
void setdealloc(const std::string &functionname, int id, int arg) {
_dealloc[functionname].groupId = id;
_dealloc[functionname].arg = arg;
}
/** add noreturn function setting */
@ -98,11 +123,17 @@ public:
static bool ismemory(int id) {
return ((id > 0) && ((id & 1) == 0));
}
static bool ismemory(const AllocFunc* func) {
return ((func->groupId > 0) && ((func->groupId & 1) == 0));
}
/** is allocation type resource? */
static bool isresource(int id) {
return ((id > 0) && ((id & 1) == 1));
}
static bool isresource(const AllocFunc* func) {
return ((func->groupId > 0) && ((func->groupId & 1) == 1));
}
bool formatstr_function(const std::string& funcname) const {
return _formatstr.find(funcname) != _formatstr.cend();
@ -443,8 +474,8 @@ private:
int allocid;
std::set<std::string> _files;
std::set<std::string> _useretval;
std::map<std::string, int> _alloc; // allocation functions
std::map<std::string, int> _dealloc; // deallocation functions
std::map<std::string, AllocFunc> _alloc; // allocation functions
std::map<std::string, AllocFunc> _dealloc; // deallocation functions
std::map<std::string, bool> _noreturn; // is function noreturn?
std::set<std::string> _ignorefunction; // ignore functions/macros from a library (gtk, qt etc)
std::map<std::string, bool> _reporterrors;
@ -462,9 +493,9 @@ private:
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);
return (it == data.end()) ? 0 : it->second;
static const AllocFunc* getAllocDealloc(const std::map<std::string, AllocFunc> &data, const std::string &name) {
const std::map<std::string, AllocFunc>::const_iterator it = data.find(name);
return (it == data.end()) ? nullptr : &it->second;
}
};

View File

@ -32,11 +32,11 @@ private:
void run() {
int id = 0;
while (!settings.library.ismemory(++id));
settings.library.setalloc("malloc", id);
settings.library.setdealloc("free", id);
settings.library.setalloc("malloc", id, -1);
settings.library.setdealloc("free", id, 1);
while (!settings.library.isresource(++id));
settings.library.setalloc("fopen", id);
settings.library.setdealloc("fclose", id);
settings.library.setalloc("fopen", id, -1);
settings.library.setdealloc("fclose", id, 1);
// Assign
TEST_CASE(assign1);
@ -1269,8 +1269,7 @@ private:
" HeapFree(MyHeap, 0, b);"
" HeapDestroy(MyHeap);"
"}");
TODO_ASSERT_EQUALS("", "[test.c:1]: (error) Mismatching allocation and deallocation: MyHeap\n"
"[test.c:1]: (error) Resource handle 'MyHeap' freed twice.\n", errout.str());
ASSERT_EQUALS("", errout.str());
check("void f() {"
" int *a = HeapAlloc(GetProcessHeap(), 0, sizeof(int));"
@ -1287,8 +1286,7 @@ private:
" HeapFree(MyHeap, 0, a);"
" HeapDestroy(MyHeap);"
"}");
TODO_ASSERT_EQUALS("[test.c:1] (error) Memory leak: b", "[test.c:1]: (error) Mismatching allocation and deallocation: MyHeap\n"
"[test.c:1]: (error) Memory leak: b\n", errout.str());
ASSERT_EQUALS("[test.c:1]: (error) Memory leak: b\n", errout.str());
check("void f() {"
" HANDLE MyHeap = HeapCreate(0, 0, 0);"
@ -1297,7 +1295,8 @@ private:
" HeapFree(MyHeap, 0, a);"
" HeapFree(MyHeap, 0, b);"
"}");
TODO_ASSERT_EQUALS("[test.c:1] (error) Resource leak: MyHeap", "[test.c:1]: (error) Mismatching allocation and deallocation: MyHeap\n", errout.str());
TODO_ASSERT_EQUALS("[test.c:1] (error) Resource leak: MyHeap",
"", errout.str());
check("void f() {"
" HANDLE MyHeap = HeapCreate(0, 0, 0);"
@ -1305,8 +1304,9 @@ private:
" int *b = HeapAlloc(MyHeap, 0, sizeof(int));"
" HeapFree(MyHeap, 0, a);"
"}");
TODO_ASSERT_EQUALS("[test.c:1] (error) Resource leak: MyHeap\n[test.c:1] (error) Memory leak: b",
"[test.c:1]: (error) Mismatching allocation and deallocation: MyHeap\n[test.c:1]: (error) Memory leak: b\n", errout.str());
TODO_ASSERT_EQUALS("[test.c:1] (error) Memory leak: MyHeap\n"
"[test.c:1] (error) Memory leak: b",
"[test.c:1]: (error) Memory leak: b\n", errout.str());
}
};

View File

@ -43,6 +43,7 @@ private:
TEST_CASE(function_warn);
TEST_CASE(memory);
TEST_CASE(memory2); // define extra "free" allocation functions
TEST_CASE(memory3);
TEST_CASE(resource);
TEST_CASE(podtype);
TEST_CASE(container);
@ -358,7 +359,11 @@ private:
ASSERT(library.argumentChecks.empty());
ASSERT(Library::ismemory(library.alloc("CreateX")));
ASSERT_EQUALS(library.alloc("CreateX"), library.dealloc("DeleteX"));
ASSERT_EQUALS(library.allocId("CreateX"), library.deallocId("DeleteX"));
const Library::AllocFunc* af = library.alloc("CreateX");
ASSERT(af && af->arg == -1);
const Library::AllocFunc* df = library.dealloc("DeleteX");
ASSERT(df && df->arg == 1);
}
void memory2() const {
const char xmldata1[] = "<?xml version=\"1.0\"?>\n"
@ -380,8 +385,30 @@ private:
library.loadxmldata(xmldata1, sizeof(xmldata1));
library.loadxmldata(xmldata2, sizeof(xmldata2));
ASSERT_EQUALS(library.dealloc("free"), library.alloc("malloc"));
ASSERT_EQUALS(library.dealloc("free"), library.alloc("foo"));
ASSERT_EQUALS(library.deallocId("free"), library.allocId("malloc"));
ASSERT_EQUALS(library.deallocId("free"), library.allocId("foo"));
}
void memory3() const {
const char xmldata[] = "<?xml version=\"1.0\"?>\n"
"<def>\n"
" <memory>\n"
" <alloc arg=\"5\" init=\"false\">CreateX</alloc>\n"
" <dealloc arg=\"2\">DeleteX</dealloc>\n"
" </memory>\n"
"</def>";
Library library;
readLibrary(library, xmldata);
ASSERT(library.use.empty());
ASSERT(library.leakignore.empty());
ASSERT(library.argumentChecks.empty());
const Library::AllocFunc* af = library.alloc("CreateX");
ASSERT(af && af->arg == 5);
const Library::AllocFunc* df = library.dealloc("DeleteX");
ASSERT(df && df->arg == 2);
ASSERT(library.returnuninitdata.find("CreateX") != library.returnuninitdata.cend());
}
void resource() const {
@ -399,8 +426,8 @@ private:
ASSERT(library.leakignore.empty());
ASSERT(library.argumentChecks.empty());
ASSERT(Library::isresource(library.alloc("CreateX")));
ASSERT_EQUALS(library.alloc("CreateX"), library.dealloc("DeleteX"));
ASSERT(Library::isresource(library.allocId("CreateX")));
ASSERT_EQUALS(library.allocId("CreateX"), library.deallocId("DeleteX"));
}
void podtype() const {