2012-05-26 08:53:46 +02:00
/*
* Cppcheck - A tool for static C / C + + code analysis
2022-02-05 11:45:17 +01:00
* Copyright ( C ) 2007 - 2022 Cppcheck team .
2012-05-26 08:53:46 +02: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
* along with this program . If not , see < http : //www.gnu.org/licenses/>.
*/
//---------------------------------------------------------------------------
// Leaks when using auto variables
//---------------------------------------------------------------------------
# include "checkleakautovar.h"
2017-05-27 04:33:47 +02:00
# include "astutils.h"
2012-06-30 16:23:10 +02:00
# include "checkmemoryleak.h" // <- CheckMemoryLeak::memoryLeak
2016-05-26 19:21:45 +02:00
# include "checknullpointer.h" // <- CheckNullPointer::isPointerDeRef
2017-05-27 04:33:47 +02:00
# include "mathlib.h"
# include "settings.h"
2020-05-23 07:16:49 +02:00
# include "errortypes.h"
2012-05-26 08:53:46 +02:00
# include "symboldatabase.h"
2017-05-27 04:33:47 +02:00
# include "token.h"
# include "tokenize.h"
2012-05-26 08:53:46 +02:00
2022-09-27 20:06:15 +02:00
# include <array>
2014-05-23 14:17:39 +02:00
# include <iostream>
2017-05-27 04:33:47 +02:00
# include <list>
2022-01-27 19:03:20 +01:00
# include <memory>
2017-05-27 04:33:47 +02:00
# include <utility>
2022-01-27 19:03:20 +01:00
# include <vector>
2017-05-27 04:33:47 +02:00
2012-05-26 08:53:46 +02:00
//---------------------------------------------------------------------------
// Register this check class (by creating a static instance of it)
namespace {
CheckLeakAutoVar instance ;
}
2016-01-25 20:01:48 +01:00
static const CWE CWE672 ( 672U ) ;
static const CWE CWE415 ( 415U ) ;
2018-04-15 01:53:00 +02:00
// Hardcoded allocation types (not from library)
static const int NEW_ARRAY = - 2 ;
static const int NEW = - 1 ;
2022-09-27 20:06:15 +02:00
static const std : : array < std : : pair < std : : string , std : : string > , 4 > alloc_failed_conds { { { " == " , " 0 " } , { " < " , " 0 " } , { " == " , " -1 " } , { " <= " , " -1 " } } } ;
static const std : : array < std : : pair < std : : string , std : : string > , 4 > alloc_success_conds { { { " != " , " 0 " } , { " > " , " 0 " } , { " != " , " -1 " } , { " >= " , " 0 " } } } ;
2019-07-17 08:59:09 +02:00
/**
* @ brief Is variable type some class with automatic deallocation ?
2020-12-27 09:15:59 +01:00
* @ param var variable token
2019-07-17 08:59:09 +02:00
* @ return true unless it can be seen there is no automatic deallocation
*/
static bool isAutoDealloc ( const Variable * var )
{
if ( var - > valueType ( ) & & var - > valueType ( ) - > type ! = ValueType : : Type : : RECORD & & var - > valueType ( ) - > type ! = ValueType : : Type : : UNKNOWN_TYPE )
return false ;
// return false if the type is a simple record type without side effects
// a type that has no side effects (no constructors and no members with constructors)
/** @todo false negative: check base class for side effects */
/** @todo false negative: check constructors for side effects */
if ( var - > typeScope ( ) & & var - > typeScope ( ) - > numConstructors = = 0 & &
2019-08-02 21:14:29 +02:00
( var - > typeScope ( ) - > varlist . empty ( ) | | var - > type ( ) - > needInitialization = = Type : : NeedInitialization : : True ) & &
2019-07-17 08:59:09 +02:00
var - > type ( ) - > derivedFrom . empty ( ) )
return false ;
return true ;
}
2022-09-27 20:06:15 +02:00
template < std : : size_t N >
2021-03-03 07:00:28 +01:00
static bool isVarTokComparison ( const Token * tok , const Token * * vartok ,
2022-09-27 20:06:15 +02:00
const std : : array < std : : pair < std : : string , std : : string > , N > & ops )
2021-03-03 07:00:28 +01:00
{
2022-10-16 13:46:26 +02:00
return std : : any_of ( ops . begin ( ) , ops . end ( ) , [ & ] ( const std : : pair < std : : string , std : : string > & op ) {
return astIsVariableComparison ( tok , op . first , op . second , vartok ) ;
} ) ;
2021-03-03 07:00:28 +01:00
}
2012-05-26 08:53:46 +02:00
//---------------------------------------------------------------------------
void VarInfo : : print ( )
{
std : : cout < < " size= " < < alloctype . size ( ) < < std : : endl ;
2019-07-16 08:54:21 +02:00
for ( std : : map < int , AllocInfo > : : const_iterator it = alloctype . begin ( ) ; it ! = alloctype . end ( ) ; + + it ) {
2012-05-26 08:53:46 +02:00
std : : string strusage ;
2019-07-16 08:54:21 +02:00
const std : : map < int , std : : string > : : const_iterator use =
2018-04-05 15:58:28 +02:00
possibleUsage . find ( it - > first ) ;
2012-05-26 08:53:46 +02:00
if ( use ! = possibleUsage . end ( ) )
strusage = use - > second ;
2016-02-07 19:53:55 +01:00
std : : string status ;
switch ( it - > second . status ) {
2018-04-16 11:11:13 +02:00
case OWNED :
status = " owned " ;
break ;
2016-02-08 10:44:04 +01:00
case DEALLOC :
status = " dealloc " ;
break ;
case ALLOC :
status = " alloc " ;
break ;
case NOALLOC :
status = " noalloc " ;
break ;
2021-01-11 07:55:05 +01:00
case REALLOC :
status = " realloc " ;
break ;
2016-02-08 10:44:04 +01:00
default :
status = " ? " ;
break ;
2020-04-21 17:27:51 +02:00
}
2016-02-07 19:53:55 +01:00
std : : cout < < " status= " < < status < < " "
< < " alloctype=' " < < it - > second . type < < " ' "
2016-02-08 10:44:04 +01:00
< < " possibleUsage=' " < < strusage < < " ' "
2016-02-07 19:53:55 +01:00
< < " conditionalAlloc= " < < ( conditionalAlloc . find ( it - > first ) ! = conditionalAlloc . end ( ) ? " yes " : " no " ) < < " "
< < " referenced= " < < ( referenced . find ( it - > first ) ! = referenced . end ( ) ? " yes " : " no " ) < < " "
2021-01-11 07:55:05 +01:00
< < " reallocedFrom= " < < it - > second . reallocedFromType
2016-02-07 19:53:55 +01:00
< < std : : endl ;
2012-05-26 08:53:46 +02:00
}
}
void VarInfo : : possibleUsageAll ( const std : : string & functionName )
{
possibleUsage . clear ( ) ;
2019-07-16 08:54:21 +02:00
for ( std : : map < int , AllocInfo > : : const_iterator it = alloctype . begin ( ) ; it ! = alloctype . end ( ) ; + + it )
2012-05-26 08:53:46 +02:00
possibleUsage [ it - > first ] = functionName ;
}
2013-07-02 07:18:19 +02:00
void CheckLeakAutoVar : : leakError ( const Token * tok , const std : : string & varname , int type )
2012-05-26 08:53:46 +02:00
{
2018-06-16 16:10:28 +02:00
const CheckMemoryLeak checkmemleak ( mTokenizer , mErrorLogger , mSettings ) ;
2019-09-20 21:54:30 +02:00
if ( Library : : isresource ( type ) )
2012-09-18 16:20:09 +02:00
checkmemleak . resourceLeakError ( tok , varname ) ;
else
checkmemleak . memleakError ( tok , varname ) ;
2012-05-26 08:53:46 +02:00
}
2019-09-22 21:50:02 +02:00
void CheckLeakAutoVar : : mismatchError ( const Token * deallocTok , const Token * allocTok , const std : : string & varname )
2012-05-26 08:53:46 +02:00
{
2018-06-16 16:10:28 +02:00
const CheckMemoryLeak c ( mTokenizer , mErrorLogger , mSettings ) ;
2019-09-22 21:50:02 +02:00
const std : : list < const Token * > callstack = { allocTok , deallocTok } ;
2012-06-30 16:23:10 +02:00
c . mismatchAllocDealloc ( callstack , varname ) ;
2012-05-26 08:53:46 +02:00
}
void CheckLeakAutoVar : : deallocUseError ( const Token * tok , const std : : string & varname )
{
2018-06-16 16:10:28 +02:00
const CheckMemoryLeak c ( mTokenizer , mErrorLogger , mSettings ) ;
2012-06-30 16:23:10 +02:00
c . deallocuseError ( tok , varname ) ;
2012-05-26 08:53:46 +02:00
}
2019-09-22 21:50:02 +02:00
void CheckLeakAutoVar : : deallocReturnError ( const Token * tok , const Token * deallocTok , const std : : string & varname )
2012-06-11 18:28:31 +02:00
{
2019-09-22 21:50:02 +02:00
const std : : list < const Token * > locations = { deallocTok , tok } ;
2021-02-24 22:00:06 +01:00
reportError ( locations , Severity : : error , " deallocret " , " $symbol: " + varname + " \n Returning/dereferencing '$symbol' after it is deallocated / released " , CWE672 , Certainty : : normal ) ;
2012-06-11 18:28:31 +02:00
}
2012-05-26 08:53:46 +02:00
void CheckLeakAutoVar : : configurationInfo ( const Token * tok , const std : : string & functionName )
{
2021-02-24 22:00:06 +01:00
if ( mSettings - > checkLibrary & & mSettings - > severity . isEnabled ( Severity : : information ) ) {
2012-05-26 08:53:46 +02:00
reportError ( tok ,
Severity : : information ,
2013-07-16 18:50:25 +02:00
" checkLibraryUseIgnore " ,
2015-02-03 17:50:32 +01:00
" --check-library: Function " + functionName + " () should have <use>/<leak-ignore> configuration " ) ;
2012-05-26 08:53:46 +02:00
}
}
2019-09-22 21:50:02 +02:00
void CheckLeakAutoVar : : doubleFreeError ( const Token * tok , const Token * prevFreeTok , const std : : string & varname , int type )
2015-01-28 13:45:40 +01:00
{
2019-09-22 21:50:02 +02:00
const std : : list < const Token * > locations = { prevFreeTok , tok } ;
2019-09-20 21:54:30 +02:00
if ( Library : : isresource ( type ) )
2021-02-24 22:00:06 +01:00
reportError ( locations , Severity : : error , " doubleFree " , " $symbol: " + varname + " \n Resource handle '$symbol' freed twice. " , CWE415 , Certainty : : normal ) ;
2015-01-28 13:45:40 +01:00
else
2021-02-24 22:00:06 +01:00
reportError ( locations , Severity : : error , " doubleFree " , " $symbol: " + varname + " \n Memory pointed to by '$symbol' is freed twice. " , CWE415 , Certainty : : normal ) ;
2015-01-28 13:45:40 +01:00
}
2012-05-26 08:53:46 +02:00
void CheckLeakAutoVar : : check ( )
{
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 ( ) ;
2012-05-26 08:53:46 +02:00
2015-01-28 13:45:40 +01:00
// Local variables that are known to be non-zero.
2019-07-16 08:54:21 +02:00
const std : : set < int > notzero ;
2015-01-28 13:45:40 +01:00
2012-05-26 08:53:46 +02:00
// Check function scopes
2018-07-14 09:54:52 +02:00
for ( const Scope * scope : symbolDatabase - > functionScopes ) {
2016-05-26 18:07:56 +02:00
if ( scope - > hasInlineOrLambdaFunction ( ) )
2016-05-26 17:42:27 +02:00
continue ;
2012-10-21 09:05:01 +02:00
// Empty variable info
VarInfo varInfo ;
2019-07-13 07:40:24 +02:00
checkScope ( scope - > bodyStart , & varInfo , notzero , 0 ) ;
2012-05-26 08:53:46 +02:00
}
}
2019-07-16 08:54:21 +02:00
static bool isVarUsedInTree ( const Token * tok , nonneg int varid )
2016-05-21 13:45:08 +02:00
{
if ( ! tok )
return false ;
if ( tok - > varId ( ) = = varid )
return true ;
2019-05-30 16:22:41 +02:00
if ( tok - > str ( ) = = " ( " & & Token : : simpleMatch ( tok - > astOperand1 ( ) , " sizeof " ) )
return false ;
2016-05-21 13:45:08 +02:00
return isVarUsedInTree ( tok - > astOperand1 ( ) , varid ) | | isVarUsedInTree ( tok - > astOperand2 ( ) , varid ) ;
}
2019-07-16 08:54:21 +02:00
static bool isPointerReleased ( const Token * startToken , const Token * endToken , nonneg int varid )
2018-05-05 18:06:49 +02:00
{
for ( const Token * tok = startToken ; tok & & tok ! = endToken ; tok = tok - > next ( ) ) {
if ( tok - > varId ( ) ! = varid )
continue ;
2018-05-06 09:50:53 +02:00
if ( Token : : Match ( tok , " %var% . release ( ) " ) )
2018-05-05 18:06:49 +02:00
return true ;
2018-05-06 09:50:53 +02:00
if ( Token : : Match ( tok , " %var% = " ) )
2018-05-05 18:06:49 +02:00
return false ;
}
return false ;
}
2019-07-18 15:23:19 +02:00
static bool isLocalVarNoAutoDealloc ( const Token * varTok , const bool isCpp )
2019-07-17 07:43:07 +02:00
{
// not a local variable nor argument?
const Variable * var = varTok - > variable ( ) ;
2019-07-18 15:23:19 +02:00
if ( ! var )
return true ;
if ( ! var - > isArgument ( ) & & ( ! var - > isLocal ( ) | | var - > isStatic ( ) ) )
2019-07-17 07:43:07 +02:00
return false ;
// Don't check reference variables
2019-07-18 15:23:19 +02:00
if ( var - > isReference ( ) )
2019-07-17 07:43:07 +02:00
return false ;
// non-pod variable
if ( isCpp ) {
// Possibly automatically deallocated memory
2019-07-17 08:59:09 +02:00
if ( isAutoDealloc ( var ) & & Token : : Match ( varTok , " %var% = new " ) )
2019-07-17 07:43:07 +02:00
return false ;
if ( ! var - > isPointer ( ) & & ! var - > typeStartToken ( ) - > isStandardType ( ) )
return false ;
}
return true ;
}
2018-05-25 08:35:37 +02:00
/** checks if nameToken is a name of a function in a function call:
2021-08-07 20:51:18 +02:00
* func ( arg )
* or
* func < temp1_arg > ( arg )
* @ param nameToken Function name token
* @ return opening parenthesis token or NULL if not a function call
*/
2018-05-25 08:35:37 +02:00
static const Token * isFunctionCall ( const Token * nameToken )
{
2022-07-19 20:41:18 +02:00
if ( ! nameToken - > isStandardType ( ) & & nameToken - > isName ( ) ) {
2018-05-22 09:08:23 +02:00
nameToken = nameToken - > next ( ) ;
// check if function is a template
if ( nameToken & & nameToken - > link ( ) & & nameToken - > str ( ) = = " < " ) {
// skip template arguments
nameToken = nameToken - > link ( ) - > next ( ) ;
}
// check for '('
if ( nameToken & & nameToken - > link ( ) & & nameToken - > str ( ) = = " ( " ) {
// returning opening parenthesis pointer
return nameToken ;
}
}
return nullptr ;
}
2012-05-26 08:53:46 +02:00
void CheckLeakAutoVar : : checkScope ( const Token * const startToken ,
VarInfo * varInfo ,
2019-07-16 08:54:21 +02:00
std : : set < int > notzero ,
nonneg int recursiveCount )
2012-05-26 08:53:46 +02:00
{
2019-07-24 09:57:53 +02:00
# if ASAN
static const nonneg int recursiveLimit = 300 ;
# else
static const nonneg int recursiveLimit = 1000 ;
# endif
if ( + + recursiveCount > recursiveLimit ) // maximum number of "else if ()"
2019-07-15 12:39:58 +02:00
throw InternalError ( startToken , " Internal limit: CheckLeakAutoVar::checkScope() Maximum recursive count of 1000 reached . " , InternalError::LIMIT) ;
2019-07-12 07:56:05 +02:00
2019-07-16 08:54:21 +02:00
std : : map < int , VarInfo : : AllocInfo > & alloctype = varInfo - > alloctype ;
std : : map < int , std : : string > & possibleUsage = varInfo - > possibleUsage ;
const std : : set < int > conditionalAlloc ( varInfo - > conditionalAlloc ) ;
2012-05-26 08:53:46 +02:00
// Parse all tokens
const Token * const endToken = startToken - > link ( ) ;
for ( const Token * tok = startToken ; tok & & tok ! = endToken ; tok = tok - > next ( ) ) {
2015-05-10 10:00:46 +02:00
if ( ! tok - > scope ( ) - > isExecutable ( ) ) {
2018-04-27 22:36:30 +02:00
tok = tok - > scope ( ) - > bodyEnd ;
2015-05-10 10:00:46 +02:00
if ( ! tok ) // Ticket #6666 (crash upon invalid code)
break ;
}
2015-01-29 21:26:06 +01:00
2018-05-22 09:08:23 +02:00
// check each token
{
const Token * nextTok = checkTokenInsideExpression ( tok , varInfo ) ;
if ( nextTok ) {
tok = nextTok ;
continue ;
2012-05-26 08:53:46 +02:00
}
}
// look for end of statement
2019-08-07 08:04:10 +02:00
if ( ! Token : : Match ( tok , " [;{},] " ) | | Token : : Match ( tok - > next ( ) , " [;{},] " ) )
2012-05-26 08:53:46 +02:00
continue ;
2015-01-28 13:45:40 +01:00
2012-05-26 08:53:46 +02:00
tok = tok - > next ( ) ;
2012-09-17 15:22:51 +02:00
if ( ! tok | | tok = = endToken )
2012-05-26 08:53:46 +02:00
break ;
2021-03-03 06:58:38 +01:00
if ( Token : : Match ( tok , " const %type% " ) )
tok = tok - > tokAt ( 2 ) ;
2022-05-01 15:46:07 +02:00
while ( tok - > str ( ) = = " ( " )
tok = tok - > next ( ) ;
while ( tok - > isUnaryOp ( " * " ) & & tok - > astOperand1 ( ) - > isUnaryOp ( " & " ) )
tok = tok - > astOperand1 ( ) - > astOperand1 ( ) ;
2015-01-29 21:26:06 +01:00
// parse statement, skip to last member
2016-05-13 14:53:32 +02:00
const Token * varTok = tok ;
while ( Token : : Match ( varTok , " %name% ::|. %name% !!( " ) )
varTok = varTok - > tokAt ( 2 ) ;
2012-05-26 08:53:46 +02:00
2017-02-25 11:36:48 +01:00
const Token * ftok = tok ;
if ( ftok - > str ( ) = = " :: " )
ftok = ftok - > next ( ) ;
while ( Token : : Match ( ftok , " %name% :: %name% " ) )
ftok = ftok - > tokAt ( 2 ) ;
2022-05-01 15:46:07 +02:00
auto isAssignment = [ ] ( const Token * varTok ) - > const Token * {
if ( varTok - > varId ( ) ) {
const Token * top = varTok ;
while ( top - > astParent ( ) ) {
top = top - > astParent ( ) ;
if ( ! Token : : Match ( top , " (|*|&|. " ) )
break ;
}
if ( top - > str ( ) = = " = " & & succeeds ( top , varTok ) )
return top ;
}
return nullptr ;
} ;
2012-05-26 08:53:46 +02:00
// assignment..
2022-05-01 15:46:07 +02:00
if ( const Token * const tokAssignOp = isAssignment ( varTok ) ) {
2018-05-22 09:08:23 +02:00
2022-07-24 12:15:04 +02:00
if ( Token : : simpleMatch ( tokAssignOp - > astOperand1 ( ) , " . " ) )
continue ;
2012-05-26 08:53:46 +02:00
// taking address of another variable..
2018-05-22 09:08:23 +02:00
if ( Token : : Match ( tokAssignOp , " = %var% [+;] " ) ) {
2016-05-13 14:53:32 +02:00
if ( varTok - > tokAt ( 2 ) - > varId ( ) ! = varTok - > varId ( ) ) {
2012-05-26 08:53:46 +02:00
// If variable points at allocated memory => error
2016-05-13 14:53:32 +02:00
leakIfAllocated ( varTok , * varInfo ) ;
2012-05-26 08:53:46 +02:00
// no multivariable checking currently => bail out for rhs variables
2016-05-13 14:53:32 +02:00
for ( const Token * tok2 = varTok ; tok2 ; tok2 = tok2 - > next ( ) ) {
2012-05-26 08:53:46 +02:00
if ( tok2 - > str ( ) = = " ; " ) {
break ;
}
if ( tok2 - > varId ( ) ) {
varInfo - > erase ( tok2 - > varId ( ) ) ;
}
}
}
}
2018-05-22 09:08:23 +02:00
// right ast part (after `=` operator)
2019-07-03 08:39:44 +02:00
const Token * tokRightAstOperand = tokAssignOp - > astOperand2 ( ) ;
while ( tokRightAstOperand & & tokRightAstOperand - > isCast ( ) )
tokRightAstOperand = tokRightAstOperand - > astOperand2 ( ) ? tokRightAstOperand - > astOperand2 ( ) : tokRightAstOperand - > astOperand1 ( ) ;
2018-05-22 09:08:23 +02:00
2012-05-26 08:53:46 +02:00
// is variable used in rhs?
2018-05-22 09:08:23 +02:00
if ( isVarUsedInTree ( tokRightAstOperand , varTok - > varId ( ) ) )
2012-05-26 08:53:46 +02:00
continue ;
// Variable has already been allocated => error
2016-05-13 14:53:32 +02:00
if ( conditionalAlloc . find ( varTok - > varId ( ) ) = = conditionalAlloc . end ( ) )
leakIfAllocated ( varTok , * varInfo ) ;
varInfo - > erase ( varTok - > varId ( ) ) ;
2012-05-26 08:53:46 +02:00
2019-07-18 15:23:19 +02:00
if ( ! isLocalVarNoAutoDealloc ( varTok , mTokenizer - > isCPP ( ) ) )
2012-07-17 16:28:34 +02:00
continue ;
2012-05-26 08:53:46 +02:00
// allocation?
2019-07-05 12:44:52 +02:00
const Token * const fTok = tokRightAstOperand ? tokRightAstOperand - > previous ( ) : nullptr ;
if ( Token : : Match ( fTok , " %type% ( " ) ) {
const Library : : AllocFunc * f = mSettings - > library . getAllocFuncInfo ( fTok ) ;
2016-05-22 17:18:50 +02:00
if ( f & & f - > arg = = - 1 ) {
2017-10-11 16:36:43 +02:00
VarInfo : : AllocInfo & varAlloc = alloctype [ varTok - > varId ( ) ] ;
varAlloc . type = f - > groupId ;
varAlloc . status = VarInfo : : ALLOC ;
2019-09-22 21:50:02 +02:00
varAlloc . allocTok = fTok ;
2012-05-26 08:53:46 +02:00
}
2019-07-05 12:44:52 +02:00
changeAllocStatusIfRealloc ( alloctype , fTok , varTok ) ;
2018-06-16 16:10:28 +02:00
} else if ( mTokenizer - > isCPP ( ) & & Token : : Match ( varTok - > tokAt ( 2 ) , " new !!( " ) ) {
2016-10-09 10:28:19 +02:00
const Token * tok2 = varTok - > tokAt ( 2 ) - > astOperand1 ( ) ;
2018-04-04 21:51:31 +02:00
const bool arrayNew = ( tok2 & & ( tok2 - > str ( ) = = " [ " | | ( tok2 - > str ( ) = = " ( " & & tok2 - > astOperand1 ( ) & & tok2 - > astOperand1 ( ) - > str ( ) = = " [ " ) ) ) ;
2017-10-11 16:36:43 +02:00
VarInfo : : AllocInfo & varAlloc = alloctype [ varTok - > varId ( ) ] ;
2018-04-15 01:53:00 +02:00
varAlloc . type = arrayNew ? NEW_ARRAY : NEW ;
2017-10-11 16:36:43 +02:00
varAlloc . status = VarInfo : : ALLOC ;
2019-09-22 21:50:02 +02:00
varAlloc . allocTok = varTok - > tokAt ( 2 ) ;
2012-05-26 08:53:46 +02:00
}
// Assigning non-zero value variable. It might be used to
// track the execution for a later if condition.
2016-05-13 14:53:32 +02:00
if ( Token : : Match ( varTok - > tokAt ( 2 ) , " %num% ; " ) & & MathLib : : toLongNumber ( varTok - > strAt ( 2 ) ) ! = 0 )
notzero . insert ( varTok - > varId ( ) ) ;
else if ( Token : : Match ( varTok - > tokAt ( 2 ) , " - %type% ; " ) && varTok->tokAt(3)->isUpperCaseName())
notzero . insert ( varTok - > varId ( ) ) ;
2012-05-26 08:53:46 +02:00
else
2016-05-13 14:53:32 +02:00
notzero . erase ( varTok - > varId ( ) ) ;
2012-05-26 08:53:46 +02:00
}
// if/else
else if ( Token : : simpleMatch ( tok , " if ( " ) ) {
// Parse function calls inside the condition
2018-05-22 09:08:23 +02:00
const Token * closingParenthesis = tok - > linkAt ( 1 ) ;
for ( const Token * innerTok = tok - > tokAt ( 2 ) ; innerTok & & innerTok ! = closingParenthesis ; innerTok = innerTok - > next ( ) ) {
// TODO: replace with checkTokenInsideExpression()
2018-05-25 08:35:37 +02:00
2019-07-18 15:23:19 +02:00
if ( ! isLocalVarNoAutoDealloc ( innerTok , mTokenizer - > isCPP ( ) ) )
2019-07-17 07:43:07 +02:00
continue ;
2022-05-27 07:57:43 +02:00
// Check assignments in the if-statement. Skip multiple assignments since we don't track those
2022-07-24 12:15:04 +02:00
if ( Token : : Match ( innerTok , " %var% = " ) & & innerTok - > astParent ( ) = = innerTok - > next ( ) & &
! ( innerTok - > next ( ) - > astParent ( ) & & innerTok - > next ( ) - > astParent ( ) - > isAssignmentOp ( ) ) ) {
2017-10-26 19:11:00 +02:00
// allocation?
2019-07-03 08:39:44 +02:00
// right ast part (after `=` operator)
2022-07-24 12:15:04 +02:00
const Token * tokRightAstOperand = innerTok - > next ( ) - > astOperand2 ( ) ;
2019-07-03 08:39:44 +02:00
while ( tokRightAstOperand & & tokRightAstOperand - > isCast ( ) )
tokRightAstOperand = tokRightAstOperand - > astOperand2 ( ) ? tokRightAstOperand - > astOperand2 ( ) : tokRightAstOperand - > astOperand1 ( ) ;
if ( tokRightAstOperand & & Token : : Match ( tokRightAstOperand - > previous ( ) , " %type% ( " ) ) {
2022-11-03 10:37:43 +01:00
const Token * fTok = tokRightAstOperand - > previous ( ) ;
const Library : : AllocFunc * f = mSettings - > library . getAllocFuncInfo ( fTok ) ;
2017-10-26 19:11:00 +02:00
if ( f & & f - > arg = = - 1 ) {
2022-07-24 12:15:04 +02:00
VarInfo : : AllocInfo & varAlloc = alloctype [ innerTok - > varId ( ) ] ;
2017-10-26 19:11:00 +02:00
varAlloc . type = f - > groupId ;
varAlloc . status = VarInfo : : ALLOC ;
2022-11-03 10:37:43 +01:00
varAlloc . allocTok = fTok ;
2019-06-02 15:25:45 +02:00
} else {
// Fixme: warn about leak
2022-07-24 12:15:04 +02:00
alloctype . erase ( innerTok - > varId ( ) ) ;
2017-10-26 19:11:00 +02:00
}
2022-11-03 10:37:43 +01:00
changeAllocStatusIfRealloc ( alloctype , fTok , varTok ) ;
2022-07-24 12:15:04 +02:00
} else if ( mTokenizer - > isCPP ( ) & & Token : : Match ( innerTok - > tokAt ( 2 ) , " new !!( " ) ) {
const Token * tok2 = innerTok - > tokAt ( 2 ) - > astOperand1 ( ) ;
2018-04-04 21:51:31 +02:00
const bool arrayNew = ( tok2 & & ( tok2 - > str ( ) = = " [ " | | ( tok2 - > str ( ) = = " ( " & & tok2 - > astOperand1 ( ) & & tok2 - > astOperand1 ( ) - > str ( ) = = " [ " ) ) ) ;
2022-07-24 12:15:04 +02:00
VarInfo : : AllocInfo & varAlloc = alloctype [ innerTok - > varId ( ) ] ;
2018-04-15 01:53:00 +02:00
varAlloc . type = arrayNew ? NEW_ARRAY : NEW ;
2017-10-26 19:11:00 +02:00
varAlloc . status = VarInfo : : ALLOC ;
2022-07-24 12:15:04 +02:00
varAlloc . allocTok = innerTok - > tokAt ( 2 ) ;
2017-10-26 19:11:00 +02:00
}
}
2018-05-22 09:08:23 +02:00
// check for function call
const Token * const openingPar = isFunctionCall ( innerTok ) ;
if ( openingPar ) {
// innerTok is a function name
2018-04-04 21:51:31 +02:00
const VarInfo : : AllocInfo allocation ( 0 , VarInfo : : NOALLOC ) ;
2018-05-22 09:08:23 +02:00
functionCall ( innerTok , openingPar , varInfo , allocation , nullptr ) ;
innerTok = openingPar - > link ( ) ;
2012-05-26 08:53:46 +02:00
}
}
2018-05-22 09:08:23 +02:00
if ( Token : : simpleMatch ( closingParenthesis , " ) { " ) ) {
2014-04-18 18:18:47 +02:00
VarInfo varInfo1 ( * varInfo ) ; // VarInfo for if code
VarInfo varInfo2 ( * varInfo ) ; // VarInfo for else code
2012-05-26 08:53:46 +02:00
2020-07-09 21:13:54 +02:00
// Skip expressions before commas
const Token * astOperand2AfterCommas = tok - > next ( ) - > astOperand2 ( ) ;
while ( Token : : simpleMatch ( astOperand2AfterCommas , " , " ) )
astOperand2AfterCommas = astOperand2AfterCommas - > astOperand2 ( ) ;
2020-07-09 22:40:52 +02:00
// Recursively scan variable comparisons in condition
visitAstNodes ( astOperand2AfterCommas , [ & ] ( const Token * tok3 ) {
2015-07-25 11:37:03 +02:00
if ( ! tok3 )
2020-07-09 22:40:52 +02:00
return ChildrenToVisit : : none ;
2019-01-27 10:27:39 +01:00
if ( tok3 - > str ( ) = = " && " | | tok3 - > str ( ) = = " || " ) {
// FIXME: handle && ! || better
2020-07-09 22:40:52 +02:00
return ChildrenToVisit : : op1_and_op2 ;
2015-07-25 11:37:03 +02:00
}
if ( tok3 - > str ( ) = = " ( " & & Token : : Match ( tok3 - > astOperand1 ( ) , " UNLIKELY|LIKELY " ) ) {
2020-07-09 22:40:52 +02:00
return ChildrenToVisit : : op2 ;
2022-06-08 16:58:57 +02:00
} else if ( tok3 - > str ( ) = = " ( " & & tok3 - > previous ( ) - > isName ( ) ) {
2017-07-01 09:10:23 +02:00
const std : : vector < const Token * > params = getArguments ( tok3 - > previous ( ) ) ;
2018-07-14 09:54:52 +02:00
for ( const Token * par : params ) {
2017-06-30 23:05:09 +02:00
if ( ! par - > isComparisonOp ( ) )
continue ;
const Token * vartok = nullptr ;
2021-03-03 07:00:28 +01:00
if ( isVarTokComparison ( par , & vartok , alloc_success_conds ) | |
( isVarTokComparison ( par , & vartok , alloc_failed_conds ) ) ) {
2017-06-30 23:05:09 +02:00
varInfo1 . erase ( vartok - > varId ( ) ) ;
varInfo2 . erase ( vartok - > varId ( ) ) ;
}
}
2020-07-09 22:40:52 +02:00
return ChildrenToVisit : : none ;
2015-07-25 11:37:03 +02:00
}
const Token * vartok = nullptr ;
2021-03-03 07:00:28 +01:00
if ( isVarTokComparison ( tok3 , & vartok , alloc_success_conds ) ) {
2021-01-11 07:55:05 +01:00
varInfo2 . reallocToAlloc ( vartok - > varId ( ) ) ;
2015-07-25 11:37:03 +02:00
varInfo2 . erase ( vartok - > varId ( ) ) ;
2021-03-03 07:00:28 +01:00
if ( astIsVariableComparison ( tok3 , " != " , " 0 " , & vartok ) & &
( notzero . find ( vartok - > varId ( ) ) ! = notzero . end ( ) ) )
2015-07-25 11:37:03 +02:00
varInfo2 . clear ( ) ;
2021-03-03 07:00:28 +01:00
} else if ( isVarTokComparison ( tok3 , & vartok , alloc_failed_conds ) ) {
2021-01-11 07:55:05 +01:00
varInfo1 . reallocToAlloc ( vartok - > varId ( ) ) ;
2015-07-25 11:37:03 +02:00
varInfo1 . erase ( vartok - > varId ( ) ) ;
}
2020-07-09 22:40:52 +02:00
return ChildrenToVisit : : none ;
} ) ;
2012-05-26 08:53:46 +02:00
2019-07-13 07:40:24 +02:00
checkScope ( closingParenthesis - > next ( ) , & varInfo1 , notzero , recursiveCount ) ;
2018-05-22 09:08:23 +02:00
closingParenthesis = closingParenthesis - > linkAt ( 1 ) ;
if ( Token : : simpleMatch ( closingParenthesis , " } else { " ) ) {
2019-07-13 07:40:24 +02:00
checkScope ( closingParenthesis - > tokAt ( 2 ) , & varInfo2 , notzero , recursiveCount ) ;
2018-05-22 09:08:23 +02:00
tok = closingParenthesis - > linkAt ( 2 ) - > previous ( ) ;
2012-05-26 08:53:46 +02:00
} else {
2018-05-22 09:08:23 +02:00
tok = closingParenthesis - > previous ( ) ;
2012-05-26 08:53:46 +02:00
}
VarInfo old ;
old . swap ( * varInfo ) ;
2019-07-16 08:54:21 +02:00
std : : map < int , VarInfo : : AllocInfo > : : const_iterator it ;
2016-05-15 20:27:44 +02:00
for ( it = old . alloctype . begin ( ) ; it ! = old . alloctype . end ( ) ; + + it ) {
2019-07-16 08:54:21 +02:00
const int varId = it - > first ;
2016-05-15 20:27:44 +02:00
if ( old . conditionalAlloc . find ( varId ) = = old . conditionalAlloc . end ( ) )
continue ;
if ( varInfo1 . alloctype . find ( varId ) = = varInfo1 . alloctype . end ( ) | |
varInfo2 . alloctype . find ( varId ) = = varInfo2 . alloctype . end ( ) ) {
varInfo1 . erase ( varId ) ;
varInfo2 . erase ( varId ) ;
}
}
// Conditional allocation in varInfo1
2012-05-26 08:53:46 +02:00
for ( it = varInfo1 . alloctype . begin ( ) ; it ! = varInfo1 . alloctype . end ( ) ; + + it ) {
if ( varInfo2 . alloctype . find ( it - > first ) = = varInfo2 . alloctype . end ( ) & &
old . alloctype . find ( it - > first ) = = old . alloctype . end ( ) ) {
varInfo - > conditionalAlloc . insert ( it - > first ) ;
}
}
// Conditional allocation in varInfo2
for ( it = varInfo2 . alloctype . begin ( ) ; it ! = varInfo2 . alloctype . end ( ) ; + + it ) {
if ( varInfo1 . alloctype . find ( it - > first ) = = varInfo1 . alloctype . end ( ) & &
old . alloctype . find ( it - > first ) = = old . alloctype . end ( ) ) {
varInfo - > conditionalAlloc . insert ( it - > first ) ;
}
}
// Conditional allocation/deallocation
for ( it = varInfo1 . alloctype . begin ( ) ; it ! = varInfo1 . alloctype . end ( ) ; + + it ) {
2018-04-16 11:11:13 +02:00
if ( it - > second . managed ( ) & & conditionalAlloc . find ( it - > first ) ! = conditionalAlloc . end ( ) ) {
2012-05-26 08:53:46 +02:00
varInfo - > conditionalAlloc . erase ( it - > first ) ;
varInfo2 . erase ( it - > first ) ;
}
}
for ( it = varInfo2 . alloctype . begin ( ) ; it ! = varInfo2 . alloctype . end ( ) ; + + it ) {
2018-04-16 11:11:13 +02:00
if ( it - > second . managed ( ) & & conditionalAlloc . find ( it - > first ) ! = conditionalAlloc . end ( ) ) {
2012-05-26 08:53:46 +02:00
varInfo - > conditionalAlloc . erase ( it - > first ) ;
varInfo1 . erase ( it - > first ) ;
}
}
alloctype . insert ( varInfo1 . alloctype . begin ( ) , varInfo1 . alloctype . end ( ) ) ;
alloctype . insert ( varInfo2 . alloctype . begin ( ) , varInfo2 . alloctype . end ( ) ) ;
possibleUsage . insert ( varInfo1 . possibleUsage . begin ( ) , varInfo1 . possibleUsage . end ( ) ) ;
possibleUsage . insert ( varInfo2 . possibleUsage . begin ( ) , varInfo2 . possibleUsage . end ( ) ) ;
}
}
2015-01-28 13:45:40 +01:00
// unknown control.. (TODO: handle loops)
2015-01-28 22:29:07 +01:00
else if ( ( Token : : Match ( tok , " %type% ( " ) & & Token : : simpleMatch ( tok - > linkAt ( 1 ) , " ) { " )) || Token::simpleMatch(tok, " do { " )) {
2012-05-26 08:53:46 +02:00
varInfo - > clear ( ) ;
break ;
}
2015-01-28 19:44:59 +01:00
// return
else if ( tok - > str ( ) = = " return " ) {
ret ( tok , * varInfo ) ;
varInfo - > clear ( ) ;
}
// throw
2018-06-16 16:10:28 +02:00
else if ( mTokenizer - > isCPP ( ) & & tok - > str ( ) = = " throw " ) {
2015-01-28 19:44:59 +01:00
bool tryFound = false ;
const Scope * scope = tok - > scope ( ) ;
while ( scope & & scope - > isExecutable ( ) ) {
if ( scope - > type = = Scope : : eTry )
tryFound = true ;
scope = scope - > nestedIn ;
}
// If the execution leaves the function then treat it as return
if ( ! tryFound )
ret ( tok , * varInfo ) ;
varInfo - > clear ( ) ;
}
2019-07-12 18:25:04 +02:00
// delete
else if ( mTokenizer - > isCPP ( ) & & tok - > str ( ) = = " delete " ) {
2019-09-22 21:50:02 +02:00
const Token * delTok = tok ;
2022-07-24 12:15:04 +02:00
if ( Token : : simpleMatch ( delTok - > astOperand1 ( ) , " . " ) )
continue ;
2019-07-12 18:25:04 +02:00
const bool arrayDelete = Token : : simpleMatch ( tok - > next ( ) , " [ ] " ) ;
if ( arrayDelete )
tok = tok - > tokAt ( 3 ) ;
else
tok = tok - > next ( ) ;
if ( tok - > str ( ) = = " ( " )
tok = tok - > next ( ) ;
while ( Token : : Match ( tok , " %name% ::|. " ) )
tok = tok - > tokAt ( 2 ) ;
const bool isnull = tok - > hasKnownIntValue ( ) & & tok - > values ( ) . front ( ) . intvalue = = 0 ;
if ( ! isnull & & tok - > varId ( ) & & tok - > strAt ( 1 ) ! = " [ " ) {
2019-09-22 21:50:02 +02:00
const VarInfo : : AllocInfo allocation ( arrayDelete ? NEW_ARRAY : NEW , VarInfo : : DEALLOC , delTok ) ;
2019-07-12 18:25:04 +02:00
changeAllocStatus ( varInfo , allocation , tok , tok ) ;
}
}
2012-05-26 08:53:46 +02:00
// Function call..
2022-07-28 22:47:15 +02:00
else if ( const Token * openingPar = isFunctionCall ( ftok ) ) {
2019-07-05 12:44:52 +02:00
const Library : : AllocFunc * af = mSettings - > library . getDeallocFuncInfo ( ftok ) ;
2019-09-22 21:50:02 +02:00
VarInfo : : AllocInfo allocation ( af ? af - > groupId : 0 , VarInfo : : DEALLOC , ftok ) ;
2015-01-28 13:45:40 +01:00
if ( allocation . type = = 0 )
allocation . status = VarInfo : : NOALLOC ;
2022-07-24 12:15:04 +02:00
if ( Token : : simpleMatch ( ftok - > astParent ( ) , " ( " ) & & Token : : simpleMatch ( ftok - > astParent ( ) - > astOperand2 ( ) , " . " ) )
continue ;
2018-05-22 09:08:23 +02:00
functionCall ( ftok , openingPar , varInfo , allocation , af ) ;
2012-05-26 08:53:46 +02:00
2017-02-25 11:36:48 +01:00
tok = ftok - > next ( ) - > link ( ) ;
2012-05-26 08:53:46 +02:00
// Handle scopes that might be noreturn
2015-01-28 13:45:40 +01:00
if ( allocation . status = = VarInfo : : NOALLOC & & Token : : simpleMatch ( tok , " ) ; } " ) ) {
2022-07-28 22:47:15 +02:00
if ( ftok - > isKeyword ( ) )
continue ;
const std : : string functionName ( mSettings - > library . getFunctionName ( ftok ) ) ;
2012-05-26 08:53:46 +02:00
bool unknown = false ;
2019-11-20 15:37:09 +01:00
if ( mTokenizer - > isScopeNoReturn ( tok - > tokAt ( 2 ) , & unknown ) ) {
2014-05-04 13:11:21 +02:00
if ( ! unknown )
2012-05-26 08:53:46 +02:00
varInfo - > clear ( ) ;
2018-06-16 16:10:28 +02:00
else if ( ! mSettings - > library . isLeakIgnore ( functionName ) & & ! mSettings - > library . isUse ( functionName ) )
2014-05-04 13:11:21 +02:00
varInfo - > possibleUsageAll ( functionName ) ;
2012-05-26 08:53:46 +02:00
}
}
continue ;
}
2012-10-08 17:23:47 +02:00
// goto => weird execution path
else if ( tok - > str ( ) = = " goto " ) {
varInfo - > clear ( ) ;
}
2013-07-28 11:53:49 +02:00
2013-07-28 10:26:46 +02:00
// continue/break
else if ( Token : : Match ( tok , " continue|break ; " ) ) {
varInfo - > clear ( ) ;
}
2018-04-16 11:11:13 +02:00
// Check smart pointer
2019-04-24 15:35:47 +02:00
else if ( Token : : Match ( ftok , " %name% < " ) & & mSettings - > library . isSmartPointer ( tok ) ) {
2018-04-17 08:37:41 +02:00
const Token * typeEndTok = ftok - > linkAt ( 1 ) ;
2018-04-21 11:24:19 +02:00
if ( ! Token : : Match ( typeEndTok , " > %var% {|( %var% ,|)|} " ) )
2018-04-16 11:11:13 +02:00
continue ;
2018-05-05 18:06:49 +02:00
tok = typeEndTok - > linkAt ( 2 ) ;
2019-07-16 08:54:21 +02:00
const int varid = typeEndTok - > next ( ) - > varId ( ) ;
2018-05-06 09:50:53 +02:00
if ( isPointerReleased ( typeEndTok - > tokAt ( 2 ) , endToken , varid ) )
2018-05-05 18:06:49 +02:00
continue ;
2018-04-16 11:11:13 +02:00
bool arrayDelete = false ;
2018-04-17 08:37:41 +02:00
if ( Token : : findsimplematch ( ftok - > next ( ) , " [ ] " , typeEndTok ) )
2018-04-16 11:11:13 +02:00
arrayDelete = true ;
// Check deleter
const Token * deleterToken = nullptr ;
const Token * endDeleterToken = nullptr ;
const Library : : AllocFunc * af = nullptr ;
2018-04-16 12:55:37 +02:00
if ( Token : : Match ( ftok , " unique_ptr < %type% , " ) ) {
2018-04-16 11:11:13 +02:00
deleterToken = ftok - > tokAt ( 4 ) ;
2018-04-17 08:37:41 +02:00
endDeleterToken = typeEndTok ;
} else if ( Token : : Match ( typeEndTok , " > %var% {|( %var% , " ) ) {
deleterToken = typeEndTok - > tokAt ( 5 ) ;
endDeleterToken = typeEndTok - > linkAt ( 2 ) ;
2018-04-16 11:11:13 +02:00
}
2018-04-16 12:55:37 +02:00
if ( deleterToken ) {
2018-05-21 08:22:18 +02:00
// Skip the decaying plus in expressions like +[](T*){}
if ( deleterToken - > str ( ) = = " + " ) {
deleterToken = deleterToken - > next ( ) ;
}
2018-04-16 11:11:13 +02:00
// Check if its a pointer to a function
const Token * dtok = Token : : findmatch ( deleterToken , " & %name% " , endDeleterToken ) ;
if ( dtok ) {
2019-07-05 12:44:52 +02:00
af = mSettings - > library . getDeallocFuncInfo ( dtok - > tokAt ( 1 ) ) ;
2018-04-16 11:11:13 +02:00
} else {
2018-05-21 08:22:18 +02:00
const Token * tscopeStart = nullptr ;
const Token * tscopeEnd = nullptr ;
// If the deleter is a lambda, check if it calls the dealloc function
if ( deleterToken - > str ( ) = = " [ " & &
Token : : simpleMatch ( deleterToken - > link ( ) , " ] ( " ) & &
// TODO: Check for mutable keyword
Token : : simpleMatch ( deleterToken - > link ( ) - > linkAt ( 1 ) , " ) { " ) ) {
tscopeStart = deleterToken - > link ( ) - > linkAt ( 1 ) - > tokAt ( 1 ) ;
tscopeEnd = tscopeStart - > link ( ) ;
2018-05-25 08:35:37 +02:00
// If the deleter is a class, check if class calls the dealloc function
2018-05-21 08:22:18 +02:00
} else if ( ( dtok = Token : : findmatch ( deleterToken , " %type% " , endDeleterToken ) ) & & dtok - > type ( ) ) {
2018-05-25 08:35:37 +02:00
const Scope * tscope = dtok - > type ( ) - > classScope ;
if ( tscope ) {
tscopeStart = tscope - > bodyStart ;
tscopeEnd = tscope - > bodyEnd ;
}
2018-05-21 08:22:18 +02:00
}
2018-05-25 08:35:37 +02:00
if ( tscopeStart & & tscopeEnd ) {
2018-05-21 08:22:18 +02:00
for ( const Token * tok2 = tscopeStart ; tok2 ! = tscopeEnd ; tok2 = tok2 - > next ( ) ) {
2019-07-05 12:44:52 +02:00
af = mSettings - > library . getDeallocFuncInfo ( tok2 ) ;
2018-04-16 12:55:37 +02:00
if ( af )
2018-04-16 11:11:13 +02:00
break ;
}
}
}
}
2018-04-17 08:37:41 +02:00
const Token * vtok = typeEndTok - > tokAt ( 3 ) ;
2019-09-22 21:50:02 +02:00
const VarInfo : : AllocInfo allocation ( af ? af - > groupId : ( arrayDelete ? NEW_ARRAY : NEW ) , VarInfo : : OWNED , ftok ) ;
2018-04-16 11:11:13 +02:00
changeAllocStatus ( varInfo , allocation , vtok , vtok ) ;
}
2012-05-26 08:53:46 +02:00
}
2021-02-26 12:58:52 +01:00
ret ( endToken , * varInfo , true ) ;
2012-05-26 08:53:46 +02:00
}
2018-05-22 09:08:23 +02:00
2018-05-25 08:35:37 +02:00
const Token * CheckLeakAutoVar : : checkTokenInsideExpression ( const Token * const tok , VarInfo * varInfo )
{
2018-05-22 09:08:23 +02:00
// Deallocation and then dereferencing pointer..
if ( tok - > varId ( ) > 0 ) {
2018-11-20 22:57:01 +01:00
// TODO : Write a separate checker for this that uses valueFlowForward.
2019-07-16 08:54:21 +02:00
const std : : map < int , VarInfo : : AllocInfo > : : const_iterator var = varInfo - > alloctype . find ( tok - > varId ( ) ) ;
2018-06-21 06:54:56 +02:00
if ( var ! = varInfo - > alloctype . end ( ) ) {
2018-05-22 09:08:23 +02:00
bool unknown = false ;
2018-12-29 09:26:57 +01:00
if ( var - > second . status = = VarInfo : : DEALLOC & & CheckNullPointer : : isPointerDeRef ( tok , unknown , mSettings ) & & ! unknown ) {
2018-05-22 09:08:23 +02:00
deallocUseError ( tok , tok - > str ( ) ) ;
2018-05-25 08:35:37 +02:00
} else if ( Token : : simpleMatch ( tok - > tokAt ( - 2 ) , " = & " ) ) {
2018-05-22 09:08:23 +02:00
varInfo - > erase ( tok - > varId ( ) ) ;
2020-01-09 08:47:36 +01:00
} else {
// check if tok is assigned into another variable
const Token * rhs = tok ;
while ( rhs - > astParent ( ) ) {
if ( rhs - > astParent ( ) - > str ( ) = = " = " )
break ;
rhs = rhs - > astParent ( ) ;
}
2021-02-25 10:55:34 +01:00
while ( rhs - > isCast ( ) ) {
2022-09-02 07:51:10 +02:00
rhs = rhs - > astOperand2 ( ) ? rhs - > astOperand2 ( ) : rhs - > astOperand1 ( ) ;
2021-02-25 10:55:34 +01:00
}
2020-01-09 08:47:36 +01:00
if ( rhs - > varId ( ) = = tok - > varId ( ) ) {
// simple assignment
varInfo - > erase ( tok - > varId ( ) ) ;
2022-04-09 19:02:37 +02:00
} else if ( rhs - > str ( ) = = " ( " & & ! mSettings - > library . returnValue ( rhs - > astOperand1 ( ) ) . empty ( ) ) {
2020-01-09 08:47:36 +01:00
// #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 ( ) ) ;
}
}
}
}
2018-05-22 09:08:23 +02:00
}
2018-05-25 08:35:37 +02:00
} else if ( Token : : Match ( tok - > previous ( ) , " & %name% = %var% ; " )) {
2018-05-22 09:08:23 +02:00
varInfo - > referenced . insert ( tok - > tokAt ( 2 ) - > varId ( ) ) ;
}
}
// check for function call
const Token * const openingPar = isFunctionCall ( tok ) ;
if ( openingPar ) {
2019-07-05 12:44:52 +02:00
const Library : : AllocFunc * allocFunc = mSettings - > library . getDeallocFuncInfo ( tok ) ;
2019-09-22 21:50:02 +02:00
VarInfo : : AllocInfo alloc ( allocFunc ? allocFunc - > groupId : 0 , VarInfo : : DEALLOC , tok ) ;
2018-05-22 09:08:23 +02:00
if ( alloc . type = = 0 )
alloc . status = VarInfo : : NOALLOC ;
functionCall ( tok , openingPar , varInfo , alloc , nullptr ) ;
2020-02-17 10:25:30 +01:00
const std : : string & returnValue = mSettings - > library . returnValue ( tok ) ;
if ( returnValue . compare ( 0 , 3 , " arg " ) = = 0 )
// the function returns one of its argument, we need to process a potential assignment
return openingPar ;
2022-09-02 07:51:10 +02:00
return isCPPCast ( tok - > astParent ( ) ) ? openingPar : openingPar - > link ( ) ;
2018-05-22 09:08:23 +02:00
}
return nullptr ;
}
2019-07-16 08:54:21 +02:00
void CheckLeakAutoVar : : changeAllocStatusIfRealloc ( std : : map < int , VarInfo : : AllocInfo > & alloctype , const Token * fTok , const Token * retTok )
2019-07-05 12:44:52 +02:00
{
const Library : : AllocFunc * f = mSettings - > library . getReallocFuncInfo ( fTok ) ;
if ( f & & f - > arg = = - 1 & & f - > reallocArg > 0 & & f - > reallocArg < = numberOfArguments ( fTok ) ) {
const Token * argTok = getArguments ( fTok ) . at ( f - > reallocArg - 1 ) ;
2021-01-11 07:55:05 +01:00
if ( alloctype . find ( argTok - > varId ( ) ) ! = alloctype . end ( ) ) {
VarInfo : : AllocInfo & argAlloc = alloctype [ argTok - > varId ( ) ] ;
if ( argAlloc . type ! = 0 & & argAlloc . type ! = f - > groupId )
mismatchError ( fTok , argAlloc . allocTok , argTok - > str ( ) ) ;
argAlloc . status = VarInfo : : REALLOC ;
argAlloc . allocTok = fTok ;
}
2019-07-05 12:44:52 +02:00
VarInfo : : AllocInfo & retAlloc = alloctype [ retTok - > varId ( ) ] ;
retAlloc . type = f - > groupId ;
retAlloc . status = VarInfo : : ALLOC ;
2019-09-22 21:50:02 +02:00
retAlloc . allocTok = fTok ;
2021-01-11 07:55:05 +01:00
retAlloc . reallocedFromType = argTok - > varId ( ) ;
2019-07-05 12:44:52 +02:00
}
}
2015-01-28 13:45:40 +01:00
void CheckLeakAutoVar : : changeAllocStatus ( VarInfo * varInfo , const VarInfo : : AllocInfo & allocation , const Token * tok , const Token * arg )
{
2019-07-16 08:54:21 +02:00
std : : map < int , VarInfo : : AllocInfo > & alloctype = varInfo - > alloctype ;
const std : : map < int , VarInfo : : AllocInfo > : : iterator var = alloctype . find ( arg - > varId ( ) ) ;
2015-01-28 13:45:40 +01:00
if ( var ! = alloctype . end ( ) ) {
if ( allocation . status = = VarInfo : : NOALLOC ) {
// possible usage
2018-06-21 06:54:56 +02:00
varInfo - > possibleUsage [ arg - > varId ( ) ] = tok - > str ( ) ;
2015-01-28 13:45:40 +01:00
if ( var - > second . status = = VarInfo : : DEALLOC & & arg - > previous ( ) - > str ( ) = = " & " )
varInfo - > erase ( arg - > varId ( ) ) ;
2018-04-16 11:11:13 +02:00
} else if ( var - > second . managed ( ) ) {
2019-09-22 21:50:02 +02:00
doubleFreeError ( tok , var - > second . allocTok , arg - > str ( ) , allocation . type ) ;
2021-01-11 07:55:05 +01:00
var - > second . status = allocation . status ;
} else if ( var - > second . type ! = allocation . type & & var - > second . type ! = 0 ) {
2015-01-28 13:45:40 +01:00
// mismatching allocation and deallocation
2019-09-22 21:50:02 +02:00
mismatchError ( tok , var - > second . allocTok , arg - > str ( ) ) ;
2015-01-28 13:45:40 +01:00
varInfo - > erase ( arg - > varId ( ) ) ;
} else {
// deallocation
2018-04-16 11:11:13 +02:00
var - > second . status = allocation . status ;
2015-01-28 13:45:40 +01:00
var - > second . type = allocation . type ;
2019-09-22 21:50:02 +02:00
var - > second . allocTok = allocation . allocTok ;
2015-01-28 13:45:40 +01:00
}
2022-03-21 22:17:50 +01:00
} else if ( allocation . status ! = VarInfo : : NOALLOC & & allocation . status ! = VarInfo : : OWNED & & ! Token : : simpleMatch ( tok - > astTop ( ) , " return " ) ) {
2015-01-28 13:45:40 +01:00
alloctype [ arg - > varId ( ) ] . status = VarInfo : : DEALLOC ;
2019-09-22 21:50:02 +02:00
alloctype [ arg - > varId ( ) ] . allocTok = tok ;
2015-01-28 13:45:40 +01:00
}
}
2018-05-22 09:08:23 +02:00
void CheckLeakAutoVar : : functionCall ( const Token * tokName , const Token * tokOpeningPar , VarInfo * varInfo , const VarInfo : : AllocInfo & allocation , const Library : : AllocFunc * af )
2012-05-26 08:53:46 +02:00
{
2012-06-24 15:38:37 +02:00
// Ignore function call?
2022-04-28 19:50:16 +02:00
if ( mSettings - > library . isLeakIgnore ( mSettings - > library . getFunctionName ( tokName ) ) )
2012-06-24 15:38:37 +02:00
return ;
2019-07-05 12:44:52 +02:00
if ( mSettings - > library . getReallocFuncInfo ( tokName ) )
return ;
2012-06-24 15:38:37 +02:00
2018-05-22 09:08:23 +02:00
const Token * const tokFirstArg = tokOpeningPar - > next ( ) ;
if ( ! tokFirstArg | | tokFirstArg - > str ( ) = = " ) " ) {
// no arguments
return ;
}
2016-05-22 17:18:50 +02:00
int argNr = 1 ;
2020-07-07 23:46:24 +02:00
for ( const Token * funcArg = tokFirstArg ; funcArg ; funcArg = funcArg - > nextArgument ( ) ) {
const Token * arg = funcArg ;
2021-11-29 22:51:35 +01:00
if ( mTokenizer - > isCPP ( ) ) {
int tokAdvance = 0 ;
if ( arg - > str ( ) = = " new " )
tokAdvance = 1 ;
else if ( Token : : simpleMatch ( arg , " * new " ) )
tokAdvance = 2 ;
if ( tokAdvance > 0 ) {
arg = arg - > tokAt ( tokAdvance ) ;
if ( Token : : simpleMatch ( arg , " ( std :: nothrow ) " ) )
arg = arg - > tokAt ( 5 ) ;
}
2016-05-21 14:03:28 +02:00
}
2014-09-30 13:02:02 +02:00
2019-07-10 09:13:59 +02:00
// Skip casts
while ( arg & & arg - > isCast ( ) )
arg = arg - > astOperand2 ( ) ? arg - > astOperand2 ( ) : arg - > astOperand1 ( ) ;
2019-04-24 15:35:47 +02:00
const Token * const argTypeStartTok = arg ;
2018-05-22 09:08:23 +02:00
while ( Token : : Match ( arg , " %name% .|:: %name% " ) )
2017-07-07 21:51:48 +02:00
arg = arg - > tokAt ( 2 ) ;
2016-05-04 12:14:42 +02:00
if ( Token : : Match ( arg , " %var% [-,)] !!. " ) | | Token : : Match ( arg , " & %var% " ) ) {
2012-05-26 08:53:46 +02:00
// goto variable
if ( arg - > str ( ) = = " & " )
arg = arg - > next ( ) ;
2018-04-04 21:51:31 +02:00
const bool isnull = arg - > hasKnownIntValue ( ) & & arg - > values ( ) . front ( ) . intvalue = = 0 ;
2017-03-03 18:36:06 +01:00
2012-05-26 08:53:46 +02:00
// Is variable allocated?
2022-09-02 07:51:10 +02:00
if ( ! isnull & & ( ! af | | af - > arg = = argNr ) ) {
const Library : : AllocFunc * deallocFunc = mSettings - > library . getDeallocFuncInfo ( tokName ) ;
VarInfo : : AllocInfo dealloc ( deallocFunc ? deallocFunc - > groupId : 0 , VarInfo : : DEALLOC , tokName ) ;
if ( dealloc . type = = 0 )
changeAllocStatus ( varInfo , allocation , tokName , arg ) ;
else
changeAllocStatus ( varInfo , dealloc , tokName , arg ) ;
}
2018-05-22 09:08:23 +02:00
}
// Check smart pointer
2019-04-24 15:35:47 +02:00
else if ( Token : : Match ( arg , " %name% < %type% " ) & & mSettings - > library . isSmartPointer ( argTypeStartTok ) ) {
2018-05-22 09:08:23 +02:00
const Token * typeEndTok = arg - > linkAt ( 1 ) ;
2019-09-22 21:50:02 +02:00
const Token * allocTok = nullptr ;
2018-05-22 09:08:23 +02:00
if ( ! Token : : Match ( typeEndTok , " > {|( %var% ,|)|} " ) )
continue ;
bool arrayDelete = false ;
if ( Token : : findsimplematch ( arg - > next ( ) , " [ ] " , typeEndTok ) )
arrayDelete = true ;
// Check deleter
const Token * deleterToken = nullptr ;
const Token * endDeleterToken = nullptr ;
const Library : : AllocFunc * sp_af = nullptr ;
if ( Token : : Match ( arg , " unique_ptr < %type% , " ) ) {
deleterToken = arg - > tokAt ( 4 ) ;
endDeleterToken = typeEndTok ;
2018-05-25 08:35:37 +02:00
} else if ( Token : : Match ( typeEndTok , " > {|( %var% , " ) ) {
2018-05-22 09:08:23 +02:00
deleterToken = typeEndTok - > tokAt ( 4 ) ;
endDeleterToken = typeEndTok - > linkAt ( 1 ) ;
}
if ( deleterToken ) {
// Check if its a pointer to a function
const Token * dtok = Token : : findmatch ( deleterToken , " & %name% " , endDeleterToken ) ;
if ( dtok ) {
2019-07-05 12:44:52 +02:00
sp_af = mSettings - > library . getDeallocFuncInfo ( dtok - > tokAt ( 1 ) ) ;
2018-05-25 08:35:37 +02:00
} else {
2018-05-22 09:08:23 +02:00
// If the deleter is a class, check if class calls the dealloc function
dtok = Token : : findmatch ( deleterToken , " %type% " , endDeleterToken ) ;
if ( dtok & & dtok - > type ( ) ) {
const Scope * tscope = dtok - > type ( ) - > classScope ;
for ( const Token * tok2 = tscope - > bodyStart ; tok2 ! = tscope - > bodyEnd ; tok2 = tok2 - > next ( ) ) {
2019-07-05 12:44:52 +02:00
sp_af = mSettings - > library . getDeallocFuncInfo ( tok2 ) ;
2019-09-22 21:50:02 +02:00
if ( sp_af ) {
allocTok = tok2 ;
2018-05-22 09:08:23 +02:00
break ;
2019-09-22 21:50:02 +02:00
}
2018-05-22 09:08:23 +02:00
}
}
}
}
const Token * vtok = typeEndTok - > tokAt ( 2 ) ;
2019-09-22 21:50:02 +02:00
const VarInfo : : AllocInfo sp_allocation ( sp_af ? sp_af - > groupId : ( arrayDelete ? NEW_ARRAY : NEW ) , VarInfo : : OWNED , allocTok ) ;
2018-05-22 09:08:23 +02:00
changeAllocStatus ( varInfo , sp_allocation , vtok , vtok ) ;
} else {
checkTokenInsideExpression ( arg , varInfo ) ;
2012-05-26 08:53:46 +02:00
}
2018-05-22 09:08:23 +02:00
// TODO: check each token in argument expression (could contain multiple variables)
2016-05-22 17:18:50 +02:00
argNr + + ;
2012-05-26 08:53:46 +02:00
}
}
void CheckLeakAutoVar : : leakIfAllocated ( const Token * vartok ,
const VarInfo & varInfo )
{
2019-07-16 08:54:21 +02:00
const std : : map < int , VarInfo : : AllocInfo > & alloctype = varInfo . alloctype ;
const std : : map < int , std : : string > & possibleUsage = varInfo . possibleUsage ;
2012-05-26 08:53:46 +02:00
2019-07-16 08:54:21 +02:00
const std : : map < int , VarInfo : : AllocInfo > : : const_iterator var = alloctype . find ( vartok - > varId ( ) ) ;
2018-04-16 11:11:13 +02:00
if ( var ! = alloctype . end ( ) & & var - > second . status = = VarInfo : : ALLOC ) {
2019-07-16 08:54:21 +02:00
const std : : map < int , std : : string > : : const_iterator use = possibleUsage . find ( vartok - > varId ( ) ) ;
2012-05-26 08:53:46 +02:00
if ( use = = possibleUsage . end ( ) ) {
2015-01-28 13:45:40 +01:00
leakError ( vartok , vartok - > str ( ) , var - > second . type ) ;
2012-05-26 08:53:46 +02:00
} else {
configurationInfo ( vartok , use - > second ) ;
}
}
}
2021-02-26 12:58:52 +01:00
void CheckLeakAutoVar : : ret ( const Token * tok , VarInfo & varInfo , const bool isEndOfScope )
2012-05-26 08:53:46 +02:00
{
2019-07-16 08:54:21 +02:00
const std : : map < int , VarInfo : : AllocInfo > & alloctype = varInfo . alloctype ;
const std : : map < int , std : : string > & possibleUsage = varInfo . possibleUsage ;
2021-02-26 12:58:52 +01:00
std : : vector < int > toRemove ;
2012-05-26 08:53:46 +02:00
2018-06-16 16:10:28 +02:00
const SymbolDatabase * symbolDatabase = mTokenizer - > getSymbolDatabase ( ) ;
2019-07-16 08:54:21 +02:00
for ( std : : map < int , VarInfo : : AllocInfo > : : const_iterator it = alloctype . begin ( ) ; it ! = alloctype . end ( ) ; + + it ) {
2021-02-26 12:58:52 +01:00
// don't warn if variable is conditionally allocated, unless it leaves the scope
if ( ! isEndOfScope & & ! it - > second . managed ( ) & & varInfo . conditionalAlloc . find ( it - > first ) ! = varInfo . conditionalAlloc . end ( ) )
2012-05-26 08:53:46 +02:00
continue ;
2012-10-27 16:36:14 +02:00
// don't warn if there is a reference of the variable
if ( varInfo . referenced . find ( it - > first ) ! = varInfo . referenced . end ( ) )
continue ;
2019-07-16 08:54:21 +02:00
const int varid = it - > first ;
2012-05-26 08:53:46 +02:00
const Variable * var = symbolDatabase - > getVariableFromVarId ( varid ) ;
if ( var ) {
2021-02-26 12:58:52 +01:00
// don't warn if we leave an inner scope
if ( isEndOfScope & & var - > scope ( ) & & tok ! = var - > scope ( ) - > bodyEnd )
continue ;
2012-05-26 08:53:46 +02:00
bool used = false ;
for ( const Token * tok2 = tok ; tok2 ; tok2 = tok2 - > next ( ) ) {
2019-09-22 19:18:31 +02:00
if ( tok2 - > str ( ) = = " ; " )
2012-05-26 08:53:46 +02:00
break ;
2019-09-20 15:09:27 +02:00
if ( ! Token : : Match ( tok2 , " return|(|{|, " ) )
continue ;
2019-09-22 19:18:31 +02:00
const Token * tok3 = tok2 - > next ( ) ;
while ( tok3 & & tok3 - > isCast ( ) & & tok3 - > valueType ( ) & &
( tok3 - > valueType ( ) - > pointer | |
( tok3 - > valueType ( ) - > typeSize ( * mSettings ) = = 0 ) | |
( tok3 - > valueType ( ) - > typeSize ( * mSettings ) > = mSettings - > sizeof_pointer ) ) )
tok3 = tok3 - > astOperand2 ( ) ? tok3 - > astOperand2 ( ) : tok3 - > astOperand1 ( ) ;
2022-06-08 16:58:57 +02:00
if ( tok3 & & tok3 - > varId ( ) = = varid )
2019-09-22 19:18:31 +02:00
tok2 = tok3 - > next ( ) ;
else if ( Token : : Match ( tok3 , " & %varid% . %name% " , varid ) )
tok2 = tok3 - > tokAt ( 4 ) ;
2019-09-20 15:09:27 +02:00
else
continue ;
2022-03-10 08:29:05 +01:00
if ( Token : : Match ( tok2 , " [});,+] " ) ) {
2012-05-26 08:53:46 +02:00
used = true ;
break ;
}
}
2012-06-11 18:28:31 +02:00
// return deallocated pointer
2015-01-28 13:45:40 +01:00
if ( used & & it - > second . status = = VarInfo : : DEALLOC )
2019-09-22 21:50:02 +02:00
deallocReturnError ( tok , it - > second . allocTok , var - > name ( ) ) ;
2012-06-11 18:28:31 +02:00
2018-04-16 11:11:13 +02:00
else if ( ! used & & ! it - > second . managed ( ) ) {
2019-07-16 08:54:21 +02:00
const std : : map < int , std : : string > : : const_iterator use = possibleUsage . find ( varid ) ;
2012-06-11 18:28:31 +02:00
if ( use = = possibleUsage . end ( ) ) {
2015-01-28 13:45:40 +01:00
leakError ( tok , var - > name ( ) , it - > second . type ) ;
2012-06-11 18:28:31 +02:00
} else {
configurationInfo ( tok , use - > second ) ;
}
2012-05-26 08:53:46 +02:00
}
2021-02-26 12:58:52 +01:00
toRemove . push_back ( varid ) ;
2012-05-26 08:53:46 +02:00
}
}
2022-10-02 07:12:40 +02:00
for ( const int varId : toRemove )
2021-02-26 12:58:52 +01:00
varInfo . erase ( varId ) ;
2012-05-26 08:53:46 +02:00
}