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:
parent
f139558d90
commit
fc1d5b187f
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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() {
|
||||
|
|
Loading…
Reference in New Issue