2010-10-31 11:51:25 +01:00
/*
* Cppcheck - A tool for static C / C + + code analysis
2011-01-09 20:33:36 +01:00
* Copyright ( C ) 2007 - 2011 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 "executionpath.h"
# include "mathlib.h"
2011-03-06 21:23:33 +01:00
# include "symboldatabase.h"
2010-10-31 11:51:25 +01:00
//---------------------------------------------------------------------------
// Register this check class (by creating a static instance of it)
namespace
{
CheckNullPointer instance ;
}
//---------------------------------------------------------------------------
2011-01-17 19:23:00 +01:00
/** Is string uppercase? */
bool CheckNullPointer : : isUpper ( const std : : string & str )
{
for ( unsigned int i = 0 ; i < str . length ( ) ; + + i )
{
if ( str [ i ] > = ' a ' & & str [ i ] < = ' z ' )
return false ;
}
return true ;
}
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 .
* @ param value 0 = > invalid with null pointers as parameter .
* 1 - . . = > invalid with uninitialized data .
*/
void CheckNullPointer : : parseFunctionCall ( const Token & tok , std : : list < const Token * > & var , unsigned char value )
{
// standard functions that dereference first parameter..
// both uninitialized data and null pointers are invalid.
static std : : set < std : : string > functionNames1 ;
if ( functionNames1 . empty ( ) )
{
functionNames1 . insert ( " memchr " ) ;
functionNames1 . insert ( " memcmp " ) ;
functionNames1 . insert ( " strcat " ) ;
functionNames1 . insert ( " strncat " ) ;
functionNames1 . insert ( " strchr " ) ;
functionNames1 . insert ( " strrchr " ) ;
functionNames1 . insert ( " strcmp " ) ;
functionNames1 . insert ( " strncmp " ) ;
functionNames1 . insert ( " strdup " ) ;
functionNames1 . insert ( " strndup " ) ;
functionNames1 . insert ( " strlen " ) ;
functionNames1 . insert ( " strstr " ) ;
2011-01-30 09:34:58 +01:00
functionNames1 . insert ( " fclose " ) ;
functionNames1 . insert ( " feof " ) ;
functionNames1 . insert ( " fwrite " ) ;
functionNames1 . insert ( " fseek " ) ;
functionNames1 . insert ( " ftell " ) ;
functionNames1 . insert ( " fgetpos " ) ;
functionNames1 . insert ( " fsetpos " ) ;
functionNames1 . insert ( " rewind " ) ;
2010-10-31 11:51:25 +01:00
}
// standard functions that dereference second parameter..
// both uninitialized data and null pointers are invalid.
static std : : set < std : : string > functionNames2 ;
if ( functionNames2 . empty ( ) )
{
functionNames2 . insert ( " memcmp " ) ;
functionNames2 . insert ( " memcpy " ) ;
functionNames2 . insert ( " memmove " ) ;
functionNames2 . insert ( " strcat " ) ;
functionNames2 . insert ( " strncat " ) ;
functionNames2 . insert ( " strcmp " ) ;
functionNames2 . insert ( " strncmp " ) ;
functionNames2 . insert ( " strcpy " ) ;
functionNames2 . insert ( " strncpy " ) ;
functionNames2 . insert ( " strstr " ) ;
}
// 1st parameter..
2010-12-26 19:29:58 +01:00
if ( ( Token : : Match ( & tok , " %var% ( %var% ,|) " ) & & tok . tokAt ( 2 ) - > varId ( ) > 0 ) | |
( value = = 0 & & Token : : Match ( & tok , " %var% ( 0 ,|) " ) ) )
2010-10-31 11:51:25 +01:00
{
if ( functionNames1 . find ( tok . str ( ) ) ! = functionNames1 . end ( ) )
var . push_back ( tok . tokAt ( 2 ) ) ;
else if ( value = = 0 & & Token : : Match ( & tok , " memchr|memcmp|memcpy|memmove|memset|strcpy|printf|sprintf|snprintf " ) )
var . push_back ( tok . tokAt ( 2 ) ) ;
2011-01-06 08:12:34 +01:00
else if ( value ! = 0 & & Token : : simpleMatch ( & tok , " fflush " ) )
2010-10-31 11:51:25 +01:00
var . push_back ( tok . tokAt ( 2 ) ) ;
}
// 2nd parameter..
2010-12-26 19:29:58 +01:00
if ( ( Token : : Match ( & tok , " %var% ( %any% , %var% ,|) " ) & & tok . tokAt ( 4 ) - > varId ( ) > 0 ) | |
( value = = 0 & & Token : : Match ( & tok , " %var% ( %any% , 0 ,|) " ) ) )
2010-10-31 11:51:25 +01:00
{
if ( functionNames2 . find ( tok . str ( ) ) ! = functionNames2 . end ( ) )
var . push_back ( tok . tokAt ( 4 ) ) ;
}
}
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
*/
bool CheckNullPointer : : isPointerDeRef ( const Token * tok , bool & unknown )
{
unknown = false ;
// Dereferencing pointer..
2010-10-31 16:07:50 +01:00
if ( Token : : Match ( tok - > tokAt ( - 3 ) , " !!sizeof [;{}=+-/(,] * %var% " ) )
2010-10-31 15:32:19 +01:00
return true ;
2010-11-29 20:30:23 +01:00
if ( ! Token : : simpleMatch ( tok - > tokAt ( - 2 ) , " & ( " ) & & tok - > strAt ( - 1 ) ! = " & " & & tok - > strAt ( - 1 ) ! = " && " & & Token : : Match ( tok - > next ( ) , " . %var% " ) )
2010-10-31 15:32:19 +01:00
return true ;
2010-10-31 15:40:34 +01:00
if ( Token : : Match ( tok - > previous ( ) , " [;{}=+-/(,] %var% [ " ) )
2010-10-31 15:32:19 +01:00
return true ;
2010-10-31 15:40:34 +01:00
if ( Token : : Match ( tok - > previous ( ) , " return %var% [ " ) )
2010-10-31 15:32:19 +01:00
return true ;
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 ;
2010-10-31 15:32:19 +01:00
// Not a dereference..
if ( Token : : Match ( tok - > previous ( ) , " [;{}] %var% = " ) )
return false ;
// unknown if it's a dereference
unknown = true ;
// assume that it's not a dereference (no false positives)
return false ;
}
2010-12-31 18:30:04 +01:00
bool CheckNullPointer : : isPointer ( const unsigned int varid )
{
// Check if given variable is a pointer
2011-03-06 21:23:33 +01:00
const SymbolDatabase * symbolDatabase = _tokenizer - > getSymbolDatabase ( ) ;
const Variable * variableInfo = symbolDatabase - > getVariableFromVarId ( varid ) ;
const Token * tok = variableInfo ? variableInfo - > typeStartToken ( ) : NULL ;
if ( Token : : Match ( tok , " %type% %type% * %varid% [;)=] " , varid ) )
return true ;
2010-10-31 15:32:19 +01:00
2010-12-31 18:30:04 +01:00
// maybe not a pointer
if ( ! Token : : Match ( tok , " %type% * %varid% [;)=] " , varid ) )
return false ;
// it is a pointer
if ( ! tok - > previous ( ) | |
Token : : Match ( tok - > previous ( ) , " [({};] " ) | |
tok - > previous ( ) - > isName ( ) )
{
return true ;
}
// it is not a pointer
return false ;
}
2010-10-31 11:51:25 +01:00
void CheckNullPointer : : nullPointerAfterLoop ( )
{
// Locate insufficient null-pointer handling after loop
for ( const Token * tok = _tokenizer - > tokens ( ) ; tok ; tok = tok - > next ( ) )
{
2010-12-31 14:17:10 +01:00
// only interested in while ( %var% )
// TODO: Aren't there false negatives. Shouldn't other loops be handled such as:
// - while ( ! %var% )
// - while ( %var% && .. )
2010-10-31 11:51:25 +01:00
if ( ! Token : : Match ( tok , " while ( %var% ) " ) )
continue ;
2010-12-31 14:17:10 +01:00
// Get variable id for the loop variable
2010-10-31 11:51:25 +01:00
const unsigned int varid ( tok - > tokAt ( 2 ) - > varId ( ) ) ;
if ( varid = = 0 )
continue ;
2010-12-31 14:17:10 +01:00
// Get variable name for the loop variable
2010-10-31 11:51:25 +01:00
const std : : string varname ( tok - > strAt ( 2 ) ) ;
2010-12-31 14:17:10 +01:00
// Locate the end of the while loop body..
const Token * tok2 = tok - > tokAt ( 4 ) - > link ( ) ;
2010-10-31 11:51:25 +01:00
2010-12-31 14:17:10 +01:00
// Check if the variable is dereferenced after the while loop
2010-12-31 14:18:24 +01:00
while ( 0 ! = ( tok2 = tok2 ? tok2 - > next ( ) : 0 ) )
2010-10-31 11:51:25 +01:00
{
2010-12-31 14:17:10 +01:00
// Don't check into inner scopes or outer scopes. Stop checking if "break" is found
2010-10-31 11:51:25 +01:00
if ( tok2 - > str ( ) = = " { " | | tok2 - > str ( ) = = " } " | | tok2 - > str ( ) = = " break " )
break ;
2010-12-31 14:17:10 +01:00
// loop variable is found..
2010-10-31 11:51:25 +01:00
if ( tok2 - > varId ( ) = = varid )
{
2010-12-31 14:17:10 +01:00
// dummy variable.. is it unknown if pointer is dereferenced or not?
2010-10-31 15:46:08 +01:00
bool unknown = false ;
2010-12-31 14:17:10 +01:00
// Is the loop variable dereferenced?
2010-10-31 15:46:08 +01:00
if ( CheckNullPointer : : isPointerDeRef ( tok2 , unknown ) )
2010-10-31 11:51:25 +01:00
{
2010-12-31 14:17:10 +01:00
// Is loop variable a pointer?
2010-12-31 18:30:04 +01:00
if ( isPointer ( varid ) )
2010-10-31 11:51:25 +01:00
nullPointerError ( tok2 , varname ) ;
}
break ;
}
}
}
}
void CheckNullPointer : : nullPointerLinkedList ( )
{
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!
// }
2010-10-31 11:51:25 +01:00
for ( const Token * tok1 = _tokenizer - > tokens ( ) ; tok1 ; tok1 = tok1 - > next ( ) )
{
// search for a "for" token..
if ( ! Token : : simpleMatch ( tok1 , " for ( " ) )
continue ;
2010-12-31 14:37:34 +01:00
// is there any dereferencing occurring in the for statement
// parlevel2 counts the parantheses when using tok2.
2010-10-31 11:51:25 +01:00
unsigned int parlevel2 = 1 ;
for ( const Token * tok2 = tok1 - > tokAt ( 2 ) ; tok2 ; tok2 = tok2 - > next ( ) )
{
2010-12-15 18:45:53 +01:00
// Parentheses..
2010-10-31 11:51:25 +01:00
if ( tok2 - > str ( ) = = " ( " )
+ + parlevel2 ;
else if ( tok2 - > str ( ) = = " ) " )
{
if ( parlevel2 < = 1 )
break ;
- - parlevel2 ;
}
2010-12-15 18:45:53 +01:00
// Dereferencing a variable inside the "for" parentheses..
2010-10-31 11:51:25 +01:00
else if ( Token : : Match ( tok2 , " %var% . %var% " ) )
{
2010-12-31 14:37:34 +01:00
// Variable id for dereferenced variable
2010-10-31 11:51:25 +01:00
const unsigned int varid ( tok2 - > varId ( ) ) ;
if ( varid = = 0 )
continue ;
if ( Token : : Match ( tok2 - > tokAt ( - 2 ) , " %varid% ? " , varid ) )
continue ;
2010-12-31 14:37:34 +01:00
// Variable name of dereferenced variable
2010-10-31 11:51:25 +01:00
const std : : string varname ( tok2 - > str ( ) ) ;
// Check usage of dereferenced variable in the loop..
unsigned int indentlevel3 = 0 ;
for ( const Token * tok3 = tok1 - > next ( ) - > link ( ) ; tok3 ; tok3 = tok3 - > next ( ) )
{
if ( tok3 - > str ( ) = = " { " )
+ + indentlevel3 ;
else if ( tok3 - > str ( ) = = " } " )
{
if ( indentlevel3 < = 1 )
break ;
- - indentlevel3 ;
}
2010-12-31 14:37:34 +01:00
// TODO: are there false negatives for "while ( %varid% ||"
2010-10-31 11:51:25 +01:00
else if ( Token : : Match ( tok3 , " while ( %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.
// indentlevel4 is a counter for { and }. When scanning the code with tok4
2010-10-31 11:51:25 +01:00
unsigned int indentlevel4 = indentlevel3 ;
for ( const Token * tok4 = tok3 - > next ( ) - > link ( ) ; tok4 ; tok4 = tok4 - > next ( ) )
{
if ( tok4 - > str ( ) = = " { " )
+ + indentlevel4 ;
else if ( tok4 - > str ( ) = = " } " )
{
if ( indentlevel4 < = 1 )
{
// Is this variable a pointer?
2010-12-31 18:30:04 +01:00
if ( isPointer ( varid ) )
2010-10-31 11:51:25 +01:00
nullPointerError ( tok1 , varname , tok3 - > linenr ( ) ) ;
break ;
}
- - indentlevel4 ;
}
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 : : nullPointerStructByDeRefAndChec ( )
{
2010-12-31 15:56:41 +01:00
// Dereferencing a struct pointer and then checking if it's NULL..
2010-12-31 15:57:08 +01:00
2010-12-31 15:56:41 +01:00
// skipvar: don't check vars that has been tested against null already
2010-10-31 11:51:25 +01:00
std : : set < unsigned int > skipvar ;
skipvar . insert ( 0 ) ;
2010-12-31 15:56:41 +01:00
// Scan through all tokens
2010-10-31 11:51:25 +01:00
for ( const Token * tok1 = _tokenizer - > tokens ( ) ; tok1 ; tok1 = tok1 - > next ( ) )
{
2010-12-31 15:56:41 +01:00
// Checking if some pointer is null.
// then add the pointer to skipvar => is it known that it isn't NULL
2010-10-31 11:51:25 +01:00
if ( Token : : Match ( tok1 , " if|while ( !| %var% ) " ) )
{
tok1 = tok1 - > tokAt ( 2 ) ;
if ( tok1 - > str ( ) = = " ! " )
tok1 = tok1 - > next ( ) ;
skipvar . insert ( tok1 - > varId ( ) ) ;
continue ;
}
2011-02-19 21:01:38 +01:00
/**
* @ todo There are lots of false negatives here . A dereference
* is only investigated if a few specific conditions are met .
*/
// dereference in assignment
if ( Token : : Match ( tok1 , " [;{}] %var% . %var% " ) )
{
tok1 = tok1 - > next ( ) ;
}
2010-10-31 11:51:25 +01:00
// dereference in assignment
2011-02-19 21:01:38 +01:00
else if ( Token : : Match ( tok1 , " [{};] %var% = %var% . %var% " ) )
2010-10-31 11:51:25 +01:00
{
if ( std : : string ( tok1 - > strAt ( 1 ) ) = = tok1 - > strAt ( 3 ) )
continue ;
tok1 = tok1 - > tokAt ( 3 ) ;
}
// dereference in function call
else if ( Token : : Match ( tok1 - > tokAt ( - 2 ) , " %var% ( %var% . %var% " ) | |
Token : : Match ( tok1 - > previous ( ) , " , %var% . %var% " ) )
{
2010-12-17 21:09:12 +01:00
// Is the function return value taken by the pointer?
bool assignment = false ;
const unsigned int varid1 ( tok1 - > varId ( ) ) ;
2010-12-19 21:27:28 +01:00
if ( varid1 = = 0 )
continue ;
2010-12-17 21:09:12 +01:00
const Token * tok2 = tok1 - > previous ( ) ;
while ( tok2 & & ! Token : : Match ( tok2 , " [;{}] " ) )
{
if ( Token : : Match ( tok2 , " %varid% = " , varid1 ) )
{
assignment = true ;
break ;
}
tok2 = tok2 - > previous ( ) ;
}
if ( assignment )
continue ;
2010-10-31 11:51:25 +01:00
}
// Goto next token
else
{
continue ;
}
// struct dereference was found - investigate if it is later
// checked that it is not NULL
const unsigned int varid1 ( tok1 - > varId ( ) ) ;
if ( skipvar . find ( varid1 ) ! = skipvar . end ( ) )
continue ;
2010-12-31 15:56:41 +01:00
// name of struct pointer
2010-10-31 11:51:25 +01:00
const std : : string varname ( tok1 - > str ( ) ) ;
2011-03-12 17:42:58 +01:00
// is pointer local?
bool isLocal = false ;
const Variable * var = _tokenizer - > getSymbolDatabase ( ) - > getVariableFromVarId ( tok1 - > varId ( ) ) ;
2011-03-12 18:27:19 +01:00
if ( var & & ( var - > isLocal ( ) | | var - > isArgument ( ) ) )
2011-03-12 17:42:58 +01:00
isLocal = true ;
2011-03-12 20:57:19 +01:00
// member function may or may not nullify the pointer if it's global (#2647)
if ( ! isLocal )
{
const Token * tok2 = tok1 ;
while ( Token : : Match ( tok2 , " %var% . " ) )
tok2 = tok2 - > tokAt ( 2 ) ;
if ( Token : : Match ( tok2 , " %var% ( " ) )
continue ;
}
2010-12-31 15:56:41 +01:00
// count { and } using tok2
2010-10-31 11:51:25 +01:00
unsigned int indentlevel2 = 0 ;
for ( const Token * tok2 = tok1 - > tokAt ( 3 ) ; tok2 ; tok2 = tok2 - > next ( ) )
{
if ( tok2 - > str ( ) = = " { " )
+ + indentlevel2 ;
else if ( tok2 - > str ( ) = = " } " )
{
if ( indentlevel2 < = 1 )
break ;
- - indentlevel2 ;
}
2010-12-31 15:56:41 +01:00
// label. goto destination..
2010-10-31 11:51:25 +01:00
else if ( tok2 - > isName ( ) & & Token : : simpleMatch ( tok2 - > next ( ) , " : " ) )
break ;
// Reassignment of the struct
else if ( tok2 - > varId ( ) = = varid1 )
{
if ( tok2 - > next ( ) - > str ( ) = = " = " )
break ;
if ( Token : : Match ( tok2 - > tokAt ( - 2 ) , " [,(] & " ) )
break ;
}
// Loop..
/** @todo don't bail out if the variable is not used in the loop */
else if ( tok2 - > str ( ) = = " do " )
break ;
// return at base level => stop checking
else if ( indentlevel2 = = 0 & & tok2 - > str ( ) = = " return " )
break ;
2011-03-12 17:42:58 +01:00
// Function call: If the pointer is not a local variable it
2011-03-12 15:02:06 +01:00
// might be changed by the call.
else if ( Token : : Match ( tok2 , " [;{}] %var% ( " ) & &
2011-03-12 17:42:58 +01:00
Token : : simpleMatch ( tok2 - > tokAt ( 2 ) - > link ( ) , " ) ; " ) & & ! isLocal )
2011-03-12 15:02:06 +01:00
{
break ;
}
2010-12-31 15:56:41 +01:00
// Check if pointer is null.
2011-02-19 21:10:31 +01:00
// TODO: false negatives for "if (!p || .."
else if ( Token : : Match ( tok2 , " if ( !| %varid% )|&& " , varid1 ) )
2010-10-31 11:51:25 +01:00
{
// Is this variable a pointer?
2010-12-31 18:30:04 +01:00
if ( isPointer ( varid1 ) )
2010-10-31 11:51:25 +01:00
nullPointerError ( tok1 , varname , tok2 - > linenr ( ) ) ;
break ;
}
}
}
}
void CheckNullPointer : : nullPointerByDeRefAndChec ( )
{
// Dereferencing a pointer and then checking if it's NULL..
2011-01-06 13:18:49 +01:00
// This check will first scan for the check. And then scan backwards
// from the check, searching for dereferencing.
2010-10-31 11:51:25 +01:00
for ( const Token * tok = _tokenizer - > tokens ( ) ; tok ; tok = tok - > next ( ) )
{
2011-01-06 13:18:49 +01:00
// TODO: false negatives.
// - logical operators
// - while
2010-10-31 11:51:25 +01:00
if ( tok - > str ( ) = = " if " & & Token : : Match ( tok - > previous ( ) , " ; if ( ! %var% ) " ) )
{
2011-01-06 13:18:49 +01:00
// Variable id for pointer
2010-10-31 11:51:25 +01:00
const unsigned int varid ( tok - > tokAt ( 3 ) - > varId ( ) ) ;
if ( varid = = 0 )
continue ;
2011-01-06 13:18:49 +01:00
// Name of pointer
2010-10-31 11:51:25 +01:00
const std : : string varname ( tok - > strAt ( 3 ) ) ;
// Check that variable is a pointer..
2010-12-31 18:30:04 +01:00
if ( ! isPointer ( varid ) )
2010-10-31 11:51:25 +01:00
continue ;
2011-01-06 13:18:49 +01:00
// Token where pointer is declared
2010-12-31 18:30:04 +01:00
const Token * const decltok = Token : : findmatch ( _tokenizer - > tokens ( ) , " %varid% " , varid ) ;
2010-10-31 11:51:25 +01:00
for ( const Token * tok1 = tok - > previous ( ) ; tok1 & & tok1 ! = decltok ; tok1 = tok1 - > previous ( ) )
{
2010-12-17 21:09:12 +01:00
if ( tok1 - > str ( ) = = " ) " & & Token : : Match ( tok1 - > link ( ) - > tokAt ( - 3 ) , " %varid% = %var% " , varid ) )
{
break ;
}
2011-01-10 19:57:26 +01:00
if ( tok1 - > str ( ) = = " ) " & & Token : : Match ( tok1 - > link ( ) - > previous ( ) , " while ( %varid% " , varid ) )
{
break ;
}
2011-01-31 17:30:27 +01:00
if ( tok1 - > str ( ) = = " ) " & & Token : : simpleMatch ( tok1 - > link ( ) - > previous ( ) , " sizeof ( " ) )
{
tok1 = tok1 - > link ( ) - > previous ( ) ;
continue ;
}
2011-01-15 12:09:36 +01:00
if ( tok1 - > str ( ) = = " break " )
break ;
2011-01-08 17:16:52 +01:00
if ( tok1 - > varId ( ) = = varid & & ! Token : : Match ( tok1 - > previous ( ) , " [?:] " ) )
2010-10-31 11:51:25 +01:00
{
2011-01-06 13:18:49 +01:00
// unknown : this is set by isPointerDeRef if it is
// uncertain
2010-10-31 16:07:50 +01:00
bool unknown = false ;
2011-01-06 13:18:49 +01:00
2010-11-04 18:18:19 +01:00
if ( Token : : Match ( tok1 - > tokAt ( - 2 ) , " %varid% = %varid% . " , varid ) )
{
break ;
}
else if ( CheckNullPointer : : isPointerDeRef ( tok1 , unknown ) )
2010-10-31 11:51:25 +01:00
{
nullPointerError ( tok1 , varname , tok - > linenr ( ) ) ;
break ;
}
else if ( Token : : simpleMatch ( tok1 - > previous ( ) , " & " ) )
{
break ;
}
else if ( Token : : simpleMatch ( tok1 - > next ( ) , " = " ) )
{
break ;
}
}
else if ( tok1 - > str ( ) = = " { " ||
tok1 - > str ( ) = = " } " )
break ;
// label..
else if ( Token : : Match ( tok1 , " %type% : " ) )
break ;
}
}
}
}
void CheckNullPointer : : nullPointerByCheckAndDeRef ( )
{
// Check if pointer is NULL and then dereference it..
2011-01-06 13:18:49 +01:00
// used to check if a variable is a pointer.
// TODO: Use isPointer?
2010-11-04 21:09:32 +01:00
std : : set < unsigned int > pointerVariables ;
2011-01-06 13:18:49 +01:00
2010-10-31 11:51:25 +01:00
for ( const Token * tok = _tokenizer - > tokens ( ) ; tok ; tok = tok - > next ( ) )
{
2010-11-04 21:09:32 +01:00
if ( Token : : Match ( tok , " * %var% [;,)=] " ) )
pointerVariables . insert ( tok - > next ( ) - > varId ( ) ) ;
2011-02-02 16:48:00 +01:00
else if ( Token : : simpleMatch ( tok , " if ( " ) )
2010-10-31 11:51:25 +01:00
{
2011-01-06 13:18:49 +01:00
// TODO: investigate false negatives:
// - handle "while"?
// - if there are logical operators
// - if (x) { } else { ... }
2011-01-17 19:23:00 +01:00
// If the if-body ends with a unknown macro then bailout
{
// goto the end paranthesis
const Token * endpar = tok - > next ( ) - > link ( ) ;
2011-02-18 20:05:44 +01:00
const Token * endbody = Token : : simpleMatch ( endpar , " ) { " ) ? endpar - > next ( ) - > link ( ) : 0 ;
2011-01-17 19:23:00 +01:00
if ( endbody & &
Token : : Match ( endbody - > tokAt ( - 3 ) , " [;{}] %var% ; " ) & &
isUpper ( endbody - > tokAt ( - 2 ) - > str ( ) ) )
continue ;
}
2011-01-06 13:18:49 +01:00
// vartok : token for the variable
2010-11-04 21:22:29 +01:00
const Token * vartok = 0 ;
2011-02-19 21:28:18 +01:00
if ( Token : : Match ( tok , " if ( ! %var% )|&& " ) )
2010-11-04 21:22:29 +01:00
vartok = tok - > tokAt ( 3 ) ;
2011-02-19 21:28:18 +01:00
else if ( Token : : Match ( tok , " if ( NULL|0 == %var% )|&& " ) )
2010-11-04 21:22:29 +01:00
vartok = tok - > tokAt ( 4 ) ;
2011-02-19 21:28:18 +01:00
else if ( Token : : Match ( tok , " if ( %var% == NULL|0 )|&& " ) )
2010-11-04 21:22:29 +01:00
vartok = tok - > tokAt ( 2 ) ;
2011-02-20 14:38:49 +01:00
else if ( Token : : Match ( tok , " if|while ( %var% )|&& " ) )
2011-01-09 20:16:16 +01:00
vartok = tok - > tokAt ( 2 ) ;
2010-11-04 21:22:29 +01:00
else
continue ;
2011-01-06 13:18:49 +01:00
// variable id for pointer
2010-11-04 21:22:29 +01:00
const unsigned int varid ( vartok - > varId ( ) ) ;
2010-10-31 11:51:25 +01:00
if ( varid = = 0 )
continue ;
2011-01-06 13:18:49 +01:00
// Check if variable is a pointer. TODO: Use isPointer?
2010-11-04 21:09:32 +01:00
if ( pointerVariables . find ( varid ) = = pointerVariables . end ( ) )
continue ;
2010-11-04 21:22:29 +01:00
2011-01-06 13:18:49 +01:00
// if this is true then it is known that the pointer is null
bool null = true ;
2011-01-09 20:16:16 +01:00
// start token = inside the if-body
const Token * tok1 = tok - > next ( ) - > link ( ) - > tokAt ( 2 ) ;
2011-02-20 14:38:49 +01:00
if ( Token : : Match ( tok , " if|while ( %var% )|&& " ) )
2011-01-09 20:16:16 +01:00
{
// pointer might be null
null = false ;
// start token = first token after the if/while body
2011-03-13 10:34:54 +01:00
tok1 = tok1 - > previous ( ) - > link ( ) ;
tok1 = tok1 ? tok1 - > next ( ) : NULL ;
if ( ! tok1 )
continue ;
2011-01-09 20:16:16 +01:00
}
2010-11-04 21:22:29 +01:00
// Name of the pointer
const std : : string & pointerName = vartok - > str ( ) ;
2011-01-06 13:18:49 +01:00
// Count { and } for tok2
2010-10-31 11:51:25 +01:00
unsigned int indentlevel = 1 ;
2011-01-09 20:16:16 +01:00
for ( const Token * tok2 = tok1 ; tok2 ; tok2 = tok2 - > next ( ) )
2010-10-31 11:51:25 +01:00
{
if ( tok2 - > str ( ) = = " { " )
+ + indentlevel ;
else if ( tok2 - > str ( ) = = " } " )
{
if ( indentlevel = = 0 )
break ;
- - indentlevel ;
if ( null & & indentlevel = = 0 )
{
// skip all "else" blocks because they are not executed in this execution path
2011-02-02 17:12:46 +01:00
while ( Token : : simpleMatch ( tok2 , " } else { " ) )
2010-10-31 11:51:25 +01:00
tok2 = tok2 - > tokAt ( 2 ) - > link ( ) ;
null = false ;
}
}
2011-01-23 08:38:09 +01:00
if ( Token : : Match ( tok2 , " goto|return|continue|break|throw|if|switch " ) )
2010-10-31 11:51:25 +01:00
{
2010-11-01 19:21:08 +01:00
if ( Token : : Match ( tok2 , " return * %varid% " , varid ) )
2010-10-31 11:51:25 +01:00
nullPointerError ( tok2 , tok - > strAt ( 3 ) ) ;
break ;
}
2010-11-29 20:19:31 +01:00
// parameters to sizeof are not dereferenced
if ( Token : : Match ( tok2 , " decltype|sizeof ( " ) )
{
tok2 = tok2 - > next ( ) - > link ( ) ;
continue ;
}
2010-12-16 21:41:50 +01:00
// calling unknown function (abort/init)..
if ( Token : : simpleMatch ( tok2 , " ) ; " ) & &
2011-01-05 21:48:26 +01:00
( Token : : Match ( tok2 - > link ( ) - > tokAt ( - 2 ) , " [;{}] %var% ( " ) | |
Token : : Match ( tok2 - > link ( ) - > tokAt ( - 5 ) , " [;{}] ( * %var% ) ( " ) ) )
2010-10-31 11:51:25 +01:00
{
break ;
}
if ( tok2 - > varId ( ) = = varid )
{
2011-01-06 13:18:49 +01:00
// unknown: this is set to true by isPointerDeRef if
// the function fails to determine if there
// is a dereference or not
2010-10-31 15:32:19 +01:00
bool unknown = false ;
2010-10-31 11:51:25 +01:00
if ( Token : : Match ( tok2 - > previous ( ) , " [;{}=] %var% = 0 ; " ) )
;
2010-10-31 15:32:19 +01:00
else if ( CheckNullPointer : : isPointerDeRef ( tok2 , unknown ) )
2010-11-04 21:22:29 +01:00
nullPointerError ( tok2 , pointerName ) ;
2010-10-31 11:51:25 +01:00
else
break ;
}
}
}
}
}
void CheckNullPointer : : nullPointer ( )
{
nullPointerAfterLoop ( ) ;
nullPointerLinkedList ( ) ;
nullPointerStructByDeRefAndChec ( ) ;
nullPointerByDeRefAndChec ( ) ;
nullPointerByCheckAndDeRef ( ) ;
}
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 ( )
{
// this is kept at 0 for all scopes that are not executing
unsigned int indentlevel = 0 ;
for ( const Token * tok = _tokenizer - > tokens ( ) ; tok ; tok = tok - > next ( ) )
{
// start of executable scope..
if ( indentlevel = = 0 & & Token : : Match ( tok , " ) const| { " ) )
indentlevel = 1 ;
else if ( indentlevel > = 1 )
{
if ( tok - > str ( ) = = " { " )
+ + indentlevel ;
else if ( tok - > str ( ) = = " } " )
{
if ( indentlevel < = 2 )
indentlevel = 0 ;
else
- - indentlevel ;
}
2010-11-29 20:19:31 +01:00
if ( tok - > str ( ) = = " ( " & & Token : : Match ( tok - > previous ( ) , " sizeof|decltype " ) )
2010-10-31 11:51:25 +01:00
tok = tok - > link ( ) ;
else if ( Token : : simpleMatch ( tok , " exit ( ) " ) )
{
// Goto end of scope
while ( tok & & tok - > str ( ) ! = " } " )
{
if ( tok - > str ( ) = = " { " )
tok = tok - > link ( ) ;
tok = tok - > next ( ) ;
}
if ( ! tok )
break ;
}
else if ( Token : : simpleMatch ( tok , " * 0 " ) )
{
if ( Token : : Match ( tok - > previous ( ) , " [<>;{}=+-*/(,] " ) | |
Token : : Match ( tok - > previous ( ) , " return|<< " ) )
{
nullPointerError ( tok ) ;
}
}
2010-12-26 19:29:58 +01:00
else if ( indentlevel > 0 & & Token : : Match ( tok , " %var% ( " ) )
{
std : : list < const Token * > var ;
parseFunctionCall ( * tok , var , 0 ) ;
// is one of the var items a NULL pointer?
for ( std : : list < const Token * > : : const_iterator it = var . begin ( ) ; it ! = var . end ( ) ; + + it )
{
if ( ( * it ) - > str ( ) = = " 0 " )
{
nullPointerError ( * it ) ;
}
}
}
2010-10-31 11:51:25 +01:00
}
}
}
/// @addtogroup Checks
/// @{
/**
* @ brief % Check for null pointer usage ( using ExecutionPath )
*/
class Nullpointer : public ExecutionPath
{
public :
/** Startup constructor */
Nullpointer ( Check * c ) : ExecutionPath ( c , 0 ) , null ( false )
{
}
private :
/** Create checking of specific variable: */
Nullpointer ( Check * c , const unsigned int id , const std : : string & name )
: ExecutionPath ( c , id ) ,
varname ( name ) ,
null ( false )
{
}
/** Copy this check */
ExecutionPath * copy ( )
{
return new Nullpointer ( * this ) ;
}
/** no implementation => compiler error if used by accident */
void operator = ( const Nullpointer & ) ;
/** is other execution path equal? */
bool is_equal ( const ExecutionPath * e ) const
{
const Nullpointer * c = static_cast < const Nullpointer * > ( e ) ;
return ( varname = = c - > varname & & null = = c - > null ) ;
}
/** variable name for this check (empty => dummy check) */
const std : : string varname ;
/** is this variable null? */
bool null ;
/** variable is set to null */
static void setnull ( std : : list < ExecutionPath * > & checks , const unsigned int varid )
{
std : : list < ExecutionPath * > : : iterator it ;
for ( it = checks . begin ( ) ; it ! = checks . end ( ) ; + + it )
{
Nullpointer * c = dynamic_cast < Nullpointer * > ( * it ) ;
if ( c & & c - > varId = = varid )
c - > null = true ;
}
}
/**
* Dereferencing variable . Check if it is safe ( if the variable is null there ' s an error )
* @ param checks Checks
* @ param tok token where dereferencing happens
*/
static void dereference ( std : : list < ExecutionPath * > & checks , const Token * tok )
{
const unsigned int varid ( tok - > varId ( ) ) ;
std : : list < ExecutionPath * > : : iterator it ;
for ( it = checks . begin ( ) ; it ! = checks . end ( ) ; + + it )
{
Nullpointer * c = dynamic_cast < Nullpointer * > ( * it ) ;
if ( c & & c - > varId = = varid & & c - > null )
{
CheckNullPointer * checkNullPointer = dynamic_cast < CheckNullPointer * > ( c - > owner ) ;
if ( checkNullPointer )
{
checkNullPointer - > nullPointerError ( tok , c - > varname ) ;
return ;
}
}
}
}
/** parse tokens */
const Token * parse ( const Token & tok , std : : list < ExecutionPath * > & checks ) const
{
if ( Token : : Match ( tok . previous ( ) , " [;{}] const| %type% * %var% ; " ) )
{
const Token * vartok = tok . tokAt ( 2 ) ;
if ( tok . str ( ) = = " const " )
vartok = vartok - > next ( ) ;
if ( vartok - > varId ( ) ! = 0 )
checks . push_back ( new Nullpointer ( owner , vartok - > varId ( ) , vartok - > str ( ) ) ) ;
return vartok - > next ( ) ;
}
// Template pointer variable..
if ( Token : : Match ( tok . previous ( ) , " [;{}] %type% ::|< " ) )
{
const Token * vartok = & tok ;
while ( Token : : Match ( vartok , " %type% :: " ) )
vartok = vartok - > tokAt ( 2 ) ;
if ( Token : : Match ( vartok , " %type% < %type% " ) )
{
vartok = vartok - > tokAt ( 3 ) ;
while ( vartok & & ( vartok - > str ( ) = = " * " | | vartok - > isName ( ) ) )
vartok = vartok - > next ( ) ;
}
if ( vartok
& & ( vartok - > str ( ) = = " > " | | vartok - > isName ( ) )
& & Token : : Match ( vartok - > next ( ) , " * %var% ;|= " ) )
{
vartok = vartok - > tokAt ( 2 ) ;
checks . push_back ( new Nullpointer ( owner , vartok - > varId ( ) , vartok - > str ( ) ) ) ;
if ( Token : : simpleMatch ( vartok - > next ( ) , " = 0 ; " ) )
setnull ( checks , vartok - > varId ( ) ) ;
return vartok - > next ( ) ;
}
}
if ( Token : : simpleMatch ( & tok , " try { " ) )
{
// Bail out all used variables
unsigned int indentlevel = 0 ;
for ( const Token * tok2 = & tok ; tok2 ; tok2 = tok2 - > next ( ) )
{
if ( tok2 - > str ( ) = = " { " )
+ + indentlevel ;
else if ( tok2 - > str ( ) = = " } " )
{
if ( indentlevel = = 0 )
break ;
if ( indentlevel = = 1 & & ! Token : : simpleMatch ( tok2 , " } catch ( " ) )
return tok2 ;
- - indentlevel ;
}
else if ( tok2 - > varId ( ) )
bailOutVar ( checks , tok2 - > varId ( ) ) ;
}
}
if ( Token : : Match ( & tok , " %var% ( " ) )
{
if ( tok . str ( ) = = " sizeof " )
return tok . next ( ) - > link ( ) ;
// parse usage..
std : : list < const Token * > var ;
CheckNullPointer : : parseFunctionCall ( tok , var , 0 ) ;
for ( std : : list < const Token * > : : const_iterator it = var . begin ( ) ; it ! = var . end ( ) ; + + it )
dereference ( checks , * it ) ;
}
2010-12-09 18:17:56 +01:00
else if ( Token : : simpleMatch ( & tok , " ( 0 && " ) )
return tok . link ( ) ;
2010-10-31 11:51:25 +01:00
if ( tok . varId ( ) ! = 0 )
{
2011-01-06 13:18:49 +01:00
// unknown : not really used. it is passed to isPointerDeRef.
// if isPointerDeRef fails to determine if there
// is a dereference the this will be set to true.
2010-10-31 15:32:19 +01:00
bool unknown = false ;
2011-01-06 13:18:49 +01:00
2010-10-31 11:51:25 +01:00
if ( Token : : Match ( tok . previous ( ) , " [;{}=] %var% = 0 ; " ) )
setnull ( checks , tok . varId ( ) ) ;
2010-10-31 15:32:19 +01:00
else if ( CheckNullPointer : : isPointerDeRef ( & tok , unknown ) )
2010-10-31 11:51:25 +01:00
dereference ( checks , & tok ) ;
else
2010-10-31 15:32:19 +01:00
// TODO: Report debug warning that it's unknown if a
// pointer is dereferenced
2010-10-31 11:51:25 +01:00
bailOutVar ( checks , tok . varId ( ) ) ;
}
else if ( tok . str ( ) = = " delete " )
{
const Token * ret = tok . next ( ) ;
if ( Token : : simpleMatch ( ret , " [ ] " ) )
ret = ret - > tokAt ( 2 ) ;
if ( Token : : Match ( ret , " %var% ; " ) )
return ret - > next ( ) ;
}
return & tok ;
}
/** parse condition. @sa ExecutionPath::parseCondition */
bool parseCondition ( const Token & tok , std : : list < ExecutionPath * > & checks )
{
for ( const Token * tok2 = & tok ; tok2 ; tok2 = tok2 - > next ( ) )
{
if ( tok2 - > str ( ) = = " ( " | | tok2 - > str ( ) = = " ) " )
break ;
if ( Token : : Match ( tok2 , " [<>=] * %var% " ) )
dereference ( checks , tok2 - > tokAt ( 2 ) ) ;
}
if ( Token : : Match ( & tok , " !| %var% ( " ) )
{
std : : list < const Token * > var ;
CheckNullPointer : : parseFunctionCall ( tok . str ( ) = = " ! " ? * tok . next ( ) : tok , var , 0 ) ;
for ( std : : list < const Token * > : : const_iterator it = var . begin ( ) ; it ! = var . end ( ) ; + + it )
dereference ( checks , * it ) ;
}
return ExecutionPath : : parseCondition ( tok , checks ) ;
}
void parseLoopBody ( const Token * tok , std : : list < ExecutionPath * > & checks ) const
{
while ( tok )
{
if ( Token : : Match ( tok , " {|}|return|goto|break|if " ) )
return ;
const Token * next = parse ( * tok , checks ) ;
if ( next )
tok = tok - > next ( ) ;
}
}
} ;
/// @}
void CheckNullPointer : : executionPaths ( )
{
// Check for null pointer errors..
Nullpointer c ( this ) ;
checkExecutionPaths ( _tokenizer - > tokens ( ) , & c ) ;
}
void CheckNullPointer : : nullPointerError ( const Token * tok )
{
reportError ( tok , Severity : : error , " nullPointer " , " Null pointer dereference " ) ;
}
void CheckNullPointer : : nullPointerError ( const Token * tok , const std : : string & varname )
{
reportError ( tok , Severity : : error , " nullPointer " , " Possible null pointer dereference: " + varname ) ;
}
void CheckNullPointer : : nullPointerError ( const Token * tok , const std : : string & varname , const unsigned int line )
{
reportError ( tok , Severity : : error , " nullPointer " , " Possible null pointer dereference: " + varname + " - otherwise it is redundant to check if " + varname + " is null at line " + MathLib : : toString < unsigned int > ( line ) ) ;
}