2009-03-19 20:55:50 +01:00
/*
* Cppcheck - A tool for static C / C + + code analysis
2019-02-09 07:24:06 +01:00
* Copyright ( C ) 2007 - 2019 Cppcheck team .
2009-03-19 20:55:50 +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-03-19 20:55:50 +01:00
*/
//---------------------------------------------------------------------------
2009-05-22 07:51:30 +02:00
// Auto variables checks
2009-03-19 20:55:50 +01:00
//---------------------------------------------------------------------------
# include "checkautovariables.h"
2017-05-27 04:33:47 +02:00
2017-08-29 22:35:55 +02:00
# include "astutils.h"
2017-05-27 04:33:47 +02:00
# include "errorlogger.h"
# include "library.h"
# include "settings.h"
2011-01-16 19:57:29 +01:00
# include "symboldatabase.h"
2017-05-27 04:33:47 +02:00
# include "token.h"
# include "tokenize.h"
# include "valueflow.h"
2009-03-19 20:55:50 +01:00
2017-05-27 04:33:47 +02:00
# include <cstddef>
# include <list>
2018-11-10 16:40:40 +01:00
# include <functional>
2009-03-19 20:55:50 +01:00
//---------------------------------------------------------------------------
// Register this check class into cppcheck by creating a static instance of it..
2011-10-13 20:53:06 +02:00
namespace {
2017-09-07 13:01:07 +02:00
CheckAutoVariables instance ;
2009-03-19 20:55:50 +01:00
}
Mapped toomanyconfigs ,AssignmentAddressToInteger
,AssignmentIntegerToAddress ,CastIntegerToAddressAtReturn
,CastAddressToIntegerAtReturn ,assertWithSideEffect ,assignmentInAssert
,uselessAssignmentArg ,uselessAssignmentPtrArg
,comparisonOfFuncReturningBoolError
,comparisonOfTwoFuncsReturningBoolError ,comparisonOfBoolWithBoolError
,incrementboolean ,comparisonOfBoolWithInt ,compareBoolExpressionWithInt
,negativeIndex ,pointerOutOfBounds ,arrayIndexThenCheck
,possibleBufferAccessOutOfBounds ,argumentSize
,arrayIndexOutOfBoundsCond ,noConstructor ,copyCtorPointerCopying
,noCopyConstructor ,uninitMemberVar ,operatorEqVarError
,unusedPrivateFunction ,memsetClassFloat ,mallocOnClassWarning
,operatorEq ,thisSubtraction ,operatorEqRetRefThis ,operatorEqToSelf
,useInitializationList ,duplInheritedMember ,assignIfError
,comparisonError ,multiCondition ,mismatchingBitAnd
,oppositeInnerCondition ,incorrectLogicOperator ,redundantCondition
,moduloAlwaysTrueFalse to their CWEs ids.
2016-02-20 23:56:36 +01:00
static const CWE CWE398 ( 398U ) ; // Indicator of Poor Code Quality
static const CWE CWE562 ( 562U ) ; // Return of Stack Variable Address
static const CWE CWE590 ( 590U ) ; // Free of Memory not on the Heap
2009-03-19 20:55:50 +01:00
2018-01-24 21:33:58 +01:00
static bool isPtrArg ( const Token * tok )
2013-02-18 17:52:49 +01:00
{
const Variable * var = tok - > variable ( ) ;
2013-08-07 16:27:37 +02:00
return ( var & & var - > isArgument ( ) & & var - > isPointer ( ) ) ;
2013-02-18 17:52:49 +01:00
}
2018-01-24 21:33:58 +01:00
static bool isArrayArg ( const Token * tok )
{
const Variable * var = tok - > variable ( ) ;
2015-08-25 21:19:19 +02:00
return ( var & & var - > isArgument ( ) & & var - > isArray ( ) ) ;
}
2018-11-03 18:55:12 +01:00
static bool isArrayVar ( const Token * tok )
{
const Variable * var = tok - > variable ( ) ;
return ( var & & var - > isArray ( ) ) ;
}
2018-01-24 21:33:58 +01:00
static bool isRefPtrArg ( const Token * tok )
2009-03-19 20:55:50 +01:00
{
2013-01-31 20:08:48 +01:00
const Variable * var = tok - > variable ( ) ;
2013-08-07 16:27:37 +02:00
return ( var & & var - > isArgument ( ) & & var - > isReference ( ) & & var - > isPointer ( ) ) ;
2012-03-01 18:38:20 +01:00
}
2018-01-24 21:33:58 +01:00
static bool isNonReferenceArg ( const Token * tok )
2012-03-01 18:38:20 +01:00
{
2013-01-31 20:08:48 +01:00
const Variable * var = tok - > variable ( ) ;
2020-01-25 16:09:58 +01:00
//if (var && !var->valueType())
// throw InternalError(tok, "var without valueType");
return ( var & & var - > isArgument ( ) & & ! var - > isReference ( ) & & ( var - > isPointer ( ) | | ( var - > valueType ( ) & & var - > valueType ( ) - > type > = ValueType : : Type : : CONTAINER ) | | var - > type ( ) ) ) ;
2009-03-19 20:55:50 +01:00
}
2009-07-27 19:34:17 +02:00
2018-01-24 21:33:58 +01:00
static bool isAutoVar ( const Token * tok )
2009-03-19 20:55:50 +01:00
{
2013-01-31 20:08:48 +01:00
const Variable * var = tok - > variable ( ) ;
2009-08-16 11:43:04 +02:00
2012-03-01 18:38:20 +01:00
if ( ! var | | ! var - > isLocal ( ) | | var - > isStatic ( ) )
2009-08-16 11:43:04 +02:00
return false ;
2012-01-28 12:32:28 +01:00
if ( var - > isReference ( ) ) {
2011-08-10 18:16:31 +02:00
// address of reference variable can be taken if the address
// of the variable it points at is not a auto-var
// TODO: check what the reference variable references.
return false ;
}
2015-11-11 16:59:13 +01:00
if ( Token : : Match ( tok , " %name% .|:: " ) ) {
do {
tok = tok - > tokAt ( 2 ) ;
} while ( Token : : Match ( tok , " %name% .|:: " ) ) ;
if ( Token : : Match ( tok , " %name% ( " ) )
return false ;
}
2011-04-23 15:50:56 +02:00
return true ;
2009-08-09 15:40:04 +02:00
}
2018-01-24 21:33:58 +01:00
static bool isAutoVarArray ( const Token * tok )
2009-03-21 18:36:41 +01:00
{
2015-11-15 14:48:13 +01:00
if ( ! tok )
return false ;
// &x[..]
2018-07-13 23:02:52 +02:00
if ( tok - > isUnaryOp ( " & " ) & & Token : : simpleMatch ( tok - > astOperand1 ( ) , " [ " ) )
2015-11-15 14:48:13 +01:00
return isAutoVarArray ( tok - > astOperand1 ( ) - > astOperand1 ( ) ) ;
// x+y
if ( tok - > str ( ) = = " + " )
return isAutoVarArray ( tok - > astOperand1 ( ) ) | | isAutoVarArray ( tok - > astOperand2 ( ) ) ;
2015-11-15 19:34:36 +01:00
// x-intexpr
if ( tok - > str ( ) = = " - " )
return isAutoVarArray ( tok - > astOperand1 ( ) ) & &
tok - > astOperand2 ( ) & &
tok - > astOperand2 ( ) - > valueType ( ) & &
tok - > astOperand2 ( ) - > valueType ( ) - > isIntegral ( ) ;
2013-01-31 20:08:48 +01:00
const Variable * var = tok - > variable ( ) ;
2015-11-15 14:48:13 +01:00
if ( ! var )
return false ;
// Variable
if ( var - > isLocal ( ) & & ! var - > isStatic ( ) & & var - > isArray ( ) & & ! var - > isPointer ( ) )
2015-11-15 12:10:35 +01:00
return true ;
// ValueFlow
2016-01-30 20:02:39 +01:00
if ( var - > isPointer ( ) & & ! var - > isArgument ( ) ) {
2017-03-27 18:48:34 +02:00
for ( std : : list < ValueFlow : : Value > : : const_iterator it = tok - > values ( ) . begin ( ) ; it ! = tok - > values ( ) . end ( ) ; + + it ) {
2015-11-15 12:10:35 +01:00
const ValueFlow : : Value & val = * it ;
2016-11-13 22:33:39 +01:00
if ( val . isTokValue ( ) & & isAutoVarArray ( val . tokvalue ) )
2015-11-15 12:10:35 +01:00
return true ;
}
}
2009-07-27 19:34:17 +02:00
2015-11-15 12:10:35 +01:00
return false ;
2012-03-01 18:38:20 +01:00
}
2009-07-27 19:34:17 +02:00
2012-03-01 18:38:20 +01:00
// Verification that we really take the address of a local variable
2013-06-13 16:19:19 +02:00
static bool checkRvalueExpression ( const Token * const vartok )
2012-03-01 18:38:20 +01:00
{
2013-06-13 16:19:19 +02:00
const Variable * const var = vartok - > variable ( ) ;
2014-02-16 10:32:10 +01:00
if ( var = = nullptr )
2013-06-13 16:19:19 +02:00
return false ;
2015-01-31 10:50:39 +01:00
if ( Token : : Match ( vartok - > previous ( ) , " & %name% [ " ) & & var - > isPointer ( ) )
2013-10-06 14:23:26 +02:00
return false ;
2014-09-01 08:05:59 +02:00
const Token * const next = vartok - > next ( ) ;
2013-06-13 16:19:19 +02:00
// &a.b[0]
2015-01-31 10:50:39 +01:00
if ( Token : : Match ( vartok , " %name% . %var% [ " ) & & ! var - > isPointer ( ) ) {
2013-06-13 16:19:19 +02:00
const Variable * var2 = next - > next ( ) - > variable ( ) ;
return var2 & & ! var2 - > isPointer ( ) ;
}
2013-08-07 16:27:37 +02:00
return ( ( next - > str ( ) ! = " . " | | ( ! var - > isPointer ( ) & & ( ! var - > isClass ( ) | | var - > type ( ) ) ) ) & & next - > strAt ( 2 ) ! = " . " ) ;
2009-08-09 15:40:04 +02:00
}
2019-03-14 13:51:35 +01:00
static bool isAddressOfLocalVariable ( const Token * expr )
2018-12-01 10:11:02 +01:00
{
if ( ! expr )
return false ;
if ( Token : : Match ( expr , " +|- " ) )
2019-03-14 13:51:35 +01:00
return isAddressOfLocalVariable ( expr - > astOperand1 ( ) ) | | isAddressOfLocalVariable ( expr - > astOperand2 ( ) ) ;
2019-03-14 06:41:11 +01:00
if ( expr - > isCast ( ) )
2019-03-14 13:51:35 +01:00
return isAddressOfLocalVariable ( expr - > astOperand2 ( ) ? expr - > astOperand2 ( ) : expr - > astOperand1 ( ) ) ;
2019-03-14 06:41:11 +01:00
if ( expr - > isUnaryOp ( " & " ) ) {
2018-12-01 10:11:02 +01:00
const Token * op = expr - > astOperand1 ( ) ;
bool deref = false ;
while ( Token : : Match ( op , " .|[ " ) ) {
2019-03-14 06:41:11 +01:00
if ( op - > originalName ( ) = = " -> " )
return false ;
if ( op - > str ( ) = = " [ " )
2018-12-01 10:11:02 +01:00
deref = true ;
op = op - > astOperand1 ( ) ;
}
return op & & isAutoVar ( op ) & & ( ! deref | | ! op - > variable ( ) - > isPointer ( ) ) ;
}
return false ;
}
2019-07-15 13:33:29 +02:00
static bool variableIsUsedInScope ( const Token * start , nonneg int varId , const Scope * scope )
2013-02-01 19:16:17 +01:00
{
2013-09-28 09:32:41 +02:00
if ( ! start ) // Ticket #5024
2013-09-28 00:14:12 +02:00
return false ;
2013-09-28 09:32:41 +02:00
2018-04-27 22:36:30 +02:00
for ( const Token * tok = start ; tok & & tok ! = scope - > bodyEnd ; tok = tok - > next ( ) ) {
2013-02-18 17:52:49 +01:00
if ( tok - > varId ( ) = = varId )
return true ;
2014-07-17 08:44:19 +02:00
const Scope : : ScopeType scopeType = tok - > scope ( ) - > type ;
if ( scopeType = = Scope : : eFor | | scopeType = = Scope : : eDo | | scopeType = = Scope : : eWhile ) // In case of loops, better checking would be necessary
2013-02-18 17:52:49 +01:00
return true ;
if ( Token : : simpleMatch ( tok , " asm ( " ) )
2013-02-01 19:16:17 +01:00
return true ;
}
return false ;
}
2013-10-06 16:07:27 +02:00
void CheckAutoVariables : : assignFunctionArg ( )
2009-03-19 20:55:50 +01:00
{
2018-06-16 16:10:28 +02:00
const bool printStyle = mSettings - > isEnabled ( Settings : : STYLE ) ;
const bool printWarning = mSettings - > isEnabled ( Settings : : WARNING ) ;
2015-04-10 14:18:52 +02:00
if ( ! printStyle & & ! printWarning )
2013-10-06 16:07:27 +02:00
return ;
2014-08-04 11:45:24 +02:00
2018-06-16 16:10:28 +02:00
const SymbolDatabase * symbolDatabase = mTokenizer - > getSymbolDatabase ( ) ;
2018-04-20 22:13:05 +02:00
for ( const Scope * scope : symbolDatabase - > functionScopes ) {
2018-04-27 22:36:30 +02:00
for ( const Token * tok = scope - > bodyStart ; tok & & tok ! = scope - > bodyEnd ; tok = tok - > next ( ) ) {
2016-12-19 15:25:36 +01:00
// TODO: What happens if this is removed?
if ( tok - > astParent ( ) )
continue ;
2018-08-17 19:56:36 +02:00
if ( ! ( tok - > isAssignmentOp ( ) | | Token : : Match ( tok , " ++|-- " ) ) | | ! Token : : Match ( tok - > astOperand1 ( ) , " %var% " ) )
2016-12-19 15:25:36 +01:00
continue ;
const Token * const vartok = tok - > astOperand1 ( ) ;
if ( isNonReferenceArg ( vartok ) & &
! Token : : Match ( vartok - > next ( ) , " = %varid% ; " , vartok - > varId ( ) ) & &
! variableIsUsedInScope ( Token : : findsimplematch ( vartok - > next ( ) , " ; " ) , vartok - > varId ( ) , scope ) & &
2018-04-27 22:36:30 +02:00
! Token : : findsimplematch ( vartok , " goto " , scope - > bodyEnd ) ) {
2016-12-19 15:25:36 +01:00
if ( vartok - > variable ( ) - > isPointer ( ) & & printWarning )
errorUselessAssignmentPtrArg ( vartok ) ;
else if ( printStyle )
errorUselessAssignmentArg ( vartok ) ;
2013-10-06 16:07:27 +02:00
}
}
}
}
2009-03-19 20:55:50 +01:00
2013-10-06 16:07:27 +02:00
void CheckAutoVariables : : autoVariables ( )
{
2018-06-16 16:10:28 +02:00
const bool printInconclusive = mSettings - > inconclusive ;
const SymbolDatabase * symbolDatabase = mTokenizer - > getSymbolDatabase ( ) ;
2018-04-20 22:13:05 +02:00
for ( const Scope * scope : symbolDatabase - > functionScopes ) {
2018-04-27 22:36:30 +02:00
for ( const Token * tok = scope - > bodyStart ; tok & & tok ! = scope - > bodyEnd ; tok = tok - > next ( ) ) {
2018-11-11 07:52:38 +01:00
// Skip lambda..
if ( const Token * lambdaEndToken = findLambdaEndToken ( tok ) ) {
tok = lambdaEndToken ;
continue ;
}
2012-03-01 18:38:20 +01:00
// Critical assignment
2013-01-31 20:08:48 +01:00
if ( Token : : Match ( tok , " [;{}] %var% = & %var% " ) & & isRefPtrArg ( tok - > next ( ) ) & & isAutoVar ( tok - > tokAt ( 4 ) ) ) {
2013-06-13 16:19:19 +02:00
if ( checkRvalueExpression ( tok - > tokAt ( 4 ) ) )
2012-03-01 18:38:20 +01:00
errorAutoVariableAssignment ( tok - > next ( ) , false ) ;
2018-12-01 10:11:02 +01:00
} else if ( Token : : Match ( tok , " [;{}] * %var% = " ) & & isPtrArg ( tok - > tokAt ( 2 ) ) & & isAddressOfLocalVariable ( tok - > tokAt ( 3 ) - > astOperand2 ( ) ) ) {
errorAutoVariableAssignment ( tok - > next ( ) , false ) ;
2019-03-14 13:51:35 +01:00
} else if ( Token : : Match ( tok , " [;{}] %var% . %var% = " ) & & isPtrArg ( tok - > next ( ) ) & & isAddressOfLocalVariable ( tok - > tokAt ( 4 ) - > astOperand2 ( ) ) ) {
errorAutoVariableAssignment ( tok - > next ( ) , false ) ;
2011-10-13 20:53:06 +02:00
} else if ( Token : : Match ( tok , " [;{}] %var% . %var% = %var% ; " ) ) {
2011-08-09 18:24:39 +02:00
// TODO: check if the parameter is only changed temporarily (#2969)
2015-09-09 10:08:37 +02:00
if ( printInconclusive & & isPtrArg ( tok - > next ( ) ) ) {
if ( isAutoVarArray ( tok - > tokAt ( 5 ) ) )
errorAutoVariableAssignment ( tok - > next ( ) , true ) ;
2011-07-22 14:31:31 +02:00
}
tok = tok - > tokAt ( 5 ) ;
2011-10-13 20:53:06 +02:00
} else if ( Token : : Match ( tok , " [;{}] * %var% = %var% ; " ) ) {
2013-01-31 20:08:48 +01:00
const Variable * var1 = tok - > tokAt ( 2 ) - > variable ( ) ;
2011-10-13 20:53:06 +02:00
if ( var1 & & var1 - > isArgument ( ) & & Token : : Match ( var1 - > nameToken ( ) - > tokAt ( - 3 ) , " %type% * * " ) ) {
2013-01-31 20:08:48 +01:00
if ( isAutoVarArray ( tok - > tokAt ( 4 ) ) )
2011-09-02 02:34:31 +02:00
errorAutoVariableAssignment ( tok - > next ( ) , false ) ;
2011-07-21 01:02:54 +02:00
}
tok = tok - > tokAt ( 4 ) ;
2019-03-14 13:51:35 +01:00
} else if ( Token : : Match ( tok , " [;{}] %var% [ " ) & & Token : : simpleMatch ( tok - > linkAt ( 2 ) , " ] = " ) & &
( isPtrArg ( tok - > next ( ) ) | | isArrayArg ( tok - > next ( ) ) ) & & isAddressOfLocalVariable ( tok - > linkAt ( 2 ) - > next ( ) - > astOperand2 ( ) ) ) {
errorAutoVariableAssignment ( tok - > next ( ) , false ) ;
2011-02-08 01:26:34 +01:00
}
// Invalid pointer deallocation
2019-07-05 12:44:52 +02:00
else if ( ( Token : : Match ( tok , " %name% ( %var% ) ; " ) && mSettings->library.getDeallocFuncInfo(tok)) ||
2018-06-16 16:10:28 +02:00
( mTokenizer - > isCPP ( ) & & Token : : Match ( tok , " delete [| ]| (| %var% !![ " ) ) ) {
2012-04-26 16:44:33 +02:00
tok = Token : : findmatch ( tok - > next ( ) , " %var% " ) ;
2018-11-03 18:55:12 +01:00
if ( isArrayVar ( tok ) )
errorInvalidDeallocation ( tok , nullptr ) ;
else if ( tok & & tok - > variable ( ) & & tok - > variable ( ) - > isPointer ( ) ) {
for ( const ValueFlow : : Value & v : tok - > values ( ) ) {
if ( v . isTokValue ( ) & & isArrayVar ( v . tokvalue ) ) {
errorInvalidDeallocation ( tok , & v ) ;
break ;
}
}
}
2019-07-05 12:44:52 +02:00
} else if ( ( Token : : Match ( tok , " %name% ( & %var% ) ; " ) && mSettings->library.getDeallocFuncInfo(tok)) ||
2018-06-16 16:10:28 +02:00
( mTokenizer - > isCPP ( ) & & Token : : Match ( tok , " delete [| ]| (| & %var% !![ " ) ) ) {
2014-03-06 06:32:30 +01:00
tok = Token : : findmatch ( tok - > next ( ) , " %var% " ) ;
2014-05-12 19:53:49 +02:00
if ( isAutoVar ( tok ) )
2018-11-03 18:55:12 +01:00
errorInvalidDeallocation ( tok , nullptr ) ;
2014-03-06 06:32:30 +01:00
}
2009-06-09 19:45:58 +02:00
}
2011-02-08 01:26:34 +01:00
}
}
//---------------------------------------------------------------------------
2011-07-21 14:50:38 +02:00
void CheckAutoVariables : : errorReturnAddressToAutoVariable ( const Token * tok )
{
2016-01-25 20:01:48 +01:00
reportError ( tok , Severity : : error , " returnAddressOfAutoVariable " , " Address of an auto-variable returned. " , CWE562 , false ) ;
2011-07-21 14:50:38 +02:00
}
2018-01-27 14:48:45 +01:00
void CheckAutoVariables : : errorReturnAddressToAutoVariable ( const Token * tok , const ValueFlow : : Value * value )
{
reportError ( tok , Severity : : error , " returnAddressOfAutoVariable " , " Address of auto-variable ' " + value - > tokvalue - > astOperand1 ( ) - > expressionString ( ) + " ' returned " , CWE562 , false ) ;
}
2009-06-09 19:45:58 +02:00
void CheckAutoVariables : : errorReturnPointerToLocalArray ( const Token * tok )
{
2016-01-25 20:01:48 +01:00
reportError ( tok , Severity : : error , " returnLocalVariable " , " Pointer to local array variable returned. " , CWE562 , false ) ;
2009-06-09 19:45:58 +02:00
}
2011-08-09 18:24:39 +02:00
void CheckAutoVariables : : errorAutoVariableAssignment ( const Token * tok , bool inconclusive )
2009-10-04 11:45:45 +02:00
{
2011-10-13 20:53:06 +02:00
if ( ! inconclusive ) {
2019-03-15 15:13:11 +01:00
reportError ( tok , Severity : : error , " autoVariables " ,
2012-07-07 20:31:18 +02:00
" Address of local auto-variable assigned to a function parameter. \n "
2012-07-08 11:38:58 +02:00
" Dangerous assignment - the function parameter is assigned the address of a local "
2012-07-07 20:31:18 +02:00
" auto-variable. Local auto-variables are reserved from the stack which "
" is freed when the function ends. So the pointer to a local variable "
2016-01-25 20:01:48 +01:00
" is invalid after the function ends. " , CWE562 , false ) ;
2011-10-13 20:53:06 +02:00
} else {
2019-03-15 15:13:11 +01:00
reportError ( tok , Severity : : error , " autoVariables " ,
2012-07-07 20:31:18 +02:00
" Address of local auto-variable assigned to a function parameter. \n "
" Function parameter is assigned the address of a local auto-variable. "
" Local auto-variables are reserved from the stack which is freed when "
2012-05-06 19:37:41 +02:00
" the function ends. The address is invalid after the function ends and it "
2015-04-25 17:48:11 +02:00
" might 'leak' from the function through the parameter. " ,
2016-01-25 20:01:48 +01:00
CWE562 ,
2015-04-25 17:48:11 +02:00
true ) ;
2011-08-09 18:24:39 +02:00
}
2009-10-04 11:45:45 +02:00
}
2009-03-19 20:55:50 +01:00
2011-09-01 03:36:31 +02:00
void CheckAutoVariables : : errorReturnAddressOfFunctionParameter ( const Token * tok , const std : : string & varname )
{
reportError ( tok , Severity : : error , " returnAddressOfFunctionParameter " ,
2018-04-09 06:43:48 +02:00
" $symbol: " + varname + " \n "
" Address of function parameter '$symbol' returned. \n "
" Address of the function parameter '$symbol' becomes invalid after the function exits because "
2012-07-07 20:31:18 +02:00
" function parameters are stored on the stack which is freed when the function exits. Thus the returned "
2016-01-25 20:01:48 +01:00
" value is invalid. " , CWE562 , false ) ;
2011-09-01 03:36:31 +02:00
}
2014-08-04 11:45:24 +02:00
void CheckAutoVariables : : errorUselessAssignmentArg ( const Token * tok )
{
reportError ( tok ,
Severity : : style ,
" uselessAssignmentArg " ,
Mapped toomanyconfigs ,AssignmentAddressToInteger
,AssignmentIntegerToAddress ,CastIntegerToAddressAtReturn
,CastAddressToIntegerAtReturn ,assertWithSideEffect ,assignmentInAssert
,uselessAssignmentArg ,uselessAssignmentPtrArg
,comparisonOfFuncReturningBoolError
,comparisonOfTwoFuncsReturningBoolError ,comparisonOfBoolWithBoolError
,incrementboolean ,comparisonOfBoolWithInt ,compareBoolExpressionWithInt
,negativeIndex ,pointerOutOfBounds ,arrayIndexThenCheck
,possibleBufferAccessOutOfBounds ,argumentSize
,arrayIndexOutOfBoundsCond ,noConstructor ,copyCtorPointerCopying
,noCopyConstructor ,uninitMemberVar ,operatorEqVarError
,unusedPrivateFunction ,memsetClassFloat ,mallocOnClassWarning
,operatorEq ,thisSubtraction ,operatorEqRetRefThis ,operatorEqToSelf
,useInitializationList ,duplInheritedMember ,assignIfError
,comparisonError ,multiCondition ,mismatchingBitAnd
,oppositeInnerCondition ,incorrectLogicOperator ,redundantCondition
,moduloAlwaysTrueFalse to their CWEs ids.
2016-02-20 23:56:36 +01:00
" Assignment of function parameter has no effect outside the function. " , CWE398 , false ) ;
2014-08-04 11:45:24 +02:00
}
2013-02-01 19:16:17 +01:00
void CheckAutoVariables : : errorUselessAssignmentPtrArg ( const Token * tok )
{
reportError ( tok ,
Severity : : warning ,
" uselessAssignmentPtrArg " ,
Mapped toomanyconfigs ,AssignmentAddressToInteger
,AssignmentIntegerToAddress ,CastIntegerToAddressAtReturn
,CastAddressToIntegerAtReturn ,assertWithSideEffect ,assignmentInAssert
,uselessAssignmentArg ,uselessAssignmentPtrArg
,comparisonOfFuncReturningBoolError
,comparisonOfTwoFuncsReturningBoolError ,comparisonOfBoolWithBoolError
,incrementboolean ,comparisonOfBoolWithInt ,compareBoolExpressionWithInt
,negativeIndex ,pointerOutOfBounds ,arrayIndexThenCheck
,possibleBufferAccessOutOfBounds ,argumentSize
,arrayIndexOutOfBoundsCond ,noConstructor ,copyCtorPointerCopying
,noCopyConstructor ,uninitMemberVar ,operatorEqVarError
,unusedPrivateFunction ,memsetClassFloat ,mallocOnClassWarning
,operatorEq ,thisSubtraction ,operatorEqRetRefThis ,operatorEqToSelf
,useInitializationList ,duplInheritedMember ,assignIfError
,comparisonError ,multiCondition ,mismatchingBitAnd
,oppositeInnerCondition ,incorrectLogicOperator ,redundantCondition
,moduloAlwaysTrueFalse to their CWEs ids.
2016-02-20 23:56:36 +01:00
" Assignment of function parameter has no effect outside the function. Did you forget dereferencing it? " , CWE398 , false ) ;
2013-02-01 19:16:17 +01:00
}
2011-02-08 01:26:34 +01:00
//---------------------------------------------------------------------------
2014-05-24 12:32:44 +02:00
static bool astHasAutoResult ( const Token * tok )
{
if ( tok - > astOperand1 ( ) & & ! astHasAutoResult ( tok - > astOperand1 ( ) ) )
return false ;
if ( tok - > astOperand2 ( ) & & ! astHasAutoResult ( tok - > astOperand2 ( ) ) )
return false ;
2015-01-31 10:12:20 +01:00
if ( tok - > isOp ( ) ) {
2016-12-22 11:49:59 +01:00
if ( tok - > tokType ( ) = = Token : : eIncDecOp )
return false ;
2015-01-31 10:12:20 +01:00
if ( ( tok - > str ( ) = = " << " | | tok - > str ( ) = = " >> " ) & & tok - > astOperand1 ( ) ) {
const Token * tok2 = tok - > astOperand1 ( ) ;
2018-07-13 23:02:52 +02:00
while ( tok2 & & tok2 - > isUnaryOp ( " * " ) )
2015-01-31 10:12:20 +01:00
tok2 = tok2 - > astOperand1 ( ) ;
return tok2 & & tok2 - > variable ( ) & & ! tok2 - > variable ( ) - > isClass ( ) & & ! tok2 - > variable ( ) - > isStlType ( ) ; // Class or unknown type on LHS: Assume it is a stream
}
2014-05-24 12:32:44 +02:00
return true ;
2015-01-31 10:12:20 +01:00
}
2014-05-24 12:32:44 +02:00
if ( tok - > isLiteral ( ) )
return true ;
if ( tok - > isName ( ) ) {
// TODO: check function calls, struct members, arrays, etc also
if ( ! tok - > variable ( ) )
return false ;
2015-01-31 10:12:20 +01:00
if ( tok - > variable ( ) - > isStlType ( ) )
2014-05-24 12:32:44 +02:00
return true ;
if ( tok - > variable ( ) - > isClass ( ) | | tok - > variable ( ) - > isPointer ( ) | | tok - > variable ( ) - > isReference ( ) ) // TODO: Properly handle pointers/references to classes in symbol database
return false ;
return true ;
}
return false ;
}
2018-11-10 16:40:40 +01:00
static bool isInScope ( const Token * tok , const Scope * scope )
{
2018-11-10 21:30:01 +01:00
if ( ! tok )
2018-11-10 16:40:40 +01:00
return false ;
2018-11-10 21:30:01 +01:00
if ( ! scope )
2018-11-10 16:40:40 +01:00
return false ;
const Variable * var = tok - > variable ( ) ;
2018-11-10 21:30:01 +01:00
if ( var & & ( var - > isGlobal ( ) | | var - > isStatic ( ) | | var - > isExtern ( ) ) )
2018-11-10 16:40:40 +01:00
return false ;
2018-11-10 21:30:01 +01:00
if ( tok - > scope ( ) & & tok - > scope ( ) - > isNestedIn ( scope ) )
2018-11-10 16:40:40 +01:00
return true ;
2018-11-10 21:30:01 +01:00
if ( ! var )
2018-11-10 16:40:40 +01:00
return false ;
2018-11-10 21:30:01 +01:00
if ( var - > isArgument ( ) & & ! var - > isReference ( ) ) {
2018-11-10 16:40:40 +01:00
const Scope * tokScope = tok - > scope ( ) ;
2018-11-10 21:30:01 +01:00
if ( ! tokScope )
2018-11-10 16:40:40 +01:00
return false ;
2018-11-10 21:30:01 +01:00
for ( const Scope * argScope : tokScope - > nestedList ) {
if ( argScope & & argScope - > isNestedIn ( scope ) )
2018-11-10 16:40:40 +01:00
return true ;
}
}
return false ;
}
2018-11-11 16:43:54 +01:00
static bool isDeadScope ( const Token * tok , const Scope * scope )
{
if ( ! tok )
return false ;
if ( ! scope )
return false ;
const Variable * var = tok - > variable ( ) ;
if ( var & & ( ! var - > isLocal ( ) | | var - > isStatic ( ) | | var - > isExtern ( ) ) )
return false ;
if ( tok - > scope ( ) & & tok - > scope ( ) - > bodyEnd ! = scope - > bodyEnd & & precedes ( tok - > scope ( ) - > bodyEnd , scope - > bodyEnd ) )
return true ;
return false ;
}
2019-11-05 07:10:32 +01:00
static const Token * getParentLifetime ( const Token * tok )
{
if ( ! tok )
return tok ;
const Variable * var = tok - > variable ( ) ;
// TODO: Call getLifetimeVariable for deeper analysis
if ( ! var )
return tok ;
if ( var - > isLocal ( ) )
return tok ;
const Token * parent = getParentMember ( tok ) ;
if ( parent ! = tok )
return getParentLifetime ( parent ) ;
return tok ;
}
2018-11-17 09:41:59 +01:00
static int getPointerDepth ( const Token * tok )
{
if ( ! tok )
return 0 ;
return tok - > valueType ( ) ? tok - > valueType ( ) - > pointer : 0 ;
}
2019-11-03 22:02:10 +01:00
static bool isDeadTemporary ( bool cpp , const Token * tok , const Token * expr , const Library * library )
2019-10-08 09:28:39 +02:00
{
2019-11-03 22:02:10 +01:00
if ( ! isTemporary ( cpp , tok , library ) )
2019-10-08 09:28:39 +02:00
return false ;
if ( expr & & ! precedes ( nextAfterAstRightmostLeaf ( tok - > astTop ( ) ) , nextAfterAstRightmostLeaf ( expr - > astTop ( ) ) ) )
return false ;
return true ;
}
2018-11-10 16:40:40 +01:00
void CheckAutoVariables : : checkVarLifetimeScope ( const Token * start , const Token * end )
{
2018-11-10 21:30:01 +01:00
if ( ! start )
2018-11-10 16:40:40 +01:00
return ;
const Scope * scope = start - > scope ( ) ;
2018-11-10 21:30:01 +01:00
if ( ! scope )
2018-11-10 16:40:40 +01:00
return ;
// If the scope is not set correctly then skip checking it
2018-11-10 21:30:01 +01:00
if ( scope - > bodyStart ! = start )
2018-11-10 16:40:40 +01:00
return ;
2019-01-23 07:29:16 +01:00
bool returnRef = Function : : returnsReference ( scope - > function ) ;
2018-11-10 16:40:40 +01:00
for ( const Token * tok = start ; tok & & tok ! = end ; tok = tok - > next ( ) ) {
2019-05-05 11:40:59 +02:00
// Return reference from function
2019-01-23 07:29:16 +01:00
if ( returnRef & & Token : : simpleMatch ( tok - > astParent ( ) , " return " ) ) {
2019-09-11 19:25:09 +02:00
for ( const LifetimeToken & lt : getLifetimeTokens ( tok ) ) {
const Variable * var = lt . token - > variable ( ) ;
2019-10-08 09:28:39 +02:00
if ( var & & ! var - > isGlobal ( ) & & ! var - > isStatic ( ) & & ! var - > isReference ( ) & & ! var - > isRValueReference ( ) & &
2019-09-11 19:25:09 +02:00
isInScope ( var - > nameToken ( ) , tok - > scope ( ) ) ) {
errorReturnReference ( tok , lt . errorPath , lt . inconclusive ) ;
break ;
2019-11-03 22:02:10 +01:00
} else if ( isDeadTemporary ( mTokenizer - > isCPP ( ) , lt . token , nullptr , & mSettings - > library ) ) {
2019-10-08 09:28:39 +02:00
errorReturnTempReference ( tok , lt . errorPath , lt . inconclusive ) ;
break ;
2019-09-11 19:25:09 +02:00
}
2019-01-23 07:29:16 +01:00
}
2019-02-22 06:38:56 +01:00
// Assign reference to non-local variable
2019-03-09 19:09:15 +01:00
} else if ( Token : : Match ( tok - > previous ( ) , " &|&& %var% = " ) & & tok - > astParent ( ) = = tok - > next ( ) & &
tok - > variable ( ) & & tok - > variable ( ) - > nameToken ( ) = = tok & &
tok - > variable ( ) - > declarationId ( ) = = tok - > varId ( ) & & tok - > variable ( ) - > isStatic ( ) & &
2019-01-23 07:29:16 +01:00
! tok - > variable ( ) - > isArgument ( ) ) {
ErrorPath errorPath ;
const Variable * var = getLifetimeVariable ( tok , errorPath ) ;
if ( var & & isInScope ( var - > nameToken ( ) , tok - > scope ( ) ) ) {
errorDanglingReference ( tok , var , errorPath ) ;
continue ;
}
}
2018-11-10 21:30:01 +01:00
for ( const ValueFlow : : Value & val : tok - > values ( ) ) {
2019-01-29 09:47:52 +01:00
if ( ! val . isLocalLifetimeValue ( ) )
2018-11-10 16:40:40 +01:00
continue ;
2019-11-05 07:10:32 +01:00
const Token * tokvalue = getParentLifetime ( val . tokvalue ) ;
2018-11-10 21:30:01 +01:00
if ( Token : : Match ( tok - > astParent ( ) , " return|throw " ) ) {
2019-11-05 07:10:32 +01:00
if ( getPointerDepth ( tok ) < getPointerDepth ( tokvalue ) )
2018-11-21 08:43:57 +01:00
continue ;
2019-05-05 11:40:59 +02:00
if ( ! isLifetimeBorrowed ( tok , mSettings ) )
2018-11-21 08:43:57 +01:00
continue ;
2019-11-05 07:10:32 +01:00
if ( ( tokvalue - > variable ( ) & & isInScope ( tokvalue - > variable ( ) - > nameToken ( ) , scope ) ) | |
isDeadTemporary ( mTokenizer - > isCPP ( ) , tokvalue , tok , & mSettings - > library ) ) {
2018-11-10 16:40:40 +01:00
errorReturnDanglingLifetime ( tok , & val ) ;
break ;
}
2019-11-05 07:10:32 +01:00
} else if ( tokvalue - > variable ( ) & & isDeadScope ( tokvalue - > variable ( ) - > nameToken ( ) , tok - > scope ( ) ) ) {
2018-11-11 16:43:54 +01:00
errorInvalidLifetime ( tok , & val ) ;
break ;
2019-11-05 07:10:32 +01:00
} else if ( ! tokvalue - > variable ( ) & & isDeadTemporary ( mTokenizer - > isCPP ( ) , tokvalue , tok , & mSettings - > library ) ) {
2019-10-08 09:28:39 +02:00
errorDanglingTemporaryLifetime ( tok , & val ) ;
break ;
2019-11-05 07:10:32 +01:00
} else if ( tokvalue - > variable ( ) & & isInScope ( tokvalue - > variable ( ) - > nameToken ( ) , tok - > scope ( ) ) ) {
2019-07-07 10:16:19 +02:00
const Variable * var = nullptr ;
const Token * tok2 = tok ;
if ( Token : : simpleMatch ( tok - > astParent ( ) , " = " ) ) {
if ( tok - > astParent ( ) - > astOperand2 ( ) = = tok ) {
var = getLHSVariable ( tok - > astParent ( ) ) ;
tok2 = tok - > astParent ( ) - > astOperand1 ( ) ;
}
} else if ( tok - > variable ( ) & & tok - > variable ( ) - > declarationId ( ) = = tok - > varId ( ) ) {
var = tok - > variable ( ) ;
}
2019-08-14 20:09:33 +02:00
if ( ! isLifetimeBorrowed ( tok , mSettings ) )
continue ;
2019-07-07 10:16:19 +02:00
if ( var & & ! var - > isLocal ( ) & & ! var - > isArgument ( ) & & ! isVariableChanged ( tok - > next ( ) , tok - > scope ( ) - > bodyEnd , var - > declarationId ( ) , var - > isGlobal ( ) , mSettings , mTokenizer - > isCPP ( ) ) ) {
errorDanglngLifetime ( tok2 , & val ) ;
break ;
}
2018-11-10 16:40:40 +01:00
}
}
const Token * lambdaEndToken = findLambdaEndToken ( tok ) ;
2018-11-10 21:30:01 +01:00
if ( lambdaEndToken ) {
2018-11-10 16:40:40 +01:00
checkVarLifetimeScope ( lambdaEndToken - > link ( ) , lambdaEndToken ) ;
tok = lambdaEndToken ;
}
2019-01-11 09:51:02 +01:00
if ( tok - > str ( ) = = " { " & & tok - > scope ( ) ) {
// Check functions in local classes
2019-01-11 12:56:31 +01:00
if ( tok - > scope ( ) - > type = = Scope : : eClass | |
tok - > scope ( ) - > type = = Scope : : eStruct | |
2019-01-11 09:51:02 +01:00
tok - > scope ( ) - > type = = Scope : : eUnion ) {
2019-01-11 12:56:31 +01:00
for ( const Function & f : tok - > scope ( ) - > functionList ) {
2019-01-11 09:51:02 +01:00
if ( f . functionScope )
checkVarLifetimeScope ( f . functionScope - > bodyStart , f . functionScope - > bodyEnd ) ;
}
tok = tok - > link ( ) ;
}
}
2018-11-10 16:40:40 +01:00
}
}
void CheckAutoVariables : : checkVarLifetime ( )
{
const SymbolDatabase * symbolDatabase = mTokenizer - > getSymbolDatabase ( ) ;
for ( const Scope * scope : symbolDatabase - > functionScopes ) {
if ( ! scope - > function )
continue ;
checkVarLifetimeScope ( scope - > bodyStart , scope - > bodyEnd ) ;
}
}
2018-11-21 08:43:57 +01:00
void CheckAutoVariables : : errorReturnDanglingLifetime ( const Token * tok , const ValueFlow : : Value * val )
{
2019-09-11 19:25:09 +02:00
const bool inconclusive = val ? val - > isInconclusive ( ) : false ;
2018-11-21 08:43:57 +01:00
ErrorPath errorPath = val ? val - > errorPath : ErrorPath ( ) ;
std : : string msg = " Returning " + lifetimeMessage ( tok , val , errorPath ) ;
2018-11-10 16:40:40 +01:00
errorPath . emplace_back ( tok , " " ) ;
2019-09-11 19:25:09 +02:00
reportError ( errorPath , Severity : : error , " returnDanglingLifetime " , msg + " that will be invalid when returning. " , CWE562 , inconclusive ) ;
2018-11-10 16:40:40 +01:00
}
2018-11-11 16:43:54 +01:00
void CheckAutoVariables : : errorInvalidLifetime ( const Token * tok , const ValueFlow : : Value * val )
{
2019-09-11 19:25:09 +02:00
const bool inconclusive = val ? val - > isInconclusive ( ) : false ;
2018-11-12 10:08:17 +01:00
ErrorPath errorPath = val ? val - > errorPath : ErrorPath ( ) ;
2018-11-21 08:43:57 +01:00
std : : string msg = " Using " + lifetimeMessage ( tok , val , errorPath ) ;
2018-11-11 16:43:54 +01:00
errorPath . emplace_back ( tok , " " ) ;
2019-09-11 19:25:09 +02:00
reportError ( errorPath , Severity : : error , " invalidLifetime " , msg + " that is out of scope. " , CWE562 , inconclusive ) ;
2018-11-11 16:43:54 +01:00
}
2019-10-08 09:28:39 +02:00
void CheckAutoVariables : : errorDanglingTemporaryLifetime ( const Token * tok , const ValueFlow : : Value * val )
{
const bool inconclusive = val ? val - > isInconclusive ( ) : false ;
ErrorPath errorPath = val ? val - > errorPath : ErrorPath ( ) ;
std : : string msg = " Using " + lifetimeMessage ( tok , val , errorPath ) ;
errorPath . emplace_back ( tok , " " ) ;
reportError ( errorPath , Severity : : error , " danglingTemporaryLifetime " , msg + " to temporary. " , CWE562 , inconclusive ) ;
}
2018-11-21 08:43:57 +01:00
void CheckAutoVariables : : errorDanglngLifetime ( const Token * tok , const ValueFlow : : Value * val )
{
2019-09-11 19:25:09 +02:00
const bool inconclusive = val ? val - > isInconclusive ( ) : false ;
2018-11-21 08:43:57 +01:00
ErrorPath errorPath = val ? val - > errorPath : ErrorPath ( ) ;
2019-07-07 10:16:19 +02:00
std : : string tokName = tok ? tok - > expressionString ( ) : " x " ;
2018-11-21 08:43:57 +01:00
std : : string msg = " Non-local variable ' " + tokName + " ' will use " + lifetimeMessage ( tok , val , errorPath ) ;
errorPath . emplace_back ( tok , " " ) ;
2019-09-11 19:25:09 +02:00
reportError ( errorPath , Severity : : error , " danglingLifetime " , msg + " . " , CWE562 , inconclusive ) ;
2018-11-21 08:43:57 +01:00
}
2019-09-11 19:25:09 +02:00
void CheckAutoVariables : : errorReturnReference ( const Token * tok , ErrorPath errorPath , bool inconclusive )
2019-01-23 07:29:16 +01:00
{
errorPath . emplace_back ( tok , " " ) ;
2019-09-11 19:25:09 +02:00
reportError (
errorPath , Severity : : error , " returnReference " , " Reference to local variable returned. " , CWE562 , inconclusive ) ;
2019-01-23 07:29:16 +01:00
}
void CheckAutoVariables : : errorDanglingReference ( const Token * tok , const Variable * var , ErrorPath errorPath )
2010-01-23 20:39:12 +01:00
{
2019-01-23 07:29:16 +01:00
std : : string tokName = tok ? tok - > str ( ) : " x " ;
std : : string varName = var ? var - > name ( ) : " y " ;
std : : string msg = " Non-local reference variable ' " + tokName + " ' to local variable ' " + varName + " ' " ;
errorPath . emplace_back ( tok , " " ) ;
reportError ( errorPath , Severity : : error , " danglingReference " , msg , CWE562 , false ) ;
2010-01-23 20:39:12 +01:00
}
2019-10-08 09:28:39 +02:00
void CheckAutoVariables : : errorReturnTempReference ( const Token * tok , ErrorPath errorPath , bool inconclusive )
2010-01-27 19:16:32 +01:00
{
2019-10-08 09:28:39 +02:00
errorPath . emplace_back ( tok , " " ) ;
reportError (
errorPath , Severity : : error , " returnTempReference " , " Reference to temporary returned. " , CWE562 , inconclusive ) ;
2010-01-27 19:16:32 +01:00
}
2018-11-03 18:55:12 +01:00
void CheckAutoVariables : : errorInvalidDeallocation ( const Token * tok , const ValueFlow : : Value * val )
2011-07-21 14:50:38 +02:00
{
2018-11-03 18:59:55 +01:00
const Variable * var = val ? val - > tokvalue - > variable ( ) : ( tok ? tok - > variable ( ) : nullptr ) ;
2018-11-03 18:55:12 +01:00
std : : string type = " auto-variable " ;
if ( var ) {
if ( var - > isGlobal ( ) )
type = " global variable " ;
else if ( var - > isStatic ( ) )
type = " static variable " ;
}
if ( val )
type + = " ( " + val - > tokvalue - > str ( ) + " ) " ;
reportError ( getErrorPath ( tok , val , " Deallocating memory that was not dynamically allocated " ) ,
2011-12-30 10:32:55 +01:00
Severity : : error ,
" autovarInvalidDeallocation " ,
2018-11-03 18:55:12 +01:00
" Deallocation of an " + type + " results in undefined behaviour. \n "
" The deallocation of an " + type + " results in undefined behaviour. You should only free memory "
2016-01-25 20:01:48 +01:00
" that has been allocated dynamically. " , CWE590 , false ) ;
2011-07-21 14:50:38 +02:00
}