diff --git a/lib/checkleakautovar.cpp b/lib/checkleakautovar.cpp index 434c97d7a42..d4c1f118b53 100644 --- a/lib/checkleakautovar.cpp +++ b/lib/checkleakautovar.cpp @@ -363,52 +363,40 @@ bool CheckLeakAutoVar::checkScope(const Token * const startToken, while (Token::Match(ftok, "%name% :: %name%")) ftok = ftok->tokAt(2); - // memcpy / memmove - if (Token::Match(varTok, "memcpy|memmove")) { - const std::vector args = getArguments(varTok); - // too few args for memcpy / memmove call - if (args.size() < 3) - continue; - const Token *dst = args[0]; - const Token *src = args[1]; - - // check that dst arg is pointer to pointer - int dstIndirectionLevel = 0; - while (dst->str() == "*") { - dst = dst->astOperand1(); - dstIndirectionLevel--; - } - if (dst->str() == "&") { - dst = dst->astOperand1(); - dstIndirectionLevel++; - } - if (!dst->isVariable()) - continue; - if (dstIndirectionLevel + dst->variable()->valueType()->pointer != 2) - continue; - - // check that src arg is pointer to pointer - int srcIndirectionLevel = 0; - while (src->str() == "*") { - src = src->astOperand1(); - srcIndirectionLevel--; - } - if (src->str() == "&") { - src = src->astOperand1(); - srcIndirectionLevel++; + if (const Library::Function *libFunc = mSettings->library.getFunction(ftok)) { + using ArgumentChecks = Library::ArgumentChecks; + using Direction = ArgumentChecks::Direction; + const std::vector args = getArguments(ftok); + const std::map &argChecks = libFunc->argumentChecks; + bool hasOutParam = false; + for (const auto &pair : argChecks) { + hasOutParam |= std::any_of(pair.second.direction.cbegin(), pair.second.direction.cend(), [&](const Direction dir) { + return dir == Direction::DIR_OUT; + }); + if (hasOutParam) + break; } - if (!src->isVariable()) - continue; - if (srcIndirectionLevel + src->variable()->valueType()->pointer != 2) - continue; - - if (!dst->variable()->isArgument()) { - varInfo.alloctype[dst->varId()].status = VarInfo::AllocStatus::ALLOC; + if (hasOutParam) { + for (int i = 0; i < args.size(); i++) { + if (!argChecks.count(i + 1)) + continue; + const ArgumentChecks argCheck = argChecks.at(i + 1); + bool isInParam = std::any_of(argCheck.direction.cbegin(), argCheck.direction.cend(), [&](const Direction dir) { + return dir == Direction::DIR_IN; + }); + if (!isInParam) + continue; + const Token *varTok = args[i]; + int indirect = 0; + while (varTok->isUnaryOp("&")) { + varTok = varTok->astOperand1(); + indirect++; + } + if (varTok->isVariable() && indirect) { + varInfo.erase(varTok->varId()); + } + } } - - // no multivariable checking currently (see assignment below) - // treat source pointer as unallocated - varInfo.erase(src->varId()); } auto isAssignment = [](const Token* varTok) -> const Token* { diff --git a/test/testleakautovar.cpp b/test/testleakautovar.cpp index 13b9a83eb18..f0c569e2d5d 100644 --- a/test/testleakautovar.cpp +++ b/test/testleakautovar.cpp @@ -64,8 +64,8 @@ class TestLeakAutoVar : public TestFixture { TEST_CASE(assign25); TEST_CASE(assign26); - TEST_CASE(memcpyWithPtr1); // #11542 - TEST_CASE(memcpyWithPtr2); + TEST_CASE(memcpy1); // #11542 + TEST_CASE(memcpy2); TEST_CASE(isAutoDealloc); @@ -636,7 +636,7 @@ class TestLeakAutoVar : public TestFixture { ASSERT_EQUALS("", errout_str()); } - void memcpyWithPtr1() { // #11542 + void memcpy1() { // #11542 const Settings s = settingsBuilder().library("std.cfg").library("posix.cfg").build(); check("void f(char** old, char* value) {\n" " char *str = strdup(value);\n" @@ -645,14 +645,13 @@ class TestLeakAutoVar : public TestFixture { ASSERT_EQUALS("", errout_str()); } - void memcpyWithPtr2() { + void memcpy2() { const Settings s = settingsBuilder().library("std.cfg").library("posix.cfg").build(); - check("void f(char* value) {\n" - " char *old = NULL;\n" + check("void f(char* old, char* value, size_t len) {\n" " char *str = strdup(value);\n" - " memcpy(&old, &str, sizeof(char*));\n" + " memcpy(old, str, len);\n" "}\n", &s); - ASSERT_EQUALS("[test.cpp:5]: (error) Memory leak: old\n", errout_str()); + ASSERT_EQUALS("[test.cpp:4]: (error) Memory leak: str\n", errout_str()); } void isAutoDealloc() {