Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 60 additions & 6 deletions lib/checkleakautovar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,40 @@ static const Token * isFunctionCall(const Token * nameToken)
return nullptr;
}

/** checks if tok is part of the LHS of an anonymous function call:
* tok should be the first token in a unary expression that evaluates to a
* function pointer, or the name of a lambda or function
* (*func_ptr)(arg)
* or
* (lambda)(arg)
* or
* get_function()(arg)
*
* @param tok on the LHS of a function call
* @return opening parenthesis token or nullptr if not a function call
*/
static const Token * isAnonymousFunctionCall(const Token * tok)
{
// match one of the supported LHS patterns
if (tok->previous()->str() == "(" && !tok->previous()->isBinaryOp() && tok->linkAt(-1)) {
tok = tok->linkAt(-1)->next();
} else if (!tok->isStandardType() && tok->isName() && tok->linkAt(1)) {
tok = tok->linkAt(1)->next();
} else {
return nullptr;
}

// skip over potential template arguments
if (tok->link() && tok->str() == "<")
tok = tok->link()->next();

// return the opening parenthesis
if (tok && tok->link() && !tok->isCast() && tok->str() == "(")
return tok;

return nullptr;
}

static const Token* getOutparamAllocation(const Token* tok, const Library& library)
{
if (!tok)
Expand Down Expand Up @@ -787,11 +821,22 @@ bool CheckLeakAutoVarImpl::checkScope(const Token * const startToken,
}
}

// a regular function call can return an anonymous function
openingPar = isAnonymousFunctionCall(ftok);
if (openingPar) {
functionCall(nullptr, openingPar, varInfo, VarInfo::AllocInfo(0, VarInfo::NOALLOC), nullptr);
tok = openingPar->link();
}

continue;
}

// goto => weird execution path
else if (tok->str() == "goto") {
// top level call to an anonymous function
} else if (const Token *lpar = isAnonymousFunctionCall(tok)) {
functionCall(nullptr, lpar, varInfo, VarInfo::AllocInfo(0, VarInfo::NOALLOC), nullptr);
tok = lpar->link();

// goto => weird execution path
} else if (tok->str() == "goto") {
varInfo.clear();
return false;
}
Expand Down Expand Up @@ -941,6 +986,7 @@ const Token * CheckLeakAutoVarImpl::checkTokenInsideExpression(const Token * con

// check for function call
const Token * const openingPar = inFuncCall ? nullptr : isFunctionCall(tok);
const Token * const anonOpeningPar = isAnonymousFunctionCall(tok);
if (openingPar) {
const Library::AllocFunc* allocFunc = mSettings.library.getDeallocFuncInfo(tok);
VarInfo::AllocInfo alloc(allocFunc ? allocFunc->groupId : 0, VarInfo::DEALLOC, tok);
Expand All @@ -951,7 +997,15 @@ const Token * CheckLeakAutoVarImpl::checkTokenInsideExpression(const Token * con
if (startsWith(returnValue, "arg"))
// the function returns one of its argument, we need to process a potential assignment
return openingPar;
return isCPPCast(tok->astParent()) ? openingPar : openingPar->link();

if (!anonOpeningPar)
return isCPPCast(tok->astParent()) ? openingPar : openingPar->link();
}

// check for anonymous function call
if (anonOpeningPar) {
functionCall(nullptr, anonOpeningPar, varInfo, VarInfo::AllocInfo(0, VarInfo::NOALLOC), nullptr);
return anonOpeningPar->link();
}

return nullptr;
Expand Down Expand Up @@ -1022,7 +1076,7 @@ void CheckLeakAutoVarImpl::functionCall(const Token *tokName, const Token *tokOp
const bool isLeakIgnore = mSettings.library.isLeakIgnore(mSettings.library.getFunctionName(tokName));
if (mSettings.library.getReallocFuncInfo(tokName))
return;
if (tokName->next()->valueType() && tokName->next()->valueType()->container && tokName->next()->valueType()->container->stdStringLike)
if (tokName && tokName->next()->valueType() && tokName->next()->valueType()->container && tokName->next()->valueType()->container->stdStringLike)
return;

const Token * const tokFirstArg = tokOpeningPar->next();
Expand Down Expand Up @@ -1269,7 +1323,7 @@ void CheckLeakAutoVarImpl::ret(const Token *tok, VarInfo &varInfo, const bool is
const auto use = possibleUsage.find(varid);
if (use == possibleUsage.end()) {
leakError(tok, var->name(), it->second.type);
} else if (!use->second.first->variable()) { // TODO: handle constructors
} else if (use->second.first && !use->second.first->variable()) { // TODO: handle constructors
configurationInfo(tok, use->second);
}
}
Expand Down
49 changes: 49 additions & 0 deletions test/testleakautovar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ class TestLeakAutoVar : public TestFixture {

// handling function calls
TEST_CASE(functioncall1);
TEST_CASE(anonymousFunctionCall1);

// goto
TEST_CASE(goto1);
Expand Down Expand Up @@ -1910,6 +1911,54 @@ class TestLeakAutoVar : public TestFixture {
ASSERT_EQUALS("[test.cpp:4:1]: (error) Memory leak: b [memleak]\n", errout_str());
}

void anonymousFunctionCall1() { // #14990
// function pointer
check("void f(void (*fptr)(void *)) {\n"
"void *buf = malloc(1);\n"
"(*fptr)(buf);\n"
"}\n");
ASSERT_EQUALS("", errout_str());

// lambda
check("void f() {\n"
"auto x = [](void *ptr) { g(ptr) };\n"
"void *p = malloc(1);\n"
"(x)(p);\n"
"}\n");
ASSERT_EQUALS("", errout_str());

// Function returning a function pointer
check("void f() {\n"
" void *buf = malloc(1);\n"
" get_function()(buf);\n"
"}\n");
ASSERT_EQUALS("", errout_str());

// Function returning a function pointer, passed as an arg to a normal
// function
check("void f() {\n"
" void *buf = malloc(1);\n"
" foo(get_function()(buf));\n"
"}\n");
ASSERT_EQUALS("[test.c:3:29]: (information) --check-library: Function foo() should have <noreturn> configuration [checkLibraryNoReturn]\n"
"[test.c:4:1]: (information) --check-library: Function foo() should have <use>/<leak-ignore> configuration [checkLibraryUseIgnore]\n",
errout_str());

// Function returning a function pointer, passed as an arg to another
// function returning a function pointer
check("void f() {\n"
" void *buf = malloc(1);\n"
" get_function()(get_function()(buf));\n"
"}\n");
ASSERT_EQUALS("", errout_str());

check("void f() {\n"
" void *buf = malloc(1);\n"
" get_function(buf)(get_function(NULL)(NULL));\n"
"}\n");
ASSERT_EQUALS("[test.c:4:1]: (information) --check-library: Function get_function() should have <use>/<leak-ignore> configuration [checkLibraryUseIgnore]\n", errout_str());
}

void goto1() {
check("static void f() {\n"
" int err = -ENOMEM;\n"
Expand Down
Loading