2014-08-06 14:20:46 +02:00
/*
* Cppcheck - A tool for static C / C + + code analysis
2023-01-28 10:16:34 +01:00
* Copyright ( C ) 2007 - 2023 Cppcheck team .
2014-08-06 14:20: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/>.
*/
# include "checkvaarg.h"
2017-05-27 04:33:47 +02:00
2019-01-10 18:32:17 +01:00
# include "astutils.h"
2022-01-27 19:03:20 +01:00
# include "errortypes.h"
2017-05-27 04:33:47 +02:00
# include "settings.h"
2014-08-06 14:20:46 +02:00
# include "symboldatabase.h"
2017-05-27 04:33:47 +02:00
# include "token.h"
# include "tokenize.h"
# include <cstddef>
2023-10-09 10:07:20 +02:00
# include <iterator>
# include <list>
2022-01-27 19:03:20 +01:00
# include <vector>
2014-08-06 14:20:46 +02:00
//---------------------------------------------------------------------------
// Register this check class (by creating a static instance of it)
namespace {
CheckVaarg instance ;
}
//---------------------------------------------------------------------------
// Ensure that correct parameter is passed to va_start()
//---------------------------------------------------------------------------
Mapped error ids stlBoundaries, stlcstr, useAutoPointerContainer, useAutoPointerArray, sprintfOverlappingData, strPlusChar, shiftTooManyBits, integerOverflow, uninitstring, uninitdata, uninitvar, uninitStructMember, deadpointer, va_start_referencePassed, va_end_missing, va_list_usedBeforeStarted, va_start_subsequentCalls to their CWEs.
2016-02-03 13:53:23 +01:00
// CWE ids used:
2023-10-08 09:10:17 +02:00
static const CWE CWE664 ( 664U ) ; // Improper Control of a Resource Through its Lifetime
static const CWE CWE688 ( 688U ) ; // Function Call With Incorrect Variable or Reference as Argument
static const CWE CWE758 ( 758U ) ; // Reliance on Undefined, Unspecified, or Implementation-Defined Behavior
Mapped error ids stlBoundaries, stlcstr, useAutoPointerContainer, useAutoPointerArray, sprintfOverlappingData, strPlusChar, shiftTooManyBits, integerOverflow, uninitstring, uninitdata, uninitvar, uninitStructMember, deadpointer, va_start_referencePassed, va_end_missing, va_list_usedBeforeStarted, va_start_subsequentCalls to their CWEs.
2016-02-03 13:53:23 +01:00
2014-08-06 14:20:46 +02:00
void CheckVaarg : : va_start_argument ( )
{
2018-06-16 16:10:28 +02:00
const SymbolDatabase * const symbolDatabase = mTokenizer - > getSymbolDatabase ( ) ;
2014-08-06 14:20:46 +02:00
const std : : size_t functions = symbolDatabase - > functionScopes . size ( ) ;
2021-02-24 22:00:06 +01:00
const bool printWarnings = mSettings - > severity . isEnabled ( Severity : : warning ) ;
2015-04-07 07:07:08 +02:00
2023-08-29 12:00:52 +02:00
logChecker ( " CheckVaarg::va_start_argument " ) ;
2014-08-06 14:20:46 +02:00
for ( std : : size_t i = 0 ; i < functions ; + + i ) {
const Scope * scope = symbolDatabase - > functionScopes [ i ] ;
const Function * function = scope - > function ;
2015-12-05 20:55:26 +01:00
if ( ! function )
continue ;
2018-04-27 22:36:30 +02:00
for ( const Token * tok = scope - > bodyStart - > next ( ) ; tok ! = scope - > bodyEnd ; tok = tok - > next ( ) ) {
2015-01-27 21:21:03 +01:00
if ( ! tok - > scope ( ) - > isExecutable ( ) )
2018-04-27 22:36:30 +02:00
tok = tok - > scope ( ) - > bodyEnd ;
2015-01-27 21:21:03 +01:00
else if ( Token : : simpleMatch ( tok , " va_start ( " ) ) {
2014-08-06 14:20:46 +02:00
const Token * param2 = tok - > tokAt ( 2 ) - > nextArgument ( ) ;
2014-08-06 20:49:27 +02:00
if ( ! param2 )
continue ;
2014-08-06 14:20:46 +02:00
const Variable * var = param2 - > variable ( ) ;
if ( var & & var - > isReference ( ) )
referenceAs_va_start_error ( param2 , var - > name ( ) ) ;
2015-04-07 07:07:08 +02:00
if ( var & & var - > index ( ) + 2 < function - > argCount ( ) & & printWarnings ) {
2023-08-28 09:28:47 +02:00
auto it = function - > argumentList . end ( ) ;
std : : advance ( it , - 2 ) ;
wrongParameterTo_va_start_error ( tok , var - > name ( ) , it - > name ( ) ) ; // cppcheck-suppress derefInvalidIterator // FP due to isVariableChangedByFunctionCall()
2014-08-06 14:20:46 +02:00
}
tok = tok - > linkAt ( 1 ) ;
}
}
}
}
void CheckVaarg : : wrongParameterTo_va_start_error ( const Token * tok , const std : : string & paramIsName , const std : : string & paramShouldName )
{
reportError ( tok , Severity : : warning ,
2021-02-24 22:00:06 +01:00
" va_start_wrongParameter " , " ' " + paramIsName + " ' given to va_start() is not last named argument of the function. Did you intend to pass ' " + paramShouldName + " '? " , CWE688 , Certainty : : normal ) ;
2014-08-06 14:20:46 +02:00
}
void CheckVaarg : : referenceAs_va_start_error ( const Token * tok , const std : : string & paramName )
{
reportError ( tok , Severity : : error ,
2021-02-24 22:00:06 +01:00
" va_start_referencePassed " , " Using reference ' " + paramName + " ' as parameter for va_start() results in undefined behaviour. " , CWE758 , Certainty : : normal ) ;
2014-08-06 14:20:46 +02:00
}
//---------------------------------------------------------------------------
// Detect missing va_end() if va_start() was used
// Detect va_list usage after va_end()
//---------------------------------------------------------------------------
void CheckVaarg : : va_list_usage ( )
{
2021-04-30 16:47:02 +02:00
if ( mSettings - > clang )
return ;
2023-08-29 12:00:52 +02:00
logChecker ( " CheckVaarg::va_list_usage " ) ; // notclang
2018-06-16 16:10:28 +02:00
const SymbolDatabase * const symbolDatabase = mTokenizer - > getSymbolDatabase ( ) ;
2018-04-28 09:38:33 +02:00
for ( const Variable * var : symbolDatabase - > variableList ( ) ) {
2014-08-06 14:20:46 +02:00
if ( ! var | | var - > isPointer ( ) | | var - > isReference ( ) | | var - > isArray ( ) | | ! var - > scope ( ) | | var - > typeStartToken ( ) - > str ( ) ! = " va_list " )
continue ;
if ( ! var - > isLocal ( ) & & ! var - > isArgument ( ) ) // Check only local variables and arguments
continue ;
bool open = var - > isArgument ( ) ; // va_list passed as argument are opened
bool exitOnEndOfStatement = false ;
const Token * tok = var - > nameToken ( ) - > next ( ) ;
2021-08-07 20:51:18 +02:00
for ( ; tok & & tok ! = var - > scope ( ) - > bodyEnd ; tok = tok - > next ( ) ) {
2019-01-10 18:32:17 +01:00
// Skip lambdas
const Token * tok2 = findLambdaEndToken ( tok ) ;
if ( tok2 )
tok = tok2 ;
2014-08-06 20:49:27 +02:00
if ( Token : : Match ( tok , " va_start ( %varid% " , var - > declarationId ( ) ) ) {
2014-08-06 14:20:46 +02:00
if ( open )
va_start_subsequentCallsError ( tok , var - > name ( ) ) ;
open = true ;
tok = tok - > linkAt ( 1 ) ;
2014-08-06 20:49:27 +02:00
} else if ( Token : : Match ( tok , " va_end ( %varid% " , var - > declarationId ( ) ) ) {
2014-08-06 14:20:46 +02:00
if ( ! open )
va_list_usedBeforeStartedError ( tok , var - > name ( ) ) ;
open = false ;
tok = tok - > linkAt ( 1 ) ;
} else if ( Token : : simpleMatch ( tok , " va_copy ( " ) ) {
bool nopen = open ;
if ( tok - > linkAt ( 1 ) - > previous ( ) - > varId ( ) = = var - > declarationId ( ) ) { // Source
if ( ! open )
va_list_usedBeforeStartedError ( tok , var - > name ( ) ) ;
}
if ( tok - > tokAt ( 2 ) - > varId ( ) = = var - > declarationId ( ) ) { // Destination
if ( open )
va_start_subsequentCallsError ( tok , var - > name ( ) ) ;
nopen = true ;
}
open = nopen ;
tok = tok - > linkAt ( 1 ) ;
2016-06-07 19:28:32 +02:00
} else if ( Token : : Match ( tok , " throw|return " ) )
2014-08-06 14:20:46 +02:00
exitOnEndOfStatement = true ;
2016-06-07 19:28:32 +02:00
else if ( tok - > str ( ) = = " break " ) {
2020-12-25 08:43:14 +01:00
tok = findNextTokenFromBreak ( tok ) ;
if ( ! tok )
2016-06-20 09:24:23 +02:00
return ;
2018-06-16 16:10:28 +02:00
} else if ( tok - > str ( ) = = " goto " | | ( mTokenizer - > isCPP ( ) & & tok - > str ( ) = = " try " ) ) {
2014-09-27 11:03:58 +02:00
open = false ;
break ;
} else if ( ! open & & tok - > varId ( ) = = var - > declarationId ( ) )
2014-08-06 14:20:46 +02:00
va_list_usedBeforeStartedError ( tok , var - > name ( ) ) ;
else if ( exitOnEndOfStatement & & tok - > str ( ) = = " ; " )
break ;
}
if ( open & & ! var - > isArgument ( ) )
va_end_missingError ( tok , var - > name ( ) ) ;
}
}
void CheckVaarg : : va_end_missingError ( const Token * tok , const std : : string & varname )
{
reportError ( tok , Severity : : error ,
2021-02-24 22:00:06 +01:00
" va_end_missing " , " va_list ' " + varname + " ' was opened but not closed by va_end(). " , CWE664 , Certainty : : normal ) ;
2014-08-06 14:20:46 +02:00
}
void CheckVaarg : : va_list_usedBeforeStartedError ( const Token * tok , const std : : string & varname )
{
reportError ( tok , Severity : : error ,
2021-02-24 22:00:06 +01:00
" va_list_usedBeforeStarted " , " va_list ' " + varname + " ' used before va_start() was called. " , CWE664 , Certainty : : normal ) ;
2014-08-06 14:20:46 +02:00
}
void CheckVaarg : : va_start_subsequentCallsError ( const Token * tok , const std : : string & varname )
{
reportError ( tok , Severity : : error ,
2021-02-24 22:00:06 +01:00
" va_start_subsequentCalls " , " va_start() or va_copy() called subsequently on ' " + varname + " ' without va_end() in between. " , CWE664 , Certainty : : normal ) ;
2014-08-06 14:20:46 +02:00
}