2014-08-06 14:20:46 +02:00
/*
* Cppcheck - A tool for static C / C + + code analysis
2020-05-10 11:16:32 +02:00
* Copyright ( C ) 2007 - 2020 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"
2017-05-27 04:33:47 +02:00
# include "errorlogger.h"
# 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>
# include <list>
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:
CWE mapping of useAutoPointerMalloc, uselessCallsCompare, uselessCallsSwap, uselessCallsSubstr, uselessCallsEmpty, uselessCallsRemove, derefInvalidIterator, reademptycontainer, multiplySizeof, divideSizeof, stringLiteralWrite, incorrectStringCompare, literalWithCharPtrCompare, charLiteralWithCharPtrCompare, incorrectStringBooleanError, staticStringCompare, stringCompare, signConversion, truncLongCastAssignment, truncLongCastReturn, unusedFunction, unusedVariable, unusedAllocatedMemory, unreadVariable, unassignedVariable, unusedStructMember, postfixOperator, va_start_wrongParameter (#824)
Add an optional extended description…
2016-09-03 00:31:35 +02:00
static const struct CWE CWE664 ( 664U ) ; // Improper Control of a Resource Through its Lifetime
static const struct CWE CWE688 ( 688U ) ; // Function Call With Incorrect Variable or Reference as Argument
static const struct 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 ( ) ;
2018-06-16 16:10:28 +02:00
const bool printWarnings = mSettings - > isEnabled ( Settings : : WARNING ) ;
2015-04-07 07:07:08 +02:00
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 ) {
2014-08-06 14:20:46 +02:00
std : : list < Variable > : : const_reverse_iterator it = function - > argumentList . rbegin ( ) ;
2014-08-06 14:31:09 +02:00
+ + it ;
2014-08-06 14:20:46 +02:00
wrongParameterTo_va_start_error ( tok , var - > name ( ) , it - > name ( ) ) ;
}
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 ,
CWE mapping of useAutoPointerMalloc, uselessCallsCompare, uselessCallsSwap, uselessCallsSubstr, uselessCallsEmpty, uselessCallsRemove, derefInvalidIterator, reademptycontainer, multiplySizeof, divideSizeof, stringLiteralWrite, incorrectStringCompare, literalWithCharPtrCompare, charLiteralWithCharPtrCompare, incorrectStringBooleanError, staticStringCompare, stringCompare, signConversion, truncLongCastAssignment, truncLongCastReturn, unusedFunction, unusedVariable, unusedAllocatedMemory, unreadVariable, unassignedVariable, unusedStructMember, postfixOperator, va_start_wrongParameter (#824)
Add an optional extended description…
2016-09-03 00:31:35 +02:00
" va_start_wrongParameter " , " ' " + paramIsName + " ' given to va_start() is not last named argument of the function. Did you intend to pass ' " + paramShouldName + " '? " , CWE688 , false ) ;
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 ,
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
" va_start_referencePassed " , " Using reference ' " + paramName + " ' as parameter for va_start() results in undefined behaviour. " , CWE758 , false ) ;
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 ( )
{
2020-01-25 17:01:17 +01:00
if ( mSettings - > clang )
return ;
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 ( ) ;
2018-04-27 22:36:30 +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 " ) {
const Scope * scope = tok - > scope ( ) ;
while ( scope - > nestedIn & & scope - > type ! = Scope : : eFor & & scope - > type ! = Scope : : eWhile & & scope - > type ! = Scope : : eDo & & scope - > type ! = Scope : : eSwitch )
scope = scope - > nestedIn ;
2018-04-27 22:36:30 +02:00
tok = scope - > bodyEnd ;
2016-06-20 09:24:23 +02:00
if ( ! tok )
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 ,
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
" va_end_missing " , " va_list ' " + varname + " ' was opened but not closed by va_end(). " , CWE664 , false ) ;
2014-08-06 14:20:46 +02:00
}
void CheckVaarg : : va_list_usedBeforeStartedError ( const Token * tok , const std : : string & varname )
{
reportError ( tok , Severity : : error ,
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
" va_list_usedBeforeStarted " , " va_list ' " + varname + " ' used before va_start() was called. " , CWE664 , false ) ;
2014-08-06 14:20:46 +02:00
}
void CheckVaarg : : va_start_subsequentCallsError ( const Token * tok , const std : : string & varname )
{
reportError ( tok , Severity : : error ,
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
" va_start_subsequentCalls " , " va_start() or va_copy() called subsequently on ' " + varname + " ' without va_end() in between. " , CWE664 , false ) ;
2014-08-06 14:20:46 +02:00
}