From 98a3c13cc8e54b7e6694ec79e3d86d8a885a0b49 Mon Sep 17 00:00:00 2001 From: Rikard Falkeborn Date: Mon, 9 Sep 2019 21:22:46 +0200 Subject: [PATCH] Fix #9343 (memleak FP when return with cast) This was most likely introduced when the checks were changed to run on the full tokenlist instead of the simplified one. Take care to warn about cases where casts destroy the pointer, such as uint8_t f() { void* x = malloc(1); return (uint8_t)x; } --- lib/checkleakautovar.cpp | 20 +++++++++++++------ test/testleakautovar.cpp | 43 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 6 deletions(-) diff --git a/lib/checkleakautovar.cpp b/lib/checkleakautovar.cpp index c12011c923b..264502acf0b 100644 --- a/lib/checkleakautovar.cpp +++ b/lib/checkleakautovar.cpp @@ -930,13 +930,21 @@ void CheckLeakAutoVar::ret(const Token *tok, const VarInfo &varInfo) if (var) { bool used = false; for (const Token *tok2 = tok; tok2; tok2 = tok2->next()) { - if (tok2->str() == ";") + if (tok2->str() == ";" || Token::simpleMatch(tok2, "return ;")) break; - if (Token::Match(tok2, "return|(|{|, %varid% [});,]", varid)) { - used = true; - break; - } - if (Token::Match(tok2, "return|(|{|, & %varid% . %name% [});,]", varid)) { + if (!Token::Match(tok2, "return|(|{|,")) + continue; + + tok2 = tok2->next(); + while (tok2 && tok2->isCast() && (tok2->valueType()->pointer || (tok2->valueType()->typeSize(*mSettings) >= mSettings->sizeof_pointer))) + tok2 = tok2->astOperand2() ? tok2->astOperand2() : tok2->astOperand1(); + if (Token::Match(tok2, "%varid%", varid)) + tok2 = tok2->next(); + else if (Token::Match(tok2, "& %varid% . %name%", varid)) + tok2 = tok2->tokAt(4); + else + continue; + if (Token::Match(tok2, "[});,]")) { used = true; break; } diff --git a/test/testleakautovar.cpp b/test/testleakautovar.cpp index 878e71c778c..d0d738715f1 100644 --- a/test/testleakautovar.cpp +++ b/test/testleakautovar.cpp @@ -144,6 +144,7 @@ class TestLeakAutoVar : public TestFixture { TEST_CASE(return4); TEST_CASE(return5); TEST_CASE(return6); // #8282 return {p, p} + TEST_CASE(return7); // #9343 return (uint8_t*)x // General tests: variable type, allocation type, etc TEST_CASE(test1); @@ -521,6 +522,16 @@ class TestLeakAutoVar : public TestFixture { " return p;\n" "}", true); ASSERT_EQUALS("", errout.str()); + + check("void f(void* p) {\n" + " if (a) {\n" + " free(p);\n" + " return;\n" + " }\n" + " g(p);\n" + " return;\n" + "}"); + ASSERT_EQUALS("", errout.str()); } void deallocuse5() { // #4018 @@ -1694,6 +1705,38 @@ class TestLeakAutoVar : public TestFixture { ASSERT_EQUALS("", errout.str()); } + void return7() { // #9343 + check("uint8_t *f() {\n" + " void *x = malloc(1);\n" + " return (uint8_t *)x;\n" + "}", true); + ASSERT_EQUALS("", errout.str()); + + check("uint8_t f() {\n" + " void *x = malloc(1);\n" + " return (uint8_t)x;\n" + "}", true); + ASSERT_EQUALS("[test.cpp:3]: (error) Memory leak: x\n", errout.str()); + + check("void** f() {\n" + " void *x = malloc(1);\n" + " return (void**)x;\n" + "}", true); + ASSERT_EQUALS("", errout.str()); + + check("void* f() {\n" + " void *x = malloc(1);\n" + " return (long long)x;\n" + "}", true); + ASSERT_EQUALS("", errout.str()); + + check("void* f() {\n" + " void *x = malloc(1);\n" + " return (void*)(short)x;\n" + "}", true); + ASSERT_EQUALS("[test.cpp:3]: (error) Memory leak: x\n", errout.str()); + } + void test1() { // 3809 check("void f(double*&p) {\n" " p = malloc(0x100);\n"