Fixed ticket #399 (Add detection for resource leaks after open() usage)

http://sourceforge.net/apps/trac/cppcheck/ticket/399
This commit is contained in:
Slava Semushin 2009-06-21 22:01:43 +07:00
parent 8715ba1458
commit 13e805f332
3 changed files with 121 additions and 2 deletions

View File

@ -117,6 +117,9 @@ CheckMemoryLeak::AllocType CheckMemoryLeak::GetAllocationType(const Token *tok2)
if (Token::Match(tok2, "fopen|tmpfile ("))
return File;
if (Token::Match(tok2, "open|openat|creat|mkstemp|mkostemp ("))
return Fd;
if (Token::simpleMatch(tok2, "popen ("))
return Pipe;
@ -188,6 +191,9 @@ CheckMemoryLeak::AllocType CheckMemoryLeak::GetDeallocationType(const Token *tok
Token::simpleMatch(tok, "fcloseall ( )"))
return File;
if (Token::simpleMatch(tok, std::string("close ( " + names + " )").c_str()))
return Fd;
if (Token::simpleMatch(tok, std::string("pclose ( " + names + " )").c_str()))
return Pipe;
@ -205,6 +211,7 @@ void CheckMemoryLeak::MemoryLeak(const Token *tok, const char varname[], AllocTy
{
if (alloctype == CheckMemoryLeak::File ||
alloctype == CheckMemoryLeak::Pipe ||
alloctype == CheckMemoryLeak::Fd ||
alloctype == CheckMemoryLeak::Dir)
resourceLeakError(tok, varname);
else if (all)
@ -400,6 +407,12 @@ const char * CheckMemoryLeakInFunction::call_func(const Token *tok, std::list<co
Token::Match(tok, "setvbuf|setbuf|setbuffer|setlinebuf|rewind"))
return 0;
// I/O functions that are not allocating nor deallocating memory..
if (Token::Match(tok, "read|readv|pread|readahead|write|writev|pwrite|lseek") ||
Token::Match(tok, "ioctl|fcntl|flock|lockf|ftruncate|fsync|fdatasync") ||
Token::Match(tok, "fstat|sync_file_range|posix_fallocate|posix_fadvise"))
return 0;
// Functions to work with directories that are not allocating nor
// deallocating memory..
if (Token::Match(tok, "readdir|readdir_r|rewinddir|telldir|seekdir|scandir"))
@ -695,6 +708,12 @@ Token *CheckMemoryLeakInFunction::getcode(const Token *tok, std::list<const Toke
while (tok->str() != ")")
tok = tok->next();
}
else if (Token::simpleMatch(tok, std::string("if ( " + varnameStr + " == -1 )").c_str()) ||
Token::simpleMatch(tok, std::string("if ( " + varnameStr + " < 0 )").c_str()))
{
// FIXME: ensure then this variable has int type and uses as file descriptor
addtoken("if(!var)");
}
else if (Token::simpleMatch(tok, "if (") && notvar(tok->tokAt(2), varnames, true))
{
addtoken("if(!var)");
@ -714,7 +733,7 @@ Token *CheckMemoryLeakInFunction::getcode(const Token *tok, std::list<const Toke
if (parlevel <= 0)
break;
}
if (Token::Match(tok2, std::string("fclose|closedir ( " + varnameStr + " )").c_str()))
if (Token::Match(tok2, std::string("close|fclose|closedir ( " + varnameStr + " )").c_str()))
{
addtoken("dealloc");
addtoken(";");
@ -1644,6 +1663,11 @@ void CheckMemoryLeakInFunction::check()
const int varname_tok = (tok->tokAt(4)->str() != "const" ? 4 : 5);
checkScope(tok->next(), tok->strAt(varname_tok), classmember, sz);
}
else if (Token::Match(tok, "[{};] int %var% [;=]"))
{
checkScope(tok->next(), tok->strAt(2), classmember, sz);
}
}
}
}

View File

@ -47,7 +47,7 @@ public:
CheckMemoryLeak() { }
/** What type of allocation are used.. the "Many" means that several types of allocation and deallocation are used */
enum AllocType { No, Malloc, gMalloc, New, NewArray, File, Pipe, Dir, Many };
enum AllocType { No, Malloc, gMalloc, New, NewArray, File, Fd, Pipe, Dir, Many };
void MemoryLeak(const Token *tok, const char varname[], AllocType alloctype, bool all);
void MismatchError(const Token *Tok1, const std::list<const Token *> &callstack, const char varname[]);

View File

@ -274,6 +274,11 @@ private:
TEST_CASE(fcloseall_function);
TEST_CASE(file_functions);
TEST_CASE(open_function);
TEST_CASE(creat_function);
TEST_CASE(close_function);
TEST_CASE(fd_functions);
TEST_CASE(opendir_function);
TEST_CASE(fdopendir_function);
TEST_CASE(closedir_function);
@ -2177,6 +2182,96 @@ private:
ASSERT_EQUALS("", errout.str());
}
void open_function()
{
check("void f(const char *path)\n"
"{\n"
" int fd = open(path, O_RDONLY);\n"
"}\n", true);
ASSERT_EQUALS("[test.cpp:4]: (error) Resource leak: fd\n", errout.str());
check("void f(const char *path)\n"
"{\n"
" int fd = open(path, O_RDONLY);\n"
" if (fd == -1)\n"
" return;\n"
" close(fd);\n"
"}\n", true);
ASSERT_EQUALS("", errout.str());
check("void f(const char *path)\n"
"{\n"
" int fd = open(path, O_RDONLY);\n"
" if (fd < 0)\n"
" return;\n"
" close(fd);\n"
"}\n", true);
ASSERT_EQUALS("", errout.str());
}
void creat_function()
{
check("void f(const char *path)\n"
"{\n"
" int fd = creat(path, S_IRWXU);\n"
"}\n");
ASSERT_EQUALS("[test.cpp:4]: (error) Resource leak: fd\n", errout.str());
}
void close_function()
{
check("void f(const char *path)\n"
"{\n"
" int fd = open(path, O_RDONLY);\n"
" close(fd);\n"
"}\n");
ASSERT_EQUALS("", errout.str());
check("void f(const char *path)\n"
"{\n"
" int fd = creat(path, S_IRWXU);\n"
" close(fd);\n"
"}\n");
ASSERT_EQUALS("", errout.str());
check("void f(const char *path)\n"
"{\n"
" int fd = creat(path, S_IRWXU);\n"
" if (close(fd) < 0) {\n"
" perror(\"close\");\n"
" }\n"
"}\n");
ASSERT_EQUALS("", errout.str());
}
void fd_functions()
{
check("void f(const char *path)\n"
"{\n"
" int fd = open(path, O_RDONLY);\n"
" read(fd, buf, count);\n"
" readv(fd, iov, iovcnt);\n"
" readahead(fd, offset, count);\n"
" pread(fd, buf, count, offset);\n"
" write(fd, buf, count);\n"
" writev(fd, iov, iovcnt);\n"
" pwrite(fd, buf, count, offset);\n"
" ioctl(fd, request);\n"
" posix_fallocate(fd, offset, len);\n"
" posix_fadvise(fd, offset, len, advise);\n"
" fsync(fd);\n"
" fdatasync(fd);\n"
" sync_file_range(fd, offset, nbytes, flags);\n"
" lseek(fd, offset, whence);\n"
" fcntl(fd, cmd);\n"
" flock(fd, op);\n"
" lockf(fd, cmd, len);\n"
" ftruncate(fd, len);\n"
" fstat(fd, buf);\n"
"}\n");
ASSERT_EQUALS("[test.cpp:23]: (error) Resource leak: fd\n", errout.str());
}
void opendir_function()
{
check("void f()\n"