Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 22 additions & 16 deletions lib/checkleakautovar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -96,10 +96,9 @@ void VarInfo::print()
std::cout << "size=" << alloctype.size() << std::endl;
for (std::map<int, AllocInfo>::const_iterator it = alloctype.cbegin(); it != alloctype.cend(); ++it) {
std::string strusage;
const std::map<int, std::string>::const_iterator use =
possibleUsage.find(it->first);
const auto use = possibleUsage.find(it->first);
if (use != possibleUsage.end())
strusage = use->second;
strusage = use->second.first;

std::string status;
switch (it->second.status) {
Expand Down Expand Up @@ -133,11 +132,11 @@ void VarInfo::print()
}
}

void VarInfo::possibleUsageAll(const std::string &functionName)
void VarInfo::possibleUsageAll(const std::pair<std::string, Usage>& functionUsage)
{
possibleUsage.clear();
for (std::map<int, AllocInfo>::const_iterator it = alloctype.cbegin(); it != alloctype.cend(); ++it)
possibleUsage[it->first] = functionName;
possibleUsage[it->first] = functionUsage;
}


Expand Down Expand Up @@ -169,13 +168,13 @@ void CheckLeakAutoVar::deallocReturnError(const Token *tok, const Token *dealloc
reportError(locations, Severity::error, "deallocret", "$symbol:" + varname + "\nReturning/dereferencing '$symbol' after it is deallocated / released", CWE672, Certainty::normal);
}

void CheckLeakAutoVar::configurationInfo(const Token* tok, const std::string &functionName)
void CheckLeakAutoVar::configurationInfo(const Token* tok, const std::pair<std::string, VarInfo::Usage>& functionUsage)
{
if (mSettings->checkLibrary) {
if (mSettings->checkLibrary && functionUsage.second == VarInfo::USED) {
reportError(tok,
Severity::information,
"checkLibraryUseIgnore",
"--check-library: Function " + functionName + "() should have <use>/<leak-ignore> configuration");
"--check-library: Function " + functionUsage.first + "() should have <use>/<leak-ignore> configuration");
}
}

Expand Down Expand Up @@ -300,7 +299,7 @@ bool CheckLeakAutoVar::checkScope(const Token * const startToken,
throw InternalError(startToken, "Internal limit: CheckLeakAutoVar::checkScope() Maximum recursive count of 1000 reached.", InternalError::LIMIT);

std::map<int, VarInfo::AllocInfo> &alloctype = varInfo.alloctype;
std::map<int, std::string> &possibleUsage = varInfo.possibleUsage;
auto& possibleUsage = varInfo.possibleUsage;
const std::set<int> conditionalAlloc(varInfo.conditionalAlloc);

// Parse all tokens
Expand Down Expand Up @@ -671,8 +670,15 @@ bool CheckLeakAutoVar::checkScope(const Token * const startToken,
if (mTokenizer->isScopeNoReturn(tok->tokAt(2), &unknown)) {
if (!unknown)
varInfo.clear();
else if (!mSettings->library.isLeakIgnore(functionName) && !mSettings->library.isUse(functionName))
varInfo.possibleUsageAll(functionName);
else {
if (ftok->function() && !ftok->function()->isAttributeNoreturn() &&
!(ftok->function()->functionScope && mTokenizer->isScopeNoReturn(ftok->function()->functionScope->bodyEnd))) // check function scope
continue;
if (!mSettings->library.isLeakIgnore(functionName) && !mSettings->library.isUse(functionName)) {
const VarInfo::Usage usage = Token::simpleMatch(openingPar, "( )") ? VarInfo::NORET : VarInfo::USED; // TODO: check parameters passed to function
varInfo.possibleUsageAll({ functionName, usage });
}
}
}
}

Expand Down Expand Up @@ -870,7 +876,7 @@ void CheckLeakAutoVar::changeAllocStatus(VarInfo &varInfo, const VarInfo::AllocI
if (var != alloctype.end()) {
if (allocation.status == VarInfo::NOALLOC) {
// possible usage
varInfo.possibleUsage[arg->varId()] = tok->str();
varInfo.possibleUsage[arg->varId()] = { tok->str(), VarInfo::USED };
if (var->second.status == VarInfo::DEALLOC && arg->previous()->str() == "&")
varInfo.erase(arg->varId());
} else if (var->second.managed()) {
Expand Down Expand Up @@ -1006,11 +1012,11 @@ void CheckLeakAutoVar::leakIfAllocated(const Token *vartok,
const VarInfo &varInfo)
{
const std::map<int, VarInfo::AllocInfo> &alloctype = varInfo.alloctype;
const std::map<int, std::string> &possibleUsage = varInfo.possibleUsage;
const auto& possibleUsage = varInfo.possibleUsage;

const std::map<int, VarInfo::AllocInfo>::const_iterator var = alloctype.find(vartok->varId());
if (var != alloctype.cend() && var->second.status == VarInfo::ALLOC) {
const std::map<int, std::string>::const_iterator use = possibleUsage.find(vartok->varId());
const auto use = possibleUsage.find(vartok->varId());
if (use == possibleUsage.end()) {
leakError(vartok, vartok->str(), var->second.type);
} else {
Expand All @@ -1022,7 +1028,7 @@ void CheckLeakAutoVar::leakIfAllocated(const Token *vartok,
void CheckLeakAutoVar::ret(const Token *tok, VarInfo &varInfo, const bool isEndOfScope)
{
const std::map<int, VarInfo::AllocInfo> &alloctype = varInfo.alloctype;
const std::map<int, std::string> &possibleUsage = varInfo.possibleUsage;
const auto& possibleUsage = varInfo.possibleUsage;
std::vector<int> toRemove;

const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();
Expand Down Expand Up @@ -1071,7 +1077,7 @@ void CheckLeakAutoVar::ret(const Token *tok, VarInfo &varInfo, const bool isEndO
deallocReturnError(tok, it->second.allocTok, var->name());

else if (!used && !it->second.managed()) {
const std::map<int, std::string>::const_iterator use = possibleUsage.find(varid);
const auto use = possibleUsage.find(varid);
if (use == possibleUsage.end()) {
leakError(tok, var->name(), it->second.type);
} else {
Expand Down
9 changes: 5 additions & 4 deletions lib/checkleakautovar.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,9 @@ class CPPCHECKLIB VarInfo {
return status < 0;
}
};
enum Usage { USED, NORET };
std::map<int, AllocInfo> alloctype;
std::map<int, std::string> possibleUsage;
std::map<int, std::pair<std::string, Usage>> possibleUsage;
std::set<int> conditionalAlloc;
std::set<int> referenced;

Expand Down Expand Up @@ -92,7 +93,7 @@ class CPPCHECKLIB VarInfo {
}

/** set possible usage for all variables */
void possibleUsageAll(const std::string &functionName);
void possibleUsageAll(const std::pair<std::string, Usage>& functionUsage);

void print();
};
Expand Down Expand Up @@ -159,12 +160,12 @@ class CPPCHECKLIB CheckLeakAutoVar : public Check {
void doubleFreeError(const Token *tok, const Token *prevFreeTok, const std::string &varname, int type);

/** message: user configuration is needed to complete analysis */
void configurationInfo(const Token* tok, const std::string &functionName);
void configurationInfo(const Token* tok, const std::pair<std::string, VarInfo::Usage>& functionUsage);

void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const override {
CheckLeakAutoVar c(nullptr, settings, errorLogger);
c.deallocReturnError(nullptr, nullptr, "p");
c.configurationInfo(nullptr, "f"); // user configuration is needed to complete analysis
c.configurationInfo(nullptr, { "f", VarInfo::USED }); // user configuration is needed to complete analysis
c.doubleFreeError(nullptr, nullptr, "varname", 0);
}

Expand Down
77 changes: 58 additions & 19 deletions test/testleakautovar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,6 @@ class TestLeakAutoVar : public TestFixture {
TEST_CASE(freopen2);

TEST_CASE(deallocuse1);
TEST_CASE(deallocuse2);
TEST_CASE(deallocuse3);
TEST_CASE(deallocuse4);
TEST_CASE(deallocuse5); // #4018: FP. free(p), p = 0;
Expand Down Expand Up @@ -508,8 +507,8 @@ class TestLeakAutoVar : public TestFixture {
settings = s;
}

void assign24() { // #7440
check("void f() {\n"
void assign24() {
check("void f() {\n" // #7440
" char* data = new char[100];\n"
" char** dataPtr = &data;\n"
" delete[] *dataPtr;\n"
Expand All @@ -523,6 +522,46 @@ class TestLeakAutoVar : public TestFixture {
" delete[] *dataPtr;\n"
"}\n", true);
ASSERT_EQUALS("", errout.str());

check("void f() {\n" // #9279
" int* p = new int;\n"
" *p = 42;\n"
" g();\n"
"}\n", /*cpp*/ true);
ASSERT_EQUALS("[test.cpp:4]: (information) --check-library: Function g() should have <noreturn> configuration\n",
errout.str());

check("void g();\n"
"void f() {\n"
" int* p = new int;\n"
" *p = 42;\n"
" g();\n"
"}\n", /*cpp*/ true);
ASSERT_EQUALS("[test.cpp:6]: (error) Memory leak: p\n", errout.str());

check("void g() {}\n"
"void f() {\n"
" int* p = new int;\n"
" *p = 42;\n"
" g();\n"
"}\n", /*cpp*/ true);
ASSERT_EQUALS("[test.cpp:6]: (error) Memory leak: p\n", errout.str());

check("[[noreturn]] void g();\n"
"void f() {\n"
" int* p = new int;\n"
" *p = 42;\n"
" g();\n"
"}\n", /*cpp*/ true);
ASSERT_EQUALS("", errout.str());

check("void g() { exit(1); }\n"
"void f() {\n"
" int* p = new int;\n"
" *p = 42;\n"
" g();\n"
"}\n", /*cpp*/ true);
ASSERT_EQUALS("", errout.str());
}

void isAutoDealloc() {
Expand Down Expand Up @@ -647,20 +686,6 @@ class TestLeakAutoVar : public TestFixture {
ASSERT_EQUALS("[test.c:3]: (error) Dereferencing 'p' after it is deallocated / released\n", errout.str());
}

void deallocuse2() {
check("void f(char *p) {\n"
" free(p);\n"
" strcpy(a, p);\n"
"}");
TODO_ASSERT_EQUALS("error (free,use)", "[test.c:3]: (information) --check-library: Function strcpy() should have <noreturn> configuration\n", errout.str());

check("void f(char *p) {\n" // #3041 - assigning pointer when it's used
" free(p);\n"
" strcpy(a, p=b());\n"
"}");
TODO_ASSERT_EQUALS("", "[test.c:3]: (information) --check-library: Function strcpy() should have <noreturn> configuration\n", errout.str());
}

void deallocuse3() {
check("void f(struct str *p) {\n"
" free(p);\n"
Expand Down Expand Up @@ -1437,8 +1462,7 @@ class TestLeakAutoVar : public TestFixture {
" char *p = malloc(10);\n"
" fatal_error();\n"
"}");
ASSERT_EQUALS("[test.c:3]: (information) --check-library: Function fatal_error() should have <noreturn> configuration\n"
"[test.c:4]: (information) --check-library: Function fatal_error() should have <use>/<leak-ignore> configuration\n",
ASSERT_EQUALS("[test.c:3]: (information) --check-library: Function fatal_error() should have <noreturn> configuration\n",
errout.str());
}

Expand Down Expand Up @@ -2711,6 +2735,7 @@ class TestLeakAutoVarStrcpy : public TestFixture {
LOAD_LIB_2(settings.library, "std.cfg");

TEST_CASE(returnedValue); // #9298
TEST_CASE(deallocuse2);
TEST_CASE(fclose_false_positive); // #9575
}

Expand All @@ -2724,6 +2749,20 @@ class TestLeakAutoVarStrcpy : public TestFixture {
ASSERT_EQUALS("", errout.str());
}

void deallocuse2() {
check("void f(char *p) {\n"
" free(p);\n"
" strcpy(a, p);\n"
"}");
TODO_ASSERT_EQUALS("error (free,use)", "", errout.str());

check("void f(char *p) {\n" // #3041 - assigning pointer when it's used
" free(p);\n"
" strcpy(a, p=b());\n"
"}");
ASSERT_EQUALS("", errout.str());
}

void fclose_false_positive() { // #9575
check("int f(FILE *fp) { return fclose(fp); }");
ASSERT_EQUALS("", errout.str());
Expand Down