2010-10-31 11:51:25 +01:00
/*
* Cppcheck - A tool for static C / C + + code analysis
2023-01-28 10:16:34 +01:00
* Copyright ( C ) 2007 - 2023 Cppcheck team .
2010-10-31 11:51:25 +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
* along with this program . If not , see < http : //www.gnu.org/licenses/>.
*/
//---------------------------------------------------------------------------
# include "checknullpointer.h"
2017-05-27 04:33:47 +02:00
2018-12-18 07:56:33 +01:00
# include "astutils.h"
2017-05-27 04:33:47 +02:00
# include "errorlogger.h"
2022-01-27 19:03:20 +01:00
# include "errortypes.h"
2017-05-27 04:33:47 +02:00
# include "library.h"
2020-04-13 13:44:48 +02:00
# include "mathlib.h"
2017-05-27 04:33:47 +02:00
# include "settings.h"
2011-03-06 21:23:33 +01:00
# include "symboldatabase.h"
2017-05-27 04:33:47 +02:00
# include "token.h"
# include "tokenize.h"
2023-01-26 22:23:22 +01:00
# include "valueflow.h"
2017-05-27 04:33:47 +02:00
# include <algorithm>
2011-10-12 22:11:27 +02:00
# include <cctype>
2022-01-27 19:03:20 +01:00
# include <map>
2017-05-27 04:33:47 +02:00
# include <set>
2022-01-27 19:03:20 +01:00
# include <vector>
2010-10-31 11:51:25 +01:00
//---------------------------------------------------------------------------
2022-09-24 11:59:13 +02:00
// CWE ids used:
static const struct CWE CWE_NULL_POINTER_DEREFERENCE ( 476U ) ;
static const struct CWE CWE_INCORRECT_CALCULATION ( 682U ) ;
2010-10-31 11:51:25 +01:00
// Register this check class (by creating a static instance of it)
2011-10-13 20:53:06 +02:00
namespace {
CheckNullPointer instance ;
2010-10-31 11:51:25 +01:00
}
//---------------------------------------------------------------------------
2015-01-02 13:35:39 +01:00
static bool checkNullpointerFunctionCallPlausibility ( const Function * func , unsigned int arg )
{
return ! func | | ( func - > argCount ( ) > = arg & & func - > getArgumentVar ( arg - 1 ) & & func - > getArgumentVar ( arg - 1 ) - > isPointer ( ) ) ;
}
2010-10-31 11:51:25 +01:00
/**
* @ brief parse a function call and extract information about variable usage
* @ param tok first token
* @ param var variables that the function read / write .
2013-07-15 18:55:40 +02:00
* @ param library - - library files data
2010-10-31 11:51:25 +01:00
*/
2017-02-24 19:10:34 +01:00
void CheckNullPointer : : parseFunctionCall ( const Token & tok , std : : list < const Token * > & var , const Library * library )
2010-10-31 11:51:25 +01:00
{
2015-01-31 10:50:39 +01:00
if ( Token : : Match ( & tok , " %name% ( ) " ) | | ! tok . tokAt ( 2 ) )
2012-02-11 12:26:48 +01:00
return ;
2019-03-16 21:21:30 +01:00
const std : : vector < const Token * > args = getArguments ( & tok ) ;
2010-10-31 11:51:25 +01:00
2017-02-24 19:10:34 +01:00
if ( library | | tok . function ( ) ! = nullptr ) {
2019-03-16 21:21:30 +01:00
for ( int argnr = 1 ; argnr < = args . size ( ) ; + + argnr ) {
const Token * param = args [ argnr - 1 ] ;
2017-02-24 19:10:34 +01:00
if ( library & & library - > isnullargbad ( & tok , argnr ) & & checkNullpointerFunctionCallPlausibility ( tok . function ( ) , argnr ) )
var . push_back ( param ) ;
else if ( tok . function ( ) ) {
const Variable * argVar = tok . function ( ) - > getArgumentVar ( argnr - 1 ) ;
if ( argVar & & argVar - > isStlStringType ( ) & & ! argVar - > isArrayOrPointer ( ) )
2015-03-14 19:23:33 +01:00
var . push_back ( param ) ;
}
}
2010-10-31 11:51:25 +01:00
}
2011-09-05 20:18:58 +02:00
2019-03-17 07:37:38 +01:00
if ( library & & library - > formatstr_function ( & tok ) ) {
const int formatStringArgNr = library - > formatstr_argno ( & tok ) ;
if ( formatStringArgNr < 0 | | formatStringArgNr > = args . size ( ) )
return ;
// 1st parameter..
if ( Token : : Match ( & tok , " snprintf|vsnprintf|fnprintf|vfnprintf " ) & & args . size ( ) > 1 & & ! ( args [ 1 ] & & args [ 1 ] - > hasKnownIntValue ( ) & & args [ 1 ] - > getKnownIntValue ( ) = = 0 ) ) // Only if length (second parameter) is not zero
var . push_back ( args [ 0 ] ) ;
if ( args [ formatStringArgNr ] - > tokType ( ) ! = Token : : eString )
return ;
const std : : string & formatString = args [ formatStringArgNr ] - > strValue ( ) ;
int argnr = formatStringArgNr + 1 ;
const bool scan = library - > formatstr_scan ( & tok ) ;
2011-10-23 11:23:48 +02:00
2019-03-16 21:21:30 +01:00
bool percent = false ;
2022-12-20 20:32:16 +01:00
for ( std : : string : : const_iterator i = formatString . cbegin ( ) ; i ! = formatString . cend ( ) ; + + i ) {
2019-03-16 21:21:30 +01:00
if ( * i = = ' % ' ) {
percent = ! percent ;
} else if ( percent ) {
percent = false ;
bool _continue = false ;
while ( ! std : : isalpha ( ( unsigned char ) * i ) ) {
if ( * i = = ' * ' ) {
if ( scan )
_continue = true ;
else
argnr + + ;
2011-11-28 21:32:07 +01:00
}
2019-03-16 21:21:30 +01:00
+ + i ;
if ( i = = formatString . end ( ) )
return ;
}
if ( _continue )
continue ;
2011-11-28 21:32:07 +01:00
2019-03-16 21:21:30 +01:00
if ( argnr < args . size ( ) & & ( * i = = ' n ' | | * i = = ' s ' | | scan ) )
var . push_back ( args [ argnr ] ) ;
2011-10-12 22:11:27 +02:00
2019-03-16 21:21:30 +01:00
if ( * i ! = ' m ' ) // %m is a non-standard glibc extension that requires no parameter
argnr + + ;
2011-10-12 22:11:27 +02:00
}
}
2011-09-05 20:18:58 +02:00
}
2010-10-31 11:51:25 +01:00
}
2015-06-13 16:22:43 +02:00
namespace {
2018-04-08 22:54:10 +02:00
const std : : set < std : : string > stl_stream = {
" fstream " , " ifstream " , " iostream " , " istream " ,
" istringstream " , " ofstream " , " ostream " , " ostringstream " ,
" stringstream " , " wistringstream " , " wostringstream " , " wstringstream "
} ;
2015-06-13 16:22:43 +02:00
}
2010-10-31 11:51:25 +01:00
2010-10-31 15:32:19 +01:00
/**
* Is there a pointer dereference ? Everything that should result in
* a nullpointer dereference error message will result in a true
* return value . If it ' s unknown if the pointer is dereferenced false
* is returned .
* @ param tok token for the pointer
* @ param unknown it is not known if there is a pointer dereference ( could be reported as a debug message )
* @ return true = > there is a dereference
*/
2018-12-29 09:26:57 +01:00
bool CheckNullPointer : : isPointerDeRef ( const Token * tok , bool & unknown ) const
{
return isPointerDeRef ( tok , unknown , mSettings ) ;
}
2022-11-27 09:20:19 +01:00
static bool isUnevaluatedOperator ( const Token * tok )
{
return Token : : Match ( tok , " sizeof|decltype|typeid|typeof|alignof|_Alignof|_alignof|__alignof|__alignof__ ( " ) ;
2022-09-16 12:11:34 +02:00
}
2018-12-29 09:26:57 +01:00
bool CheckNullPointer : : isPointerDeRef ( const Token * tok , bool & unknown , const Settings * settings )
2010-10-31 15:32:19 +01:00
{
unknown = false ;
2018-12-29 09:26:57 +01:00
// Is pointer used as function parameter?
if ( Token : : Match ( tok - > previous ( ) , " [(,] %name% [,)] " ) & & settings ) {
const Token * ftok = tok - > previous ( ) ;
while ( ftok & & ftok - > str ( ) ! = " ( " ) {
if ( ftok - > str ( ) = = " ) " )
ftok = ftok - > link ( ) ;
ftok = ftok - > previous ( ) ;
}
if ( ftok & & ftok - > previous ( ) ) {
std : : list < const Token * > varlist ;
parseFunctionCall ( * ftok - > previous ( ) , varlist , & settings - > library ) ;
2022-12-30 15:13:47 +01:00
if ( std : : find ( varlist . cbegin ( ) , varlist . cend ( ) , tok ) ! = varlist . cend ( ) ) {
2018-12-29 09:26:57 +01:00
return true ;
}
}
}
2019-12-15 16:36:46 +01:00
if ( tok - > str ( ) = = " ( " & & ! tok - > scope ( ) - > isExecutable ( ) )
return false ;
2014-05-22 19:48:00 +02:00
const Token * parent = tok - > astParent ( ) ;
if ( ! parent )
return false ;
2021-12-06 20:06:48 +01:00
const bool addressOf = parent - > astParent ( ) & & parent - > astParent ( ) - > str ( ) = = " & " ;
if ( parent - > str ( ) = = " . " & & astIsRHS ( tok ) )
2018-12-29 09:26:57 +01:00
return isPointerDeRef ( parent , unknown , settings ) ;
2016-01-20 08:42:58 +01:00
const bool firstOperand = parent - > astOperand1 ( ) = = tok ;
2020-08-17 23:36:45 +02:00
parent = astParentSkipParens ( tok ) ;
if ( ! parent )
return false ;
2013-04-03 10:27:08 +02:00
2010-10-31 15:32:19 +01:00
// Dereferencing pointer..
2022-11-27 09:20:19 +01:00
const Token * grandParent = parent - > astParent ( ) ;
if ( parent - > isUnaryOp ( " * " ) & & ! ( grandParent & & isUnevaluatedOperator ( grandParent - > previous ( ) ) ) ) {
2022-04-26 17:25:56 +02:00
// declaration of function pointer
if ( tok - > variable ( ) & & tok - > variable ( ) - > nameToken ( ) = = tok )
return false ;
if ( ! addressOf )
return true ;
}
2014-05-22 19:48:00 +02:00
// array access
2021-12-06 20:06:48 +01:00
if ( firstOperand & & parent - > str ( ) = = " [ " & & ! addressOf )
2010-10-31 15:32:19 +01:00
return true ;
2015-01-05 14:54:24 +01:00
// address of member variable / array element
const Token * parent2 = parent ;
while ( Token : : Match ( parent2 , " [|. " ) )
parent2 = parent2 - > astParent ( ) ;
2018-07-14 10:26:22 +02:00
if ( parent2 ! = parent & & parent2 & & parent2 - > isUnaryOp ( " & " ) )
2015-01-05 14:54:24 +01:00
return false ;
2011-10-18 19:34:14 +02:00
// read/write member variable
2021-12-06 20:06:48 +01:00
if ( firstOperand & & parent - > originalName ( ) = = " -> " & & ! addressOf )
return true ;
2010-10-31 15:32:19 +01:00
2020-11-10 16:00:55 +01:00
// If its a function pointer then check if its called
if ( tok - > variable ( ) & & tok - > variable ( ) - > isPointer ( ) & & Token : : Match ( tok - > variable ( ) - > nameToken ( ) , " %name% ) ( " ) & &
Token : : Match ( tok , " %name% ( " ) )
2010-10-31 15:32:19 +01:00
return true ;
2010-10-31 15:46:08 +01:00
if ( Token : : Match ( tok , " %var% = %var% . " ) & &
tok - > varId ( ) = = tok - > tokAt ( 2 ) - > varId ( ) )
return true ;
2012-01-25 15:16:22 +01:00
// std::string dereferences nullpointers
2022-05-18 13:52:44 +02:00
if ( Token : : Match ( parent - > tokAt ( - 3 ) , " std :: string|wstring (|{ %name% )|} " ) )
2012-01-25 15:16:22 +01:00
return true ;
2022-05-18 13:52:44 +02:00
if ( Token : : Match ( parent - > previous ( ) , " %name% (|{ %name% )|} " ) ) {
2013-02-01 19:10:14 +01:00
const Variable * var = tok - > tokAt ( - 2 ) - > variable ( ) ;
2014-09-05 12:03:08 +02:00
if ( var & & ! var - > isPointer ( ) & & ! var - > isArray ( ) & & var - > isStlStringType ( ) )
2012-01-26 16:50:59 +01:00
return true ;
}
2012-01-25 15:16:22 +01:00
2012-03-16 17:24:03 +01:00
// streams dereference nullpointers
2014-05-22 19:48:00 +02:00
if ( Token : : Match ( parent , " <<|>> " ) & & ! firstOperand ) {
2013-02-01 19:10:14 +01:00
const Variable * var = tok - > variable ( ) ;
2013-01-16 15:37:07 +01:00
if ( var & & var - > isPointer ( ) & & Token : : Match ( var - > typeStartToken ( ) , " char|wchar_t " ) ) { // Only outputting or reading to char* can cause problems
2014-05-22 19:48:00 +02:00
const Token * tok2 = parent ; // Find start of statement
2012-03-16 17:24:03 +01:00
for ( ; tok2 ; tok2 = tok2 - > previous ( ) ) {
if ( Token : : Match ( tok2 - > previous ( ) , " ;|{|}|: " ) )
break ;
}
if ( Token : : Match ( tok2 , " std :: cout|cin|cerr " ) )
return true ;
if ( tok2 & & tok2 - > varId ( ) ! = 0 ) {
2013-02-01 19:10:14 +01:00
const Variable * var2 = tok2 - > variable ( ) ;
2014-01-30 05:26:48 +01:00
if ( var2 & & var2 - > isStlType ( stl_stream ) )
2012-03-16 17:24:03 +01:00
return true ;
}
}
}
2014-02-15 16:12:37 +01:00
const Variable * ovar = nullptr ;
2014-05-22 19:48:00 +02:00
if ( Token : : Match ( parent , " +|==|!= " ) | | ( parent - > str ( ) = = " = " & & ! firstOperand ) ) {
if ( parent - > astOperand1 ( ) = = tok & & parent - > astOperand2 ( ) )
ovar = parent - > astOperand2 ( ) - > variable ( ) ;
2014-07-06 14:48:24 +02:00
else if ( parent - > astOperand1 ( ) & & parent - > astOperand2 ( ) = = tok )
2014-05-22 19:48:00 +02:00
ovar = parent - > astOperand1 ( ) - > variable ( ) ;
}
2014-09-05 12:03:08 +02:00
if ( ovar & & ! ovar - > isPointer ( ) & & ! ovar - > isArray ( ) & & ovar - > isStlStringType ( ) )
2013-02-01 19:10:14 +01:00
return true ;
2012-01-25 15:16:22 +01:00
2010-10-31 15:32:19 +01:00
// assume that it's not a dereference (no false positives)
return false ;
}
2011-12-25 17:01:45 +01:00
2019-09-30 21:04:43 +02:00
static bool isNullablePointer ( const Token * tok , const Settings * settings )
{
if ( ! tok )
return false ;
2019-12-01 12:16:31 +01:00
if ( Token : : simpleMatch ( tok , " new " ) & & tok - > varId ( ) = = 0 )
return false ;
2019-09-30 21:04:43 +02:00
if ( astIsPointer ( tok ) )
return true ;
if ( astIsSmartPointer ( tok ) )
return true ;
if ( Token : : simpleMatch ( tok , " . " ) )
return isNullablePointer ( tok - > astOperand2 ( ) , settings ) ;
if ( const Variable * var = tok - > variable ( ) ) {
return ( var - > isPointer ( ) | | var - > isSmartPointer ( ) ) ;
}
return false ;
}
2010-10-31 11:51:25 +01:00
void CheckNullPointer : : nullPointerByDeRefAndChec ( )
{
2021-02-24 22:00:06 +01:00
const bool printInconclusive = ( mSettings - > certainty . isEnabled ( Certainty : : inconclusive ) ) ;
2015-04-07 07:07:08 +02:00
2018-06-16 16:10:28 +02:00
for ( const Token * tok = mTokenizer - > tokens ( ) ; tok ; tok = tok - > next ( ) ) {
2022-11-27 09:20:19 +01:00
if ( isUnevaluatedOperator ( tok ) ) {
2015-04-20 13:46:02 +02:00
tok = tok - > next ( ) - > link ( ) ;
continue ;
}
2019-09-30 21:04:43 +02:00
if ( Token : : Match ( tok , " %num%|%char%|%str% " ) )
2019-04-26 11:30:09 +02:00
continue ;
2022-08-02 21:43:18 +02:00
if ( ! isNullablePointer ( tok , mSettings ) | |
( tok - > str ( ) = = " . " & & isNullablePointer ( tok - > astOperand2 ( ) , mSettings ) & & tok - > astOperand2 ( ) - > getValue ( 0 ) ) ) // avoid duplicate warning
2014-01-17 17:47:59 +01:00
continue ;
2014-01-08 17:37:39 +01:00
2014-01-17 17:47:59 +01:00
// Can pointer be NULL?
2014-01-20 06:49:45 +01:00
const ValueFlow : : Value * value = tok - > getValue ( 0 ) ;
2014-01-17 17:47:59 +01:00
if ( ! value )
continue ;
2014-01-08 17:37:39 +01:00
2017-09-20 22:41:36 +02:00
if ( ! printInconclusive & & value - > isInconclusive ( ) )
2014-03-18 17:04:33 +01:00
continue ;
2014-01-17 17:47:59 +01:00
// Pointer dereference.
bool unknown = false ;
2014-01-21 19:50:52 +01:00
if ( ! isPointerDeRef ( tok , unknown ) ) {
2017-05-15 20:05:11 +02:00
if ( unknown )
2019-09-30 21:04:43 +02:00
nullPointerError ( tok , tok - > expressionString ( ) , value , true ) ;
2014-01-17 17:47:59 +01:00
continue ;
2014-01-21 19:50:52 +01:00
}
2010-10-31 11:51:25 +01:00
2019-09-30 21:04:43 +02:00
nullPointerError ( tok , tok - > expressionString ( ) , value , value - > isInconclusive ( ) ) ;
2010-10-31 11:51:25 +01:00
}
}
void CheckNullPointer : : nullPointer ( )
{
2014-08-16 12:48:20 +02:00
nullPointerByDeRefAndChec ( ) ;
2010-10-31 11:51:25 +01:00
}
2015-06-13 16:22:43 +02:00
namespace {
2018-04-08 22:54:10 +02:00
const std : : set < std : : string > stl_istream = {
" fstream " , " ifstream " , " iostream " , " istream " ,
" istringstream " , " stringstream " , " wistringstream " , " wstringstream "
} ;
2015-06-13 16:22:43 +02:00
}
2010-12-15 18:45:53 +01:00
/** Dereferencing null constant (simplified token list) */
2010-10-31 11:51:25 +01:00
void CheckNullPointer : : nullConstantDereference ( )
{
2018-06-16 16:10:28 +02:00
const SymbolDatabase * symbolDatabase = mTokenizer - > getSymbolDatabase ( ) ;
2014-01-30 05:26:48 +01:00
2018-06-03 09:27:18 +02:00
for ( const Scope * scope : symbolDatabase - > functionScopes ) {
2017-08-09 20:00:26 +02:00
if ( scope - > function = = nullptr | | ! scope - > function - > hasBody ( ) ) // We only look for functions with a body
2012-01-21 19:55:32 +01:00
continue ;
2010-10-31 11:51:25 +01:00
2018-04-27 22:36:30 +02:00
const Token * tok = scope - > bodyStart ;
2012-01-26 16:50:59 +01:00
2017-11-03 10:39:57 +01:00
if ( scope - > function - > isConstructor ( ) )
2012-11-25 14:55:31 +01:00
tok = scope - > function - > token ; // Check initialization list
2012-01-26 16:50:59 +01:00
2018-04-27 22:36:30 +02:00
for ( ; tok ! = scope - > bodyEnd ; tok = tok - > next ( ) ) {
2022-11-27 09:20:19 +01:00
if ( isUnevaluatedOperator ( tok ) )
2012-01-25 15:16:22 +01:00
tok = tok - > next ( ) - > link ( ) ;
2010-10-31 11:51:25 +01:00
2011-10-13 20:53:06 +02:00
else if ( Token : : simpleMatch ( tok , " * 0 " ) ) {
2013-02-28 21:50:29 +01:00
if ( Token : : Match ( tok - > previous ( ) , " return|throw|;|{|}|:|[|(|, " ) | | tok - > previous ( ) - > isOp ( ) ) {
2010-10-31 11:51:25 +01:00
nullPointerError ( tok ) ;
}
}
2010-12-26 19:29:58 +01:00
2012-01-25 15:16:22 +01:00
else if ( Token : : Match ( tok , " 0 [ " ) & & ( tok - > previous ( ) - > str ( ) ! = " & " | | ! Token : : Match ( tok - > next ( ) - > link ( ) - > next ( ) , " [.(] " ) ) )
nullPointerError ( tok ) ;
2022-05-18 13:52:44 +02:00
else if ( Token : : Match ( tok - > previous ( ) , " !!. %name% (| { " ) && (tok->previous()->str() != " : : " || tok->strAt(-2) == " std " )) {
if ( Token : : Match ( tok - > tokAt ( 2 ) , " 0|NULL|nullptr )|} " ) & & tok - > varId ( ) ) { // constructor call
2013-02-06 06:39:58 +01:00
const Variable * var = tok - > variable ( ) ;
2014-09-05 12:03:08 +02:00
if ( var & & ! var - > isPointer ( ) & & ! var - > isArray ( ) & & var - > isStlStringType ( ) )
2012-02-11 12:26:48 +01:00
nullPointerError ( tok ) ;
2012-04-02 12:12:02 +02:00
} else { // function call
std : : list < const Token * > var ;
2018-06-16 16:10:28 +02:00
parseFunctionCall ( * tok , var , & mSettings - > library ) ;
2012-04-02 12:12:02 +02:00
// is one of the var items a NULL pointer?
2018-07-14 10:23:41 +02:00
for ( const Token * vartok : var ) {
2019-03-16 21:21:30 +01:00
if ( vartok - > hasKnownIntValue ( ) & & vartok - > getKnownIntValue ( ) = = 0 )
2018-07-14 10:23:41 +02:00
nullPointerError ( vartok ) ;
2010-12-26 19:29:58 +01:00
}
}
2017-02-24 19:44:28 +01:00
} else if ( Token : : Match ( tok , " std :: string|wstring ( 0|NULL|nullptr ) " ) )
2012-01-25 15:16:22 +01:00
nullPointerError ( tok ) ;
2018-03-18 19:02:30 +01:00
else if ( Token : : Match ( tok - > previous ( ) , " ::|. %name% ( " ) ) {
2018-03-19 09:53:14 +01:00
const std : : vector < const Token * > & args = getArguments ( tok ) ;
2018-03-18 19:02:30 +01:00
for ( int argnr = 0 ; argnr < args . size ( ) ; + + argnr ) {
const Token * argtok = args [ argnr ] ;
if ( ! argtok - > hasKnownIntValue ( ) )
continue ;
if ( argtok - > values ( ) . front ( ) . intvalue ! = 0 )
continue ;
2018-06-16 16:10:28 +02:00
if ( mSettings - > library . isnullargbad ( tok , argnr + 1 ) )
2018-03-18 19:02:30 +01:00
nullPointerError ( argtok ) ;
}
}
2017-02-24 19:44:28 +01:00
else if ( Token : : Match ( tok - > previous ( ) , " >> 0|NULL|nullptr " ) ) { // Only checking input stream operations is safe here, because otherwise 0 can be an integer as well
2012-03-16 17:24:03 +01:00
const Token * tok2 = tok - > previous ( ) ; // Find start of statement
for ( ; tok2 ; tok2 = tok2 - > previous ( ) ) {
2014-05-17 18:18:20 +02:00
if ( Token : : Match ( tok2 - > previous ( ) , " ;|{|}|:|( " ) )
2012-03-16 17:24:03 +01:00
break ;
}
2014-05-12 17:18:51 +02:00
if ( tok2 & & tok2 - > previous ( ) & & tok2 - > previous ( ) - > str ( ) = = " ( " )
2014-05-17 19:14:29 +02:00
continue ;
2012-03-16 17:24:03 +01:00
if ( Token : : simpleMatch ( tok2 , " std :: cin " ) )
nullPointerError ( tok ) ;
if ( tok2 & & tok2 - > varId ( ) ! = 0 ) {
2013-02-06 06:39:58 +01:00
const Variable * var = tok2 - > variable ( ) ;
2015-06-13 16:22:43 +02:00
if ( var & & var - > isStlType ( stl_istream ) )
2012-03-16 17:24:03 +01:00
nullPointerError ( tok ) ;
}
}
2014-02-16 11:47:52 +01:00
const Variable * ovar = nullptr ;
2015-09-13 10:53:05 +02:00
const Token * tokNull = nullptr ;
2017-02-24 19:44:28 +01:00
if ( Token : : Match ( tok , " 0|NULL|nullptr ==|!=|>|>=|<|<= %var% " ) ) {
2015-09-13 10:53:05 +02:00
if ( ! Token : : Match ( tok - > tokAt ( 3 ) , " .|[ " ) ) {
ovar = tok - > tokAt ( 2 ) - > variable ( ) ;
tokNull = tok ;
}
2017-02-24 19:44:28 +01:00
} else if ( Token : : Match ( tok , " %var% ==|!=|>|>=|<|<= 0|NULL|nullptr " ) | |
Token : : Match ( tok , " %var% =|+ 0|NULL|nullptr )|]|,|;|+ " ) ) {
2013-02-06 06:39:58 +01:00
ovar = tok - > variable ( ) ;
2015-09-13 10:53:05 +02:00
tokNull = tok - > tokAt ( 2 ) ;
}
if ( ovar & & ! ovar - > isPointer ( ) & & ! ovar - > isArray ( ) & & ovar - > isStlStringType ( ) & & tokNull & & tokNull - > originalName ( ) ! = " ' \\ 0' " )
nullPointerError ( tokNull ) ;
2010-10-31 11:51:25 +01:00
}
}
}
2017-05-15 20:05:11 +02:00
void CheckNullPointer : : nullPointerError ( const Token * tok , const std : : string & varname , const ValueFlow : : Value * value , bool inconclusive )
2010-10-31 11:51:25 +01:00
{
2018-04-09 06:43:48 +02:00
const std : : string errmsgcond ( " $symbol: " + varname + ' \n ' + ValueFlow : : eitherTheConditionIsRedundant ( value ? value - > condition : nullptr ) + " or there is possible null pointer dereference: $symbol. " ) ;
const std : : string errmsgdefarg ( " $symbol: " + varname + " \n Possible null pointer dereference if the default parameter value is used: $symbol " ) ;
2010-10-31 11:51:25 +01:00
2017-05-15 20:05:11 +02:00
if ( ! tok ) {
2021-02-24 22:00:06 +01:00
reportError ( tok , Severity : : error , " nullPointer " , " Null pointer dereference " , CWE_NULL_POINTER_DEREFERENCE , Certainty : : normal ) ;
reportError ( tok , Severity : : warning , " nullPointerDefaultArg " , errmsgdefarg , CWE_NULL_POINTER_DEREFERENCE , Certainty : : normal ) ;
reportError ( tok , Severity : : warning , " nullPointerRedundantCheck " , errmsgcond , CWE_NULL_POINTER_DEREFERENCE , Certainty : : normal ) ;
2017-05-15 20:05:11 +02:00
return ;
}
2010-10-31 11:51:25 +01:00
2017-05-15 20:35:57 +02:00
if ( ! value ) {
2021-02-24 22:00:06 +01:00
reportError ( tok , Severity : : error , " nullPointer " , " Null pointer dereference " , CWE_NULL_POINTER_DEREFERENCE , inconclusive ? Certainty : : inconclusive : Certainty : : normal ) ;
2017-05-15 20:35:57 +02:00
return ;
}
2018-06-16 16:10:28 +02:00
if ( ! mSettings - > isEnabled ( value , inconclusive ) )
2016-11-22 23:59:39 +01:00
return ;
2017-05-15 20:05:11 +02:00
2017-05-19 17:29:16 +02:00
const ErrorPath errorPath = getErrorPath ( tok , value , " Null pointer dereference " ) ;
2017-05-15 20:05:11 +02:00
if ( value - > condition ) {
2021-02-24 22:00:06 +01:00
reportError ( errorPath , Severity : : warning , " nullPointerRedundantCheck " , errmsgcond , CWE_NULL_POINTER_DEREFERENCE , inconclusive | | value - > isInconclusive ( ) ? Certainty : : inconclusive : Certainty : : normal ) ;
2017-05-15 20:05:11 +02:00
} else if ( value - > defaultArg ) {
2021-02-24 22:00:06 +01:00
reportError ( errorPath , Severity : : warning , " nullPointerDefaultArg " , errmsgdefarg , CWE_NULL_POINTER_DEREFERENCE , inconclusive | | value - > isInconclusive ( ) ? Certainty : : inconclusive : Certainty : : normal ) ;
2017-05-15 20:05:11 +02:00
} else {
std : : string errmsg ;
errmsg = std : : string ( value - > isKnown ( ) ? " Null " : " Possible null " ) + " pointer dereference " ;
if ( ! varname . empty ( ) )
2018-04-09 06:43:48 +02:00
errmsg = " $symbol: " + varname + ' \n ' + errmsg + " : $symbol " ;
2017-05-15 20:05:11 +02:00
reportError ( errorPath ,
value - > isKnown ( ) ? Severity : : error : Severity : : warning ,
" nullPointer " ,
errmsg ,
2021-02-24 22:00:06 +01:00
CWE_NULL_POINTER_DEREFERENCE , inconclusive | | value - > isInconclusive ( ) ? Certainty : : inconclusive : Certainty : : normal ) ;
2017-05-15 20:05:11 +02:00
}
2010-10-31 11:51:25 +01:00
}
2016-10-09 15:15:29 +02:00
void CheckNullPointer : : arithmetic ( )
{
2018-06-16 16:10:28 +02:00
const SymbolDatabase * symbolDatabase = mTokenizer - > getSymbolDatabase ( ) ;
2018-06-03 09:27:18 +02:00
for ( const Scope * scope : symbolDatabase - > functionScopes ) {
2018-04-27 22:36:30 +02:00
for ( const Token * tok = scope - > bodyStart - > next ( ) ; tok ! = scope - > bodyEnd ; tok = tok - > next ( ) ) {
2018-04-03 21:32:37 +02:00
if ( ! Token : : Match ( tok , " -|+|+=|-=|++|-- " ) )
2016-10-09 15:15:29 +02:00
continue ;
2018-04-03 21:32:37 +02:00
const Token * pointerOperand ;
2018-05-09 09:06:49 +02:00
const Token * numericOperand ;
if ( tok - > astOperand1 ( ) & & tok - > astOperand1 ( ) - > valueType ( ) & & tok - > astOperand1 ( ) - > valueType ( ) - > pointer ! = 0 ) {
2018-04-03 21:32:37 +02:00
pointerOperand = tok - > astOperand1 ( ) ;
2018-05-09 09:06:49 +02:00
numericOperand = tok - > astOperand2 ( ) ;
2018-05-10 17:57:16 +02:00
} else if ( tok - > astOperand2 ( ) & & tok - > astOperand2 ( ) - > valueType ( ) & & tok - > astOperand2 ( ) - > valueType ( ) - > pointer ! = 0 ) {
2018-04-03 21:32:37 +02:00
pointerOperand = tok - > astOperand2 ( ) ;
2018-05-09 09:06:49 +02:00
numericOperand = tok - > astOperand1 ( ) ;
2018-05-10 17:57:16 +02:00
} else
2016-10-09 15:15:29 +02:00
continue ;
2018-05-09 09:06:49 +02:00
if ( numericOperand & & numericOperand - > valueType ( ) & & ! numericOperand - > valueType ( ) - > isIntegral ( ) )
continue ;
2022-06-03 13:11:54 +02:00
const ValueFlow : : Value * numValue = numericOperand ? numericOperand - > getValue ( 0 ) : nullptr ;
if ( numValue & & numValue - > intvalue = = 0 ) // don't warn for arithmetic with 0
continue ;
2020-02-13 16:27:06 +01:00
const ValueFlow : : Value * value = pointerOperand - > getValue ( 0 ) ;
2016-10-09 15:15:29 +02:00
if ( ! value )
continue ;
2021-02-24 22:00:06 +01:00
if ( ! mSettings - > certainty . isEnabled ( Certainty : : inconclusive ) & & value - > isInconclusive ( ) )
2016-10-09 15:15:29 +02:00
continue ;
2021-02-24 22:00:06 +01:00
if ( value - > condition & & ! mSettings - > severity . isEnabled ( Severity : : warning ) )
2016-10-09 15:15:29 +02:00
continue ;
2018-12-29 21:34:22 +01:00
if ( value - > condition )
redundantConditionWarning ( tok , value , value - > condition , value - > isInconclusive ( ) ) ;
else
pointerArithmeticError ( tok , value , value - > isInconclusive ( ) ) ;
2016-10-09 15:15:29 +02:00
}
}
}
2018-12-29 21:34:22 +01:00
static std : : string arithmeticTypeString ( const Token * tok )
2016-10-09 15:15:29 +02:00
{
2018-05-02 12:57:24 +02:00
if ( tok & & tok - > str ( ) [ 0 ] = = ' - ' )
2018-12-29 21:34:22 +01:00
return " subtraction " ;
2018-05-02 12:57:24 +02:00
else if ( tok & & tok - > str ( ) [ 0 ] = = ' + ' )
2018-12-29 21:34:22 +01:00
return " addition " ;
2018-05-02 12:57:24 +02:00
else
2018-12-29 21:34:22 +01:00
return " arithmetic " ;
}
2018-05-02 12:57:24 +02:00
2018-12-30 11:59:06 +01:00
void CheckNullPointer : : pointerArithmeticError ( const Token * tok , const ValueFlow : : Value * value , bool inconclusive )
{
2018-12-29 21:34:22 +01:00
std : : string arithmetic = arithmeticTypeString ( tok ) ;
2016-10-09 15:15:29 +02:00
std : : string errmsg ;
2018-05-02 12:57:24 +02:00
if ( tok & & tok - > str ( ) [ 0 ] = = ' - ' ) {
2018-12-29 21:34:22 +01:00
errmsg = " Overflow in pointer arithmetic, NULL pointer is subtracted. " ;
2018-04-03 21:32:37 +02:00
} else {
2018-12-29 21:34:22 +01:00
errmsg = " Pointer " + arithmetic + " with NULL pointer. " ;
2018-04-03 21:32:37 +02:00
}
2018-05-02 12:57:24 +02:00
const ErrorPath errorPath = getErrorPath ( tok , value , " Null pointer " + arithmetic ) ;
2018-12-29 21:34:22 +01:00
reportError ( errorPath ,
Severity : : error ,
" nullPointerArithmetic " ,
errmsg ,
2020-01-01 09:09:10 +01:00
CWE_INCORRECT_CALCULATION ,
2021-02-24 22:00:06 +01:00
inconclusive ? Certainty : : inconclusive : Certainty : : normal ) ;
2018-12-29 21:34:22 +01:00
}
2016-10-09 15:15:29 +02:00
2018-12-30 11:59:06 +01:00
void CheckNullPointer : : redundantConditionWarning ( const Token * tok , const ValueFlow : : Value * value , const Token * condition , bool inconclusive )
{
2018-12-29 21:34:22 +01:00
std : : string arithmetic = arithmeticTypeString ( tok ) ;
std : : string errmsg ;
if ( tok & & tok - > str ( ) [ 0 ] = = ' - ' ) {
errmsg = ValueFlow : : eitherTheConditionIsRedundant ( condition ) + " or there is overflow in pointer " + arithmetic + " . " ;
} else {
errmsg = ValueFlow : : eitherTheConditionIsRedundant ( condition ) + " or there is pointer arithmetic with NULL pointer. " ;
}
const ErrorPath errorPath = getErrorPath ( tok , value , " Null pointer " + arithmetic ) ;
2018-05-01 17:30:29 +02:00
reportError ( errorPath ,
2018-12-29 21:34:22 +01:00
Severity : : warning ,
" nullPointerArithmeticRedundantCheck " ,
2016-10-09 15:15:29 +02:00
errmsg ,
2020-01-01 09:09:10 +01:00
CWE_INCORRECT_CALCULATION ,
2021-02-24 22:00:06 +01:00
inconclusive ? Certainty : : inconclusive : Certainty : : normal ) ;
2016-10-09 15:15:29 +02:00
}
2018-12-18 07:56:33 +01:00
2018-12-25 21:11:23 +01:00
std : : string CheckNullPointer : : MyFileInfo : : toString ( ) const
{
2018-12-26 15:56:10 +01:00
return CTU : : toString ( unsafeUsage ) ;
2018-12-25 21:11:23 +01:00
}
2022-09-16 18:58:59 +02:00
// NOLINTNEXTLINE(readability-non-const-parameter) - used as callback so we need to preserve the signature
2019-03-23 08:36:10 +01:00
static bool isUnsafeUsage ( const Check * check , const Token * vartok , MathLib : : bigint * value )
2018-12-18 07:56:33 +01:00
{
2019-03-23 08:36:10 +01:00
( void ) value ;
2018-12-29 09:26:57 +01:00
const CheckNullPointer * checkNullPointer = dynamic_cast < const CheckNullPointer * > ( check ) ;
bool unknown = false ;
return checkNullPointer & & checkNullPointer - > isPointerDeRef ( vartok , unknown ) ;
2018-12-18 07:56:33 +01:00
}
2018-12-25 21:11:23 +01:00
Check : : FileInfo * CheckNullPointer : : getFileInfo ( const Tokenizer * tokenizer , const Settings * settings ) const
{
2018-12-29 09:26:57 +01:00
CheckNullPointer check ( tokenizer , settings , nullptr ) ;
const std : : list < CTU : : FileInfo : : UnsafeUsage > & unsafeUsage = CTU : : getUnsafeUsage ( tokenizer , settings , & check , : : isUnsafeUsage ) ;
2018-12-26 19:17:49 +01:00
if ( unsafeUsage . empty ( ) )
return nullptr ;
2018-12-25 21:11:23 +01:00
MyFileInfo * fileInfo = new MyFileInfo ;
2018-12-26 19:17:49 +01:00
fileInfo - > unsafeUsage = unsafeUsage ;
2018-12-25 21:11:23 +01:00
return fileInfo ;
}
Check : : FileInfo * CheckNullPointer : : loadFileInfoFromXml ( const tinyxml2 : : XMLElement * xmlElement ) const
{
const std : : list < CTU : : FileInfo : : UnsafeUsage > & unsafeUsage = CTU : : loadUnsafeUsageListFromXml ( xmlElement ) ;
if ( unsafeUsage . empty ( ) )
return nullptr ;
MyFileInfo * fileInfo = new MyFileInfo ;
fileInfo - > unsafeUsage = unsafeUsage ;
return fileInfo ;
}
bool CheckNullPointer : : analyseWholeProgram ( const CTU : : FileInfo * ctu , const std : : list < Check : : FileInfo * > & fileInfo , const Settings & settings , ErrorLogger & errorLogger )
{
if ( ! ctu )
return false ;
bool foundErrors = false ;
( void ) settings ; // This argument is unused
2018-12-30 16:23:25 +01:00
const std : : map < std : : string , std : : list < const CTU : : FileInfo : : CallBase * > > callsMap = ctu - > getCallsMap ( ) ;
2018-12-25 21:11:23 +01:00
2023-05-31 16:51:03 +02:00
for ( const Check : : FileInfo * fi1 : fileInfo ) {
2022-12-19 20:01:12 +01:00
const MyFileInfo * fi = dynamic_cast < const MyFileInfo * > ( fi1 ) ;
2018-12-25 21:11:23 +01:00
if ( ! fi )
continue ;
for ( const CTU : : FileInfo : : UnsafeUsage & unsafeUsage : fi - > unsafeUsage ) {
2018-12-30 18:31:37 +01:00
for ( int warning = 0 ; warning < = 1 ; warning + + ) {
2021-02-24 22:00:06 +01:00
if ( warning = = 1 & & ! settings . severity . isEnabled ( Severity : : warning ) )
2018-12-30 18:31:37 +01:00
break ;
2018-12-25 21:11:23 +01:00
2020-05-23 07:16:49 +02:00
const std : : list < ErrorMessage : : FileLocation > & locationList =
2021-01-09 20:32:38 +01:00
CTU : : FileInfo : : getErrorPath ( CTU : : FileInfo : : InvalidValueType : : null ,
2021-01-10 14:46:19 +01:00
unsafeUsage ,
callsMap ,
" Dereferencing argument ARG that is null " ,
nullptr ,
warning ) ;
2018-12-30 18:31:37 +01:00
if ( locationList . empty ( ) )
continue ;
2018-12-25 21:11:23 +01:00
2020-05-23 07:16:49 +02:00
const ErrorMessage errmsg ( locationList ,
emptyString ,
warning ? Severity : : warning : Severity : : error ,
" Null pointer dereference: " + unsafeUsage . myArgumentName ,
" ctunullpointer " ,
2021-02-24 22:00:06 +01:00
CWE_NULL_POINTER_DEREFERENCE , Certainty : : normal ) ;
2018-12-30 18:31:37 +01:00
errorLogger . reportErr ( errmsg ) ;
foundErrors = true ;
break ;
}
2018-12-25 21:11:23 +01:00
}
}
return foundErrors ;
}