2009-01-15 21:34:39 +01:00
/*
2009-01-21 21:04:20 +01:00
* Cppcheck - A tool for static C / C + + code analysis
2023-01-28 10:16:34 +01:00
* Copyright ( C ) 2007 - 2023 Cppcheck team .
2009-01-15 21:34:39 +01:00
*
* This program is free software : you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation , either version 3 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
2009-09-27 17:08:31 +02:00
* along with this program . If not , see < http : //www.gnu.org/licenses/>.
2009-01-15 21:34:39 +01:00
*/
# include "checkmemoryleak.h"
2017-05-27 04:33:47 +02:00
# include "astutils.h"
2020-05-23 07:16:49 +02:00
# include "errorlogger.h"
2017-05-27 04:33:47 +02:00
# include "library.h"
# include "settings.h"
# include "symboldatabase.h"
# include "token.h"
2009-03-20 18:16:21 +01:00
# include "tokenize.h"
2009-01-15 21:34:39 +01:00
# include <algorithm>
2022-01-27 19:03:20 +01:00
# include <unordered_set>
2022-08-14 12:44:19 +02:00
# include <utility>
2022-01-27 19:03:20 +01:00
# include <vector>
2009-01-15 21:34:39 +01:00
//---------------------------------------------------------------------------
2009-03-20 18:16:21 +01:00
// Register this check class (by creating a static instance of it)
2011-10-13 20:53:06 +02:00
namespace {
2011-11-25 14:47:45 +01:00
CheckMemoryLeakInFunction instance1 ;
2011-10-13 20:53:06 +02:00
CheckMemoryLeakInClass instance2 ;
CheckMemoryLeakStructMember instance3 ;
CheckMemoryLeakNoVar instance4 ;
2016-01-25 20:01:48 +01:00
}
2012-03-18 07:49:22 +01:00
CWE mapping of unsafeClassCanLeak, zerodivcond, invalidPointerCast, redundantCopy, redundantAssignment, comparisonFunctionIsAlwaysTrueOrFalse, checkCastIntToCharAndBack, cstyleCast, passedByValue, clarifyCondition, exceptThrowInDestructor, exceptDeallocThrow, exceptRethrowCopy, catchExceptionByValue, fflushOnInputStream, seekOnAppendedFile, publicAllocationError
CWE mapping of unsafeClassCanLeak, zerodivcond, invalidPointerCast, redundantCopy, redundantAssignment, comparisonFunctionIsAlwaysTrueOrFalse, checkCastIntToCharAndBack, cstyleCast, passedByValue, clarifyCondition, exceptThrowInDestructor, exceptDeallocThrow, exceptRethrowCopy, catchExceptionByValue, fflushOnInputStream, seekOnAppendedFile, publicAllocationError
2016-04-12 19:29:40 +02:00
// CWE ID used:
2016-08-25 19:17:07 +02:00
static const CWE CWE398 ( 398U ) ; // Indicator of Poor Code Quality
static const CWE CWE401 ( 401U ) ; // Improper Release of Memory Before Removing Last Reference ('Memory Leak')
static const CWE CWE771 ( 771U ) ; // Missing Reference to Active Allocated Resource
static const CWE CWE772 ( 772U ) ; // Missing Release of Resource after Effective Lifetime
2015-12-05 18:22:01 +01:00
2016-01-25 20:01:48 +01:00
/** List of functions that can be ignored when searching for memory leaks.
* These functions don ' t take the address of the given pointer
* This list contains function names with const parameters e . g . : atof ( const char * )
* TODO : This list should be replaced by < leak - ignore / > in . cfg files .
*/
2021-01-16 13:52:09 +01:00
static const std : : unordered_set < std : : string > call_func_white_list = {
2018-04-08 22:54:10 +02:00
" _open " , " _wopen " , " access " , " adjtime " , " asctime_r " , " asprintf " , " chdir " , " chmod " , " chown "
, " creat " , " ctime_r " , " execl " , " execle " , " execlp " , " execv " , " execve " , " fchmod " , " fcntl "
, " fdatasync " , " fclose " , " flock " , " fmemopen " , " fnmatch " , " fopen " , " fopencookie " , " for " , " free "
, " freopen " , " fseeko " , " fstat " , " fsync " , " ftello " , " ftruncate " , " getgrnam " , " gethostbyaddr " , " gethostbyname "
, " getnetbyname " , " getopt " , " getopt_long " , " getprotobyname " , " getpwnam " , " getservbyname " , " getservbyport "
, " glob " , " gmtime " , " gmtime_r " , " if " , " index " , " inet_addr " , " inet_aton " , " inet_network " , " initgroups "
, " ioctl " , " link " , " localtime_r " , " lockf " , " lseek " , " lstat " , " mkdir " , " mkfifo " , " mknod " , " mkstemp "
, " obstack_printf " , " obstack_vprintf " , " open " , " opendir " , " parse_printf_format " , " pathconf "
, " perror " , " popen " , " posix_fadvise " , " posix_fallocate " , " pread " , " psignal " , " pwrite " , " read " , " readahead "
, " readdir " , " readdir_r " , " readlink " , " readv " , " realloc " , " regcomp " , " return " , " rewinddir " , " rindex "
, " rmdir " , " scandir " , " seekdir " , " setbuffer " , " sethostname " , " setlinebuf " , " sizeof " , " strdup "
, " stat " , " stpcpy " , " strcasecmp " , " stricmp " , " strncasecmp " , " switch "
, " symlink " , " sync_file_range " , " telldir " , " tempnam " , " time " , " typeid " , " unlink "
, " utime " , " utimes " , " vasprintf " , " while " , " wordexp " , " write " , " writev "
} ;
2016-01-25 20:01:48 +01:00
2009-03-20 18:16:21 +01:00
//---------------------------------------------------------------------------
2009-01-15 21:34:39 +01:00
2019-07-17 09:11:42 +02:00
CheckMemoryLeak : : AllocType CheckMemoryLeak : : getAllocationType ( const Token * tok2 , nonneg int varid , std : : list < const Function * > * callstack ) const
2009-01-15 21:34:39 +01:00
{
// What we may have...
// * var = (char *)malloc(10);
// * var = new char[10];
// * var = strdup("hello");
2009-05-19 22:29:10 +02:00
// * var = strndup("hello", 3);
2011-10-13 20:53:06 +02:00
if ( tok2 & & tok2 - > str ( ) = = " ( " ) {
2009-08-17 22:23:37 +02:00
tok2 = tok2 - > link ( ) ;
2014-02-15 16:17:25 +01:00
tok2 = tok2 ? tok2 - > next ( ) : nullptr ;
2009-01-15 21:34:39 +01:00
}
2021-08-07 20:51:18 +02:00
if ( ! tok2 )
2009-01-15 21:34:39 +01:00
return No ;
2014-10-01 10:59:08 +02:00
if ( tok2 - > str ( ) = = " :: " )
tok2 = tok2 - > next ( ) ;
2021-08-07 20:51:18 +02:00
if ( ! tok2 - > isName ( ) )
2009-01-15 21:34:39 +01:00
return No ;
2015-11-18 20:33:39 +01:00
if ( ! Token : : Match ( tok2 , " %name% ::|. %type% " ) ) {
2013-01-22 21:33:39 +01:00
// Using realloc..
2019-07-22 10:37:36 +02:00
AllocType reallocType = getReallocationType ( tok2 , varid ) ;
if ( reallocType ! = No )
return reallocType ;
2009-01-15 21:34:39 +01:00
2018-06-17 23:09:41 +02:00
if ( mTokenizer_ - > isCPP ( ) & & tok2 - > str ( ) = = " new " ) {
2016-01-20 10:34:03 +01:00
if ( tok2 - > strAt ( 1 ) = = " ( " & & ! Token : : Match ( tok2 - > next ( ) , " ( std| ::| nothrow ) " ) )
return No ;
2015-11-27 11:04:18 +01:00
if ( tok2 - > astOperand1 ( ) & & ( tok2 - > astOperand1 ( ) - > str ( ) = = " [ " | | ( tok2 - > astOperand1 ( ) - > astOperand1 ( ) & & tok2 - > astOperand1 ( ) - > astOperand1 ( ) - > str ( ) = = " [ " ) ) )
2015-11-18 20:33:39 +01:00
return NewArray ;
2019-02-03 12:15:05 +01:00
const Token * typeTok = tok2 - > next ( ) ;
while ( Token : : Match ( typeTok , " %name% :: %name% " ) )
typeTok = typeTok - > tokAt ( 2 ) ;
2021-11-29 07:06:43 +01:00
const Scope * classScope = nullptr ;
2019-02-03 12:15:05 +01:00
if ( typeTok - > type ( ) & & typeTok - > type ( ) - > isClassType ( ) ) {
2021-11-29 07:06:43 +01:00
classScope = typeTok - > type ( ) - > classScope ;
} else if ( typeTok - > function ( ) & & typeTok - > function ( ) - > isConstructor ( ) ) {
classScope = typeTok - > function ( ) - > nestedIn ;
2019-02-03 12:15:05 +01:00
}
2021-11-29 07:06:43 +01:00
if ( classScope & & classScope - > numConstructors > 0 )
return No ;
2013-01-22 21:33:39 +01:00
return New ;
2015-11-18 20:33:39 +01:00
}
2013-01-22 21:33:39 +01:00
2019-04-12 06:47:28 +02:00
if ( mSettings_ - > posix ( ) ) {
2014-04-18 17:05:44 +02:00
if ( Token : : Match ( tok2 , " open|openat|creat|mkstemp|mkostemp|socket ( " ) ) {
2013-06-29 12:55:24 +02:00
// simple sanity check of function parameters..
// TODO: Make such check for all these functions
2019-07-17 09:04:42 +02:00
const int num = numberOfArguments ( tok2 ) ;
2013-06-29 12:55:24 +02:00
if ( tok2 - > str ( ) = = " open " & & num ! = 2 & & num ! = 3 )
return No ;
// is there a user function with this name?
2014-10-31 11:40:42 +01:00
if ( tok2 - > function ( ) )
2013-06-29 12:55:24 +02:00
return No ;
return Fd ;
}
2012-03-18 07:49:22 +01:00
2014-04-18 16:30:16 +02:00
if ( Token : : simpleMatch ( tok2 , " popen ( " ) )
return Pipe ;
}
2013-05-23 06:34:22 +02:00
2015-11-19 17:33:52 +01:00
// Does tok2 point on a Library allocation function?
2019-07-05 12:44:52 +02:00
const int alloctype = mSettings_ - > library . getAllocId ( tok2 , - 1 ) ;
2014-03-17 16:10:54 +01:00
if ( alloctype > 0 ) {
2018-06-17 23:09:41 +02:00
if ( alloctype = = mSettings_ - > library . deallocId ( " free " ) )
2014-03-17 16:10:54 +01:00
return Malloc ;
2018-06-17 23:09:41 +02:00
if ( alloctype = = mSettings_ - > library . deallocId ( " fclose " ) )
2014-04-20 10:50:32 +02:00
return File ;
2013-07-08 18:26:18 +02:00
return Library : : ismemory ( alloctype ) ? OtherMem : OtherRes ;
2014-03-17 16:10:54 +01:00
}
2012-03-18 07:49:22 +01:00
}
2009-05-22 09:24:03 +02:00
2015-11-18 20:33:39 +01:00
while ( Token : : Match ( tok2 , " %name% ::|. %type% " ) )
2013-01-22 21:33:39 +01:00
tok2 = tok2 - > tokAt ( 2 ) ;
2011-03-19 14:05:22 +01:00
// User function
2013-01-31 06:41:18 +01:00
const Function * func = tok2 - > function ( ) ;
2014-02-15 16:17:25 +01:00
if ( func = = nullptr )
2011-03-23 18:45:47 +01:00
return No ;
// Prevent recursion
2022-12-30 15:13:47 +01:00
if ( callstack & & std : : find ( callstack - > cbegin ( ) , callstack - > cend ( ) , func ) ! = callstack - > cend ( ) )
2011-03-23 18:45:47 +01:00
return No ;
2012-09-11 18:03:47 +02:00
std : : list < const Function * > cs ;
2011-03-23 18:45:47 +01:00
if ( ! callstack )
callstack = & cs ;
2012-09-11 18:03:47 +02:00
callstack - > push_back ( func ) ;
return functionReturnType ( func , callstack ) ;
2009-01-15 21:34:39 +01:00
}
2009-08-19 19:42:07 +02:00
2019-07-22 10:37:36 +02:00
CheckMemoryLeak : : AllocType CheckMemoryLeak : : getReallocationType ( const Token * tok2 , nonneg int varid ) const
2009-01-15 21:34:39 +01:00
{
// What we may have...
// * var = (char *)realloc(..;
2011-10-13 20:53:06 +02:00
if ( tok2 & & tok2 - > str ( ) = = " ( " ) {
2009-08-17 22:23:37 +02:00
tok2 = tok2 - > link ( ) ;
2014-02-15 16:17:25 +01:00
tok2 = tok2 ? tok2 - > next ( ) : nullptr ;
2009-01-15 21:34:39 +01:00
}
2021-08-07 20:51:18 +02:00
if ( ! tok2 )
2009-01-15 21:34:39 +01:00
return No ;
2019-07-22 10:37:36 +02:00
if ( ! Token : : Match ( tok2 , " %name% ( " ) )
2009-08-18 20:49:08 +02:00
return No ;
2019-07-22 10:37:36 +02:00
const Library : : AllocFunc * f = mSettings_ - > library . getReallocFuncInfo ( tok2 ) ;
if ( ! ( f & & f - > reallocArg > 0 & & f - > reallocArg < = numberOfArguments ( tok2 ) ) )
return No ;
2022-01-18 20:50:06 +01:00
const auto args = getArguments ( tok2 ) ;
if ( args . size ( ) < ( f - > reallocArg ) )
return No ;
const Token * arg = args . at ( f - > reallocArg - 1 ) ;
2019-07-22 10:37:36 +02:00
while ( arg & & arg - > isCast ( ) )
arg = arg - > astOperand1 ( ) ;
while ( arg & & arg - > isUnaryOp ( " * " ) )
arg = arg - > astOperand1 ( ) ;
if ( varid > 0 & & ! Token : : Match ( arg , " %varid% [,)] " , varid ) )
return No ;
2009-01-15 21:34:39 +01:00
2019-07-22 10:37:36 +02:00
const int realloctype = mSettings_ - > library . getReallocId ( tok2 , - 1 ) ;
if ( realloctype > 0 ) {
if ( realloctype = = mSettings_ - > library . deallocId ( " free " ) )
return Malloc ;
if ( realloctype = = mSettings_ - > library . deallocId ( " fclose " ) )
return File ;
return Library : : ismemory ( realloctype ) ? OtherMem : OtherRes ;
}
2009-01-15 21:34:39 +01:00
return No ;
}
2019-07-17 09:11:42 +02:00
CheckMemoryLeak : : AllocType CheckMemoryLeak : : getDeallocationType ( const Token * tok , nonneg int varid ) const
2009-08-19 19:42:07 +02:00
{
2018-06-17 23:09:41 +02:00
if ( mTokenizer_ - > isCPP ( ) & & tok - > str ( ) = = " delete " & & tok - > astOperand1 ( ) ) {
2015-11-18 21:37:37 +01:00
const Token * vartok = tok - > astOperand1 ( ) ;
2015-11-18 21:17:50 +01:00
if ( Token : : Match ( vartok , " .|:: " ) )
vartok = vartok - > astOperand2 ( ) ;
2009-08-19 19:42:07 +02:00
2015-11-18 21:17:50 +01:00
if ( vartok & & vartok - > varId ( ) = = varid ) {
if ( tok - > strAt ( 1 ) = = " [ " )
return NewArray ;
2015-01-30 20:55:53 +01:00
return New ;
2015-11-18 21:17:50 +01:00
}
2015-01-30 20:55:53 +01:00
}
2009-08-19 19:42:07 +02:00
2016-01-16 14:15:51 +01:00
if ( tok - > str ( ) = = " :: " )
2012-07-24 09:28:08 +02:00
tok = tok - > next ( ) ;
2015-11-18 21:17:50 +01:00
if ( Token : : Match ( tok , " %name% ( " ) ) {
if ( Token : : simpleMatch ( tok , " fcloseall ( ) " ) )
return File ;
2009-02-14 07:54:23 +01:00
2016-05-22 17:18:50 +02:00
int argNr = 1 ;
for ( const Token * tok2 = tok - > tokAt ( 2 ) ; tok2 ; tok2 = tok2 - > nextArgument ( ) ) {
const Token * vartok = tok2 ;
while ( Token : : Match ( vartok , " %name% .|:: " ) )
vartok = vartok - > tokAt ( 2 ) ;
2009-01-15 21:34:39 +01:00
2016-05-22 17:18:50 +02:00
if ( Token : : Match ( vartok , " %varid% )|,|- " , varid ) ) {
if ( tok - > str ( ) = = " realloc " & & Token : : simpleMatch ( vartok - > next ( ) , " , 0 ) " ) )
2015-11-19 17:33:52 +01:00
return Malloc ;
2016-05-22 17:18:50 +02:00
2019-04-12 06:47:28 +02:00
if ( mSettings_ - > posix ( ) ) {
2016-05-22 17:18:50 +02:00
if ( tok - > str ( ) = = " close " )
return Fd ;
if ( tok - > str ( ) = = " pclose " )
return Pipe ;
}
// Does tok point on a Library deallocation function?
2019-07-05 12:44:52 +02:00
const int dealloctype = mSettings_ - > library . getDeallocId ( tok , argNr ) ;
2016-05-22 17:18:50 +02:00
if ( dealloctype > 0 ) {
2018-06-17 23:09:41 +02:00
if ( dealloctype = = mSettings_ - > library . deallocId ( " free " ) )
2016-05-22 17:18:50 +02:00
return Malloc ;
2018-06-17 23:09:41 +02:00
if ( dealloctype = = mSettings_ - > library . deallocId ( " fclose " ) )
2016-05-22 17:18:50 +02:00
return File ;
return Library : : ismemory ( dealloctype ) ? OtherMem : OtherRes ;
}
2015-11-19 17:33:52 +01:00
}
2016-05-22 17:18:50 +02:00
argNr + + ;
2015-11-18 21:17:50 +01:00
}
2013-07-05 20:55:31 +02:00
}
2009-01-15 21:34:39 +01:00
return No ;
}
2010-12-07 07:07:36 +01:00
2019-08-12 12:53:59 +02:00
bool CheckMemoryLeak : : isReopenStandardStream ( const Token * tok ) const
{
if ( getReallocationType ( tok , 0 ) = = File ) {
const Library : : AllocFunc * f = mSettings_ - > library . getReallocFuncInfo ( tok ) ;
if ( f & & f - > reallocArg > 0 & & f - > reallocArg < = numberOfArguments ( tok ) ) {
const Token * arg = getArguments ( tok ) . at ( f - > reallocArg - 1 ) ;
if ( Token : : Match ( arg , " stdin|stdout|stderr " ) )
return true ;
}
}
return false ;
}
2022-09-27 20:09:04 +02:00
bool CheckMemoryLeak : : isOpenDevNull ( const Token * tok ) const
{
if ( mSettings_ - > posix ( ) & & tok - > str ( ) = = " open " & & numberOfArguments ( tok ) = = 2 ) {
const Token * arg = getArguments ( tok ) . at ( 0 ) ;
if ( Token : : simpleMatch ( arg , " \" /dev/null \" " ) )
return true ;
}
return false ;
}
2009-01-15 21:34:39 +01:00
//--------------------------------------------------------------------------
2009-06-08 20:20:43 +02:00
//--------------------------------------------------------------------------
2017-01-07 14:13:22 +01:00
void CheckMemoryLeak : : memoryLeak ( const Token * tok , const std : : string & varname , AllocType alloctype ) const
2009-06-08 20:20:43 +02:00
{
2010-04-02 07:30:58 +02:00
if ( alloctype = = CheckMemoryLeak : : File | |
alloctype = = CheckMemoryLeak : : Pipe | |
2021-08-07 20:51:18 +02:00
alloctype = = CheckMemoryLeak : : Fd | |
2013-12-12 18:23:42 +01:00
alloctype = = CheckMemoryLeak : : OtherRes )
2011-12-08 21:28:34 +01:00
resourceLeakError ( tok , varname ) ;
2009-06-08 20:20:43 +02:00
else
2011-12-08 21:28:34 +01:00
memleakError ( tok , varname ) ;
2009-06-08 20:20:43 +02:00
}
//---------------------------------------------------------------------------
2016-02-27 16:03:50 +01:00
void CheckMemoryLeak : : reportErr ( const Token * tok , Severity : : SeverityType severity , const std : : string & id , const std : : string & msg , const CWE & cwe ) const
2009-07-13 15:07:26 +02:00
{
2009-08-04 21:36:55 +02:00
std : : list < const Token * > callstack ;
2009-07-13 15:07:26 +02:00
2010-04-02 07:30:58 +02:00
if ( tok )
2009-08-04 21:36:55 +02:00
callstack . push_back ( tok ) ;
2009-07-13 15:07:26 +02:00
2015-04-25 17:48:11 +02:00
reportErr ( callstack , severity , id , msg , cwe ) ;
2009-07-13 15:07:26 +02:00
}
2016-02-27 16:03:50 +01:00
void CheckMemoryLeak : : reportErr ( const std : : list < const Token * > & callstack , Severity : : SeverityType severity , const std : : string & id , const std : : string & msg , const CWE & cwe ) const
2009-07-13 15:07:26 +02:00
{
2022-04-11 07:30:55 +02:00
const ErrorMessage errmsg ( callstack , mTokenizer_ ? & mTokenizer_ - > list : nullptr , severity , id , msg , cwe , Certainty : : normal ) ;
2018-06-17 23:09:41 +02:00
if ( mErrorLogger_ )
mErrorLogger_ - > reportErr ( errmsg ) ;
2009-08-04 21:36:55 +02:00
else
2010-07-23 22:53:29 +02:00
Check : : reportError ( errmsg ) ;
2009-07-13 15:07:26 +02:00
}
2009-06-08 20:20:43 +02:00
2011-12-13 00:24:34 +01:00
void CheckMemoryLeak : : memleakError ( const Token * tok , const std : : string & varname ) const
2009-06-08 20:20:43 +02:00
{
2018-04-09 06:43:48 +02:00
reportErr ( tok , Severity : : error , " memleak " , " $symbol: " + varname + " \n Memory leak: $symbol " , CWE ( 401U ) ) ;
2009-06-08 20:20:43 +02:00
}
2019-07-22 10:37:36 +02:00
void CheckMemoryLeak : : memleakUponReallocFailureError ( const Token * tok , const std : : string & reallocfunction , const std : : string & varname ) const
2010-05-18 07:46:48 +02:00
{
2019-07-22 10:37:36 +02:00
reportErr ( tok , Severity : : error , " memleakOnRealloc " , " $symbol: " + varname + " \n Common " + reallocfunction + " mistake: \' $symbol \' nulled but not freed upon failure " , CWE ( 401U ) ) ;
2010-05-18 07:46:48 +02:00
}
2012-05-17 10:33:24 +02:00
void CheckMemoryLeak : : resourceLeakError ( const Token * tok , const std : : string & varname ) const
2009-06-08 20:20:43 +02:00
{
2009-10-03 21:46:22 +02:00
std : : string errmsg ( " Resource leak " ) ;
2010-04-02 07:30:58 +02:00
if ( ! varname . empty ( ) )
2018-04-09 06:43:48 +02:00
errmsg = " $symbol: " + varname + ' \n ' + errmsg + " : $symbol " ;
2016-02-27 16:03:50 +01:00
reportErr ( tok , Severity : : error , " resourceLeak " , errmsg , CWE ( 775U ) ) ;
2009-06-08 20:20:43 +02:00
}
2011-12-13 00:24:34 +01:00
void CheckMemoryLeak : : deallocuseError ( const Token * tok , const std : : string & varname ) const
2009-06-08 20:20:43 +02:00
{
2018-04-09 06:43:48 +02:00
reportErr ( tok , Severity : : error , " deallocuse " , " $symbol: " + varname + " \n Dereferencing '$symbol' after it is deallocated / released " , CWE ( 416U ) ) ;
2009-06-08 20:20:43 +02:00
}
2011-12-13 00:24:34 +01:00
void CheckMemoryLeak : : mismatchAllocDealloc ( const std : : list < const Token * > & callstack , const std : : string & varname ) const
2009-06-08 20:20:43 +02:00
{
2018-04-09 06:43:48 +02:00
reportErr ( callstack , Severity : : error , " mismatchAllocDealloc " , " $symbol: " + varname + " \n Mismatching allocation and deallocation: $symbol " , CWE ( 762U ) ) ;
2009-06-08 20:20:43 +02:00
}
2012-09-11 18:03:47 +02:00
CheckMemoryLeak : : AllocType CheckMemoryLeak : : functionReturnType ( const Function * func , std : : list < const Function * > * callstack ) const
2009-06-15 17:44:59 +02:00
{
2019-07-09 16:04:22 +02:00
if ( ! func | | ! func - > hasBody ( ) | | ! func - > functionScope )
2011-03-20 09:16:52 +01:00
return No ;
2010-05-09 13:46:13 +02:00
// Get return pointer..
2022-08-21 17:21:02 +02:00
const Variable * var = nullptr ;
2018-04-27 22:36:30 +02:00
for ( const Token * tok2 = func - > functionScope - > bodyStart ; tok2 ! = func - > functionScope - > bodyEnd ; tok2 = tok2 - > next ( ) ) {
2018-11-12 11:28:26 +01:00
if ( const Token * endOfLambda = findLambdaEndToken ( tok2 ) )
tok2 = endOfLambda ;
2019-04-22 17:37:41 +02:00
if ( tok2 - > str ( ) = = " { " & & ! tok2 - > scope ( ) - > isExecutable ( ) )
tok2 = tok2 - > link ( ) ;
2015-11-18 22:09:27 +01:00
if ( tok2 - > str ( ) = = " return " ) {
2018-04-04 21:51:31 +02:00
const AllocType allocType = getAllocationType ( tok2 - > next ( ) , 0 , callstack ) ;
2010-05-09 13:46:13 +02:00
if ( allocType ! = No )
return allocType ;
2015-11-18 22:09:27 +01:00
if ( tok2 - > scope ( ) ! = func - > functionScope | | ! tok2 - > astOperand1 ( ) )
return No ;
const Token * tok = tok2 - > astOperand1 ( ) ;
if ( Token : : Match ( tok , " .|:: " ) )
2015-11-27 11:18:40 +01:00
tok = tok - > astOperand2 ( ) ? tok - > astOperand2 ( ) : tok - > astOperand1 ( ) ;
2015-12-05 18:43:29 +01:00
if ( tok )
2022-08-21 17:21:02 +02:00
var = tok - > variable ( ) ;
2015-11-18 22:09:27 +01:00
break ;
2010-04-24 20:40:57 +02:00
}
2010-05-09 13:46:13 +02:00
}
2010-04-24 20:40:57 +02:00
2010-05-09 13:46:13 +02:00
// Not returning pointer value..
2022-08-21 17:21:02 +02:00
if ( ! var )
2010-05-09 13:46:13 +02:00
return No ;
2014-06-12 19:58:43 +02:00
// If variable is not local then alloctype shall be "No"
// Todo: there can be false negatives about mismatching allocation/deallocation.
// => Generate "alloc ; use ;" if variable is not local?
2022-08-21 17:21:02 +02:00
if ( ! var - > isLocal ( ) | | var - > isStatic ( ) )
2014-06-12 19:58:43 +02:00
return No ;
2012-06-25 20:00:50 +02:00
2010-05-09 13:46:13 +02:00
// Check if return pointer is allocated..
AllocType allocType = No ;
2022-10-02 07:12:40 +02:00
nonneg int const varid = var - > declarationId ( ) ;
2018-04-27 22:36:30 +02:00
for ( const Token * tok = func - > functionScope - > bodyStart ; tok ! = func - > functionScope - > bodyEnd ; tok = tok - > next ( ) ) {
2011-10-13 20:53:06 +02:00
if ( Token : : Match ( tok , " %varid% = " , varid ) ) {
2011-03-23 18:45:47 +01:00
allocType = getAllocationType ( tok - > tokAt ( 2 ) , varid , callstack ) ;
2010-12-29 22:18:23 +01:00
}
2011-10-13 20:53:06 +02:00
if ( Token : : Match ( tok , " = %varid% ; " , varid ) ) {
2010-12-29 22:18:23 +01:00
return No ;
2009-06-15 21:13:39 +02:00
}
2018-06-17 23:09:41 +02:00
if ( ! mTokenizer_ - > isC ( ) & & Token : : Match ( tok , " [(,] %varid% [,)] " , varid ) ) {
2011-03-24 17:14:12 +01:00
return No ;
}
2015-01-05 13:23:38 +01:00
if ( Token : : Match ( tok , " [(,] & %varid% [.,)] " , varid ) ) {
return No ;
}
2016-12-27 08:12:37 +01:00
if ( Token : : Match ( tok , " [;{}] %varid% . " , varid ) ) {
return No ;
}
2015-01-05 13:23:38 +01:00
if ( allocType = = No & & tok - > str ( ) = = " return " )
return No ;
2009-06-15 21:13:39 +02:00
}
2010-12-29 22:18:23 +01:00
return allocType ;
2009-06-15 17:44:59 +02:00
}
2009-06-08 20:20:43 +02:00
2009-11-14 09:06:28 +01:00
2019-07-17 09:11:42 +02:00
static bool notvar ( const Token * tok , nonneg int varid )
2009-06-08 20:20:43 +02:00
{
2015-07-25 11:37:03 +02:00
if ( ! tok )
return false ;
2015-07-21 20:27:59 +02:00
if ( Token : : Match ( tok , " &&|; " ) )
return notvar ( tok - > astOperand1 ( ) , varid ) | | notvar ( tok - > astOperand2 ( ) , varid ) ;
2015-07-25 11:37:03 +02:00
if ( tok - > str ( ) = = " ( " & & Token : : Match ( tok - > astOperand1 ( ) , " UNLIKELY|LIKELY " ) )
return notvar ( tok - > astOperand2 ( ) , varid ) ;
2015-08-03 09:20:50 +02:00
const Token * vartok = astIsVariableComparison ( tok , " == " , " 0 " ) ;
2015-07-22 12:31:18 +02:00
return vartok & & ( vartok - > varId ( ) = = varid ) ;
2009-06-08 20:20:43 +02:00
}
2019-07-17 09:11:42 +02:00
static bool ifvar ( const Token * tok , nonneg int varid , const std : : string & comp , const std : : string & rhs )
2015-07-22 16:45:14 +02:00
{
if ( ! Token : : simpleMatch ( tok , " if ( " ) )
return false ;
2015-07-25 11:37:03 +02:00
const Token * condition = tok - > next ( ) - > astOperand2 ( ) ;
if ( condition & & condition - > str ( ) = = " ( " & & Token : : Match ( condition - > astOperand1 ( ) , " UNLIKELY|LIKELY " ) )
condition = condition - > astOperand2 ( ) ;
2015-07-22 16:45:14 +02:00
if ( ! condition | | condition - > str ( ) = = " && " )
return false ;
2015-07-25 11:37:03 +02:00
2015-08-03 09:20:50 +02:00
const Token * vartok = astIsVariableComparison ( condition , comp , rhs ) ;
2015-07-22 16:45:14 +02:00
return ( vartok & & vartok - > varId ( ) = = varid ) ;
}
2009-06-08 20:20:43 +02:00
2015-01-30 20:55:53 +01:00
bool CheckMemoryLeakInFunction : : test_white_list ( const std : : string & funcname , const Settings * settings , bool cpp )
2014-02-05 08:18:39 +01:00
{
2016-12-06 12:31:16 +01:00
return ( ( call_func_white_list . find ( funcname ) ! = call_func_white_list . end ( ) ) | | settings - > library . isLeakIgnore ( funcname ) | | ( cpp & & funcname = = " delete " ) ) ;
2014-02-05 08:18:39 +01:00
}
2009-01-15 21:34:39 +01:00
2010-05-18 07:46:48 +02:00
//---------------------------------------------------------------------------
// Check for memory leaks due to improper realloc() usage.
// Below, "a" may be set to null without being freed if realloc() cannot
// allocate the requested memory:
2010-05-18 20:08:27 +02:00
// a = malloc(10); a = realloc(a, 100);
2010-05-18 07:46:48 +02:00
//---------------------------------------------------------------------------
2013-07-31 16:02:37 +02:00
2010-05-18 07:46:48 +02:00
void CheckMemoryLeakInFunction : : checkReallocUsage ( )
{
2012-10-21 09:07:51 +02:00
// only check functions
2018-06-18 09:40:27 +02:00
const SymbolDatabase * symbolDatabase = mTokenizer - > getSymbolDatabase ( ) ;
for ( const Scope * scope : symbolDatabase - > functionScopes ) {
2010-12-08 07:49:01 +01:00
2011-10-11 22:07:14 +02:00
// Search for the "var = realloc(var, 100" pattern within this function
2018-04-27 22:36:30 +02:00
for ( const Token * tok = scope - > bodyStart - > next ( ) ; tok ! = scope - > bodyEnd ; tok = tok - > next ( ) ) {
2019-07-22 10:37:36 +02:00
if ( tok - > varId ( ) > 0 & & Token : : Match ( tok , " %name% = " ) ) {
// Get the parenthesis in "realloc("
const Token * parTok = tok - > next ( ) - > astOperand2 ( ) ;
// Skip casts
while ( parTok & & parTok - > isCast ( ) )
parTok = parTok - > astOperand1 ( ) ;
if ( ! parTok )
continue ;
const Token * const reallocTok = parTok - > astOperand1 ( ) ;
if ( ! reallocTok )
continue ;
const Library : : AllocFunc * f = mSettings - > library . getReallocFuncInfo ( reallocTok ) ;
if ( ! ( f & & f - > arg = = - 1 & & mSettings - > library . isnotnoreturn ( reallocTok ) ) )
continue ;
const AllocType allocType = getReallocationType ( reallocTok , tok - > varId ( ) ) ;
if ( ! ( ( allocType = = Malloc | | allocType = = OtherMem ) ) )
continue ;
const Token * arg = getArguments ( reallocTok ) . at ( f - > reallocArg - 1 ) ;
while ( arg & & arg - > isCast ( ) )
arg = arg - > astOperand1 ( ) ;
const Token * tok2 = tok ;
while ( arg & & arg - > isUnaryOp ( " * " ) & & tok2 & & tok2 - > astParent ( ) & & tok2 - > astParent ( ) - > isUnaryOp ( " * " ) ) {
arg = arg - > astOperand1 ( ) ;
tok2 = tok2 - > astParent ( ) ;
}
2019-07-22 10:38:17 +02:00
if ( ! arg | | ! tok2 )
2019-07-22 10:37:36 +02:00
continue ;
2022-08-21 17:21:02 +02:00
if ( ! ( tok - > varId ( ) = = arg - > varId ( ) & & tok - > variable ( ) & & ! tok - > variable ( ) - > isArgument ( ) ) )
2019-07-22 10:37:36 +02:00
continue ;
2010-07-27 08:17:27 +02:00
// Check that another copy of the pointer wasn't saved earlier in the function
2018-04-27 22:36:30 +02:00
if ( Token : : findmatch ( scope - > bodyStart , " %name% = %varid% ; " , tok , tok - > varId ( ) ) | |
2020-07-31 01:44:33 +02:00
Token : : findmatch ( scope - > bodyStart , " [{};] %varid% = *| %var% .| %var%| [;=] " , tok , tok - > varId ( ) ) )
2011-01-13 07:33:46 +01:00
continue ;
2020-09-12 18:46:01 +02:00
// Check if the argument is known to be null, which means it is not a memory leak
if ( arg - > hasKnownIntValue ( ) & & arg - > getKnownIntValue ( ) = = 0 ) {
continue ;
}
2019-07-22 10:37:36 +02:00
const Token * tokEndRealloc = reallocTok - > linkAt ( 1 ) ;
2011-01-13 07:33:46 +01:00
// Check that the allocation isn't followed immediately by an 'if (!var) { error(); }' that might handle failure
2015-07-22 12:31:18 +02:00
if ( Token : : simpleMatch ( tokEndRealloc - > next ( ) , " ; if ( " ) & &
notvar ( tokEndRealloc - > tokAt ( 3 ) - > astOperand2 ( ) , tok - > varId ( ) ) ) {
const Token * tokEndBrace = tokEndRealloc - > linkAt ( 3 ) - > linkAt ( 1 ) ;
2019-11-20 15:37:09 +01:00
if ( tokEndBrace & & mTokenizer - > isScopeNoReturn ( tokEndBrace ) )
2011-01-13 07:33:46 +01:00
continue ;
}
2019-07-22 10:37:36 +02:00
memleakUponReallocFailureError ( tok , reallocTok - > str ( ) , tok - > str ( ) ) ;
2011-10-11 22:07:14 +02:00
}
2010-05-18 07:46:48 +02:00
}
}
}
//---------------------------------------------------------------------------
2009-01-15 21:34:39 +01:00
//---------------------------------------------------------------------------
// Checks for memory leaks in classes..
//---------------------------------------------------------------------------
2009-06-08 20:20:43 +02:00
void CheckMemoryLeakInClass : : check ( )
2009-01-15 21:34:39 +01:00
{
2018-06-16 16:10:28 +02:00
const SymbolDatabase * symbolDatabase = mTokenizer - > getSymbolDatabase ( ) ;
2009-01-15 21:34:39 +01:00
2012-10-21 09:07:51 +02:00
// only check classes and structures
2018-07-14 10:09:12 +02:00
for ( const Scope * scope : symbolDatabase - > classAndStructScopes ) {
for ( const Variable & var : scope - > varlist ) {
2022-02-15 14:28:19 +01:00
if ( ! var . isStatic ( ) & & ( var . isPointer ( ) | | var . isPointerArray ( ) ) ) {
2012-10-21 09:07:51 +02:00
// allocation but no deallocation of private variables in public function..
2018-07-14 10:09:12 +02:00
const Token * tok = var . typeStartToken ( ) ;
2015-11-19 17:33:52 +01:00
// Either it is of standard type or a non-derived type
2018-07-14 10:09:12 +02:00
if ( tok - > isStandardType ( ) | | ( var . type ( ) & & var . type ( ) - > derivedFrom . empty ( ) ) ) {
if ( var . isPrivate ( ) )
checkPublicFunctions ( scope , var . nameToken ( ) ) ;
2012-10-21 09:07:51 +02:00
2018-07-14 10:09:12 +02:00
variable ( scope , var . nameToken ( ) ) ;
2012-10-21 09:07:51 +02:00
}
2010-11-23 18:41:07 +01:00
}
2009-01-15 21:34:39 +01:00
}
}
}
2010-11-23 18:41:07 +01:00
2011-01-17 18:29:19 +01:00
void CheckMemoryLeakInClass : : variable ( const Scope * scope , const Token * tokVarname )
2009-01-15 21:34:39 +01:00
{
2012-04-23 15:23:01 +02:00
const std : : string & varname = tokVarname - > str ( ) ;
2019-07-17 09:11:42 +02:00
const int varid = tokVarname - > varId ( ) ;
2012-04-23 15:23:01 +02:00
const std : : string & classname = scope - > className ;
2009-03-01 21:34:04 +01:00
2009-01-15 21:34:39 +01:00
// Check if member variable has been allocated and deallocated..
2019-11-20 15:37:09 +01:00
CheckMemoryLeak : : AllocType memberAlloc = CheckMemoryLeak : : No ;
CheckMemoryLeak : : AllocType memberDealloc = CheckMemoryLeak : : No ;
2009-01-15 21:34:39 +01:00
2009-10-23 20:04:47 +02:00
bool allocInConstructor = false ;
bool deallocInDestructor = false ;
2010-11-23 18:41:07 +01:00
// Inspect member functions
2018-07-14 10:09:12 +02:00
for ( const Function & func : scope - > functionList ) {
const bool constructor = func . isConstructor ( ) ;
const bool destructor = func . isDestructor ( ) ;
if ( ! func . hasBody ( ) ) {
Fix defaulted and deleted functions (#4540)
* Fix 9392, but for destructors: out-of-line defaulted destructors skipped everything after
Context:
```
struct S {
~S();
};
S::~S() = default;
void g() {
int j;
++j;
}
```
Everything after `S::~S() = default;` was skipped, so the uninitialized variables in g() weren't found.
Out-of-line destructors are useful e.g. when you have a forward declared unique_ptr in the .h,
and `= default` the destructor in the .cpp, so only the cpp needs to know the header for destructing
your unique_ptr (like in the pImpl-idiom)
* Fix unit test, by correctly fixing 10789
Previous commit broke this test, but also provided the tools for a cleaner fix
* Document current behaviour
* Rewrite control flow
* Fix deleted functions, which skipped everything after
`a::b f() = delete` triggered the final else in SymbolDatabase::addNewFunction,
which sets tok to nullptr, effectively skipping to the end of the stream.
* Remove troublesome nullptr, which skips every analysis afterwards
It was introduced in 0746c241 to fix a memory leak.
But setting tok to nullptr, effectively skipping to the end, seems not needed.
Previous commits fixes prevented some cases where you could enter the `else`.
This commit is more of a fall back.
* fixup! Fix deleted functions, which skipped everything after
`a::b f() = delete` triggered the final else in SymbolDatabase::addNewFunction,
which sets tok to nullptr, effectively skipping to the end of the stream.
* fixup! Fix deleted functions, which skipped everything after
`a::b f() = delete` triggered the final else in SymbolDatabase::addNewFunction,
which sets tok to nullptr, effectively skipping to the end of the stream.
* Make it heard when encountering unexpected syntax/tokens
Co-authored-by: Gerbo Engels <gerbo.engels@ortec-finance.com>
2022-10-10 20:17:33 +02:00
if ( destructor & & ! func . isDefault ( ) ) { // implementation for destructor is not seen and not defaulted => assume it deallocates all variables properly
2013-02-24 08:14:25 +01:00
deallocInDestructor = true ;
2019-11-20 15:37:09 +01:00
memberDealloc = CheckMemoryLeak : : Many ;
2013-02-24 08:14:25 +01:00
}
continue ;
}
2012-03-16 19:52:18 +01:00
bool body = false ;
2018-07-14 10:09:12 +02:00
const Token * end = func . functionScope - > bodyEnd ;
for ( const Token * tok = func . arg - > link ( ) ; tok ! = end ; tok = tok - > next ( ) ) {
if ( tok = = func . functionScope - > bodyStart )
2012-03-16 19:52:18 +01:00
body = true ;
2012-04-23 15:23:01 +02:00
else {
2012-03-16 19:52:18 +01:00
if ( ! body ) {
2012-04-26 18:56:58 +02:00
if ( ! Token : : Match ( tok , " :|, %varid% ( " , varid ) )
2009-03-14 20:19:36 +01:00
continue ;
}
2009-03-14 18:21:37 +01:00
// Allocate..
2022-02-15 20:03:02 +01:00
if ( ! body | | Token : : Match ( tok , " %varid% =|[ " , varid ) ) {
2010-06-13 19:00:11 +02:00
// var1 = var2 = ...
// bail out
2012-03-16 19:52:18 +01:00
if ( tok - > strAt ( - 1 ) = = " = " )
2010-06-13 19:00:11 +02:00
return ;
// Foo::var1 = ..
2010-12-07 07:07:36 +01:00
// bail out when not same class
2012-03-16 19:52:18 +01:00
if ( tok - > strAt ( - 1 ) = = " :: " & &
2011-01-17 18:29:19 +01:00
tok - > strAt ( - 2 ) ! = scope - > className )
2010-06-13 19:00:11 +02:00
return ;
2022-02-15 20:03:02 +01:00
const Token * allocTok = tok - > tokAt ( body ? 2 : 3 ) ;
if ( tok - > astParent ( ) & & tok - > astParent ( ) - > str ( ) = = " [ " & & tok - > astParent ( ) - > astParent ( ) )
allocTok = tok - > astParent ( ) - > astParent ( ) - > astOperand2 ( ) ;
AllocType alloc = getAllocationType ( allocTok , 0 ) ;
2011-10-13 20:53:06 +02:00
if ( alloc ! = CheckMemoryLeak : : No ) {
2010-04-02 07:30:58 +02:00
if ( constructor )
2009-10-23 20:04:47 +02:00
allocInConstructor = true ;
2019-11-20 15:37:09 +01:00
if ( memberAlloc ! = No & & memberAlloc ! = alloc )
2009-06-08 20:20:43 +02:00
alloc = CheckMemoryLeak : : Many ;
2009-01-15 21:34:39 +01:00
2019-11-20 15:37:09 +01:00
if ( alloc ! = CheckMemoryLeak : : Many & & memberDealloc ! = CheckMemoryLeak : : No & & memberDealloc ! = CheckMemoryLeak : : Many & & memberDealloc ! = alloc ) {
2022-08-16 22:28:39 +02:00
mismatchAllocDealloc ( { tok } , classname + " :: " + varname ) ;
2009-03-14 18:21:37 +01:00
}
2009-01-15 21:34:39 +01:00
2019-11-20 15:37:09 +01:00
memberAlloc = alloc ;
2009-03-14 18:21:37 +01:00
}
}
2009-01-15 21:34:39 +01:00
2012-03-16 19:52:18 +01:00
if ( ! body )
2009-03-14 20:19:36 +01:00
continue ;
2009-03-14 18:21:37 +01:00
// Deallocate..
2015-11-18 21:17:50 +01:00
AllocType dealloc = getDeallocationType ( tok , varid ) ;
2011-11-28 20:08:29 +01:00
// some usage in the destructor => assume it's related
// to deallocation
if ( destructor & & tok - > str ( ) = = varname )
dealloc = CheckMemoryLeak : : Many ;
2011-10-13 20:53:06 +02:00
if ( dealloc ! = CheckMemoryLeak : : No ) {
2010-04-02 07:30:58 +02:00
if ( destructor )
2009-10-23 20:04:47 +02:00
deallocInDestructor = true ;
2021-08-07 20:51:18 +02:00
if ( dealloc ! = CheckMemoryLeak : : Many & & memberAlloc ! = CheckMemoryLeak : : No & & memberAlloc ! = Many & & memberAlloc ! = dealloc ) {
2022-08-16 22:28:39 +02:00
mismatchAllocDealloc ( { tok } , classname + " :: " + varname ) ;
2009-02-01 16:47:36 +01:00
}
2009-02-06 07:11:47 +01:00
2022-08-25 22:52:51 +02:00
// several types of allocation/deallocation?
if ( memberDealloc ! = CheckMemoryLeak : : No & & memberDealloc ! = dealloc )
dealloc = CheckMemoryLeak : : Many ;
2019-11-20 15:37:09 +01:00
memberDealloc = dealloc ;
2009-01-15 21:34:39 +01:00
}
2009-10-24 15:07:14 +02:00
2010-06-16 19:28:47 +02:00
// Function call .. possible deallocation
2015-01-31 10:50:39 +01:00
else if ( Token : : Match ( tok - > previous ( ) , " [ { } ; ] % name % ( " )) {
2018-06-17 23:09:41 +02:00
if ( ! CheckMemoryLeakInFunction : : test_white_list ( tok - > str ( ) , mSettings , mTokenizer - > isCPP ( ) ) ) {
2009-10-24 15:07:14 +02:00
return ;
}
}
2009-01-23 20:24:52 +01:00
}
2009-01-15 21:34:39 +01:00
}
}
2011-10-13 20:53:06 +02:00
if ( allocInConstructor & & ! deallocInDestructor ) {
2019-11-20 15:37:09 +01:00
unsafeClassError ( tokVarname , classname , classname + " :: " + varname /*, memberAlloc*/ ) ;
} else if ( memberAlloc ! = CheckMemoryLeak : : No & & memberDealloc = = CheckMemoryLeak : : No ) {
unsafeClassError ( tokVarname , classname , classname + " :: " + varname /*, memberAlloc*/ ) ;
2009-01-15 21:34:39 +01:00
}
}
2012-09-10 17:27:41 +02:00
void CheckMemoryLeakInClass : : unsafeClassError ( const Token * tok , const std : : string & classname , const std : : string & varname )
{
2021-02-24 22:00:06 +01:00
if ( ! mSettings - > severity . isEnabled ( Severity : : style ) )
2015-04-06 11:40:45 +02:00
return ;
2012-09-10 17:27:41 +02:00
reportError ( tok , Severity : : style , " unsafeClassCanLeak " ,
2018-04-09 06:43:48 +02:00
" $symbol: " + classname + " \n "
" $symbol: " + varname + " \n "
2012-09-10 17:27:41 +02:00
" Class ' " + classname + " ' is unsafe, ' " + varname + " ' can leak by wrong usage. \n "
2021-02-24 22:00:06 +01:00
" The class ' " + classname + " ' is unsafe, wrong usage can cause memory/resource leaks for ' " + varname + " '. This can for instance be fixed by adding proper cleanup in the destructor. " , CWE398 , Certainty : : normal ) ;
2012-09-10 17:27:41 +02:00
}
2009-01-15 21:34:39 +01:00
2011-01-17 18:29:19 +01:00
void CheckMemoryLeakInClass : : checkPublicFunctions ( const Scope * scope , const Token * classtok )
2010-05-15 19:40:32 +02:00
{
// Check that public functions deallocate the pointers that they allocate.
// There is no checking how these functions are used and therefore it
// isn't established if there is real leaks or not.
2021-02-24 22:00:06 +01:00
if ( ! mSettings - > severity . isEnabled ( Severity : : warning ) )
2010-05-15 19:40:32 +02:00
return ;
2009-01-15 21:34:39 +01:00
2019-07-17 09:11:42 +02:00
const int varid = classtok - > varId ( ) ;
2010-11-25 21:04:49 +01:00
2010-05-15 19:40:32 +02:00
// Parse public functions..
// If they allocate member variables, they should also deallocate
2018-07-14 10:09:12 +02:00
for ( const Function & func : scope - > functionList ) {
if ( ( func . type = = Function : : eFunction | | func . type = = Function : : eOperatorEqual ) & &
2019-07-23 14:29:02 +02:00
func . access = = AccessControl : : Public & & func . hasBody ( ) ) {
2018-07-14 10:09:12 +02:00
const Token * tok2 = func . functionScope - > bodyStart - > next ( ) ;
2015-08-16 14:22:46 +02:00
if ( Token : : Match ( tok2 , " %varid% = " , varid ) ) {
const CheckMemoryLeak : : AllocType alloc = getAllocationType ( tok2 - > tokAt ( 2 ) , varid ) ;
2010-11-25 21:04:49 +01:00
if ( alloc ! = CheckMemoryLeak : : No )
2015-08-16 14:22:46 +02:00
publicAllocationError ( tok2 , tok2 - > str ( ) ) ;
} else if ( Token : : Match ( tok2 , " %type% :: %varid% = " , varid ) & &
tok2 - > str ( ) = = scope - > className ) {
const CheckMemoryLeak : : AllocType alloc = getAllocationType ( tok2 - > tokAt ( 4 ) , varid ) ;
2010-12-07 07:07:36 +01:00
if ( alloc ! = CheckMemoryLeak : : No )
2015-08-16 14:22:46 +02:00
publicAllocationError ( tok2 , tok2 - > strAt ( 2 ) ) ;
2010-12-07 07:07:36 +01:00
}
2010-05-15 19:40:32 +02:00
}
}
}
2009-01-15 21:34:39 +01:00
2010-05-15 19:40:32 +02:00
void CheckMemoryLeakInClass : : publicAllocationError ( const Token * tok , const std : : string & varname )
{
2021-02-24 22:00:06 +01:00
reportError ( tok , Severity : : warning , " publicAllocationError " , " $symbol: " + varname + " \n Possible leak in public function. The pointer '$symbol' is not deallocated before it is allocated. " , CWE398 , Certainty : : normal ) ;
2010-05-15 19:40:32 +02:00
}
2009-01-15 21:34:39 +01:00
2009-07-19 16:51:31 +02:00
void CheckMemoryLeakStructMember : : check ( )
2011-05-11 18:19:14 +02:00
{
2021-04-30 16:47:02 +02:00
if ( mSettings - > clang )
return ;
2018-06-16 16:10:28 +02:00
const SymbolDatabase * symbolDatabase = mTokenizer - > getSymbolDatabase ( ) ;
2018-04-28 09:38:33 +02:00
for ( const Variable * var : symbolDatabase - > variableList ( ) ) {
2022-05-26 15:27:36 +02:00
if ( ! var | | ( ! var - > isLocal ( ) & & ! ( var - > isArgument ( ) & & var - > scope ( ) ) ) | | var - > isStatic ( ) )
continue ;
if ( var - > isReference ( ) | | ( var - > valueType ( ) & & var - > valueType ( ) - > pointer > 1 ) )
2011-05-11 18:19:14 +02:00
continue ;
2012-05-03 10:43:47 +02:00
if ( var - > typeEndToken ( ) - > isStandardType ( ) )
continue ;
2020-07-07 21:36:14 +02:00
if ( var - > scope ( ) - > hasInlineOrLambdaFunction ( ) )
continue ;
2012-05-03 10:43:47 +02:00
checkStructVariable ( var ) ;
2011-05-11 18:19:14 +02:00
}
}
2012-05-03 10:43:47 +02:00
bool CheckMemoryLeakStructMember : : isMalloc ( const Variable * variable )
2011-05-11 18:19:14 +02:00
{
2019-07-17 09:11:42 +02:00
const int declarationId ( variable - > declarationId ( ) ) ;
2011-05-11 18:19:14 +02:00
bool alloc = false ;
2018-04-27 22:36:30 +02:00
for ( const Token * tok2 = variable - > nameToken ( ) ; tok2 & & tok2 ! = variable - > scope ( ) - > bodyEnd ; tok2 = tok2 - > next ( ) ) {
2013-07-20 12:31:04 +02:00
if ( Token : : Match ( tok2 , " = %varid% [;=] " , declarationId ) ) {
2011-05-11 18:19:14 +02:00
return false ;
2013-07-20 12:31:04 +02:00
} else if ( Token : : Match ( tok2 , " %varid% = malloc|kmalloc ( " , declarationId ) ) {
2011-05-11 18:19:14 +02:00
alloc = true ;
}
}
return alloc ;
}
2012-05-03 10:43:47 +02:00
void CheckMemoryLeakStructMember : : checkStructVariable ( const Variable * const variable )
2009-07-19 16:51:31 +02:00
{
2011-05-11 18:19:14 +02:00
// Is struct variable a pointer?
2022-05-26 15:27:36 +02:00
if ( variable - > isArrayOrPointer ( ) ) {
2011-05-11 18:19:14 +02:00
// Check that variable is allocated with malloc
2012-05-03 10:43:47 +02:00
if ( ! isMalloc ( variable ) )
2011-05-11 18:19:14 +02:00
return ;
2018-06-16 16:10:28 +02:00
} else if ( ! mTokenizer - > isC ( ) & & ( ! variable - > typeScope ( ) | | variable - > typeScope ( ) - > getDestructor ( ) ) ) {
2011-12-10 11:55:14 +01:00
// For non-C code a destructor might cleanup members
return ;
2011-05-11 18:19:14 +02:00
}
2009-09-28 22:58:06 +02:00
2011-05-11 18:19:14 +02:00
// Check struct..
2019-07-17 09:11:42 +02:00
int indentlevel2 = 0 ;
2022-02-02 13:13:12 +01:00
auto deallocInFunction = [ this ] ( const Token * tok , int structid ) - > bool {
// Calling non-function / function that doesn't deallocate?
if ( CheckMemoryLeakInFunction : : test_white_list ( tok - > str ( ) , mSettings , mTokenizer - > isCPP ( ) ) )
return false ;
// Check if the struct is used..
bool deallocated = false ;
const Token * const end = tok - > linkAt ( 1 ) ;
for ( const Token * tok2 = tok ; tok2 ! = end ; tok2 = tok2 - > next ( ) ) {
if ( Token : : Match ( tok2 , " [(,] &| %varid% [,)] " , structid ) ) {
/** @todo check if the function deallocates the memory */
deallocated = true ;
break ;
}
if ( Token : : Match ( tok2 , " [(,] &| %varid% . %name% [,)] " , structid ) ) {
/** @todo check if the function deallocates the memory */
deallocated = true ;
break ;
}
} ;
return deallocated ;
} ;
2022-05-23 23:21:36 +02:00
// return { memberTok, rhsTok }
auto isMemberAssignment = [ ] ( const Token * varTok , int varId ) - > std : : pair < const Token * , const Token * > {
if ( varTok - > varId ( ) ! = varId )
return { } ;
2022-05-24 14:02:06 +02:00
const Token * top = varTok ;
while ( top - > astParent ( ) ) {
2022-05-26 00:03:30 +02:00
if ( Token : : Match ( top - > astParent ( ) , " (|[ " ) )
2022-05-24 14:02:06 +02:00
return { } ;
top = top - > astParent ( ) ;
}
if ( ! Token : : simpleMatch ( top , " = " ) | | ! precedes ( varTok , top ) )
2022-05-23 23:21:36 +02:00
return { } ;
const Token * dot = top - > astOperand1 ( ) ;
while ( dot & & dot - > str ( ) ! = " . " )
dot = dot - > astOperand1 ( ) ;
if ( ! dot )
return { } ;
return { dot - > astOperand2 ( ) , top - > next ( ) } ;
} ;
std : : pair < const Token * , const Token * > assignToks ;
const Token * tokStart = variable - > nameToken ( ) ;
if ( variable - > isArgument ( ) & & variable - > scope ( ) )
tokStart = variable - > scope ( ) - > bodyStart - > next ( ) ;
for ( const Token * tok2 = tokStart ; tok2 & & tok2 ! = variable - > scope ( ) - > bodyEnd ; tok2 = tok2 - > next ( ) ) {
2011-05-11 18:19:14 +02:00
if ( tok2 - > str ( ) = = " { " )
+ + indentlevel2 ;
2011-10-13 20:53:06 +02:00
else if ( tok2 - > str ( ) = = " } " ) {
2011-05-11 18:19:14 +02:00
if ( indentlevel2 = = 0 )
break ;
- - indentlevel2 ;
}
2009-07-19 16:51:31 +02:00
2011-05-11 18:19:14 +02:00
// Unknown usage of struct
/** @todo Check how the struct is used. Only bail out if necessary */
2013-07-20 12:31:04 +02:00
else if ( Token : : Match ( tok2 , " [(,] %varid% [,)] " , variable - > declarationId ( ) ) )
2011-05-11 18:19:14 +02:00
break ;
// Struct member is allocated => check if it is also properly deallocated..
2022-05-24 14:02:06 +02:00
else if ( ( assignToks = isMemberAssignment ( tok2 , variable - > declarationId ( ) ) ) . first & & assignToks . first - > varId ( ) ) {
2022-05-23 23:21:36 +02:00
if ( getAllocationType ( assignToks . second , assignToks . first - > varId ( ) ) = = AllocType : : No )
2015-11-19 16:10:00 +01:00
continue ;
2022-05-30 06:55:15 +02:00
if ( variable - > isArgument ( ) & & variable - > valueType ( ) & & variable - > valueType ( ) - > type = = ValueType : : UNKNOWN_TYPE & & assignToks . first - > astParent ( ) ) {
const Token * accessTok = assignToks . first - > astParent ( ) ;
while ( Token : : simpleMatch ( accessTok - > astOperand1 ( ) , " . " ) )
accessTok = accessTok - > astOperand1 ( ) ;
if ( Token : : simpleMatch ( accessTok , " . " ) & & accessTok - > originalName ( ) = = " -> " )
continue ;
}
2022-05-27 23:52:56 +02:00
2019-07-17 09:11:42 +02:00
const int structid ( variable - > declarationId ( ) ) ;
2022-05-23 23:21:36 +02:00
const int structmemberid ( assignToks . first - > varId ( ) ) ;
2011-05-11 18:19:14 +02:00
// This struct member is allocated.. check that it is deallocated
2019-07-17 09:11:42 +02:00
int indentlevel3 = indentlevel2 ;
2011-10-13 20:53:06 +02:00
for ( const Token * tok3 = tok2 ; tok3 ; tok3 = tok3 - > next ( ) ) {
2011-05-11 18:19:14 +02:00
if ( tok3 - > str ( ) = = " { " )
+ + indentlevel3 ;
2011-10-13 20:53:06 +02:00
else if ( tok3 - > str ( ) = = " } " ) {
if ( indentlevel3 = = 0 ) {
2012-05-03 10:43:47 +02:00
memoryLeak ( tok3 , variable - > name ( ) + " . " + tok2 - > strAt ( 2 ) , Malloc ) ;
2009-07-19 16:51:31 +02:00
break ;
}
2011-05-11 18:19:14 +02:00
- - indentlevel3 ;
2009-07-19 16:51:31 +02:00
}
2011-05-11 18:19:14 +02:00
// Deallocating the struct member..
2015-11-19 16:10:00 +01:00
else if ( getDeallocationType ( tok3 , structmemberid ) ! = AllocType : : No ) {
2011-05-11 18:19:14 +02:00
// If the deallocation happens at the base level, don't check this member anymore
if ( indentlevel3 = = 0 )
2009-07-19 16:51:31 +02:00
break ;
2011-05-11 18:19:14 +02:00
// deallocating and then returning from function in a conditional block =>
// skip ahead out of the block
bool ret = false ;
2011-10-13 20:53:06 +02:00
while ( tok3 ) {
2011-05-11 18:19:14 +02:00
if ( tok3 - > str ( ) = = " return " )
ret = true ;
else if ( tok3 - > str ( ) = = " { " || tok3->str() == " } " )
break ;
tok3 = tok3 - > next ( ) ;
}
if ( ! ret | | ! tok3 | | tok3 - > str ( ) ! = " } " )
break ;
- - indentlevel3 ;
continue ;
2009-07-19 16:51:31 +02:00
}
2011-05-11 18:19:14 +02:00
// Deallocating the struct..
2015-11-19 16:33:04 +01:00
else if ( Token : : Match ( tok3 , " free|kfree ( %varid% ) " , structid)) {
if ( indentlevel2 = = 0 )
memoryLeak ( tok3 , variable - > name ( ) + " . " + tok2 - > strAt ( 2 ) , Malloc ) ;
2009-07-29 11:38:20 +02:00
break ;
2011-05-11 18:19:14 +02:00
}
2009-07-29 11:38:20 +02:00
2011-05-11 18:19:14 +02:00
// failed allocation => skip code..
2015-07-21 20:56:47 +02:00
else if ( Token : : simpleMatch ( tok3 , " if ( " ) & &
notvar ( tok3 - > next ( ) - > astOperand2 ( ) , structmemberid ) ) {
2011-05-11 18:19:14 +02:00
// Goto the ")"
tok3 = tok3 - > next ( ) - > link ( ) ;
2009-07-19 16:51:31 +02:00
2011-05-11 18:19:14 +02:00
// make sure we have ") {".. it should be
if ( ! Token : : simpleMatch ( tok3 , " ) { " ) )
break ;
2009-01-15 21:34:39 +01:00
2011-05-11 18:19:14 +02:00
// Goto the "}"
tok3 = tok3 - > next ( ) - > link ( ) ;
}
2009-07-19 16:51:31 +02:00
2011-05-11 18:19:14 +02:00
// succeeded allocation
2015-07-22 16:45:14 +02:00
else if ( ifvar ( tok3 , structmemberid , " != " , " 0 " ) ) {
2011-05-11 18:19:14 +02:00
// goto the ")"
tok3 = tok3 - > next ( ) - > link ( ) ;
2009-07-20 14:39:24 +02:00
2011-05-11 18:19:14 +02:00
// check if the variable is deallocated or returned..
2019-07-17 09:11:42 +02:00
int indentlevel4 = 0 ;
2011-10-13 20:53:06 +02:00
for ( const Token * tok4 = tok3 ; tok4 ; tok4 = tok4 - > next ( ) ) {
2011-05-11 18:19:14 +02:00
if ( tok4 - > str ( ) = = " { " )
+ + indentlevel4 ;
2011-10-13 20:53:06 +02:00
else if ( tok4 - > str ( ) = = " } " ) {
2011-05-11 18:19:14 +02:00
- - indentlevel4 ;
if ( indentlevel4 = = 0 )
2009-07-20 14:39:24 +02:00
break ;
2011-10-13 20:53:06 +02:00
} else if ( Token : : Match ( tok4 , " free|kfree ( %var% . %varid% ) " , structmemberid)) {
2009-07-19 16:51:31 +02:00
break ;
}
2011-05-11 18:19:14 +02:00
}
2009-07-19 16:51:31 +02:00
2011-05-11 18:19:14 +02:00
// was there a proper deallocation?
if ( indentlevel4 > 0 )
break ;
}
2009-07-19 16:51:31 +02:00
2011-05-11 18:19:14 +02:00
// Returning from function..
2011-10-13 20:53:06 +02:00
else if ( tok3 - > str ( ) = = " return " ) {
2011-05-11 18:19:14 +02:00
// Returning from function without deallocating struct member?
if ( ! Token : : Match ( tok3 , " return %varid% ; " , structid ) & &
2016-01-31 10:08:00 +01:00
! Token : : Match ( tok3 , " return & %varid% " , structid ) & &
2022-02-02 13:13:12 +01:00
! ( Token : : Match ( tok3 , " return %varid% . %var% " , structid ) & & tok3 - > tokAt ( 3 ) - > varId ( ) = = structmemberid ) & &
! ( Token : : Match ( tok3 , " return %name% ( " ) & & tok3 - > astOperand1 ( ) & & deallocInFunction ( tok3 - > astOperand1 ( ) , structid ) ) ) {
2012-05-03 10:43:47 +02:00
memoryLeak ( tok3 , variable - > name ( ) + " . " + tok2 - > strAt ( 2 ) , Malloc ) ;
2011-05-11 18:19:14 +02:00
}
break ;
}
2009-08-17 22:23:37 +02:00
2011-08-08 19:58:25 +02:00
// struct assignment..
2011-10-13 20:53:06 +02:00
else if ( Token : : Match ( tok3 , " = %varid% ; " , structid ) ) {
2011-08-08 19:58:25 +02:00
break ;
2012-06-23 20:15:58 +02:00
} else if ( Token : : Match ( tok3 , " = %var% . %varid% ; " , structmemberid ) ) {
break ;
2011-08-08 19:58:25 +02:00
}
2011-05-11 18:19:14 +02:00
// goto isn't handled well.. bail out even though there might be leaks
else if ( tok3 - > str ( ) = = " goto " )
break ;
2009-07-19 16:51:31 +02:00
2011-05-11 18:19:14 +02:00
// using struct in a function call..
2015-01-31 10:50:39 +01:00
else if ( Token : : Match ( tok3 , " %name% ( " ) ) {
2022-02-02 13:13:12 +01:00
if ( deallocInFunction ( tok3 , structid ) )
2011-05-11 18:19:14 +02:00
break ;
2009-07-19 16:51:31 +02:00
}
}
}
}
}
2009-01-15 21:34:39 +01:00
2009-03-21 17:58:13 +01:00
2010-11-12 21:09:34 +01:00
void CheckMemoryLeakNoVar : : check ( )
{
2018-06-16 16:10:28 +02:00
const SymbolDatabase * symbolDatabase = mTokenizer - > getSymbolDatabase ( ) ;
2010-12-09 06:15:01 +01:00
2012-10-21 09:07:51 +02:00
// only check functions
2018-07-14 10:09:12 +02:00
for ( const Scope * scope : symbolDatabase - > functionScopes ) {
2010-11-12 21:09:34 +01:00
2014-01-03 10:12:32 +01:00
// Checks if a call to an allocation function like malloc() is made and its return value is not assigned.
checkForUnusedReturnValue ( scope ) ;
2015-01-04 11:07:53 +01:00
// Checks to see if a function is called with memory allocated for an argument that
// could be leaked if a function called for another argument throws.
checkForUnsafeArgAlloc ( scope ) ;
2019-08-13 13:00:59 +02:00
// Check for leaks where a the return value of an allocation function like malloc() is an input argument,
// for example f(malloc(1)), where f is known to not release the input argument.
checkForUnreleasedInputArgument ( scope ) ;
}
}
//---------------------------------------------------------------------------
// Checks if an input argument to a function is the return value of an allocation function
// like malloc(), and the function does not release it.
//---------------------------------------------------------------------------
void CheckMemoryLeakNoVar : : checkForUnreleasedInputArgument ( const Scope * scope )
{
// parse the executable scope until tok is reached...
for ( const Token * tok = scope - > bodyStart ; tok ! = scope - > bodyEnd ; tok = tok - > next ( ) ) {
// allocating memory in parameter for function call..
2022-06-28 14:02:02 +02:00
if ( tok - > varId ( ) | | ! Token : : Match ( tok , " %name% ( " ) )
2019-08-13 13:00:59 +02:00
continue ;
2019-08-14 22:01:40 +02:00
// check if the output of the function is assigned
const Token * tok2 = tok - > next ( ) - > astParent ( ) ;
2022-09-29 21:58:11 +02:00
while ( tok2 & & ( tok2 - > isCast ( ) | | Token : : Match ( tok2 , " ?|: " ) ) )
2019-08-14 22:01:40 +02:00
tok2 = tok2 - > astParent ( ) ;
2022-09-29 21:58:11 +02:00
if ( Token : : Match ( tok2 , " %assign% " ) ) // TODO: check if function returns allocated resource
2022-06-29 07:22:26 +02:00
continue ;
if ( Token : : simpleMatch ( tok - > astTop ( ) , " return " ) )
2019-08-13 13:00:59 +02:00
continue ;
2019-08-14 22:01:40 +02:00
const std : : string & functionName = tok - > str ( ) ;
2019-08-13 13:00:59 +02:00
if ( ( mTokenizer - > isCPP ( ) & & functionName = = " delete " ) | |
functionName = = " free " | |
functionName = = " fclose " | |
2019-10-30 17:55:47 +01:00
functionName = = " realloc " | |
functionName = = " return " )
2019-08-23 06:33:00 +02:00
continue ;
2019-08-14 22:01:40 +02:00
2022-06-30 13:05:28 +02:00
if ( Token : : simpleMatch ( tok - > next ( ) - > astParent ( ) , " ( " ) ) // passed to another function
continue ;
2022-06-29 13:43:17 +02:00
if ( ! tok - > isKeyword ( ) & & mSettings - > library . isNotLibraryFunction ( tok ) )
continue ;
2019-08-14 22:01:40 +02:00
if ( ! CheckMemoryLeakInFunction : : test_white_list ( functionName , mSettings , mTokenizer - > isCPP ( ) ) )
2019-08-13 13:00:59 +02:00
continue ;
2019-08-14 22:01:40 +02:00
const std : : vector < const Token * > args = getArguments ( tok ) ;
for ( const Token * arg : args ) {
2022-06-28 14:02:02 +02:00
if ( arg - > isOp ( ) & & ! ( tok - > isKeyword ( ) & & arg - > str ( ) = = " * " ) ) // e.g. switch (*new int)
2019-08-14 22:01:40 +02:00
continue ;
2022-06-28 14:02:02 +02:00
while ( arg - > astOperand1 ( ) ) {
if ( mTokenizer - > isCPP ( ) & & Token : : simpleMatch ( arg , " new " ) )
break ;
arg = arg - > astOperand1 ( ) ;
2022-06-27 14:19:19 +02:00
}
2022-07-13 21:09:53 +02:00
const AllocType alloc = getAllocationType ( arg , 0 ) ;
if ( alloc = = No )
continue ;
if ( ( alloc = = New | | alloc = = NewArray ) & & arg - > next ( ) & & ! ( arg - > next ( ) - > isStandardType ( ) | | mSettings - > library . detectContainerOrIterator ( arg ) ) )
2019-08-14 22:01:40 +02:00
continue ;
if ( isReopenStandardStream ( arg ) )
continue ;
functionCallLeak ( arg , arg - > str ( ) , functionName ) ;
2010-11-12 21:09:34 +01:00
}
2019-08-14 22:01:40 +02:00
2010-11-12 21:09:34 +01:00
}
}
2014-01-03 10:12:32 +01:00
//---------------------------------------------------------------------------
// Checks if a call to an allocation function like malloc() is made and its return value is not assigned.
//---------------------------------------------------------------------------
void CheckMemoryLeakNoVar : : checkForUnusedReturnValue ( const Scope * scope )
{
2018-04-27 22:36:30 +02:00
for ( const Token * tok = scope - > bodyStart ; tok ! = scope - > bodyEnd ; tok = tok - > next ( ) ) {
2022-04-09 19:06:32 +02:00
const bool isNew = mTokenizer - > isCPP ( ) & & tok - > str ( ) = = " new " ;
if ( ! isNew & & ! Token : : Match ( tok , " %name% ( " ) )
2016-01-25 10:33:11 +01:00
continue ;
if ( tok - > varId ( ) )
continue ;
const AllocType allocType = getAllocationType ( tok , 0 ) ;
if ( allocType = = No )
continue ;
2022-04-09 19:06:32 +02:00
if ( tok ! = tok - > next ( ) - > astOperand1 ( ) & & ! isNew )
2016-01-25 10:33:11 +01:00
continue ;
2019-08-12 12:53:59 +02:00
if ( isReopenStandardStream ( tok ) )
continue ;
2022-09-27 20:09:04 +02:00
if ( isOpenDevNull ( tok ) )
continue ;
2019-07-25 21:09:23 +02:00
2016-01-25 10:33:11 +01:00
// get ast parent, skip casts
2022-04-09 19:06:32 +02:00
const Token * parent = isNew ? tok - > astParent ( ) : tok - > next ( ) - > astParent ( ) ;
2022-09-27 20:09:04 +02:00
while ( parent & & parent - > isCast ( ) )
2016-01-25 10:33:11 +01:00
parent = parent - > astParent ( ) ;
2022-04-09 19:06:32 +02:00
bool warn = true ;
if ( isNew ) {
const Token * typeTok = tok - > next ( ) ;
warn = typeTok & & ( typeTok - > isStandardType ( ) | | mSettings - > library . detectContainer ( typeTok ) ) ;
}
if ( ! parent & & warn ) {
2018-04-05 06:47:59 +02:00
// Check if we are in a C++11 constructor
const Token * closingBrace = Token : : findmatch ( tok , " }|; " ) ;
2022-04-09 19:06:32 +02:00
if ( closingBrace - > str ( ) = = " } " & & Token : : Match ( closingBrace - > link ( ) - > tokAt ( - 1 ) , " %name% " ) & & ( ! isNew & & precedes ( tok , closingBrace - > link ( ) ) ) )
2018-04-05 08:21:43 +02:00
continue ;
2016-01-25 10:33:11 +01:00
returnValueNotUsedError ( tok , tok - > str ( ) ) ;
2022-06-27 20:55:09 +02:00
} else if ( Token : : Match ( parent , " %comp%|!|,|%oror%|&&|: " ) ) {
if ( parent - > astParent ( ) & & parent - > str ( ) = = " , " )
continue ;
if ( parent - > str ( ) = = " : " ) {
if ( ! ( Token : : simpleMatch ( parent - > astParent ( ) , " ? " ) & & ! parent - > astParent ( ) - > astParent ( ) ) )
continue ;
}
returnValueNotUsedError ( tok , tok - > str ( ) ) ;
2018-04-05 06:47:59 +02:00
}
2014-01-03 10:12:32 +01:00
}
}
2015-01-04 11:07:53 +01:00
//---------------------------------------------------------------------------
// Check if an exception could cause a leak in an argument constructed with
// shared_ptr/unique_ptr. For example, in the following code, it is possible
// that if g() throws an exception, the memory allocated by "new int(42)"
// could be leaked. See stackoverflow.com/questions/19034538/
// why-is-there-memory-leak-while-using-shared-ptr-as-a-function-parameter
//
// void x() {
// f(shared_ptr<int>(new int(42)), g());
// }
//---------------------------------------------------------------------------
void CheckMemoryLeakNoVar : : checkForUnsafeArgAlloc ( const Scope * scope )
{
// This test only applies to C++ source
2021-02-24 22:00:06 +01:00
if ( ! mTokenizer - > isCPP ( ) | | ! mSettings - > certainty . isEnabled ( Certainty : : inconclusive ) | | ! mSettings - > severity . isEnabled ( Severity : : warning ) )
2015-01-04 11:07:53 +01:00
return ;
2018-04-27 22:36:30 +02:00
for ( const Token * tok = scope - > bodyStart ; tok ! = scope - > bodyEnd ; tok = tok - > next ( ) ) {
2015-01-31 10:50:39 +01:00
if ( Token : : Match ( tok , " %name% ( " ) ) {
2015-01-04 11:07:53 +01:00
const Token * endParamToken = tok - > next ( ) - > link ( ) ;
2015-11-19 15:29:15 +01:00
const Token * pointerType = nullptr ;
const Token * functionCalled = nullptr ;
2015-01-04 11:07:53 +01:00
// Scan through the arguments to the function call
for ( const Token * tok2 = tok - > tokAt ( 2 ) ; tok2 & & tok2 ! = endParamToken ; tok2 = tok2 - > nextArgument ( ) ) {
2015-02-27 10:02:12 +01:00
const Function * func = tok2 - > function ( ) ;
const bool isNothrow = func & & ( func - > isAttributeNothrow ( ) | | func - > isThrow ( ) ) ;
2015-01-04 11:07:53 +01:00
2019-04-06 06:55:46 +02:00
if ( Token : : Match ( tok2 , " shared_ptr|unique_ptr < " ) & & Token : : Match ( tok2 - > next ( ) - > link ( ) , " > ( new %name% " ) ) {
2015-11-19 15:29:15 +01:00
pointerType = tok2 ;
2015-01-04 11:07:53 +01:00
} else if ( ! isNothrow ) {
2015-01-31 10:50:39 +01:00
if ( Token : : Match ( tok2 , " %name% ( " ) )
2015-11-19 15:29:15 +01:00
functionCalled = tok2 ;
2019-04-06 06:55:46 +02:00
else if ( tok2 - > isName ( ) & & Token : : simpleMatch ( tok2 - > next ( ) - > link ( ) , " > ( " ) )
2015-11-19 15:29:15 +01:00
functionCalled = tok2 ;
2015-01-04 11:07:53 +01:00
}
}
2015-11-19 15:29:15 +01:00
if ( pointerType & & functionCalled ) {
std : : string functionName = functionCalled - > str ( ) ;
if ( functionCalled - > strAt ( 1 ) = = " < " ) {
functionName + = ' < ' ;
for ( const Token * tok2 = functionCalled - > tokAt ( 2 ) ; tok2 ! = functionCalled - > next ( ) - > link ( ) ; tok2 = tok2 - > next ( ) )
functionName + = tok2 - > str ( ) ;
functionName + = ' > ' ;
}
std : : string objectTypeName ;
for ( const Token * tok2 = pointerType - > tokAt ( 2 ) ; tok2 ! = pointerType - > next ( ) - > link ( ) ; tok2 = tok2 - > next ( ) )
objectTypeName + = tok2 - > str ( ) ;
unsafeArgAllocError ( tok , functionName , pointerType - > str ( ) , objectTypeName ) ;
}
2015-01-04 11:07:53 +01:00
}
}
}
2010-11-12 21:09:34 +01:00
void CheckMemoryLeakNoVar : : functionCallLeak ( const Token * loc , const std : : string & alloc , const std : : string & functionCall )
{
2021-02-24 22:00:06 +01:00
reportError ( loc , Severity : : error , " leakNoVarFunctionCall " , " Allocation with " + alloc + " , " + functionCall + " doesn't release it. " , CWE772 , Certainty : : normal ) ;
2010-11-12 21:09:34 +01:00
}
2014-01-03 10:12:32 +01:00
void CheckMemoryLeakNoVar : : returnValueNotUsedError ( const Token * tok , const std : : string & alloc )
{
2021-02-24 22:00:06 +01:00
reportError ( tok , Severity : : error , " leakReturnValNotUsed " , " $symbol: " + alloc + " \n Return value of allocation function '$symbol' is not stored. " , CWE771 , Certainty : : normal ) ;
2014-01-03 10:12:32 +01:00
}
2015-01-04 11:07:53 +01:00
void CheckMemoryLeakNoVar : : unsafeArgAllocError ( const Token * tok , const std : : string & funcName , const std : : string & ptrType , const std : : string & objType )
{
const std : : string factoryFunc = ptrType = = " shared_ptr " ? " make_shared " : " make_unique " ;
reportError ( tok , Severity : : warning , " leakUnsafeArgAlloc " ,
2018-04-09 06:43:48 +02:00
" $symbol: " + funcName + " \n "
" Unsafe allocation. If $symbol() throws, memory could be leaked. Use " + factoryFunc + " < " + objType + " >() instead. " ,
2016-08-24 16:37:14 +02:00
CWE401 ,
2021-02-24 22:00:06 +01:00
Certainty : : inconclusive ) ; // Inconclusive because funcName may never throw
2015-01-04 11:07:53 +01:00
}