2010-10-31 11:51:25 +01:00
/*
* Cppcheck - A tool for static C / C + + code analysis
2014-02-15 07:45:39 +01:00
* Copyright ( C ) 2007 - 2014 Daniel Marjamäki and 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"
# include "mathlib.h"
2011-03-06 21:23:33 +01:00
# include "symboldatabase.h"
2011-10-12 22:11:27 +02:00
# include <cctype>
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
}
//---------------------------------------------------------------------------
/**
* @ 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
* @ param value 0 = > invalid with null pointers as parameter .
2013-06-07 06:37:53 +02:00
* 1 - . . = > only invalid with uninitialized data .
2010-10-31 11:51:25 +01:00
*/
2013-07-15 18:55:40 +02:00
void CheckNullPointer : : parseFunctionCall ( const Token & tok , std : : list < const Token * > & var , const Library * library , unsigned char value )
2010-10-31 11:51:25 +01:00
{
2012-02-11 12:26:48 +01:00
if ( Token : : Match ( & tok , " %var% ( ) " ) | | ! tok . tokAt ( 2 ) )
return ;
const Token * firstParam = tok . tokAt ( 2 ) ;
const Token * secondParam = firstParam - > nextArgument ( ) ;
2010-10-31 11:51:25 +01:00
// 1st parameter..
2012-02-11 12:26:48 +01:00
if ( ( Token : : Match ( firstParam , " %var% ,|) " ) & & firstParam - > varId ( ) > 0 ) | |
2013-06-07 06:37:53 +02:00
( value = = 0 & & Token : : Match ( firstParam , " 0|NULL ,|) " ) ) ) {
2014-06-24 21:53:00 +02:00
if ( value = = 0 & & Token : : Match ( & tok , " snprintf|vsnprintf|fnprintf|vfnprintf " ) & & secondParam & & secondParam - > str ( ) ! = " 0 " ) // Only if length (second parameter) is not zero
2012-02-11 12:26:48 +01:00
var . push_back ( firstParam ) ;
2014-02-15 16:12:37 +01:00
else if ( value = = 0 & & library ! = nullptr & & library - > isnullargbad ( tok . str ( ) , 1 ) )
2013-07-15 21:57:10 +02:00
var . push_back ( firstParam ) ;
2014-02-15 16:12:37 +01:00
else if ( value = = 1 & & library ! = nullptr & & library - > isuninitargbad ( tok . str ( ) , 1 ) )
2013-07-15 21:57:10 +02:00
var . push_back ( firstParam ) ;
}
2010-10-31 11:51:25 +01:00
// 2nd parameter..
2013-11-04 23:09:15 +01:00
if ( ( value = = 0 & & Token : : Match ( secondParam , " 0|NULL ,|) " ) ) | | ( secondParam & & secondParam - > varId ( ) > 0 & & Token : : Match ( secondParam - > next ( ) , " [,)] " ) ) ) {
2014-06-24 21:53:00 +02:00
if ( value = = 0 & & library ! = nullptr & & library - > isnullargbad ( tok . str ( ) , 2 ) )
2013-07-15 21:57:10 +02:00
var . push_back ( secondParam ) ;
2014-02-15 16:12:37 +01:00
else if ( value = = 1 & & library ! = nullptr & & library - > isuninitargbad ( tok . str ( ) , 2 ) )
2013-07-15 21:57:10 +02:00
var . push_back ( secondParam ) ;
2010-10-31 11:51:25 +01:00
}
2011-09-05 20:18:58 +02:00
2012-11-06 19:54:52 +01:00
if ( Token : : Match ( & tok , " printf|sprintf|snprintf|fprintf|fnprintf|scanf|sscanf|fscanf|wprintf|swprintf|fwprintf|wscanf|swscanf|fwscanf " ) ) {
2011-10-12 22:11:27 +02:00
const Token * argListTok = 0 ; // Points to first va_list argument
std : : string formatString ;
2012-11-06 19:54:52 +01:00
bool scan = Token : : Match ( & tok , " scanf|sscanf|fscanf|wscanf|swscanf|fwscanf " ) ;
2011-10-23 11:23:48 +02:00
2012-11-06 19:54:52 +01:00
if ( Token : : Match ( & tok , " printf|scanf|wprintf|wscanf ( %str% " ) ) {
2012-02-11 12:26:48 +01:00
formatString = firstParam - > strValue ( ) ;
argListTok = secondParam ;
2012-11-06 19:54:52 +01:00
} else if ( Token : : Match ( & tok , " sprintf|fprintf|sscanf|fscanf|fwprintf|fwscanf|swscanf " ) ) {
2012-02-11 12:26:48 +01:00
const Token * formatStringTok = secondParam ; // Find second parameter (format string)
2012-04-25 09:56:07 +02:00
if ( formatStringTok & & formatStringTok - > type ( ) = = Token : : eString ) {
2011-10-23 11:23:48 +02:00
argListTok = formatStringTok - > nextArgument ( ) ; // Find third parameter (first argument of va_args)
2012-02-11 12:26:48 +01:00
formatString = formatStringTok - > strValue ( ) ;
2011-10-23 11:23:48 +02:00
}
2012-11-06 19:54:52 +01:00
} else if ( Token : : Match ( & tok , " snprintf|fnprintf|swprintf " ) & & secondParam ) {
2012-05-11 19:38:19 +02:00
const Token * formatStringTok = secondParam - > nextArgument ( ) ; // Find third parameter (format string)
2012-04-25 09:56:07 +02:00
if ( formatStringTok & & formatStringTok - > type ( ) = = Token : : eString ) {
2011-10-23 11:23:48 +02:00
argListTok = formatStringTok - > nextArgument ( ) ; // Find fourth parameter (first argument of va_args)
2012-02-11 12:26:48 +01:00
formatString = formatStringTok - > strValue ( ) ;
2011-10-23 11:23:48 +02:00
}
2011-10-12 22:11:27 +02:00
}
2011-10-23 11:23:48 +02:00
2011-10-13 20:53:06 +02:00
if ( argListTok ) {
2011-10-12 22:11:27 +02:00
bool percent = false ;
2011-10-13 20:53:06 +02:00
for ( std : : string : : iterator i = formatString . begin ( ) ; i ! = formatString . end ( ) ; + + i ) {
if ( * i = = ' % ' ) {
2011-10-12 22:11:27 +02:00
percent = ! percent ;
2011-11-28 21:32:07 +01:00
} else if ( percent ) {
2011-12-02 17:09:32 +01:00
percent = false ;
bool _continue = false ;
2014-03-18 21:41:47 +01:00
while ( ! std : : isalpha ( ( unsigned char ) * i ) ) {
2011-12-02 17:09:32 +01:00
if ( * i = = ' * ' ) {
if ( scan )
_continue = true ;
else
argListTok = argListTok - > nextArgument ( ) ;
}
2011-11-28 21:32:07 +01:00
+ + i ;
2011-11-30 20:23:29 +01:00
if ( ! argListTok | | i = = formatString . end ( ) )
return ;
2011-11-28 21:32:07 +01:00
}
2011-12-02 17:09:32 +01:00
if ( _continue )
continue ;
2011-11-28 21:32:07 +01:00
2011-10-16 07:06:18 +02:00
if ( ( * i = = ' n ' | | * i = = ' s ' | | scan ) & & ( ! scan | | value = = 0 ) ) {
2013-11-04 23:09:15 +01:00
if ( ( value = = 0 & & argListTok - > str ( ) = = " 0 " ) | | ( argListTok - > varId ( ) > 0 & & Token : : Match ( argListTok , " %var% [,)] " ) ) ) {
2011-10-12 22:11:27 +02:00
var . push_back ( argListTok ) ;
}
}
2011-11-28 21:32:07 +01:00
if ( * i ! = ' m ' ) // %m is a non-standard glibc extension that requires no parameter
argListTok = argListTok - > nextArgument ( ) ; // Find next argument
2011-10-16 07:50:34 +02:00
if ( ! argListTok )
2011-10-16 07:06:18 +02:00
break ;
2011-10-12 22:11:27 +02:00
}
}
}
2011-09-05 20:18:58 +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
*/
2013-02-01 19:10:14 +01:00
bool CheckNullPointer : : isPointerDeRef ( const Token * tok , bool & unknown )
2010-10-31 15:32:19 +01:00
{
2014-01-30 05:26:48 +01:00
// THIS ARRAY MUST BE ORDERED ALPHABETICALLY
static const char * const stl_stream [ ] = {
" fstream " , " ifstream " , " iostream " , " istream " ,
" istringstream " , " ofstream " , " ostream " , " ostringstream " ,
" stringstream " , " wistringstream " , " wostringstream " , " wstringstream "
} ;
2010-10-31 15:32:19 +01:00
unknown = false ;
2014-05-22 19:48:00 +02:00
const Token * parent = tok - > astParent ( ) ;
if ( ! parent )
return false ;
bool firstOperand = parent - > astOperand1 ( ) = = tok ;
while ( parent - > str ( ) = = " ( " & & ( parent - > astOperand2 ( ) = = nullptr & & parent - > strAt ( 1 ) ! = " ) " ) ) { // Skip over casts
parent = parent - > astParent ( ) ;
if ( ! parent )
return false ;
2013-04-03 10:27:08 +02:00
}
2010-10-31 15:32:19 +01:00
// Dereferencing pointer..
2014-05-22 19:48:00 +02:00
if ( parent - > str ( ) = = " * " & & ! parent - > astOperand2 ( ) & & ! Token : : Match ( parent - > tokAt ( - 2 ) , " sizeof|decltype|typeof " ) )
return true ;
// array access
if ( parent - > str ( ) = = " [ " & & ( ! parent - > astParent ( ) | | parent - > astParent ( ) - > str ( ) ! = " & " ) )
2010-10-31 15:32:19 +01:00
return true ;
2011-10-18 19:34:14 +02:00
// read/write member variable
2014-05-22 19:48:00 +02:00
if ( firstOperand & & parent - > str ( ) = = " . " & & ( ! parent - > astParent ( ) | | parent - > astParent ( ) - > str ( ) ! = " & " ) ) {
if ( ! parent - > astParent ( ) | | parent - > astParent ( ) - > str ( ) ! = " ( " | | parent - > astParent ( ) = = tok - > previous ( ) )
2011-10-18 19:34:14 +02:00
return true ;
unknown = true ;
return false ;
}
2010-10-31 15:32:19 +01:00
if ( Token : : Match ( tok , " %var% ( " ) )
return true ;
2010-10-31 15:46:08 +01:00
if ( Token : : Match ( tok , " %var% = %var% . " ) & &
tok - > varId ( ) > 0 & &
tok - > varId ( ) = = tok - > tokAt ( 2 ) - > varId ( ) )
return true ;
2012-01-25 15:16:22 +01:00
// std::string dereferences nullpointers
2014-05-22 19:48:00 +02:00
if ( Token : : Match ( parent - > tokAt ( - 3 ) , " std :: string|wstring ( " ) & & tok - > strAt ( 1 ) = = " ) " )
2012-01-25 15:16:22 +01:00
return true ;
2014-05-22 19:48:00 +02:00
if ( Token : : Match ( parent - > previous ( ) , " %var% ( " ) & & tok - > strAt ( 1 ) = = " ) " ) {
2013-02-01 19:10:14 +01:00
const Variable * var = tok - > tokAt ( - 2 ) - > variable ( ) ;
2012-11-10 19:53:20 +01:00
if ( var & & ! var - > isPointer ( ) & & ! var - > isArray ( ) & & Token : : Match ( var - > typeStartToken ( ) , " std :: string|wstring !!:: " ) )
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 ( ) ;
else if ( parent - > astOperand2 ( ) = = tok )
ovar = parent - > astOperand1 ( ) - > variable ( ) ;
}
2013-02-01 19:10:14 +01:00
if ( ovar & & ! ovar - > isPointer ( ) & & ! ovar - > isArray ( ) & & Token : : Match ( ovar - > typeStartToken ( ) , " std :: string|wstring !!:: " ) )
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
// check if function can assign pointer
2013-03-29 17:55:29 +01:00
bool CheckNullPointer : : CanFunctionAssignPointer ( const Token * functiontoken , unsigned int varid , bool & unknown )
2011-12-25 17:01:45 +01:00
{
2012-01-21 19:55:32 +01:00
if ( Token : : Match ( functiontoken , " if|while|for|switch|sizeof|catch " ) )
2011-12-25 17:01:45 +01:00
return false ;
2012-09-16 16:41:15 +02:00
unsigned int argumentNumber = 0 ;
2011-12-25 17:01:45 +01:00
for ( const Token * arg = functiontoken - > tokAt ( 2 ) ; arg ; arg = arg - > nextArgument ( ) ) {
if ( Token : : Match ( arg , " %varid% [,)] " , varid ) ) {
2013-01-31 06:41:18 +01:00
const Function * func = functiontoken - > function ( ) ;
2012-09-06 18:33:15 +02:00
if ( ! func ) { // Unknown function
unknown = true ;
return true ; // assume that the function might assign the pointer
2011-12-25 17:01:45 +01:00
}
2012-09-06 18:33:15 +02:00
const Variable * var = func - > getArgumentVar ( argumentNumber ) ;
if ( ! var ) { // Unknown variable
unknown = true ;
return true ;
} else if ( var - > isReference ( ) ) // Assume every pointer passed by reference is assigned
return true ;
else
2011-12-25 17:01:45 +01:00
return false ;
}
2012-09-06 18:33:15 +02:00
+ + argumentNumber ;
2011-12-25 17:01:45 +01:00
}
// pointer is not passed
return false ;
}
2010-10-31 11:51:25 +01:00
void CheckNullPointer : : nullPointerLinkedList ( )
{
2011-12-09 22:28:10 +01:00
const SymbolDatabase * const symbolDatabase = _tokenizer - > getSymbolDatabase ( ) ;
2010-12-31 14:37:34 +01:00
// looping through items in a linked list in a inner loop.
// Here is an example:
// for (const Token *tok = tokens; tok; tok = tok->next) {
// if (tok->str() == "hello")
// tok = tok->next; // <- tok might become a null pointer!
// }
2011-12-09 22:28:10 +01:00
for ( std : : list < Scope > : : const_iterator i = symbolDatabase - > scopeList . begin ( ) ; i ! = symbolDatabase - > scopeList . end ( ) ; + + i ) {
const Token * const tok1 = i - > classDef ;
// search for a "for" scope..
if ( i - > type ! = Scope : : eFor | | ! tok1 )
2010-10-31 11:51:25 +01:00
continue ;
2010-12-31 14:37:34 +01:00
// is there any dereferencing occurring in the for statement
2011-11-27 07:30:58 +01:00
const Token * end2 = tok1 - > linkAt ( 1 ) ;
2011-11-26 21:02:04 +01:00
for ( const Token * tok2 = tok1 - > tokAt ( 2 ) ; tok2 ! = end2 ; tok2 = tok2 - > next ( ) ) {
2010-12-15 18:45:53 +01:00
// Dereferencing a variable inside the "for" parentheses..
2011-11-26 21:02:04 +01:00
if ( Token : : Match ( tok2 , " %var% . %var% " ) ) {
2012-01-25 15:16:22 +01:00
// Is this variable a pointer?
2013-02-06 06:39:58 +01:00
const Variable * var = tok2 - > variable ( ) ;
2012-01-25 15:16:22 +01:00
if ( ! var | | ! var - > isPointer ( ) )
2010-10-31 11:51:25 +01:00
continue ;
2013-02-06 06:39:58 +01:00
// Variable id for dereferenced variable
const unsigned int varid ( tok2 - > varId ( ) ) ;
2013-10-12 10:50:59 +02:00
// We don't support variables without a varid
if ( varid = = 0 )
continue ;
2012-01-25 15:16:22 +01:00
if ( Token : : Match ( tok2 - > tokAt ( - 2 ) , " %varid% ? " , varid ) )
continue ;
2010-10-31 11:51:25 +01:00
// Check usage of dereferenced variable in the loop..
2012-01-25 15:16:22 +01:00
for ( std : : list < Scope * > : : const_iterator j = i - > nestedList . begin ( ) ; j ! = i - > nestedList . end ( ) ; + + j ) {
Scope * scope = * j ;
if ( scope - > type ! = Scope : : eWhile )
continue ;
2010-12-31 14:37:34 +01:00
// TODO: are there false negatives for "while ( %varid% ||"
2012-01-25 15:16:22 +01:00
if ( Token : : Match ( scope - > classDef - > next ( ) , " ( %varid% &&|) " , varid ) ) {
2010-12-31 14:37:34 +01:00
// Make sure there is a "break" or "return" inside the loop.
// Without the "break" a null pointer could be dereferenced in the
// for statement.
2012-01-25 15:16:22 +01:00
for ( const Token * tok4 = scope - > classStart ; tok4 ; tok4 = tok4 - > next ( ) ) {
2012-08-12 12:13:07 +02:00
if ( tok4 = = i - > classEnd ) {
nullPointerError ( tok1 , var - > name ( ) , scope - > classDef ) ;
break ;
2010-10-31 11:51:25 +01:00
}
2010-12-31 14:37:34 +01:00
// There is a "break" or "return" inside the loop.
// TODO: there can be false negatives. There could still be
// execution paths that are not properly terminated
2010-10-31 11:51:25 +01:00
else if ( tok4 - > str ( ) = = " break " | | tok4 - > str ( ) = = " return " )
break ;
}
}
}
}
}
}
}
void CheckNullPointer : : nullPointerByDeRefAndChec ( )
{
2014-01-17 17:47:59 +01:00
for ( const Token * tok = _tokenizer - > tokens ( ) ; tok ; tok = tok - > next ( ) ) {
const Variable * var = tok - > variable ( ) ;
if ( ! var | | ! var - > isPointer ( ) | | tok = = var - > nameToken ( ) )
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
2014-03-18 17:04:33 +01:00
if ( ! _settings - > inconclusive & & value - > inconclusive )
continue ;
2014-01-17 17:47:59 +01:00
// Is pointer used as function parameter?
if ( Token : : Match ( tok - > previous ( ) , " [(,] %var% [,)] " ) ) {
const Token * ftok = tok - > previous ( ) ;
while ( ftok & & ftok - > str ( ) ! = " ( " ) {
if ( ftok - > str ( ) = = " ) " )
ftok = ftok - > link ( ) ;
ftok = ftok - > previous ( ) ;
}
if ( ! ftok | | ! ftok - > previous ( ) )
2013-02-06 06:39:58 +01:00
continue ;
2014-01-17 17:47:59 +01:00
std : : list < const Token * > varlist ;
parseFunctionCall ( * ftok - > previous ( ) , varlist , & _settings - > library , 0 ) ;
if ( std : : find ( varlist . begin ( ) , varlist . end ( ) , tok ) ! = varlist . end ( ) ) {
2014-02-15 16:12:37 +01:00
if ( value - > condition = = nullptr )
2014-01-22 06:38:25 +01:00
nullPointerError ( tok , tok - > str ( ) ) ;
2014-01-17 17:47:59 +01:00
else if ( _settings - > isEnabled ( " warning " ) )
nullPointerError ( tok , tok - > str ( ) , value - > condition , value - > inconclusive ) ;
2013-05-03 16:18:44 +02:00
}
2014-01-17 17:47:59 +01:00
continue ;
}
2013-05-03 16:18:44 +02:00
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 ) ) {
2014-01-22 20:16:31 +01:00
if ( _settings - > inconclusive & & unknown ) {
2014-02-15 16:12:37 +01:00
if ( value - > condition = = nullptr )
2014-01-22 20:16:31 +01:00
nullPointerError ( tok , tok - > str ( ) , true ) ;
else
nullPointerError ( tok , tok - > str ( ) , value - > condition , 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
2014-02-15 16:12:37 +01:00
if ( value - > condition = = nullptr )
2014-01-22 20:16:31 +01:00
nullPointerError ( tok , tok - > str ( ) , value - > inconclusive ) ;
2014-01-17 17:47:59 +01:00
else if ( _settings - > isEnabled ( " warning " ) )
nullPointerError ( tok , tok - > str ( ) , value - > condition , value - > inconclusive ) ;
2010-10-31 11:51:25 +01:00
}
}
void CheckNullPointer : : nullPointer ( )
{
nullPointerLinkedList ( ) ;
2013-02-02 15:46:29 +01:00
if ( _settings - > isEnabled ( " warning " ) ) {
nullPointerByDeRefAndChec ( ) ;
2013-04-26 09:19:39 +02:00
nullPointerDefaultArgument ( ) ;
2013-02-02 15:46:29 +01:00
}
2010-10-31 11:51:25 +01: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 ( )
{
2012-01-21 19:55:32 +01:00
const SymbolDatabase * symbolDatabase = _tokenizer - > getSymbolDatabase ( ) ;
2010-10-31 11:51:25 +01:00
2014-01-30 05:26:48 +01:00
// THIS ARRAY MUST BE ORDERED ALPHABETICALLY
static const char * const stl_stream [ ] = {
" fstream " , " ifstream " , " iostream " , " istream " ,
" istringstream " , " stringstream " , " wistringstream " , " wstringstream "
} ;
2012-11-25 14:55:31 +01:00
const std : : size_t functions = symbolDatabase - > functionScopes . size ( ) ;
for ( std : : size_t i = 0 ; i < functions ; + + i ) {
const Scope * scope = symbolDatabase - > functionScopes [ i ] ;
if ( scope - > function = = 0 | | ! 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
2012-11-25 14:55:31 +01:00
const Token * tok = scope - > classStart ;
2012-01-26 16:50:59 +01:00
2013-04-10 21:57:22 +02:00
if ( scope - > function & & 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
2012-11-25 14:55:31 +01:00
for ( ; tok ! = scope - > classEnd ; tok = tok - > next ( ) ) {
2013-10-12 15:19:15 +02:00
if ( Token : : Match ( tok , " sizeof|decltype|typeid|typeof ( " ) )
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 ) ;
2012-02-11 12:26:48 +01:00
else if ( Token : : Match ( tok - > previous ( ) , " !!. %var% ( " ) & & ( tok - > previous ( ) - > str ( ) ! = " :: " | | tok - > strAt ( - 2 ) = = " std " ) ) {
2012-04-02 12:12:02 +02:00
if ( Token : : simpleMatch ( tok - > tokAt ( 2 ) , " 0 ) " ) & & tok - > varId ( ) ) { // constructor call
2013-02-06 06:39:58 +01:00
const Variable * var = tok - > variable ( ) ;
2012-11-06 19:54:52 +01:00
if ( var & & ! var - > isPointer ( ) & & ! var - > isArray ( ) & & Token : : Match ( var - > typeStartToken ( ) , " std :: string|wstring !!:: " ) )
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 ;
2013-07-15 18:55:40 +02:00
parseFunctionCall ( * tok , var , & _settings - > library , 0 ) ;
2012-04-02 12:12:02 +02:00
// is one of the var items a NULL pointer?
for ( std : : list < const Token * > : : const_iterator it = var . begin ( ) ; it ! = var . end ( ) ; + + it ) {
2013-06-07 06:37:53 +02:00
if ( Token : : Match ( * it , " 0|NULL [,)] " ) ) {
2012-04-02 12:12:02 +02:00
nullPointerError ( * it ) ;
}
2010-12-26 19:29:58 +01:00
}
}
2012-11-06 19:54:52 +01:00
} else if ( Token : : Match ( tok , " std :: string|wstring ( 0 ) " ) )
2012-01-25 15:16:22 +01:00
nullPointerError ( tok ) ;
2012-03-16 17:24:03 +01:00
else if ( Token : : simpleMatch ( tok - > previous ( ) , " >> 0 " ) ) { // Only checking input stream operations is safe here, because otherwise 0 can be an integer as well
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 ( ) ;
2014-01-30 05:26:48 +01:00
if ( var & & var - > isStlType ( stl_stream ) )
2012-03-16 17:24:03 +01:00
nullPointerError ( tok ) ;
}
}
2014-02-16 11:47:52 +01:00
const Variable * ovar = nullptr ;
2013-08-24 21:50:19 +02:00
if ( Token : : Match ( tok , " 0 ==|!= %var% !!. " ) )
2013-02-06 06:39:58 +01:00
ovar = tok - > tokAt ( 2 ) - > variable ( ) ;
2012-04-02 12:12:02 +02:00
else if ( Token : : Match ( tok , " %var% ==|!= 0 " ) )
2013-02-06 06:39:58 +01:00
ovar = tok - > variable ( ) ;
2012-11-30 07:08:16 +01:00
else if ( Token : : Match ( tok , " %var% =|+ 0 ) | ] | , | ; | + " ))
2013-02-06 06:39:58 +01:00
ovar = tok - > variable ( ) ;
if ( ovar & & ! ovar - > isPointer ( ) & & ! ovar - > isArray ( ) & & Token : : Match ( ovar - > typeStartToken ( ) , " std :: string|wstring !!:: " ) )
nullPointerError ( tok ) ;
2010-10-31 11:51:25 +01:00
}
}
}
2013-04-25 09:25:56 +02:00
/**
* @ brief If tok is a function call that passes in a pointer such that
* the pointer may be modified , this function will remove that
* pointer from pointerArgs .
*/
void CheckNullPointer : : removeAssignedVarFromSet ( const Token * tok , std : : set < unsigned int > & pointerArgs )
{
// If a pointer's address is passed into a function, stop considering it
if ( Token : : Match ( tok - > previous ( ) , " [;{}] %var% ( " ) ) {
2013-04-27 05:06:17 +02:00
// Common functions that are known NOT to modify their pointer argument
const char safeFunctions [ ] = " printf|sprintf|fprintf|vprintf " ;
2013-04-25 09:25:56 +02:00
const Token * endParen = tok - > next ( ) - > link ( ) ;
for ( const Token * tok2 = tok - > next ( ) ; tok2 ! = endParen ; tok2 = tok2 - > next ( ) ) {
if ( tok2 - > isName ( ) & & tok2 - > varId ( ) > 0 & & ! Token : : Match ( tok , safeFunctions ) ) {
pointerArgs . erase ( tok2 - > varId ( ) ) ;
}
}
}
}
2010-10-31 11:51:25 +01:00
2012-11-21 08:56:17 +01:00
/**
* @ brief Does one part of the check for nullPointer ( ) .
* - # default argument that sets a pointer to 0
* - # dereference pointer
*/
void CheckNullPointer : : nullPointerDefaultArgument ( )
{
const SymbolDatabase * symbolDatabase = _tokenizer - > getSymbolDatabase ( ) ;
2012-11-25 12:40:14 +01:00
const std : : size_t functions = symbolDatabase - > functionScopes . size ( ) ;
for ( std : : size_t i = 0 ; i < functions ; + + i ) {
const Scope * scope = symbolDatabase - > functionScopes [ i ] ;
if ( scope - > function = = 0 | | ! scope - > function - > hasBody ) // We only look for functions with a body
2012-11-21 08:56:17 +01:00
continue ;
// Scan the argument list for default arguments that are pointers and
// which default to a NULL pointer if no argument is specified.
std : : set < unsigned int > pointerArgs ;
2012-11-25 12:40:14 +01:00
for ( const Token * tok = scope - > function - > arg ; tok ! = scope - > function - > arg - > link ( ) ; tok = tok - > next ( ) ) {
2012-11-21 08:56:17 +01:00
if ( Token : : Match ( tok , " %var% = 0 ,|) " ) & & tok - > varId ( ) ! = 0 ) {
2013-02-06 06:39:58 +01:00
const Variable * var = tok - > variable ( ) ;
2012-11-21 08:56:17 +01:00
if ( var & & var - > isPointer ( ) )
pointerArgs . insert ( tok - > varId ( ) ) ;
}
}
// Report an error if any of the default-NULL arguments are dereferenced
if ( ! pointerArgs . empty ( ) ) {
2012-11-25 12:40:14 +01:00
for ( const Token * tok = scope - > classStart ; tok ! = scope - > classEnd ; tok = tok - > next ( ) ) {
2013-04-25 09:25:56 +02:00
2012-11-21 08:56:17 +01:00
// If we encounter a possible NULL-pointer check, skip over its body
2014-05-22 19:48:00 +02:00
if ( tok - > str ( ) = = " ? " ) { // TODO: Skip this if the condition is unrelated to the variables
// Find end of statement
tok = tok - > astOperand2 ( ) ;
while ( tok & & ! Token : : Match ( tok , " )|; " ) ) {
if ( tok - > link ( ) & & Token : : Match ( tok , " (|[|<|{ " ) )
tok = tok - > link ( ) ;
tok = tok - > next ( ) ;
}
if ( ! tok )
break ;
} else if ( Token : : simpleMatch ( tok , " if ( " ) ) {
2012-11-21 08:56:17 +01:00
bool dependsOnPointer = false ;
const Token * endOfCondition = tok - > next ( ) - > link ( ) ;
2013-02-28 08:45:21 +01:00
if ( ! endOfCondition )
continue ;
const Token * startOfIfBlock =
2014-02-15 16:12:37 +01:00
Token : : simpleMatch ( endOfCondition , " ) { " ) ? endOfCondition - > next ( ) : nullptr ;
2013-02-28 08:45:21 +01:00
if ( ! startOfIfBlock )
continue ;
// If this if() statement may return, it may be a null
// pointer check for the pointers referenced in its condition
const Token * endOfIf = startOfIfBlock - > link ( ) ;
bool isExitOrReturn =
2014-02-15 16:12:37 +01:00
Token : : findmatch ( startOfIfBlock , " exit|return|throw " , endOfIf ) ! = nullptr ;
2013-02-28 08:45:21 +01:00
2013-04-25 09:25:56 +02:00
if ( Token : : Match ( tok , " if ( %var% == 0 ) " ) ) {
const unsigned int var = tok - > tokAt ( 2 ) - > varId ( ) ;
if ( var > 0 & & pointerArgs . count ( var ) > 0 ) {
2013-02-28 08:45:21 +01:00
if ( isExitOrReturn )
2013-04-25 09:25:56 +02:00
pointerArgs . erase ( var ) ;
2013-02-28 08:45:21 +01:00
else
dependsOnPointer = true ;
2012-11-21 08:56:17 +01:00
}
2013-04-25 09:25:56 +02:00
} else {
for ( const Token * tok2 = tok - > next ( ) ; tok2 ! = endOfCondition ; tok2 = tok2 - > next ( ) ) {
if ( tok2 - > isName ( ) & & tok2 - > varId ( ) > 0 & &
pointerArgs . count ( tok2 - > varId ( ) ) > 0 ) {
// If the if() depends on a pointer and may return, stop
// considering that pointer because it may be a NULL-pointer
// check that returns if the pointer is NULL.
if ( isExitOrReturn )
pointerArgs . erase ( tok2 - > varId ( ) ) ;
else
dependsOnPointer = true ;
}
}
2012-11-21 08:56:17 +01:00
}
2013-04-25 09:25:56 +02:00
2013-02-28 08:45:21 +01:00
if ( dependsOnPointer & & endOfIf ) {
2013-01-10 08:22:54 +01:00
for ( ; tok ! = endOfIf ; tok = tok - > next ( ) ) {
// If a pointer is assigned a new value, stop considering it.
if ( Token : : Match ( tok , " %var% = " ) )
pointerArgs . erase ( tok - > varId ( ) ) ;
2013-04-25 09:25:56 +02:00
else
removeAssignedVarFromSet ( tok , pointerArgs ) ;
2013-01-10 08:22:54 +01:00
}
2012-11-21 08:56:17 +01:00
continue ;
}
}
2013-04-25 09:25:56 +02:00
// If there is a noreturn function (e.g. exit()), stop considering the rest of
// this function.
bool unknown = false ;
if ( Token : : Match ( tok , " return|throw|exit " ) | |
( _tokenizer - > IsScopeNoReturn ( tok , & unknown ) & & ! unknown ) )
break ;
removeAssignedVarFromSet ( tok , pointerArgs ) ;
2012-11-21 08:56:17 +01:00
if ( tok - > varId ( ) = = 0 | | pointerArgs . count ( tok - > varId ( ) ) = = 0 )
continue ;
// If a pointer is assigned a new value, stop considering it.
2013-01-10 08:22:54 +01:00
if ( Token : : Match ( tok , " %var% = " ) )
2012-11-21 08:56:17 +01:00
pointerArgs . erase ( tok - > varId ( ) ) ;
2013-02-28 08:45:21 +01:00
// If a pointer dereference is preceded by an && or ||,
// they serve as a sequence point so the dereference
// may not be executed.
2013-04-25 09:25:56 +02:00
if ( isPointerDeRef ( tok , unknown ) & & ! unknown & &
2013-02-28 08:45:21 +01:00
tok - > strAt ( - 1 ) ! = " && " & & tok - > strAt ( - 1 ) ! = " || " & &
tok - > strAt ( - 2 ) ! = " && " & & tok - > strAt ( - 2 ) ! = " || " )
2012-11-21 08:56:17 +01:00
nullPointerDefaultArgError ( tok , tok - > str ( ) ) ;
}
}
}
}
2010-10-31 11:51:25 +01:00
void CheckNullPointer : : nullPointerError ( const Token * tok )
{
reportError ( tok , Severity : : error , " nullPointer " , " Null pointer dereference " ) ;
}
2014-01-22 20:16:31 +01:00
void CheckNullPointer : : nullPointerError ( const Token * tok , const std : : string & varname , bool inconclusive )
2010-10-31 11:51:25 +01:00
{
2014-01-22 20:16:31 +01:00
reportError ( tok , Severity : : error , " nullPointer " , " Possible null pointer dereference: " + varname , inconclusive ) ;
2010-10-31 11:51:25 +01:00
}
2012-08-05 10:38:48 +02:00
void CheckNullPointer : : nullPointerError ( const Token * tok , const std : : string & varname , const Token * nullCheck , bool inconclusive )
2010-10-31 11:51:25 +01:00
{
2012-08-05 10:38:48 +02:00
std : : list < const Token * > callstack ;
callstack . push_back ( tok ) ;
callstack . push_back ( nullCheck ) ;
const std : : string errmsg ( " Possible null pointer dereference: " + varname + " - otherwise it is redundant to check it against null. " ) ;
2013-04-07 03:41:02 +02:00
reportError ( callstack , Severity : : warning , " nullPointer " , errmsg , inconclusive ) ;
2010-10-31 11:51:25 +01:00
}
2012-11-21 08:56:17 +01:00
void CheckNullPointer : : nullPointerDefaultArgError ( const Token * tok , const std : : string & varname )
{
reportError ( tok , Severity : : warning , " nullPointer " , " Possible null pointer dereference if the default parameter value is used: " + varname ) ;
}