Fix 9298 (#2476)
* Fix 9298 Tell cppcheck that strcpy returns its first argument, and use that knowledge in checkTokenInsideExpression. * Add missing unit tests in cmake
This commit is contained in:
parent
bca5d0d820
commit
0b7649ca9b
|
@ -4641,7 +4641,7 @@ The obsolete function 'gets' is called. With 'gets' you'll get a buffer overrun
|
||||||
</function>
|
</function>
|
||||||
<!-- char *strcpy(char *desstr, const char *srcstr); -->
|
<!-- char *strcpy(char *desstr, const char *srcstr); -->
|
||||||
<function name="strcpy,std::strcpy">
|
<function name="strcpy,std::strcpy">
|
||||||
<returnValue type="char *"/>
|
<returnValue type="char *">arg1</returnValue>
|
||||||
<noreturn>false</noreturn>
|
<noreturn>false</noreturn>
|
||||||
<leak-ignore/>
|
<leak-ignore/>
|
||||||
<arg nr="1" direction="out">
|
<arg nr="1" direction="out">
|
||||||
|
|
|
@ -743,8 +743,31 @@ const Token * CheckLeakAutoVar::checkTokenInsideExpression(const Token * const t
|
||||||
deallocUseError(tok, tok->str());
|
deallocUseError(tok, tok->str());
|
||||||
} else if (Token::simpleMatch(tok->tokAt(-2), "= &")) {
|
} else if (Token::simpleMatch(tok->tokAt(-2), "= &")) {
|
||||||
varInfo->erase(tok->varId());
|
varInfo->erase(tok->varId());
|
||||||
} else if (Token::Match(tok->previous(), "= %var% [;,)]")) {
|
} else {
|
||||||
varInfo->erase(tok->varId());
|
// check if tok is assigned into another variable
|
||||||
|
const Token *rhs = tok;
|
||||||
|
while (rhs->astParent()) {
|
||||||
|
if (rhs->astParent()->str() == "=")
|
||||||
|
break;
|
||||||
|
rhs = rhs->astParent();
|
||||||
|
}
|
||||||
|
if (rhs->varId() == tok->varId()) {
|
||||||
|
// simple assignment
|
||||||
|
varInfo->erase(tok->varId());
|
||||||
|
} else if (rhs->str() == "(" && mSettings->library.returnValue(rhs->astOperand1()) != emptyString) {
|
||||||
|
// #9298, assignment through return value of a function
|
||||||
|
const std::string &returnValue = mSettings->library.returnValue(rhs->astOperand1());
|
||||||
|
if (returnValue.compare(0, 3, "arg") == 0) {
|
||||||
|
int argn;
|
||||||
|
const Token *func = getTokenArgumentFunction(tok, argn);
|
||||||
|
if (func) {
|
||||||
|
const std::string arg = "arg" + std::to_string(argn + 1);
|
||||||
|
if (returnValue == arg) {
|
||||||
|
varInfo->erase(tok->varId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if (Token::Match(tok->previous(), "& %name% = %var% ;")) {
|
} else if (Token::Match(tok->previous(), "& %name% = %var% ;")) {
|
||||||
varInfo->referenced.insert(tok->tokAt(2)->varId());
|
varInfo->referenced.insert(tok->tokAt(2)->varId());
|
||||||
|
@ -759,7 +782,7 @@ const Token * CheckLeakAutoVar::checkTokenInsideExpression(const Token * const t
|
||||||
if (alloc.type == 0)
|
if (alloc.type == 0)
|
||||||
alloc.status = VarInfo::NOALLOC;
|
alloc.status = VarInfo::NOALLOC;
|
||||||
functionCall(tok, openingPar, varInfo, alloc, nullptr);
|
functionCall(tok, openingPar, varInfo, alloc, nullptr);
|
||||||
return openingPar->link();
|
return openingPar;
|
||||||
}
|
}
|
||||||
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
|
@ -59,6 +59,12 @@ if (BUILD_TESTS)
|
||||||
add_fixture(${CMAKE_MATCH_1})
|
add_fixture(${CMAKE_MATCH_1})
|
||||||
endif()
|
endif()
|
||||||
endforeach()
|
endforeach()
|
||||||
|
add_fixture("TestLeakAutoVarStrcpy")
|
||||||
|
add_fixture("TestLeakAutoVarWindows")
|
||||||
|
add_fixture("TestMemleakInFunction")
|
||||||
|
add_fixture("TestMemleakInClass")
|
||||||
|
add_fixture("TestMemleakStructMember")
|
||||||
|
add_fixture("TestMemleakNoVar")
|
||||||
|
|
||||||
function(add_cfg CFG_TEST)
|
function(add_cfg CFG_TEST)
|
||||||
set(options INCONCLUSIVE)
|
set(options INCONCLUSIVE)
|
||||||
|
|
|
@ -2020,7 +2020,48 @@ private:
|
||||||
REGISTER_TEST(TestLeakAutoVar)
|
REGISTER_TEST(TestLeakAutoVar)
|
||||||
|
|
||||||
|
|
||||||
|
class TestLeakAutoVarStrcpy: public TestFixture {
|
||||||
|
public:
|
||||||
|
TestLeakAutoVarStrcpy() : TestFixture("TestLeakAutoVarStrcpy") {
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Settings settings;
|
||||||
|
|
||||||
|
void check(const char code[]) {
|
||||||
|
// Clear the error buffer..
|
||||||
|
errout.str("");
|
||||||
|
|
||||||
|
// Tokenize..
|
||||||
|
Tokenizer tokenizer(&settings, this);
|
||||||
|
std::istringstream istr(code);
|
||||||
|
tokenizer.tokenize(istr, "test.cpp");
|
||||||
|
|
||||||
|
// Check for leaks..
|
||||||
|
CheckLeakAutoVar checkLeak;
|
||||||
|
settings.checkLibrary = true;
|
||||||
|
settings.addEnabled("information");
|
||||||
|
checkLeak.runChecks(&tokenizer, &settings, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void run() OVERRIDE {
|
||||||
|
LOAD_LIB_2(settings.library, "std.cfg");
|
||||||
|
|
||||||
|
TEST_CASE(returnedValue); // #9298
|
||||||
|
}
|
||||||
|
|
||||||
|
void returnedValue() { // #9298
|
||||||
|
check("char *m;\n"
|
||||||
|
"void strcpy_returnedvalue(const char* str)\n"
|
||||||
|
"{\n"
|
||||||
|
" char* ptr = new char[strlen(str)+1];\n"
|
||||||
|
" m = strcpy(ptr, str);\n"
|
||||||
|
"}");
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
REGISTER_TEST(TestLeakAutoVarStrcpy)
|
||||||
|
|
||||||
|
|
||||||
class TestLeakAutoVarWindows : public TestFixture {
|
class TestLeakAutoVarWindows : public TestFixture {
|
||||||
|
|
Loading…
Reference in New Issue