leakNoVarFunctionCall: Use AST more (fix #9252) (#2086)

Use the AST a little bit more to improve the check. In order to do so,
rewrite the check to work from the outer function first and then check
the arguments, instead of the other way around.

It also fixes Trac ticket #9252, no warning is now given for

	void* malloc1() {
		return(malloc1(1));
	}

This FP seems to be common in daca results.

It also makes it possible to improve handling of casts, for example
cppcheck now warns about

	void f() {
		strcpy(a, (void*) strdup(p));
	}

But not for

	char* f() {
		char* ret = (char*)strcpy(malloc(10), "abc");
		return ret;
	}

These FP/FN were introduced when the check was switched to use the
simplified token list.
This commit is contained in:
Rikard Falkeborn 2019-08-14 22:01:40 +02:00 committed by Daniel Marjamäki
parent f139558d90
commit fc1d5b187f
2 changed files with 40 additions and 16 deletions

View File

@ -985,31 +985,40 @@ void CheckMemoryLeakNoVar::checkForUnreleasedInputArgument(const Scope *scope)
// parse the executable scope until tok is reached...
for (const Token *tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) {
// allocating memory in parameter for function call..
if (!(Token::Match(tok, "[(,] %name% (") && Token::Match(tok->linkAt(2), ") [,)]")))
if (!Token::Match(tok, "%name% ("))
continue;
if (getAllocationType(tok->next(), 0) == No)
// check if the output of the function is assigned
const Token* tok2 = tok->next()->astParent();
while (tok2 && tok2->isCast())
tok2 = tok2->astParent();
if (tok2 && tok2->isAssignmentOp())
continue;
// locate outer function call..
const Token* tok3 = tok;
while (tok3 && tok3->astParent() && tok3->str() == ",")
tok3 = tok3->astParent();
if (!tok3 || tok3->str() != "(")
continue;
// Is it a function call..
if (!Token::Match(tok3->tokAt(-2), "!!= %name% ("))
continue;
const std::string& functionName = tok3->strAt(-1);
const std::string& functionName = tok->str();
if ((mTokenizer->isCPP() && functionName == "delete") ||
functionName == "free" ||
functionName == "fclose" ||
functionName == "realloc")
break;
if (isReopenStandardStream(tok->next()))
if (!CheckMemoryLeakInFunction::test_white_list(functionName, mSettings, mTokenizer->isCPP()))
continue;
if (CheckMemoryLeakInFunction::test_white_list(functionName, mSettings, mTokenizer->isCPP())) {
functionCallLeak(tok, tok->strAt(1), functionName);
const std::vector<const Token *> args = getArguments(tok);
for (const Token* arg : args) {
if (arg->isOp())
continue;
while (arg->astOperand1())
arg = arg->astOperand1();
if (getAllocationType(arg, 0) == No)
continue;
if (isReopenStandardStream(arg))
continue;
functionCallLeak(arg, arg->str(), functionName);
break;
}
}
}

View File

@ -2058,7 +2058,6 @@ private:
Tokenizer tokenizer(&settings, this);
std::istringstream istr(code);
tokenizer.tokenize(istr, "test.cpp");
tokenizer.simplifyTokenList2();
// Check for memory leaks..
CheckMemoryLeakNoVar checkMemoryLeakNoVar(&tokenizer, &settings, this);
@ -2144,6 +2143,22 @@ private:
" assert(freopen(\"/dev/null\", \"r\", stdin));\n"
"}");
ASSERT_EQUALS("", errout.str());
check("void x() {\n"
" strcpy(a, (void*)strdup(p));\n"
"}");
ASSERT_EQUALS("[test.cpp:2]: (error) Allocation with strdup, strcpy doesn't release it.\n", errout.str());
check("void* malloc1() {\n"
" return (malloc(1));\n"
"}");
ASSERT_EQUALS("", errout.str());
check("char *x() {\n"
" char *ret = (char*)strcpy(malloc(10), \"abc\");\n"
" return ret;\n"
"}");
ASSERT_EQUALS("", errout.str());
}
void missingAssignment() {