2009-01-31 20:29:27 +01:00
/*
* Cppcheck - A tool for static C / C + + code analysis
2013-01-01 17:29:08 +01:00
* Copyright ( C ) 2007 - 2013 Daniel Marjamäki and Cppcheck team .
2009-01-31 20:29:27 +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
2009-09-27 17:08:31 +02:00
* along with this program . If not , see < http : //www.gnu.org/licenses/>.
2009-01-31 20:29:27 +01:00
*/
//---------------------------------------------------------------------------
// Buffer overrun..
//---------------------------------------------------------------------------
# include "checkbufferoverrun.h"
2009-02-11 06:16:10 +01:00
# include "tokenize.h"
# include "errorlogger.h"
2009-08-02 14:26:26 +02:00
# include "mathlib.h"
2011-06-23 04:44:11 +02:00
# include "symboldatabase.h"
2009-02-11 06:16:10 +01:00
2009-01-31 20:29:27 +01:00
# include <algorithm>
# include <sstream>
# include <list>
2009-09-27 16:09:41 +02:00
# include <cassert> // <- assert
2011-12-23 22:31:48 +01:00
# include <cstdlib>
2009-01-31 20:29:27 +01:00
//---------------------------------------------------------------------------
2009-03-20 18:16:21 +01:00
// Register this check class (by creating a static instance of it)
2011-10-13 20:53:06 +02:00
namespace {
CheckBufferOverrun instance ;
2009-03-20 18:16:21 +01:00
}
//---------------------------------------------------------------------------
2009-01-31 20:29:27 +01:00
2011-12-08 21:28:34 +01:00
static void makeArrayIndexOutOfBoundsError ( std : : ostream & oss , const CheckBufferOverrun : : ArrayInfo & arrayInfo , const std : : vector < MathLib : : bigint > & index )
2010-04-18 11:08:29 +02:00
{
2011-06-23 02:35:58 +02:00
oss < < " Array ' " < < arrayInfo . varname ( ) ;
for ( unsigned int i = 0 ; i < arrayInfo . num ( ) . size ( ) ; + + i )
oss < < " [ " < < arrayInfo . num ( i ) < < " ] " ;
2010-04-23 16:26:40 +02:00
if ( index . size ( ) = = 1 )
2012-06-23 09:51:32 +02:00
oss < < " ' accessed at index " < < index [ 0 ] < < " , which is " ;
2011-10-13 20:53:06 +02:00
else {
2012-06-23 09:51:32 +02:00
oss < < " ' index " < < arrayInfo . varname ( ) ;
2010-04-23 16:26:40 +02:00
for ( unsigned int i = 0 ; i < index . size ( ) ; + + i )
oss < < " [ " < < index [ i ] < < " ] " ;
}
2012-07-08 15:51:24 +02:00
oss < < " out of bounds. " ;
2011-12-08 21:28:34 +01:00
}
void CheckBufferOverrun : : arrayIndexOutOfBoundsError ( const Token * tok , const ArrayInfo & arrayInfo , const std : : vector < MathLib : : bigint > & index )
{
std : : ostringstream oss ;
makeArrayIndexOutOfBoundsError ( oss , arrayInfo , index ) ;
2011-10-23 17:47:48 +02:00
reportError ( tok , Severity : : error , " arrayIndexOutOfBounds " , oss . str ( ) ) ;
2010-04-18 11:08:29 +02:00
}
2011-08-24 13:11:39 +02:00
void CheckBufferOverrun : : arrayIndexOutOfBoundsError ( const std : : list < const Token * > & callstack , const ArrayInfo & arrayInfo , const std : : vector < MathLib : : bigint > & index )
2010-08-05 11:01:47 +02:00
{
std : : ostringstream oss ;
2011-12-08 21:28:34 +01:00
makeArrayIndexOutOfBoundsError ( oss , arrayInfo , index ) ;
2011-10-23 17:47:48 +02:00
reportError ( callstack , Severity : : error , " arrayIndexOutOfBounds " , oss . str ( ) ) ;
2010-08-05 11:01:47 +02:00
}
2011-12-11 08:16:58 +01:00
static std : : string bufferOverrunMessage ( std : : string varnames )
2009-03-21 21:33:27 +01:00
{
2012-07-08 15:51:24 +02:00
varnames . erase ( std : : remove ( varnames . begin ( ) , varnames . end ( ) , ' ' ) , varnames . end ( ) ) ;
2010-02-21 15:23:50 +01:00
2012-07-08 15:51:24 +02:00
std : : string errmsg ( " Buffer is accessed out of bounds " ) ;
2011-12-11 08:16:58 +01:00
if ( ! varnames . empty ( ) )
errmsg + = " : " + varnames ;
2012-07-08 15:51:24 +02:00
else
errmsg + = " . " ;
2011-12-11 08:48:55 +01:00
2011-12-11 08:16:58 +01:00
return errmsg ;
}
void CheckBufferOverrun : : bufferOverrunError ( const Token * tok , const std : : string & varnames )
{
reportError ( tok , Severity : : error , " bufferAccessOutOfBounds " , bufferOverrunMessage ( varnames ) ) ;
}
2010-02-21 15:23:50 +01:00
2011-12-11 08:16:58 +01:00
void CheckBufferOverrun : : bufferOverrunError ( const std : : list < const Token * > & callstack , const std : : string & varnames )
{
reportError ( callstack , Severity : : error , " bufferAccessOutOfBounds " , bufferOverrunMessage ( varnames ) ) ;
2009-03-21 21:33:27 +01:00
}
2011-08-21 21:18:41 +02:00
void CheckBufferOverrun : : possibleBufferOverrunError ( const Token * tok , const std : : string & src , const std : : string & dst , bool cat )
2011-08-21 20:44:55 +02:00
{
2011-08-21 21:18:41 +02:00
if ( cat )
reportError ( tok , Severity : : warning , " possibleBufferAccessOutOfBounds " ,
" Possible buffer overflow if strlen( " + src + " ) is larger than sizeof( " + dst + " )-strlen( " + dst + " ). \n "
2012-07-08 15:51:24 +02:00
" Possible buffer overflow if strlen( " + src + " ) is larger than sizeof( " + dst + " )-strlen( " + dst + " ). "
2011-08-21 21:18:41 +02:00
" The source buffer is larger than the destination buffer so there is the potential for overflowing the destination buffer. " ) ;
else
reportError ( tok , Severity : : warning , " possibleBufferAccessOutOfBounds " ,
" Possible buffer overflow if strlen( " + src + " ) is larger than or equal to sizeof( " + dst + " ). \n "
2012-07-08 15:51:24 +02:00
" Possible buffer overflow if strlen( " + src + " ) is larger than or equal to sizeof( " + dst + " ). "
2011-08-21 21:18:41 +02:00
" The source buffer is larger than the destination buffer so there is the potential for overflowing the destination buffer. " ) ;
2011-08-21 20:44:55 +02:00
}
2011-10-24 21:22:04 +02:00
void CheckBufferOverrun : : possibleReadlinkBufferOverrunError ( const Token * tok , const std : : string & funcname , const std : : string & varname )
2011-10-14 19:45:51 +02:00
{
2011-10-24 21:22:04 +02:00
const std : : string errmsg = funcname + " () might return the full size of ' " + varname + " '. Lower the supplied size by one. \n " +
2012-07-08 15:51:24 +02:00
funcname + " () might return the full size of ' " + varname + " '. "
" If a " + varname + " [len] = ' \\ 0'; statement follows, it will overrun the buffer. Lower the supplied size by one. " ;
2011-10-14 19:45:51 +02:00
2012-05-06 19:37:41 +02:00
reportError ( tok , Severity : : warning , " possibleReadlinkBufferOverrun " , errmsg , true ) ;
2011-10-14 19:45:51 +02:00
}
2011-08-24 13:11:39 +02:00
void CheckBufferOverrun : : strncatUsageError ( const Token * tok )
2009-03-21 21:33:27 +01:00
{
2013-03-03 11:41:59 +01:00
if ( _settings & & ! _settings - > isEnabled ( " warning " ) )
2009-10-13 22:33:41 +02:00
return ;
2011-01-03 17:33:03 +01:00
reportError ( tok , Severity : : warning , " strncatUsage " ,
" Dangerous usage of strncat - 3rd parameter is the maximum number of characters to append. \n "
" strncat appends at max its 3rd parameter's amount of characters. The safe way to use "
" strncat is to calculate remaining space in the buffer and use it as 3rd parameter. " ) ;
2009-03-21 21:33:27 +01:00
}
2011-10-05 20:17:57 +02:00
void CheckBufferOverrun : : outOfBoundsError ( const Token * tok , const std : : string & what , const bool show_size_info , const MathLib : : bigint & supplied_size , const MathLib : : bigint & actual_size )
2009-03-21 21:33:27 +01:00
{
2011-10-05 20:17:57 +02:00
std : : ostringstream oss ;
oss < < what < < " is out of bounds " ;
if ( show_size_info )
2012-07-08 15:51:24 +02:00
oss < < " : Supplied size " < < supplied_size < < " is larger than actual size " < < actual_size ;
oss < < ' . ' ;
2011-10-05 20:17:57 +02:00
reportError ( tok , Severity : : error , " outOfBounds " , oss . str ( ) ) ;
2009-03-21 21:33:27 +01:00
}
2011-08-24 13:11:39 +02:00
void CheckBufferOverrun : : pointerOutOfBoundsError ( const Token * tok , const std : : string & object )
2010-12-26 21:23:28 +01:00
{
2012-07-08 15:51:24 +02:00
reportError ( tok , Severity : : portability , " pointerOutOfBounds " , " Undefined behaviour: Pointer arithmetic result does not point into or just past the end of the " + object + " . \n "
2012-07-09 11:11:05 +02:00
" Undefined behaviour: The result of this pointer arithmetic does not point into or just one element past the end of the " + object + " . Further information: https://www.securecoding.cert.org/confluence/display/seccode/ARR30-C.+Do+not+form+or+use+out+of+bounds+pointers+or+array+subscripts " ) ;
2010-12-26 21:23:28 +01:00
}
2011-08-24 13:11:39 +02:00
void CheckBufferOverrun : : sizeArgumentAsCharError ( const Token * tok )
2009-03-25 07:25:10 +01:00
{
2013-03-03 11:41:59 +01:00
if ( _settings & & ! _settings - > isEnabled ( " warning " ) )
2009-10-13 22:33:41 +02:00
return ;
2012-07-08 15:51:24 +02:00
reportError ( tok , Severity : : warning , " sizeArgumentAsChar " , " The size argument is given as a char constant. " ) ;
2009-03-25 07:25:10 +01:00
}
2009-12-18 17:26:15 +01:00
2011-09-05 21:59:41 +02:00
void CheckBufferOverrun : : terminateStrncpyError ( const Token * tok , const std : : string & varname )
2009-12-18 17:26:15 +01:00
{
2011-09-05 21:59:41 +02:00
reportError ( tok , Severity : : warning , " terminateStrncpy " ,
2012-07-09 11:11:05 +02:00
" The buffer ' " + varname + " ' may not be null-terminated after the call to strncpy(). \n "
2012-07-08 15:51:24 +02:00
" If the source string's size fits or exceeds the given size, strncpy() does not add a "
" zero at the end of the buffer. This causes bugs later in the code if the code "
2012-09-07 16:11:15 +02:00
" assumes buffer is null-terminated. " , true ) ;
2009-12-18 17:26:15 +01:00
}
2010-06-02 07:41:07 +02:00
void CheckBufferOverrun : : cmdLineArgsError ( const Token * tok )
{
2012-07-09 11:11:05 +02:00
reportError ( tok , Severity : : error , " insecureCmdLineArgs " , " Buffer overrun possible for long command line arguments. " ) ;
2010-06-02 07:41:07 +02:00
}
2011-09-05 21:59:41 +02:00
void CheckBufferOverrun : : bufferNotZeroTerminatedError ( const Token * tok , const std : : string & varname , const std : : string & function )
2011-09-05 21:41:37 +02:00
{
2012-07-09 11:11:05 +02:00
const std : : string errmsg = " The buffer ' " + varname + " ' is not null-terminated after the call to " + function + " (). \n "
" The buffer ' " + varname + " ' is not null-terminated after the call to " + function + " (). "
" This will cause bugs later in the code if the code assumes the buffer is null-terminated. " ;
2011-10-14 19:45:51 +02:00
2012-05-06 19:37:41 +02:00
reportError ( tok , Severity : : warning , " bufferNotZeroTerminated " , errmsg , true ) ;
2011-09-05 21:41:37 +02:00
}
2009-12-18 17:26:15 +01:00
2012-12-22 09:23:34 +01:00
void CheckBufferOverrun : : argumentSizeError ( const Token * tok , const std : : string & functionName , const std : : string & varname )
{
2013-02-16 09:52:27 +01:00
reportError ( tok , Severity : : warning , " argumentSize " , " The array ' " + varname + " ' is too small, the function ' " + functionName + " ' expects a bigger one. " ) ;
2012-12-22 09:23:34 +01:00
}
2009-01-31 20:29:27 +01:00
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
// Check array usage..
//---------------------------------------------------------------------------
2010-02-27 22:47:56 +01:00
/**
* @ brief This is a helper class to be used with std : : find_if
*/
2011-10-13 20:53:06 +02:00
class TokenStrEquals {
2010-02-27 22:47:56 +01:00
public :
/**
* @ param str Token : : str ( ) is compared against this .
*/
2011-10-28 22:10:19 +02:00
explicit TokenStrEquals ( const std : : string & str )
2011-10-13 20:53:06 +02:00
: value ( str ) {
2010-02-27 22:47:56 +01:00
}
/**
* Called automatically by std : : find_if
* @ param tok Token inside the list
*/
2011-10-13 20:53:06 +02:00
bool operator ( ) ( const Token * tok ) const {
2010-02-27 22:47:56 +01:00
return value = = tok - > str ( ) ;
}
private :
2010-04-13 19:25:08 +02:00
TokenStrEquals & operator = ( const TokenStrEquals & ) ; // disallow assignments
2010-02-27 22:47:56 +01:00
const std : : string value ;
} ;
2010-12-31 17:43:38 +01:00
/**
* bailout if variable is used inside if / else / switch block or if there is " break "
* @ param tok token for " if " or " switch "
* @ param varid variable id
* @ return is bailout recommended ?
*/
static bool bailoutIfSwitch ( const Token * tok , const unsigned int varid )
{
2011-01-06 13:02:21 +01:00
// Used later to check if the body belongs to a "if"
2011-11-26 21:02:04 +01:00
bool is_if = tok - > str ( ) = = " if " ;
2010-12-31 17:43:38 +01:00
2012-03-17 21:55:08 +01:00
const Token * end = tok - > linkAt ( 1 ) - > linkAt ( 1 ) ;
if ( Token : : simpleMatch ( end , " } else { " ) ) // scan the else-block
end = end - > linkAt ( 2 ) ;
for ( ; tok ! = end ; tok = tok - > next ( ) ) {
2010-12-31 17:43:38 +01:00
// If scanning a "if" block then bailout for "break"
2012-07-08 14:34:47 +02:00
if ( is_if & & ( tok - > str ( ) = = " break " | | tok - > str ( ) = = " continue " ) )
2010-12-31 17:43:38 +01:00
return true ;
2010-12-31 18:07:46 +01:00
// bailout for "return"
else if ( tok - > str ( ) = = " return " )
return true ;
2010-12-31 17:43:38 +01:00
// bailout if varid is found
else if ( tok - > varId ( ) = = varid )
return true ;
}
// No bailout stuff found => return false
return false ;
}
2010-04-21 18:33:21 +02:00
/**
* Parse for loop initialization statement . Look for a counter variable
2010-05-08 10:39:45 +02:00
* \ param tok [ in ] first token inside the parentheses
2010-04-21 18:33:21 +02:00
* \ param varid [ out ] varid of counter variable
2012-06-16 17:44:51 +02:00
* \ param varname [ out ] name of counter variable
2010-04-21 18:33:21 +02:00
* \ param init_value [ out ] init value of counter variable
2012-09-23 17:15:39 +02:00
* \ return success = > pointer to the for loop condition . fail = > 0. If 0 is returned and varname has been set then there is
* a missing varid for the counter variable
2010-04-21 18:33:21 +02:00
*/
2012-06-16 17:44:51 +02:00
static const Token * for_init ( const Token * tok , unsigned int & varid , std : : string & varname , std : : string & init_value )
2010-04-21 18:33:21 +02:00
{
2011-10-13 20:53:06 +02:00
if ( Token : : Match ( tok , " %var% = %any% ; " ) ) {
if ( tok - > tokAt ( 2 ) - > isNumber ( ) ) {
2010-04-21 18:33:21 +02:00
init_value = tok - > strAt ( 2 ) ;
}
varid = tok - > varId ( ) ;
2012-06-16 17:44:51 +02:00
varname = tok - > str ( ) ;
2010-04-21 18:33:21 +02:00
tok = tok - > tokAt ( 4 ) ;
2012-09-23 17:15:39 +02:00
if ( varid = = 0 )
return 0 ; // failed
2011-10-13 20:53:06 +02:00
} else if ( Token : : Match ( tok , " %type% %var% = %any% ; " ) ) {
if ( tok - > tokAt ( 3 ) - > isNumber ( ) ) {
2010-04-21 18:33:21 +02:00
init_value = tok - > strAt ( 3 ) ;
}
varid = tok - > next ( ) - > varId ( ) ;
2012-06-16 17:44:51 +02:00
varname = tok - > next ( ) - > str ( ) ;
2010-04-21 18:33:21 +02:00
tok = tok - > tokAt ( 5 ) ;
2011-10-13 20:53:06 +02:00
} else if ( Token : : Match ( tok , " %type% %type% %var% = %any% ; " ) ) {
if ( tok - > tokAt ( 4 ) - > isNumber ( ) ) {
2010-04-21 18:33:21 +02:00
init_value = tok - > strAt ( 4 ) ;
}
varid = tok - > tokAt ( 2 ) - > varId ( ) ;
2012-09-07 12:36:40 +02:00
varname = tok - > strAt ( 2 ) ;
2010-04-21 18:33:21 +02:00
tok = tok - > tokAt ( 6 ) ;
2011-10-13 20:53:06 +02:00
} else
2010-04-21 18:33:21 +02:00
return 0 ;
2012-08-12 17:06:54 +02:00
if ( ! init_value . empty ( ) & & ( Token : : Match ( tok , " -- %varid% " , varid ) | | Token : : Match ( tok , " %varid% -- " , varid ) ) ) {
init_value = MathLib : : subtract ( init_value , " 1 " ) ;
}
2010-04-21 18:33:21 +02:00
return tok ;
}
/** Parse for condition */
2012-06-23 09:23:14 +02:00
static bool for_condition ( const Token * tok2 , unsigned int varid , std : : string & min_value , std : : string & max_value , bool & maxMinFlipped )
2010-04-21 18:33:21 +02:00
{
2012-06-23 09:23:14 +02:00
if ( Token : : Match ( tok2 , " %varid% < %num% ;|&&|%oror% " , varid ) | |
2011-01-09 18:51:28 +01:00
Token : : Match ( tok2 , " %varid% != %num% ; ++ %varid% " , varid ) | |
2011-10-13 20:53:06 +02:00
Token : : Match ( tok2 , " %varid% != %num% ; %varid% ++ " , varid ) ) {
2010-04-21 18:33:21 +02:00
maxMinFlipped = false ;
2010-11-20 11:48:03 +01:00
const MathLib : : bigint value = MathLib : : toLongNumber ( tok2 - > strAt ( 2 ) ) ;
2012-09-11 16:50:42 +02:00
max_value = MathLib : : longToString ( value - 1 ) ;
2012-06-23 09:23:14 +02:00
} else if ( Token : : Match ( tok2 , " %varid% <= %num% ;|&&|%oror% " , varid ) ) {
2010-04-21 18:33:21 +02:00
maxMinFlipped = false ;
max_value = tok2 - > strAt ( 2 ) ;
2012-06-23 09:23:14 +02:00
} else if ( Token : : Match ( tok2 , " %num% < %varid% ;|&&|%oror% " , varid ) | |
2011-10-13 20:53:06 +02:00
Token : : Match ( tok2 , " %num% != %varid% ; ++ %varid% " , varid ) | |
Token : : Match ( tok2 , " %num% != %varid% ; %varid% ++ " , varid ) ) {
2010-04-21 18:33:21 +02:00
maxMinFlipped = true ;
2010-11-20 11:48:03 +01:00
const MathLib : : bigint value = MathLib : : toLongNumber ( tok2 - > str ( ) ) ;
2010-04-21 18:33:21 +02:00
max_value = min_value ;
2012-09-11 16:50:42 +02:00
min_value = MathLib : : longToString ( value + 1 ) ;
2012-06-23 09:23:14 +02:00
} else if ( Token : : Match ( tok2 , " %num% <= %varid% ;|&&|%oror% " , varid ) ) {
2010-04-21 18:33:21 +02:00
maxMinFlipped = true ;
max_value = min_value ;
min_value = tok2 - > str ( ) ;
2011-10-13 20:53:06 +02:00
} else {
2012-06-23 09:23:14 +02:00
// parse condition
while ( tok2 & & tok2 - > str ( ) ! = " ; " ) {
if ( tok2 - > str ( ) = = " ( " )
tok2 = tok2 - > link ( ) ;
2012-09-15 15:32:00 +02:00
else if ( tok2 - > str ( ) = = " ) " ) // unexpected ")" => break
2012-06-23 09:23:14 +02:00
break ;
if ( tok2 - > str ( ) = = " && " | | tok2 - > str ( ) = = " || " ) {
if ( for_condition ( tok2 - > next ( ) , varid , min_value , max_value , maxMinFlipped ) )
return true ;
}
tok2 = tok2 - > next ( ) ;
}
2010-04-21 18:33:21 +02:00
return false ;
}
return true ;
}
2011-04-04 17:33:43 +02:00
/**
* calculate maximum value of loop variable
* @ param stepvalue token that contains the step value
* @ param min_value the minimum value of loop variable
* @ param max_value maximum value of the loop variable
*/
static bool for_maxvalue ( const Token * const stepvalue , const std : : string & min_value , std : : string & max_value )
{
if ( ! MathLib : : isInt ( stepvalue - > str ( ) ) )
return false ;
// We have for example code: "for(i=2;i<22;i+=6)
// We can calculate that max value for i is 20, not 21
// 21-2 = 19
// 19/6 = 3
// 6*3+2 = 20
const MathLib : : bigint num = MathLib : : toLongNumber ( stepvalue - > str ( ) ) ;
MathLib : : bigint max = MathLib : : toLongNumber ( max_value ) ;
const MathLib : : bigint min = MathLib : : toLongNumber ( min_value ) ;
max = ( ( max - min ) / num ) * num + min ;
2012-09-11 16:50:42 +02:00
max_value = MathLib : : longToString ( max ) ;
2011-04-04 17:33:43 +02:00
return true ;
}
2010-04-21 18:33:21 +02:00
/**
2010-05-08 10:39:45 +02:00
* Parse the third sub - statement in for head
2010-04-21 18:33:21 +02:00
* \ param tok first token
* \ param varid variable id of counter
* \ param min_value min value of counter
* \ param max_value max value of counter
* \ param maxMinFlipped counting from max to min
*/
static bool for3 ( const Token * const tok ,
unsigned int varid ,
std : : string & min_value ,
std : : string & max_value ,
const bool maxMinFlipped )
{
assert ( tok ! = 0 ) ;
2012-11-30 07:08:16 +01:00
if ( Token : : Match ( tok , " %varid% = %num% + %varid% ) " , varid ) ) {
2011-04-04 17:33:43 +02:00
if ( ! for_maxvalue ( tok - > tokAt ( 2 ) , min_value , max_value ) )
2010-04-21 18:33:21 +02:00
return false ;
2011-10-13 20:53:06 +02:00
} else if ( Token : : Match ( tok , " %varid% = %varid% + %num% ) " , varid)) {
2011-04-04 17:33:43 +02:00
if ( ! for_maxvalue ( tok - > tokAt ( 4 ) , min_value , max_value ) )
2010-04-21 18:33:21 +02:00
return false ;
2012-11-30 07:08:16 +01:00
} else if ( Token : : Match ( tok , " %varid% = %num% - %varid% ) " , varid)) {
2011-04-04 17:33:43 +02:00
if ( ! for_maxvalue ( tok - > tokAt ( 2 ) , min_value , max_value ) )
2010-04-21 18:33:21 +02:00
return false ;
2011-10-13 20:53:06 +02:00
} else if ( Token : : Match ( tok , " %varid% = %varid% - %num% ) " , varid)) {
2011-04-04 17:33:43 +02:00
if ( ! for_maxvalue ( tok - > tokAt ( 4 ) , min_value , max_value ) )
2010-04-21 18:33:21 +02:00
return false ;
2011-10-13 20:53:06 +02:00
} else if ( Token : : Match ( tok , " --| %varid% --| ) " , varid)) {
if ( ! maxMinFlipped & & MathLib : : toLongNumber ( min_value ) < MathLib : : toLongNumber ( max_value ) ) {
2010-04-21 18:33:21 +02:00
// Code relies on the fact that integer will overflow:
// for (unsigned int i = 3; i < 5; --i)
// Set min value in this case to zero.
max_value = min_value ;
min_value = " 0 " ;
}
2011-10-13 20:53:06 +02:00
} else if ( ! Token : : Match ( tok , " ++| %varid% ++| ) " , varid)) {
2010-04-21 18:33:21 +02:00
return false ;
}
return true ;
}
/**
* Check is the counter variable increased elsewhere inside the loop or used
* for anything else except reading
2010-06-14 07:54:41 +02:00
* \ param tok1 first token of for - body
2010-04-21 18:33:21 +02:00
* \ param varid counter variable id
* \ return bailout needed = > true
*/
static bool for_bailout ( const Token * const tok1 , unsigned int varid )
{
2011-10-13 20:53:06 +02:00
for ( const Token * loopTok = tok1 ; loopTok & & loopTok ! = tok1 - > link ( ) ; loopTok = loopTok - > next ( ) ) {
if ( loopTok - > varId ( ) = = varid ) {
2010-04-21 18:33:21 +02:00
// Counter variable used inside loop
2012-11-30 07:08:16 +01:00
if ( Token : : Match ( loopTok - > next ( ) , " ++|--|= " ) | |
2012-04-25 09:56:07 +02:00
( loopTok - > previous ( ) - > type ( ) = = Token : : eIncDecOp ) ) {
2010-04-21 18:33:21 +02:00
return true ;
}
}
}
return false ;
}
2011-12-17 19:04:03 +01:00
void CheckBufferOverrun : : parse_for_body ( const Token * tok , const ArrayInfo & arrayInfo , const std : : string & strindex , bool condition_out_of_bounds , unsigned int counter_varid , const std : : string & min_counter_value , const std : : string & max_counter_value )
2010-04-21 18:33:21 +02:00
{
2011-12-30 18:47:42 +01:00
const std : : string pattern = ( arrayInfo . varid ( ) ? std : : string ( " %varid% " ) : arrayInfo . varname ( ) ) + " [ " + strindex + " ] " ;
2010-04-21 18:33:21 +02:00
2011-12-17 19:04:03 +01:00
for ( const Token * tok2 = tok ; tok2 & & tok2 ! = tok - > link ( ) ; tok2 = tok2 - > next ( ) ) {
2012-11-30 09:01:15 +01:00
// TestBufferOverrun::array_index_for_question
if ( tok2 - > str ( ) = = " ? " ) {
// does condition check counter variable?
bool usesCounter = false ;
const Token * tok3 = tok2 - > previous ( ) ;
2012-12-01 01:31:35 +01:00
while ( Token : : Match ( tok3 , " %comp%|%num%|%var%|) " ) ) {
2012-11-30 09:01:15 +01:00
if ( tok3 - > str ( ) = = strindex ) {
usesCounter = true ;
break ;
}
tok3 = tok3 - > previous ( ) ;
}
// If strindex is used in the condition then skip the
// conditional expressions
if ( usesCounter ) {
while ( tok2 & & ! Token : : Match ( tok2 , " [)],;] " ) ) {
if ( tok2 - > str ( ) = = " ( " | | tok2 - > str ( ) = = " [ " )
tok2 = tok2 - > link ( ) ;
tok2 = tok2 - > next ( ) ;
}
if ( ! tok2 )
break ;
continue ;
}
}
2011-02-12 11:31:10 +01:00
2011-10-13 20:53:06 +02:00
if ( Token : : simpleMatch ( tok2 , " for ( " ) & & Token : : simpleMatch ( tok2 - > next ( ) - > link ( ) , " ) { " ) ) {
2011-03-08 19:49:56 +01:00
const Token * endpar = tok2 - > next ( ) - > link ( ) ;
const Token * startbody = endpar - > next ( ) ;
const Token * endbody = startbody - > link ( ) ;
tok2 = endbody ;
continue ;
}
2011-10-13 20:53:06 +02:00
if ( Token : : Match ( tok2 , " if|switch " ) ) {
2011-06-23 02:35:58 +02:00
if ( bailoutIfSwitch ( tok2 , arrayInfo . varid ( ) ) )
2010-12-28 20:46:31 +01:00
break ;
2010-04-21 18:33:21 +02:00
}
2011-10-13 20:53:06 +02:00
if ( condition_out_of_bounds & & Token : : Match ( tok2 , pattern . c_str ( ) , arrayInfo . varid ( ) ) ) {
2011-08-24 13:11:39 +02:00
bufferOverrunError ( tok2 , arrayInfo . varname ( ) ) ;
2010-04-21 18:33:21 +02:00
break ;
}
2011-12-17 21:35:12 +01:00
else if ( arrayInfo . varid ( ) & & tok2 - > varId ( ) & & counter_varid > 0 & & ! min_counter_value . empty ( ) & & ! max_counter_value . empty ( ) ) {
2011-01-02 14:16:58 +01:00
// Is the loop variable used to calculate the array index?
// In this scope it is determined if such calculated
// array indexes are out of bounds.
// Only the minimum and maximum results of the calculation is
// determined
// Minimum calculated array index
2010-04-21 18:33:21 +02:00
int min_index = 0 ;
2011-01-02 14:16:58 +01:00
// Maximum calculated array index
2010-04-21 18:33:21 +02:00
int max_index = 0 ;
2011-06-23 02:35:58 +02:00
if ( Token : : Match ( tok2 , " %varid% [ %var% +|-|*|/ %num% ] " , arrayInfo . varid ( ) ) & &
2011-10-13 20:53:06 +02:00
tok2 - > tokAt ( 2 ) - > varId ( ) = = counter_varid ) {
2011-01-02 14:16:58 +01:00
// operator: +-*/
2010-04-21 18:33:21 +02:00
const char action = tok2 - > strAt ( 3 ) [ 0 ] ;
2011-01-02 14:16:58 +01:00
// second operator
2011-11-13 13:10:59 +01:00
const std : : string & second ( tok2 - > strAt ( 4 ) ) ;
2010-04-21 18:33:21 +02:00
//printf("min_index: %s %c %s\n", min_counter_value.c_str(), action, second.c_str());
//printf("max_index: %s %c %s\n", max_counter_value.c_str(), action, second.c_str());
2012-01-08 21:19:44 +01:00
min_index = std : : atoi ( MathLib : : calculate ( min_counter_value , second , action ) . c_str ( ) ) ;
max_index = std : : atoi ( MathLib : : calculate ( max_counter_value , second , action ) . c_str ( ) ) ;
2011-10-13 20:53:06 +02:00
} else if ( Token : : Match ( tok2 , " %varid% [ %num% +|-|*|/ %var% ] " , arrayInfo . varid ( ) ) & &
tok2 - > tokAt ( 4 ) - > varId ( ) = = counter_varid ) {
2011-01-02 14:16:58 +01:00
// operator: +-*/
2010-04-21 18:33:21 +02:00
const char action = tok2 - > strAt ( 3 ) [ 0 ] ;
2011-01-02 14:16:58 +01:00
// first operand
2011-11-13 13:10:59 +01:00
const std : : string & first ( tok2 - > strAt ( 2 ) ) ;
2010-04-21 18:33:21 +02:00
//printf("min_index: %s %c %s\n", first.c_str(), action, min_counter_value.c_str());
//printf("max_index: %s %c %s\n", first.c_str(), action, max_counter_value.c_str());
2012-01-08 21:19:44 +01:00
min_index = std : : atoi ( MathLib : : calculate ( first , min_counter_value , action ) . c_str ( ) ) ;
max_index = std : : atoi ( MathLib : : calculate ( first , max_counter_value , action ) . c_str ( ) ) ;
2010-04-21 18:33:21 +02:00
}
2011-12-17 21:35:12 +01:00
else {
continue ;
}
2010-04-21 18:33:21 +02:00
//printf("min_index = %d, max_index = %d, size = %d\n", min_index, max_index, size);
2011-10-13 20:53:06 +02:00
if ( min_index < 0 | | max_index < 0 ) {
2011-09-11 15:54:26 +02:00
std : : vector < MathLib : : bigint > indexes ;
indexes . push_back ( std : : min ( min_index , max_index ) ) ;
arrayIndexOutOfBoundsError ( tok2 , arrayInfo , indexes ) ;
2010-04-25 07:34:50 +02:00
}
2011-09-23 03:23:40 +02:00
// skip 0 length arrays
2011-12-17 21:35:12 +01:00
if ( arrayInfo . num ( 0 ) = = 0 )
;
// taking address.
else if ( tok2 - > previous ( ) - > str ( ) = = " & " & & max_index = = arrayInfo . num ( 0 ) )
;
else if ( arrayInfo . num ( 0 ) & & ( min_index > = arrayInfo . num ( 0 ) | | max_index > = arrayInfo . num ( 0 ) ) ) {
2011-09-11 15:54:26 +02:00
std : : vector < MathLib : : bigint > indexes ;
indexes . push_back ( std : : max ( min_index , max_index ) ) ;
arrayIndexOutOfBoundsError ( tok2 , arrayInfo , indexes ) ;
2010-04-21 18:33:21 +02:00
}
}
}
}
2011-12-11 08:16:58 +01:00
void CheckBufferOverrun : : checkFunctionParameter ( const Token & tok , unsigned int par , const ArrayInfo & arrayInfo , std : : list < const Token * > callstack )
2010-04-21 19:27:28 +02:00
{
2011-01-02 14:16:58 +01:00
// total_size : which parameter in function call takes the total size?
2010-04-21 19:27:28 +02:00
std : : map < std : : string , unsigned int > total_size ;
2011-01-02 14:16:58 +01:00
2011-10-23 17:47:48 +02:00
total_size [ " fgets " ] = 2 ; // The second argument for fgets can't exceed the total size of the array
2010-04-21 19:27:28 +02:00
total_size [ " memcmp " ] = 3 ;
total_size [ " memcpy " ] = 3 ;
total_size [ " memmove " ] = 3 ;
2011-11-27 07:54:52 +01:00
total_size [ " memchr " ] = 3 ;
2010-04-21 19:27:28 +02:00
2011-10-13 20:53:06 +02:00
if ( par = = 1 ) {
2010-04-21 20:02:58 +02:00
// reading from array
// if it is zero terminated properly there won't be buffer overruns
total_size [ " strncat " ] = 3 ;
total_size [ " strncpy " ] = 3 ;
total_size [ " memset " ] = 3 ;
2010-04-22 19:49:02 +02:00
total_size [ " fread " ] = 1001 ; // parameter 2 * parameter 3
total_size [ " fwrite " ] = 1001 ; // parameter 2 * parameter 3
2010-04-21 20:02:58 +02:00
}
2010-04-21 21:08:47 +02:00
2011-12-08 21:28:34 +01:00
else if ( par = = 2 ) {
2010-04-21 20:06:59 +02:00
total_size [ " read " ] = 3 ;
2010-08-14 20:19:23 +02:00
total_size [ " pread " ] = 3 ;
2010-04-21 21:08:47 +02:00
total_size [ " write " ] = 3 ;
2010-08-14 20:19:23 +02:00
total_size [ " recv " ] = 3 ;
total_size [ " recvfrom " ] = 3 ;
total_size [ " send " ] = 3 ;
total_size [ " sendto " ] = 3 ;
2010-04-21 21:08:47 +02:00
}
2010-04-21 20:02:58 +02:00
std : : map < std : : string , unsigned int > : : const_iterator it = total_size . find ( tok . str ( ) ) ;
2011-10-13 20:53:06 +02:00
if ( it ! = total_size . end ( ) ) {
2011-06-23 02:35:58 +02:00
if ( arrayInfo . element_size ( ) = = 0 )
2010-10-24 11:32:27 +02:00
return ;
2011-01-06 13:02:21 +01:00
// arg : the index of the "wanted" argument in the function call.
2010-04-21 19:27:28 +02:00
unsigned int arg = it - > second ;
2011-01-06 13:02:21 +01:00
// Parse function call. When a ',' is seen, arg is decremented.
// if arg becomes 1 then the current function parameter is the wanted parameter.
2012-12-28 11:31:50 +01:00
// if arg becomes 1001 then multiply current and next argument.
2012-03-17 21:55:08 +01:00
const Token * tok2 = tok . tokAt ( 2 ) - > nextArgument ( ) ;
if ( arg = = 3 )
tok2 = tok2 - > nextArgument ( ) ;
if ( ( arg = = 2 | | arg = = 3 ) & & tok2 ) {
if ( Token : : Match ( tok2 , " %num% ,|) " ) ) {
const MathLib : : bigint sz = MathLib : : toLongNumber ( tok2 - > str ( ) ) ;
MathLib : : bigint elements = 1 ;
for ( unsigned int i = 0 ; i < arrayInfo . num ( ) . size ( ) ; + + i )
elements * = arrayInfo . num ( i ) ;
if ( sz < 0 | | sz > int ( elements * arrayInfo . element_size ( ) ) ) {
bufferOverrunError ( callstack , arrayInfo . varname ( ) ) ;
2010-04-21 19:27:28 +02:00
}
2012-03-17 21:55:08 +01:00
}
2010-04-22 19:49:02 +02:00
2012-04-25 09:56:07 +02:00
else if ( Token : : Match ( tok2 - > next ( ) , " ,|) " ) & & tok2 - > type ( ) = = Token : : eChar ) {
2012-03-17 21:55:08 +01:00
sizeArgumentAsCharError ( tok2 ) ;
}
} else if ( arg = = 1001 ) { // special code. This parameter multiplied with the next must not exceed total_size
if ( Token : : Match ( tok2 , " %num% , %num% ,|) " ) ) {
const MathLib : : bigint sz = MathLib : : toLongNumber ( MathLib : : multiply ( tok2 - > str ( ) , tok2 - > strAt ( 2 ) ) ) ;
MathLib : : bigint elements = 1 ;
for ( unsigned int i = 0 ; i < arrayInfo . num ( ) . size ( ) ; + + i )
elements * = arrayInfo . num ( i ) ;
if ( sz < 0 | | sz > int ( elements * arrayInfo . element_size ( ) ) ) {
bufferOverrunError ( & tok , arrayInfo . varname ( ) ) ;
2010-04-22 19:49:02 +02:00
}
2010-04-21 19:27:28 +02:00
}
}
}
2010-08-05 11:01:47 +02:00
// Calling a user function?
// only 1-dimensional arrays can be checked currently
2011-10-13 20:53:06 +02:00
else if ( arrayInfo . num ( ) . size ( ) = = 1 ) {
2013-01-31 06:41:18 +01:00
const Function * func = tok . function ( ) ;
2012-12-22 09:23:34 +01:00
2012-09-11 18:03:47 +02:00
if ( func & & func - > hasBody ) {
// Get corresponding parameter..
const Variable * parameter = func - > getArgumentVar ( par - 1 ) ;
2010-08-05 11:01:47 +02:00
2012-09-11 18:03:47 +02:00
// Ensure that it has a compatible size..
if ( ! parameter | | _tokenizer - > sizeOfType ( parameter - > typeStartToken ( ) ) ! = arrayInfo . element_size ( ) )
2010-08-05 11:01:47 +02:00
return ;
2012-09-23 13:25:28 +02:00
// No variable id occur for instance when:
// - Variable function arguments: "void f(...)"
// - Unnamed parameter: "void f(char *)"
if ( parameter - > varId ( ) = = 0 )
2012-09-22 16:19:19 +02:00
return ;
2010-08-05 11:01:47 +02:00
// Check the parameter usage in the function scope..
2012-09-11 18:03:47 +02:00
for ( const Token * ftok = func - > functionScope - > classStart ; ftok ! = func - > functionScope - > classEnd ; ftok = ftok - > next ( ) ) {
2011-10-13 20:53:06 +02:00
if ( Token : : Match ( ftok , " if|for|while ( " ) ) {
2010-12-31 18:07:46 +01:00
// bailout if there is buffer usage..
2012-09-11 18:03:47 +02:00
if ( bailoutIfSwitch ( ftok , parameter - > varId ( ) ) ) {
2010-12-31 18:07:46 +01:00
break ;
}
// no bailout is needed. skip the if-block
2011-10-13 20:53:06 +02:00
else {
2010-12-31 18:07:46 +01:00
// goto end of if block..
ftok = ftok - > next ( ) - > link ( ) - > next ( ) - > link ( ) ;
if ( Token : : simpleMatch ( ftok , " } else { " ) )
2011-11-20 14:22:39 +01:00
ftok = ftok - > linkAt ( 2 ) ;
2011-08-29 19:16:52 +02:00
if ( ! ftok )
break ;
2010-12-31 18:07:46 +01:00
continue ;
}
}
2010-08-05 11:01:47 +02:00
if ( ftok - > str ( ) = = " } " )
break ;
2012-09-11 18:03:47 +02:00
if ( ftok - > varId ( ) = = parameter - > varId ( ) ) {
2010-10-14 20:00:32 +02:00
if ( Token : : Match ( ftok - > previous ( ) , " -- %var% " ) | |
Token : : Match ( ftok , " %var% -- " ) )
break ;
2013-03-01 11:43:59 +01:00
if ( Token : : Match ( ftok - > previous ( ) , " ;|{|}|%op% %var% [ %num% ] " ) ) {
2010-11-21 11:48:27 +01:00
const MathLib : : bigint index = MathLib : : toLongNumber ( ftok - > strAt ( 2 ) ) ;
2011-10-13 20:53:06 +02:00
if ( index > = 0 & & arrayInfo . num ( 0 ) > 0 & & index > = arrayInfo . num ( 0 ) ) {
2011-12-11 08:16:58 +01:00
std : : list < const Token * > callstack2 ( callstack ) ;
callstack2 . push_back ( ftok ) ;
2010-08-05 11:01:47 +02:00
2010-12-31 09:30:56 +01:00
std : : vector < MathLib : : bigint > indexes ;
indexes . push_back ( index ) ;
2010-08-05 11:01:47 +02:00
2011-12-11 08:16:58 +01:00
arrayIndexOutOfBoundsError ( callstack2 , arrayInfo , indexes ) ;
2010-08-05 11:01:47 +02:00
}
}
}
2011-12-11 08:16:58 +01:00
// Calling function..
if ( Token : : Match ( ftok , " %var% ( " ) ) {
ArrayInfo ai ( arrayInfo ) ;
2012-09-11 18:03:47 +02:00
ai . varid ( parameter - > varId ( ) ) ;
2011-12-11 08:16:58 +01:00
checkFunctionCall ( ftok , ai , callstack ) ;
}
2010-08-05 11:01:47 +02:00
}
}
}
2012-12-22 09:23:34 +01:00
// Check 'float x[10]' arguments in declaration
2013-03-03 11:41:59 +01:00
if ( _settings - > isEnabled ( " warning " ) ) {
2013-01-31 06:41:18 +01:00
const Function * func = tok . function ( ) ;
2012-12-22 09:23:34 +01:00
// If argument is '%type% a[num]' then check bounds against num
if ( func ) {
const Variable * argument = func - > getArgumentVar ( par - 1 ) ;
2012-12-28 11:15:18 +01:00
const Token * nameToken ;
if ( argument & & Token : : Match ( argument - > typeStartToken ( ) , " %type% %var% [ %num% ] [,)[] " )
& & ( nameToken = argument - > nameToken ( ) ) ! = NULL ) {
const Token * tok2 = nameToken - > next ( ) ;
2012-12-22 09:23:34 +01:00
MathLib : : bigint argsize = _tokenizer - > sizeOfType ( argument - > typeStartToken ( ) ) ;
if ( argsize = = 100 ) // unknown size
argsize = 0 ;
while ( Token : : Match ( tok2 , " [ %num% ] [,)[] " ) ) {
argsize * = MathLib : : toLongNumber ( tok2 - > strAt ( 1 ) ) ;
tok2 = tok2 - > tokAt ( 3 ) ;
}
MathLib : : bigint arraysize = arrayInfo . element_size ( ) ;
if ( arraysize = = 100 ) // unknown size
arraysize = 0 ;
for ( unsigned int i = 0 ; i < arrayInfo . num ( ) . size ( ) ; i + + )
arraysize * = arrayInfo . num ( i ) ;
if ( Token : : Match ( tok2 , " [,)] " ) & & arraysize > 0 & & argsize > arraysize )
argumentSizeError ( & tok , tok . str ( ) , arrayInfo . varname ( ) ) ;
}
}
}
2010-04-21 19:27:28 +02:00
}
2011-12-11 08:16:58 +01:00
void CheckBufferOverrun : : checkFunctionCall ( const Token * tok , const ArrayInfo & arrayInfo , std : : list < const Token * > callstack )
2011-01-22 21:31:26 +01:00
{
2011-12-11 08:16:58 +01:00
// Don't go deeper than 2 levels, the checking can get very slow
// when there is no limit
if ( callstack . size ( ) > = 2 )
return ;
// Prevent recursion
for ( std : : list < const Token * > : : const_iterator it = callstack . begin ( ) ; it ! = callstack . end ( ) ; + + it ) {
// Same function name => bail out
if ( tok - > str ( ) = = ( * it ) - > str ( ) )
return ;
}
callstack . push_back ( tok ) ;
2011-01-22 21:31:26 +01:00
2011-12-17 19:04:03 +01:00
const Token * tok2 = tok - > tokAt ( 2 ) ;
2011-01-22 21:31:26 +01:00
// 1st parameter..
2011-12-17 19:04:03 +01:00
if ( Token : : Match ( tok2 , " %varid% ,|) " , arrayInfo . varid ( ) ) )
2011-12-11 08:16:58 +01:00
checkFunctionParameter ( * tok , 1 , arrayInfo , callstack ) ;
2011-12-17 19:04:03 +01:00
else if ( Token : : Match ( tok2 , " %varid% + %num% ,|) " , arrayInfo.varid())) {
const ArrayInfo ai ( arrayInfo . limit ( MathLib : : toLongNumber ( tok2 - > strAt ( 2 ) ) ) ) ;
2011-12-11 08:16:58 +01:00
checkFunctionParameter ( * tok , 1 , ai , callstack ) ;
2011-01-22 21:31:26 +01:00
}
// goto 2nd parameter and check it..
2011-12-17 19:04:03 +01:00
tok2 = tok2 - > nextArgument ( ) ;
if ( Token : : Match ( tok2 , " %varid% ,|) " , arrayInfo . varid ( ) ) )
checkFunctionParameter ( * tok , 2 , arrayInfo , callstack ) ;
else if ( Token : : Match ( tok2 , " %varid% + %num% ,|) " , arrayInfo.varid())) {
const ArrayInfo ai ( arrayInfo . limit ( MathLib : : toLongNumber ( tok2 - > strAt ( 2 ) ) ) ) ;
checkFunctionParameter ( * tok , 2 , ai , callstack ) ;
2011-01-22 21:31:26 +01:00
}
}
2010-04-21 18:33:21 +02:00
2011-02-10 21:56:06 +01:00
void CheckBufferOverrun : : checkScopeForBody ( const Token * tok , const ArrayInfo & arrayInfo , bool & bailout )
{
bailout = false ;
const Token * tok2 = tok - > tokAt ( 2 ) ;
2011-06-23 02:35:58 +02:00
const MathLib : : bigint size = arrayInfo . num ( 0 ) ;
2011-02-10 21:56:06 +01:00
// Check if there is a break in the body..
{
const Token * bodyStart = tok - > next ( ) - > link ( ) - > next ( ) ;
const Token * bodyEnd = bodyStart - > link ( ) ;
2011-10-27 10:54:50 +02:00
if ( Token : : findsimplematch ( bodyStart , " break ; " , bodyEnd ) )
2011-02-10 21:56:06 +01:00
return ;
}
2012-06-16 17:44:51 +02:00
std : : string counter_name ;
2011-02-10 21:56:06 +01:00
unsigned int counter_varid = 0 ;
2012-06-16 17:44:51 +02:00
std : : string counter_init_value ;
2011-02-10 21:56:06 +01:00
2012-06-16 17:44:51 +02:00
tok2 = for_init ( tok2 , counter_varid , counter_name , counter_init_value ) ;
2012-09-23 17:15:39 +02:00
if ( tok2 = = 0 & & ! counter_name . empty ( ) )
_tokenizer - > getSymbolDatabase ( ) - > debugMessage ( tok , " for loop variable \' " + counter_name + " \' has varid 0. " ) ;
2011-02-10 21:56:06 +01:00
if ( tok2 = = 0 | | counter_varid = = 0 )
return ;
bool maxMinFlipped = false ;
2012-06-16 17:44:51 +02:00
std : : string min_counter_value = counter_init_value ;
std : : string max_counter_value ;
if ( ! for_condition ( tok2 , counter_varid , min_counter_value , max_counter_value , maxMinFlipped ) ) {
// Can't understand the condition. Check that the start value
// is used correctly
const Token * startForScope = tok - > next ( ) - > link ( ) - > next ( ) ;
if ( ! for_bailout ( startForScope , counter_varid ) ) {
// Get index variable and stopsize.
bool condition_out_of_bounds = bool ( size > 0 ) ;
if ( MathLib : : toLongNumber ( counter_init_value ) < size )
condition_out_of_bounds = false ;
parse_for_body ( startForScope , arrayInfo , counter_name , condition_out_of_bounds , counter_varid , counter_init_value , counter_init_value ) ;
}
2011-02-10 21:56:06 +01:00
return ;
2012-06-16 17:44:51 +02:00
}
2011-02-10 21:56:06 +01:00
// Get index variable and stopsize.
2011-02-12 18:34:12 +01:00
bool condition_out_of_bounds = bool ( size > 0 ) ;
2011-02-10 21:56:06 +01:00
if ( MathLib : : toLongNumber ( max_counter_value ) < size )
condition_out_of_bounds = false ;
2012-06-23 09:23:14 +02:00
// Goto the end of the condition
while ( tok2 & & tok2 - > str ( ) ! = " ; " ) {
if ( tok2 - > str ( ) = = " ( " )
tok2 = tok2 - > link ( ) ;
else if ( tok2 - > str ( ) = = " ) " ) // unexpected ")" => break
break ;
tok2 = tok2 - > next ( ) ;
}
if ( ! tok2 | | tok2 - > str ( ) ! = " ; " )
return ;
if ( ! for3 ( tok2 - > next ( ) , counter_varid , min_counter_value , max_counter_value , maxMinFlipped ) )
2011-02-10 21:56:06 +01:00
return ;
2012-12-22 08:00:05 +01:00
if ( Token : : Match ( tok2 - > next ( ) , " %var% = " ) & & MathLib : : toLongNumber ( max_counter_value ) < size )
2011-02-10 21:56:06 +01:00
condition_out_of_bounds = false ;
2013-01-16 15:37:07 +01:00
// Goto the end parentheses of the for-statement: "for (x; y; z)" ..
2011-02-10 21:56:06 +01:00
tok2 = tok - > next ( ) - > link ( ) ;
2011-10-13 20:53:06 +02:00
if ( ! tok2 | | ! tok2 - > tokAt ( 5 ) ) {
2011-02-10 21:56:06 +01:00
bailout = true ;
return ;
}
// Check is the counter variable increased elsewhere inside the loop or used
// for anything else except reading
2011-10-13 20:53:06 +02:00
if ( for_bailout ( tok2 - > next ( ) , counter_varid ) ) {
2011-02-10 21:56:06 +01:00
bailout = true ;
return ;
}
2012-06-16 17:44:51 +02:00
parse_for_body ( tok2 - > next ( ) , arrayInfo , counter_name , condition_out_of_bounds , counter_varid , min_counter_value , max_counter_value ) ;
2011-02-10 21:56:06 +01:00
}
2012-09-01 19:17:28 +02:00
void CheckBufferOverrun : : arrayIndexInForLoop ( const Token * tok , const ArrayInfo & arrayInfo )
{
const MathLib : : bigint size = arrayInfo . num ( 0 ) ;
const Token * tok3 = tok - > tokAt ( 2 ) ;
std : : string counter_name ;
unsigned int counter_varid = 0 ;
std : : string counter_init_value ;
tok3 = for_init ( tok3 , counter_varid , counter_name , counter_init_value ) ;
2012-09-23 17:15:39 +02:00
if ( tok3 = = 0 & & ! counter_name . empty ( ) )
_tokenizer - > getSymbolDatabase ( ) - > debugMessage ( tok , " for loop variable \' " + counter_name + " \' has varid 0. " ) ;
if ( tok3 = = 0 | | counter_varid = = 0 )
return ;
2012-09-01 19:17:28 +02:00
bool maxMinFlipped = false ;
std : : string min_counter_value = counter_init_value ;
std : : string max_counter_value ;
MathLib : : bigint max_value = MathLib : : toLongNumber ( max_counter_value ) ;
for_condition ( tok3 , counter_varid , min_counter_value , max_counter_value , maxMinFlipped ) ;
while ( tok3 & & tok3 - > str ( ) ! = " ; " ) {
tok3 = tok3 - > next ( ) ;
}
for ( const Token * tok2 = tok ; tok2 ; tok2 = tok2 - > next ( ) ) {
if ( Token : : Match ( tok2 , " %var% < %num% " ) ) {
max_value = MathLib : : toLongNumber ( tok2 - > strAt ( 2 ) ) ;
max_value = max_value - 1 ;
}
}
if ( max_value > size ) {
2012-09-17 15:22:51 +02:00
if ( tok3 & & tok3 - > strAt ( 1 ) = = " ) " ) {
2012-09-04 15:29:51 +02:00
bool usedInArray = false ;
2012-09-01 19:17:28 +02:00
for ( const Token * loopTok = tok3 - > tokAt ( 2 ) ; loopTok - > str ( ) ! = " } " ; loopTok = loopTok - > next ( ) ) {
if ( loopTok - > varId ( ) = = arrayInfo . varid ( ) & & loopTok - > tokAt ( 2 ) - > varId ( ) = = counter_varid )
usedInArray = true ;
}
for ( const Token * loopTok = tok3 - > tokAt ( 2 ) ; loopTok - > str ( ) ! = " } " ; loopTok = loopTok - > next ( ) ) {
if ( usedInArray & & ( counter_varid = = loopTok - > varId ( ) ) ) {
2012-09-07 11:59:20 +02:00
if ( loopTok - > strAt ( 1 ) = = " ++ " | |
2012-09-01 19:17:28 +02:00
( loopTok - > previous ( ) - > type ( ) = = Token : : eIncDecOp ) ) {
bufferOverrunError ( tok , arrayInfo . varname ( ) ) ;
}
}
}
}
}
}
2011-02-10 21:56:06 +01:00
2011-09-12 01:21:13 +02:00
void CheckBufferOverrun : : checkScope ( const Token * tok , const std : : vector < std : : string > & varname , const ArrayInfo & arrayInfo )
2009-01-31 20:29:27 +01:00
{
2011-09-12 01:21:13 +02:00
const MathLib : : bigint size = arrayInfo . num ( 0 ) ;
2011-12-17 13:20:42 +01:00
if ( size = = 0 ) // unknown size
return ;
2011-09-12 01:21:13 +02:00
const MathLib : : bigint total_size = arrayInfo . element_size ( ) * arrayInfo . num ( 0 ) ;
unsigned int varid = arrayInfo . varid ( ) ;
2011-09-10 17:21:52 +02:00
2009-01-31 20:29:27 +01:00
std : : string varnames ;
2010-04-02 07:30:58 +02:00
for ( unsigned int i = 0 ; i < varname . size ( ) ; + + i )
2010-02-14 20:46:40 +01:00
varnames + = ( i = = 0 ? " " : " . " ) + varname [ i ] ;
2009-01-31 20:29:27 +01:00
2011-12-30 18:47:42 +01:00
const unsigned char varc = static_cast < unsigned char > ( varname . empty ( ) ? 0U : ( varname . size ( ) - 1 ) * 2U ) ;
2010-03-09 12:41:40 +01:00
2012-09-17 15:22:51 +02:00
if ( tok - > str ( ) = = " return " ) {
2010-02-05 22:55:10 +01:00
tok = tok - > next ( ) ;
2010-04-02 07:30:58 +02:00
if ( ! tok )
2010-02-05 22:55:10 +01:00
return ;
}
2009-01-31 20:29:27 +01:00
// Array index..
2011-09-11 20:00:53 +02:00
if ( ( varid > 0 & & Token : : Match ( tok , " %varid% [ %num% ] " , varid ) ) | |
2011-10-13 20:53:06 +02:00
( varid = = 0 & & Token : : Match ( tok , ( varnames + " [ %num% ] " ) . c_str ( ) ) ) ) {
2010-11-21 11:48:27 +01:00
const MathLib : : bigint index = MathLib : : toLongNumber ( tok - > strAt ( 2 + varc ) ) ;
2011-10-13 20:53:06 +02:00
if ( index > = size ) {
2011-09-11 15:54:26 +02:00
std : : vector < MathLib : : bigint > indexes ;
indexes . push_back ( index ) ;
2011-09-12 01:21:13 +02:00
arrayIndexOutOfBoundsError ( tok - > tokAt ( varc ) , arrayInfo , indexes ) ;
2009-01-31 20:29:27 +01:00
}
}
2011-01-01 20:56:21 +01:00
// If the result of pointer arithmetic means that the pointer is
// out of bounds then this flag will be set.
bool pointerIsOutOfBounds = false ;
2012-08-12 12:13:07 +02:00
for ( const Token * const end = tok - > scope ( ) - > classEnd ; tok ! = end ; tok = tok - > next ( ) ) {
2011-10-13 20:53:06 +02:00
if ( varid ! = 0 & & Token : : Match ( tok , " %varid% = new|malloc|realloc " , varid ) ) {
2010-02-10 22:11:08 +01:00
// Abort
break ;
}
2011-10-12 20:54:39 +02:00
// reassign buffer
2012-03-17 21:55:08 +01:00
if ( varid > 0 & & Token : : Match ( tok , " [;{}] %varid% = %any% " , varid ) ) {
2011-10-12 22:06:19 +02:00
// using varid .. bailout
2012-03-17 21:55:08 +01:00
if ( tok - > tokAt ( 3 ) - > varId ( ) ! = varid )
2011-10-12 22:06:19 +02:00
break ;
pointerIsOutOfBounds = false ;
}
2011-08-08 18:22:15 +02:00
2009-01-31 20:29:27 +01:00
// Array index..
2011-09-11 20:00:53 +02:00
if ( ( varid > 0 & & ( ( tok - > str ( ) = = " return " | | ( ! tok - > isName ( ) & & ! Token : : Match ( tok , " [.&] " ) ) ) & & Token : : Match ( tok - > next ( ) , " %varid% [ %num% ] " , varid ) ) ) | |
2013-03-14 06:34:12 +01:00
( varid = = 0 & & ( ( tok - > str ( ) = = " return " | | ( ! tok - > isName ( ) & & ! Token : : Match ( tok , " [.&] " ) ) ) & & ( Token : : Match ( tok - > next ( ) , ( varnames + " [ %num% ] " ) . c_str ( ) ) | | Token : : Match ( tok - > next ( ) , ( varname [ 0 ] + " [ %num% ] . " + varname [ 1 ] + " [ %num% ] " ) . c_str ( ) ) ) ) ) ) {
2011-09-12 02:42:57 +02:00
std : : vector < MathLib : : bigint > indexes ;
const Token * tok2 = tok - > tokAt ( 2 + varc ) ;
2011-10-13 20:53:06 +02:00
for ( ; Token : : Match ( tok2 , " [ %num% ] " ) ; tok2 = tok2 - > tokAt ( 3 ) ) {
2011-09-12 02:42:57 +02:00
const MathLib : : bigint index = MathLib : : toLongNumber ( tok2 - > strAt ( 1 ) ) ;
2011-09-11 15:54:26 +02:00
indexes . push_back ( index ) ;
2009-01-31 20:29:27 +01:00
}
2013-03-14 06:34:12 +01:00
for ( ; Token : : Match ( tok2 - > tokAt ( 3 ) , " [ %num% ] " ) ; tok2 = tok2 - > tokAt ( 3 ) ) {
const MathLib : : bigint index = MathLib : : toLongNumber ( tok2 - > strAt ( 4 ) ) ;
indexes . push_back ( index ) ;
}
2011-09-12 02:42:57 +02:00
2011-10-13 20:53:06 +02:00
if ( indexes . size ( ) = = arrayInfo . num ( ) . size ( ) ) {
2011-09-12 02:42:57 +02:00
// Check if the indexes point outside the whole array..
// char a[10][10];
// a[0][20] <-- ok.
// a[9][20] <-- error.
// total number of elements of array..
MathLib : : bigint totalElements = 1 ;
// total index..
MathLib : : bigint totalIndex = 0 ;
// calculate the totalElements and totalIndex..
2011-10-13 20:53:06 +02:00
for ( unsigned int i = 0 ; i < indexes . size ( ) ; + + i ) {
2011-09-12 02:42:57 +02:00
std : : size_t ri = indexes . size ( ) - 1 - i ;
totalIndex + = indexes [ ri ] * totalElements ;
totalElements * = arrayInfo . num ( ri ) ;
}
// totalElements == 0 => Unknown size
if ( totalElements = = 0 )
continue ;
const Token * tok3 = tok - > previous ( ) ;
2011-11-20 15:09:57 +01:00
while ( tok3 & & Token : : Match ( tok3 - > previous ( ) , " %var% . " ) )
2011-09-12 02:42:57 +02:00
tok3 = tok3 - > tokAt ( - 2 ) ;
// just taking the address?
2011-11-26 21:02:04 +01:00
const bool addr ( tok3 & & ( tok3 - > str ( ) = = " & " | |
2011-11-20 16:50:41 +01:00
Token : : simpleMatch ( tok3 - > previous ( ) , " & ( " ) ) ) ;
2011-09-12 02:42:57 +02:00
// taking address of 1 past end?
if ( addr & & totalIndex = = totalElements )
continue ;
// Is totalIndex in bounds?
2011-10-13 20:53:06 +02:00
if ( totalIndex > totalElements | | totalIndex < 0 ) {
2011-09-12 02:42:57 +02:00
arrayIndexOutOfBoundsError ( tok - > tokAt ( 1 + varc ) , arrayInfo , indexes ) ;
}
// Is any array index out of bounds?
2011-10-13 20:53:06 +02:00
else {
2011-09-12 02:42:57 +02:00
// check each index for overflow
2011-10-13 20:53:06 +02:00
for ( unsigned int i = 0 ; i < indexes . size ( ) ; + + i ) {
if ( indexes [ i ] > = arrayInfo . num ( i ) ) {
2012-03-13 21:30:03 +01:00
if ( indexes . size ( ) = = 1U ) {
arrayIndexOutOfBoundsError ( tok - > tokAt ( 1 + varc ) , arrayInfo , indexes ) ;
break ; // only warn about the first one
}
2011-09-12 02:42:57 +02:00
// The access is still within the memory range for the array
// so it may be intentional.
2012-03-13 21:30:03 +01:00
else if ( _settings - > inconclusive ) {
2011-09-12 02:42:57 +02:00
arrayIndexOutOfBoundsError ( tok - > tokAt ( 1 + varc ) , arrayInfo , indexes ) ;
break ; // only warn about the first one
}
}
}
}
}
tok = tok2 ;
2009-01-31 20:29:27 +01:00
continue ;
}
// memset, memcmp, memcpy, strncpy, fgets..
2011-10-13 20:53:06 +02:00
if ( varid = = 0 & & size > 0 ) {
2011-12-11 08:16:58 +01:00
std : : list < const Token * > callstack ;
callstack . push_back ( tok ) ;
2010-04-21 20:02:58 +02:00
if ( Token : : Match ( tok , ( " %var% ( " + varnames + " , " ) . c_str ( ) ) )
2011-12-11 08:16:58 +01:00
checkFunctionParameter ( * tok , 1 , arrayInfo , callstack ) ;
2010-04-21 20:02:58 +02:00
if ( Token : : Match ( tok , ( " %var% ( %var% , " + varnames + " , " ) . c_str ( ) ) )
2011-12-11 08:16:58 +01:00
checkFunctionParameter ( * tok , 2 , arrayInfo , callstack ) ;
2009-01-31 20:29:27 +01:00
}
// Loop..
2011-10-13 20:53:06 +02:00
if ( Token : : simpleMatch ( tok , " for ( " ) ) {
2011-09-12 01:21:13 +02:00
const ArrayInfo arrayInfo1 ( varid , varnames , ( unsigned int ) size , ( unsigned int ) total_size ) ;
2011-02-10 21:56:06 +01:00
bool bailout = false ;
2011-09-12 01:21:13 +02:00
checkScopeForBody ( tok , arrayInfo1 , bailout ) ;
2011-02-10 21:56:06 +01:00
if ( bailout )
2009-10-01 10:33:53 +02:00
break ;
2009-01-31 20:29:27 +01:00
continue ;
}
// Writing data into array..
2010-04-02 07:30:58 +02:00
if ( ( varid > 0 & & Token : : Match ( tok , " strcpy|strcat ( %varid% , %str% ) " , varid ) ) | |
2011-10-13 20:53:06 +02:00
( varid = = 0 & & Token : : Match ( tok , ( " strcpy|strcat ( " + varnames + " , %str% ) " ) . c_str ( ) ) ) ) {
2010-12-31 09:30:56 +01:00
const std : : size_t len = Token : : getStrLength ( tok - > tokAt ( varc + 4 ) ) ;
2011-10-13 20:53:06 +02:00
if ( total_size > 0 & & len > = ( unsigned int ) total_size ) {
2013-03-03 11:41:59 +01:00
bufferOverrunError ( tok , varid > 0 ? std : : string ( ) : varnames ) ;
2009-08-30 13:44:23 +02:00
continue ;
2009-01-31 20:29:27 +01:00
}
2011-10-13 20:53:06 +02:00
} else if ( ( varid > 0 & & Token : : Match ( tok , " strcpy|strcat ( %varid% , %var% ) " , varid ) ) | |
( varid = = 0 & & Token : : Match ( tok , ( " strcpy|strcat ( " + varnames + " , %var% ) " ) . c_str ( ) ) ) ) {
2013-01-31 20:08:48 +01:00
const Variable * var = tok - > tokAt ( 4 ) - > variable ( ) ;
2011-10-13 20:53:06 +02:00
if ( var & & var - > isArray ( ) & & var - > dimensions ( ) . size ( ) = = 1 ) {
2011-09-06 01:02:26 +02:00
const std : : size_t len = ( std : : size_t ) var - > dimension ( 0 ) ;
2011-10-13 20:53:06 +02:00
if ( total_size > 0 & & len > ( unsigned int ) total_size ) {
2011-08-21 20:44:55 +02:00
if ( _settings - > inconclusive )
2011-08-21 21:18:41 +02:00
possibleBufferOverrunError ( tok , tok - > strAt ( 4 ) , tok - > strAt ( 2 ) , tok - > str ( ) = = " strcat " ) ;
2011-08-21 20:44:55 +02:00
continue ;
}
}
}
2009-02-20 22:16:07 +01:00
2009-08-30 13:44:23 +02:00
// Detect few strcat() calls
2010-06-19 08:52:14 +02:00
const std : : string strcatPattern = varid > 0 ? std : : string ( " strcat ( %varid% , %str% ) ; " ) : ( " strcat ( " + varnames + " , %str% ) ; " ) ;
2011-10-13 20:53:06 +02:00
if ( Token : : Match ( tok , strcatPattern . c_str ( ) , varid ) ) {
2012-07-08 23:39:46 +02:00
std : : size_t charactersAppend = 0 ;
2009-08-30 13:44:23 +02:00
const Token * tok2 = tok ;
2012-09-17 15:22:51 +02:00
while ( Token : : Match ( tok2 , strcatPattern . c_str ( ) , varid ) ) {
2010-05-29 07:51:28 +02:00
charactersAppend + = Token : : getStrLength ( tok2 - > tokAt ( 4 + varc ) ) ;
2012-07-08 23:39:46 +02:00
if ( charactersAppend > = static_cast < std : : size_t > ( total_size ) ) {
2011-08-24 13:11:39 +02:00
bufferOverrunError ( tok2 ) ;
2009-08-30 13:44:23 +02:00
break ;
}
2010-05-29 07:51:28 +02:00
tok2 = tok2 - > tokAt ( 7 + varc ) ;
2009-08-30 13:44:23 +02:00
}
}
2009-01-31 20:29:27 +01:00
2010-05-29 07:51:28 +02:00
// sprintf..
2010-08-06 21:02:43 +02:00
// TODO: change total_size to an unsigned value and remove the "&& total_size > 0" check.
2010-06-19 08:52:14 +02:00
const std : : string sprintfPattern = varid > 0 ? std : : string ( " sprintf ( %varid% , %str% [,)] " ) : ( " sprintf ( " + varnames + " , %str% [,)] " ) ;
2011-10-13 20:53:06 +02:00
if ( Token : : Match ( tok , sprintfPattern . c_str ( ) , varid ) & & total_size > 0 ) {
2010-08-06 21:02:43 +02:00
checkSprintfCall ( tok , static_cast < unsigned int > ( total_size ) ) ;
2010-05-29 07:51:28 +02:00
}
2009-01-31 20:29:27 +01:00
2010-05-29 07:51:28 +02:00
// snprintf..
2010-06-19 08:52:14 +02:00
const std : : string snprintfPattern = varid > 0 ? std : : string ( " snprintf ( %varid% , %num% , " ) : ( " snprintf ( " + varnames + " , %num% , " ) ;
2011-10-13 20:53:06 +02:00
if ( Token : : Match ( tok , snprintfPattern . c_str ( ) , varid ) ) {
2010-11-20 11:48:03 +01:00
const MathLib : : bigint n = MathLib : : toLongNumber ( tok - > strAt ( 4 + varc ) ) ;
2010-05-29 07:51:28 +02:00
if ( n > total_size )
2011-10-05 20:17:57 +02:00
outOfBoundsError ( tok - > tokAt ( 4 + varc ) , " snprintf size " , true , n , total_size ) ;
2009-02-21 13:22:04 +01:00
}
2009-01-31 20:29:27 +01:00
2011-01-22 21:31:26 +01:00
// Check function call..
2011-10-13 20:53:06 +02:00
if ( Token : : Match ( tok , " %var% ( " ) & & total_size > 0 ) {
2011-01-22 21:31:26 +01:00
// No varid => function calls are not handled
if ( varid = = 0 )
continue ;
2011-09-30 23:28:59 +02:00
const ArrayInfo arrayInfo1 ( varid , varnames , total_size / size , size ) ;
2011-12-11 08:16:58 +01:00
const std : : list < const Token * > callstack ;
checkFunctionCall ( tok , arrayInfo1 , callstack ) ;
2009-01-31 20:29:27 +01:00
}
2011-01-01 20:56:21 +01:00
// undefined behaviour: result of pointer arithmetic is out of bounds
2011-12-17 19:04:03 +01:00
else if ( varid & & Token : : Match ( tok , " = %varid% + %num% ; " , varid ) ) {
2011-01-01 20:56:21 +01:00
const MathLib : : bigint index = MathLib : : toLongNumber ( tok - > strAt ( 3 ) ) ;
2011-09-03 15:30:30 +02:00
if ( index > size & & _settings - > isEnabled ( " portability " ) )
2011-08-24 13:11:39 +02:00
pointerOutOfBoundsError ( tok - > next ( ) , " buffer " ) ;
2011-01-01 20:56:21 +01:00
if ( index > = size & & Token : : Match ( tok - > tokAt ( - 2 ) , " [;{}] %varid% = " , varid ) )
pointerIsOutOfBounds = true ;
}
2011-12-17 19:04:03 +01:00
else if ( pointerIsOutOfBounds & & Token : : Match ( tok , " [;{}=] * %varid% [;=] " , varid ) ) {
2011-10-05 20:17:57 +02:00
outOfBoundsError ( tok - > tokAt ( 2 ) , tok - > strAt ( 2 ) , false , 0 , 0 ) ;
2011-01-01 20:56:21 +01:00
}
2009-01-31 20:29:27 +01:00
}
}
2010-04-21 18:33:21 +02:00
void CheckBufferOverrun : : checkScope ( const Token * tok , const ArrayInfo & arrayInfo )
{
2011-06-23 02:35:58 +02:00
const MathLib : : bigint total_size = arrayInfo . num ( 0 ) * arrayInfo . element_size ( ) ;
2010-04-21 18:33:21 +02:00
2011-10-14 19:45:51 +02:00
const Token * scope_begin = tok - > previous ( ) ;
assert ( scope_begin ! = 0 ) ;
2012-08-12 12:13:07 +02:00
for ( const Token * const end = tok - > scope ( ) - > classEnd ; tok ! = end ; tok = tok - > next ( ) ) {
2011-05-07 11:34:48 +02:00
// Skip array declarations
2012-08-12 12:13:07 +02:00
if ( Token : : Match ( tok , " [;{}] %type% *| %var% [ " ) & & tok - > strAt ( 1 ) ! = " return " ) {
2011-05-07 11:34:48 +02:00
tok = tok - > tokAt ( 3 ) ;
continue ;
}
2011-10-13 20:53:06 +02:00
else if ( Token : : Match ( tok , " %varid% [ %num% ] " , arrayInfo . varid ( ) ) ) {
2010-12-31 09:30:56 +01:00
std : : vector < MathLib : : bigint > indexes ;
2011-10-13 20:53:06 +02:00
for ( const Token * tok2 = tok - > next ( ) ; Token : : Match ( tok2 , " [ %num% ] " ) ; tok2 = tok2 - > tokAt ( 3 ) ) {
2010-11-20 11:48:03 +01:00
const MathLib : : bigint index = MathLib : : toLongNumber ( tok2 - > strAt ( 1 ) ) ;
2011-10-13 20:53:06 +02:00
if ( index < 0 ) {
2010-04-23 16:26:40 +02:00
indexes . clear ( ) ;
break ;
}
2010-12-31 09:30:56 +01:00
indexes . push_back ( index ) ;
2010-04-23 16:26:40 +02:00
}
2011-10-13 20:53:06 +02:00
if ( indexes . size ( ) = = arrayInfo . num ( ) . size ( ) ) {
2010-04-23 16:26:40 +02:00
// Check if the indexes point outside the whole array..
// char a[10][10];
// a[0][20] <-- ok.
// a[9][20] <-- error.
// total number of elements of array..
2010-12-31 09:30:56 +01:00
MathLib : : bigint totalElements = 1 ;
2010-04-23 16:26:40 +02:00
// total index..
2010-12-31 09:30:56 +01:00
MathLib : : bigint totalIndex = 0 ;
2010-04-23 16:26:40 +02:00
// calculate the totalElements and totalIndex..
2011-10-13 20:53:06 +02:00
for ( unsigned int i = 0 ; i < indexes . size ( ) ; + + i ) {
2010-12-31 09:30:56 +01:00
std : : size_t ri = indexes . size ( ) - 1 - i ;
2010-04-23 16:26:40 +02:00
totalIndex + = indexes [ ri ] * totalElements ;
2011-06-23 02:35:58 +02:00
totalElements * = arrayInfo . num ( ri ) ;
2010-04-23 16:26:40 +02:00
}
2011-08-05 13:08:48 +02:00
// totalElements == 0 => Unknown size
if ( totalElements = = 0 )
continue ;
2011-09-05 01:54:57 +02:00
const Token * tok2 = tok - > previous ( ) ;
2011-11-20 15:09:57 +01:00
while ( tok2 & & Token : : Match ( tok2 - > previous ( ) , " %var% . " ) )
2011-09-05 01:54:57 +02:00
tok2 = tok2 - > tokAt ( - 2 ) ;
2010-04-21 18:33:21 +02:00
// just taking the address?
2011-11-26 21:02:04 +01:00
const bool addr ( tok2 & & ( tok2 - > str ( ) = = " & " | |
2011-11-27 07:29:09 +01:00
Token : : simpleMatch ( tok2 - > previous ( ) , " & ( " ) ) ) ;
2011-09-05 01:54:57 +02:00
// taking address of 1 past end?
if ( addr & & totalIndex = = totalElements )
continue ;
2010-04-21 18:33:21 +02:00
2010-04-23 16:26:40 +02:00
// Is totalIndex in bounds?
2011-11-19 15:28:06 +01:00
if ( totalIndex > = totalElements ) {
2011-08-24 13:11:39 +02:00
arrayIndexOutOfBoundsError ( tok , arrayInfo , indexes ) ;
2010-04-23 16:26:40 +02:00
}
2011-09-03 01:35:09 +02:00
// Is any array index out of bounds?
2011-10-13 20:53:06 +02:00
else {
2011-09-03 01:35:09 +02:00
// check each index for overflow
2011-10-13 20:53:06 +02:00
for ( unsigned int i = 0 ; i < indexes . size ( ) ; + + i ) {
if ( indexes [ i ] > = arrayInfo . num ( i ) ) {
2011-09-03 01:35:09 +02:00
// The access is still within the memory range for the array
// so it may be intentional.
2011-10-13 20:53:06 +02:00
if ( _settings - > inconclusive ) {
2011-09-03 01:35:09 +02:00
arrayIndexOutOfBoundsError ( tok , arrayInfo , indexes ) ;
break ; // only warn about the first one
}
}
}
}
2010-04-21 18:33:21 +02:00
}
}
2010-05-17 19:51:35 +02:00
2010-04-21 18:33:21 +02:00
// Loop..
2011-10-13 20:53:06 +02:00
else if ( Token : : simpleMatch ( tok , " for ( " ) ) {
2011-02-10 21:56:06 +01:00
bool bailout = false ;
2012-09-01 19:17:28 +02:00
arrayIndexInForLoop ( tok , arrayInfo ) ;
2011-02-10 21:56:06 +01:00
checkScopeForBody ( tok , arrayInfo , bailout ) ;
if ( bailout )
2010-04-21 18:33:21 +02:00
break ;
continue ;
}
2010-04-21 19:27:28 +02:00
// Check function call..
2011-10-13 20:53:06 +02:00
if ( Token : : Match ( tok , " %var% ( " ) ) {
2011-12-11 08:16:58 +01:00
const std : : list < const Token * > callstack ;
checkFunctionCall ( tok , arrayInfo , callstack ) ;
2010-04-24 21:48:58 +02:00
}
2010-04-21 19:27:28 +02:00
2011-10-13 20:53:06 +02:00
if ( Token : : Match ( tok , " strncpy|memcpy|memmove ( %varid% , %str% , %num% ) " , arrayInfo . varid ( ) ) ) {
2011-09-05 21:19:38 +02:00
unsigned int num = ( unsigned int ) MathLib : : toLongNumber ( tok - > strAt ( 6 ) ) ;
2011-10-13 20:53:06 +02:00
if ( Token : : getStrLength ( tok - > tokAt ( 4 ) ) > = ( unsigned int ) total_size & & ( unsigned int ) total_size = = num ) {
2011-09-05 21:19:38 +02:00
if ( _settings - > inconclusive )
2011-09-05 21:59:41 +02:00
bufferNotZeroTerminatedError ( tok , tok - > strAt ( 2 ) , tok - > str ( ) ) ;
2011-09-05 21:19:38 +02:00
}
}
2013-03-14 18:18:48 +01:00
if ( ( Token : : Match ( tok , " strncpy|strncat ( %varid% , " , arrayInfo . varid ( ) ) & & Token : : Match ( tok - > linkAt ( 1 ) - > tokAt ( - 2 ) , " , %num% ) " ) ) ) {
2012-03-17 21:55:08 +01:00
const Token * param3 = tok - > linkAt ( 1 ) - > previous ( ) ;
2011-08-28 03:18:39 +02:00
2010-04-22 20:07:41 +02:00
// check for strncpy which is not terminated
2011-10-13 20:53:06 +02:00
if ( tok - > str ( ) = = " strncpy " ) {
2010-04-22 20:07:41 +02:00
// strncpy takes entire variable length as input size
2012-03-17 21:55:08 +01:00
unsigned int num = ( unsigned int ) MathLib : : toLongNumber ( param3 - > str ( ) ) ;
2011-08-28 03:18:39 +02:00
2012-03-13 21:19:10 +01:00
// this is currently 'inconclusive'. See TestBufferOverrun::terminateStrncpy3
2013-03-03 11:41:59 +01:00
if ( num > = total_size & & _settings - > isEnabled ( " warning " ) & & _settings - > inconclusive ) {
2010-04-22 20:07:41 +02:00
const Token * tok2 = tok - > next ( ) - > link ( ) - > next ( ) ;
2011-10-13 20:53:06 +02:00
for ( ; tok2 ; tok2 = tok2 - > next ( ) ) {
if ( tok2 - > varId ( ) = = tok - > tokAt ( 2 ) - > varId ( ) ) {
if ( ! Token : : Match ( tok2 , " %varid% [ %any% ] = 0 ; " , tok - > tokAt ( 2 ) - > varId ( ) ) ) {
2012-03-13 21:19:10 +01:00
terminateStrncpyError ( tok , tok - > strAt ( 2 ) ) ;
2010-04-21 18:33:21 +02:00
}
2010-04-22 20:07:41 +02:00
break ;
2010-04-21 18:33:21 +02:00
}
}
}
}
2011-08-28 03:18:39 +02:00
// Dangerous usage of strncat..
2012-03-17 21:55:08 +01:00
else if ( tok - > str ( ) = = " strncat " ) {
const MathLib : : bigint n = MathLib : : toLongNumber ( param3 - > str ( ) ) ;
2010-12-31 09:30:56 +01:00
if ( n > = total_size )
2011-08-24 13:11:39 +02:00
strncatUsageError ( tok ) ;
2010-04-21 18:33:21 +02:00
}
// Dangerous usage of strncpy + strncat..
2012-03-17 21:55:08 +01:00
if ( Token : : Match ( param3 - > tokAt ( 2 ) , " ; strncat ( %varid% , " , arrayInfo . varid ( ) ) & & Token : : Match ( param3 - > linkAt ( 4 ) - > tokAt ( - 2 ) , " , %num% ) " ) ) {
const MathLib : : bigint n = MathLib : : toLongNumber ( param3 - > str ( ) ) + MathLib : : toLongNumber ( param3 - > linkAt ( 4 ) - > strAt ( - 1 ) ) ;
2010-12-31 09:30:56 +01:00
if ( n > total_size )
2012-03-17 21:55:08 +01:00
strncatUsageError ( param3 - > tokAt ( 3 ) ) ;
2010-04-21 18:33:21 +02:00
}
}
// Writing data into array..
2011-10-13 20:53:06 +02:00
if ( Token : : Match ( tok , " strcpy|strcat ( %varid% , %str% ) " , arrayInfo . varid ( ) ) ) {
2010-12-31 09:30:56 +01:00
const std : : size_t len = Token : : getStrLength ( tok - > tokAt ( 4 ) ) ;
2011-10-13 20:53:06 +02:00
if ( total_size > 0 & & len > = ( unsigned int ) total_size ) {
2011-08-24 13:11:39 +02:00
bufferOverrunError ( tok , arrayInfo . varname ( ) ) ;
2010-04-21 18:33:21 +02:00
continue ;
}
}
// Detect few strcat() calls
2011-10-13 20:53:06 +02:00
if ( total_size > 0 & & Token : : Match ( tok , " strcat ( %varid% , %str% ) ; " , arrayInfo . varid ( ) ) ) {
2010-12-31 09:30:56 +01:00
std : : size_t charactersAppend = 0 ;
2010-04-21 18:33:21 +02:00
const Token * tok2 = tok ;
2011-10-13 20:53:06 +02:00
while ( tok2 & & Token : : Match ( tok2 , " strcat ( %varid% , %str% ) ; " , arrayInfo . varid ( ) ) ) {
2010-04-21 18:33:21 +02:00
charactersAppend + = Token : : getStrLength ( tok2 - > tokAt ( 4 ) ) ;
2011-10-13 20:53:06 +02:00
if ( charactersAppend > = ( unsigned int ) total_size ) {
2011-08-24 13:11:39 +02:00
bufferOverrunError ( tok2 , arrayInfo . varname ( ) ) ;
2010-04-21 18:33:21 +02:00
break ;
}
tok2 = tok2 - > tokAt ( 7 ) ;
}
}
2011-10-13 20:53:06 +02:00
if ( Token : : Match ( tok , " sprintf ( %varid% , %str% [,)] " , arrayInfo . varid ( ) ) ) {
2010-04-21 18:33:21 +02:00
checkSprintfCall ( tok , total_size ) ;
}
// snprintf..
2011-10-13 20:53:06 +02:00
if ( total_size > 0 & & Token : : Match ( tok , " snprintf ( %varid% , %num% , " , arrayInfo . varid ( ) ) ) {
2010-11-20 11:48:03 +01:00
const MathLib : : bigint n = MathLib : : toLongNumber ( tok - > strAt ( 4 ) ) ;
2010-12-31 09:30:56 +01:00
if ( n > total_size )
2011-10-05 20:17:57 +02:00
outOfBoundsError ( tok - > tokAt ( 4 ) , " snprintf size " , true , n , total_size ) ;
2010-04-21 18:33:21 +02:00
}
2010-12-26 21:23:28 +01:00
2011-10-24 21:22:04 +02:00
// readlink() / readlinkat() buffer usage
if ( _settings - > standards . posix ) {
2012-03-17 21:55:08 +01:00
if ( Token : : simpleMatch ( tok , " readlink ( " ) & & Token : : Match ( tok - > tokAt ( 2 ) - > nextArgument ( ) , " %varid% , %num% ) " , arrayInfo . varid ( ) ) )
2011-10-24 21:22:04 +02:00
checkReadlinkBufferUsage ( tok , scope_begin , total_size , false ) ;
2012-03-17 21:55:08 +01:00
else if ( Token : : simpleMatch ( tok , " readlinkat ( " ) & & Token : : Match ( tok - > tokAt ( 2 ) - > nextArgument ( ) - > nextArgument ( ) , " %varid% , %num% ) " , arrayInfo . varid ( ) ) )
2011-10-24 21:22:04 +02:00
checkReadlinkBufferUsage ( tok , scope_begin , total_size , true ) ;
2011-10-14 19:45:51 +02:00
}
2010-12-26 21:23:28 +01:00
// undefined behaviour: result of pointer arithmetic is out of bounds
2011-10-13 20:53:06 +02:00
if ( _settings - > isEnabled ( " portability " ) & & Token : : Match ( tok , " = %varid% + %num% ; " , arrayInfo . varid ( ) ) ) {
2010-12-26 21:23:28 +01:00
const MathLib : : bigint index = MathLib : : toLongNumber ( tok - > strAt ( 3 ) ) ;
2011-10-13 20:53:06 +02:00
if ( index < 0 | | index > arrayInfo . num ( 0 ) ) {
2011-08-24 13:11:39 +02:00
pointerOutOfBoundsError ( tok - > next ( ) , " array " ) ;
2010-12-26 21:23:28 +01:00
}
}
2010-04-21 18:33:21 +02:00
}
}
2013-03-14 06:34:12 +01:00
//---------------------------------------------------------------------------
// Checking member variables of structs..
//---------------------------------------------------------------------------
bool CheckBufferOverrun : : isArrayOfStruct ( const Token * tok , int & position )
{
if ( Token : : Match ( tok - > next ( ) , " %var% [ %num% ] " ) ) {
tok = tok - > tokAt ( 4 ) ;
int i = 1 ;
while ( true ) {
if ( Token : : Match ( tok - > next ( ) , " [ %num% ] " ) ) {
i + + ;
tok = tok - > tokAt ( 4 ) ;
} else
break ;
}
if ( Token : : Match ( tok - > next ( ) , " ; " ) ) {
position = i ;
return true ;
}
}
return false ;
}
2011-10-24 21:22:04 +02:00
void CheckBufferOverrun : : checkReadlinkBufferUsage ( const Token * tok , const Token * scope_begin , const MathLib : : bigint total_size , const bool is_readlinkat )
{
2012-03-17 21:55:08 +01:00
const Token * bufParam = tok - > tokAt ( 2 ) - > nextArgument ( ) ;
if ( is_readlinkat )
bufParam = bufParam - > nextArgument ( ) ;
2011-10-24 21:22:04 +02:00
const std : : string funcname = is_readlinkat ? " readlinkat " : " readlink " ;
2012-03-17 21:55:08 +01:00
const MathLib : : bigint n = MathLib : : toLongNumber ( bufParam - > strAt ( 2 ) ) ;
2011-10-24 21:22:04 +02:00
if ( total_size > 0 & & n > total_size )
2012-03-17 21:55:08 +01:00
outOfBoundsError ( bufParam , funcname + " () buf size " , true , n , total_size ) ;
2011-10-24 21:22:04 +02:00
if ( ! _settings - > inconclusive )
return ;
// readlink()/readlinkat() never terminates the buffer, check the end of the scope for buffer termination.
bool found_termination = false ;
const Token * scope_end = scope_begin - > link ( ) ;
2012-03-17 21:55:08 +01:00
for ( const Token * tok2 = bufParam - > tokAt ( 4 ) ; tok2 & & tok2 ! = scope_end ; tok2 = tok2 - > next ( ) ) {
if ( Token : : Match ( tok2 , " %varid% [ %any% ] = 0 ; " , bufParam - > varId ( ) ) ) {
2011-10-24 21:22:04 +02:00
found_termination = true ;
break ;
}
}
if ( ! found_termination ) {
2012-03-17 21:55:08 +01:00
bufferNotZeroTerminatedError ( tok , bufParam - > str ( ) , funcname ) ;
2011-10-24 21:22:04 +02:00
} else if ( n = = total_size ) {
2012-03-17 21:55:08 +01:00
possibleReadlinkBufferOverrunError ( tok , funcname , bufParam - > str ( ) ) ;
2011-10-24 21:22:04 +02:00
}
}
2010-04-21 18:33:21 +02:00
2009-01-31 20:29:27 +01:00
//---------------------------------------------------------------------------
// Checking local variables in a scope
//---------------------------------------------------------------------------
2009-07-13 16:00:15 +02:00
void CheckBufferOverrun : : checkGlobalAndLocalVariable ( )
2009-01-31 20:29:27 +01:00
{
2011-12-01 04:17:09 +01:00
// check string literals
for ( const Token * tok = _tokenizer - > tokens ( ) ; tok ; tok = tok - > next ( ) ) {
if ( Token : : Match ( tok , " %str% [ %num% ] " ) ) {
std : : string str = tok - > strValue ( ) ;
2012-09-16 16:41:15 +02:00
std : : size_t index = ( std : : size_t ) std : : atoi ( tok - > strAt ( 2 ) . c_str ( ) ) ;
2011-12-01 04:17:09 +01:00
if ( index > str . length ( ) ) {
bufferOverrunError ( tok , tok - > str ( ) ) ;
}
}
}
2011-06-24 14:02:41 +02:00
// check all known fixed size arrays first by just looking them up
2012-03-17 21:55:08 +01:00
const SymbolDatabase * symbolDatabase = _tokenizer - > getSymbolDatabase ( ) ;
2011-10-31 02:24:59 +01:00
for ( unsigned int i = 1 ; i < = _tokenizer - > varIdCount ( ) ; i + + ) {
2012-03-17 21:55:08 +01:00
const Variable * var = symbolDatabase - > getVariableFromVarId ( i ) ;
2011-10-13 20:53:06 +02:00
if ( var & & var - > isArray ( ) & & var - > dimension ( 0 ) > 0 ) {
2011-11-19 15:28:06 +01:00
const ArrayInfo arrayInfo ( var , _tokenizer ) ;
2011-06-24 14:02:41 +02:00
const Token * tok = var - > nameToken ( ) ;
2011-10-13 20:53:06 +02:00
while ( tok & & tok - > str ( ) ! = " ; " ) {
if ( tok - > str ( ) = = " { " ) {
2011-09-12 02:42:57 +02:00
if ( Token : : simpleMatch ( tok - > previous ( ) , " = { " ) )
tok = tok - > link ( ) ;
else
break ;
2011-08-07 17:54:25 +02:00
}
2011-09-12 02:42:57 +02:00
tok = tok - > next ( ) ;
2011-08-07 17:54:25 +02:00
}
2011-09-12 02:42:57 +02:00
if ( ! tok )
break ;
if ( tok - > str ( ) = = " { " )
tok = tok - > next ( ) ;
2011-06-24 14:02:41 +02:00
checkScope ( tok , arrayInfo ) ;
}
}
2012-10-13 11:16:48 +02:00
// find all dynamically allocated arrays next
const std : : size_t functions = symbolDatabase - > functionScopes . size ( ) ;
for ( std : : size_t i = 0 ; i < functions ; + + i ) {
const Scope * scope = symbolDatabase - > functionScopes [ i ] ;
2011-11-26 21:02:04 +01:00
2012-03-17 21:55:08 +01:00
for ( const Token * tok = scope - > classStart ; tok ! = scope - > classEnd ; tok = tok - > next ( ) ) {
// if the previous token exists, it must be either a variable name or "[;{}]"
if ( tok - > previous ( ) & & ( ! tok - > previous ( ) - > isName ( ) & & ! Token : : Match ( tok - > previous ( ) , " [;{}] " ) ) )
continue ;
2011-01-06 13:02:21 +01:00
2012-03-17 21:55:08 +01:00
// size : Max array index
MathLib : : bigint size = 0 ;
2011-01-06 13:02:21 +01:00
2012-03-17 21:55:08 +01:00
// type : The type of a array element
std : : string type ;
2009-01-31 20:29:27 +01:00
2012-03-17 21:55:08 +01:00
// varid : The variable id for the array
2013-02-06 06:39:58 +01:00
const Variable * var = 0 ;
2010-07-24 20:54:42 +02:00
2012-03-17 21:55:08 +01:00
// nextTok : number of tokens used in variable declaration - used to skip to next statement.
int nextTok = 0 ;
2012-05-05 18:33:26 +02:00
_errorLogger - > reportProgress ( _tokenizer - > getSourceFilePath ( ) ,
2012-03-17 21:55:08 +01:00
" Check (BufferOverrun::checkGlobalAndLocalVariable) " ,
tok - > progressValue ( ) ) ;
if ( Token : : Match ( tok , " [*;{}] %var% = new %type% [ %num% ] " ) ) {
size = MathLib : : toLongNumber ( tok - > strAt ( 6 ) ) ;
type = tok - > strAt ( 4 ) ;
2013-02-06 06:39:58 +01:00
var = tok - > next ( ) - > variable ( ) ;
2012-03-17 21:55:08 +01:00
nextTok = 8 ;
} else if ( Token : : Match ( tok , " [*;{}] %var% = new %type% ( %num% ) " ) ) {
size = 1 ;
type = tok - > strAt ( 4 ) ;
2013-02-06 06:39:58 +01:00
var = tok - > next ( ) - > variable ( ) ;
2012-03-17 21:55:08 +01:00
nextTok = 8 ;
} else if ( Token : : Match ( tok , " [;{}] %var% = %str% ; " ) & &
tok - > next ( ) - > varId ( ) > 0 & &
NULL ! = Token : : findmatch ( _tokenizer - > tokens ( ) , " [;{}] const| %type% * %varid% ; " , tok - > next ( ) - > varId ( ) ) ) {
size = 1 + int ( tok - > tokAt ( 3 ) - > strValue ( ) . size ( ) ) ;
type = " char " ;
2013-02-06 06:39:58 +01:00
var = tok - > next ( ) - > variable ( ) ;
2012-03-17 21:55:08 +01:00
nextTok = 4 ;
} else if ( Token : : Match ( tok , " [*;{}] %var% = malloc|alloca ( %num% ) ; " ) ) {
size = MathLib : : toLongNumber ( tok - > strAt ( 5 ) ) ;
type = " char " ; // minimum type, typesize=1
2013-02-06 06:39:58 +01:00
var = tok - > next ( ) - > variable ( ) ;
2012-03-17 21:55:08 +01:00
nextTok = 7 ;
2013-02-06 06:39:58 +01:00
/** @todo false negatives: this may be too conservative */
if ( ! var | | var - > typeEndToken ( ) - > str ( ) ! = " * " | | var - > typeStartToken ( ) - > next ( ) ! = var - > typeEndToken ( ) )
continue ;
2010-04-11 20:57:30 +02:00
2013-02-06 06:39:58 +01:00
// get name of variable
type = var - > typeStartToken ( ) - > str ( ) ;
2010-04-10 07:57:29 +02:00
2013-02-06 06:39:58 +01:00
// malloc() gets count of bytes and not count of
// elements, so we should calculate count of elements
// manually
unsigned int sizeOfType = _tokenizer - > sizeOfType ( var - > typeStartToken ( ) ) ;
if ( sizeOfType > 0 )
size / = static_cast < int > ( sizeOfType ) ;
2012-03-17 21:55:08 +01:00
} else {
continue ;
2009-11-15 13:38:57 +01:00
}
2009-01-31 20:29:27 +01:00
2013-02-06 06:39:58 +01:00
if ( var = = 0 )
2012-03-17 21:55:08 +01:00
continue ;
2009-11-06 23:58:33 +01:00
2012-03-17 21:55:08 +01:00
Token sizeTok ( 0 ) ;
sizeTok . str ( type ) ;
const MathLib : : bigint total_size = size * static_cast < int > ( _tokenizer - > sizeOfType ( & sizeTok ) ) ;
if ( total_size = = 0 )
continue ;
2009-01-31 20:29:27 +01:00
2012-03-17 21:55:08 +01:00
std : : vector < std : : string > v ;
2013-02-06 06:39:58 +01:00
ArrayInfo temp ( var - > varId ( ) , tok - > next ( ) - > str ( ) , total_size / size , size ) ;
2012-03-17 21:55:08 +01:00
checkScope ( tok - > tokAt ( nextTok ) , v , temp ) ;
}
2009-01-31 20:29:27 +01:00
}
}
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
// Checking member variables of structs..
//---------------------------------------------------------------------------
2009-07-13 16:00:15 +02:00
void CheckBufferOverrun : : checkStructVariable ( )
2009-01-31 20:29:27 +01:00
{
2011-09-05 03:39:52 +02:00
const SymbolDatabase * symbolDatabase = _tokenizer - > getSymbolDatabase ( ) ;
// find every class and struct
2012-10-13 11:16:48 +02:00
const std : : size_t classes = symbolDatabase - > classAndStructScopes . size ( ) ;
for ( std : : size_t i = 0 ; i < classes ; + + i ) {
const Scope * scope = symbolDatabase - > classAndStructScopes [ i ] ;
2011-09-05 03:39:52 +02:00
2011-09-09 13:16:39 +02:00
// check all variables to see if they are arrays
2011-09-05 03:39:52 +02:00
std : : list < Variable > : : const_iterator var ;
2011-10-13 20:53:06 +02:00
for ( var = scope - > varlist . begin ( ) ; var ! = scope - > varlist . end ( ) ; + + var ) {
if ( var - > isArray ( ) ) {
2011-09-09 13:16:39 +02:00
// create ArrayInfo from the array variable
2011-09-05 03:39:52 +02:00
ArrayInfo arrayInfo ( & * var , _tokenizer ) ;
2011-09-09 13:16:39 +02:00
// find every function
2012-10-13 11:16:48 +02:00
const std : : size_t functions = symbolDatabase - > functionScopes . size ( ) ;
for ( std : : size_t j = 0 ; j < functions ; + + j ) {
const Scope * func_scope = symbolDatabase - > functionScopes [ j ] ;
2011-09-09 13:16:39 +02:00
2011-12-18 16:35:51 +01:00
// If struct is declared in a function then check
// if scope_func matches
if ( scope - > nestedIn - > type = = Scope : : eFunction & &
scope - > nestedIn ! = & * func_scope ) {
continue ;
}
2011-09-12 03:51:05 +02:00
// check for member variables
2011-10-13 20:53:06 +02:00
if ( func_scope - > functionOf = = & * scope ) {
2011-09-09 13:16:39 +02:00
// only check non-empty function
2012-05-22 21:58:46 +02:00
if ( func_scope - > classStart - > next ( ) ! = func_scope - > classEnd ) {
2011-09-09 13:16:39 +02:00
// start checking after the {
2012-05-22 21:58:46 +02:00
const Token * tok = func_scope - > classStart - > next ( ) ;
2011-09-09 13:16:39 +02:00
checkScope ( tok , arrayInfo ) ;
}
2011-09-05 03:39:52 +02:00
}
2010-11-06 09:10:10 +01:00
2011-09-12 03:51:05 +02:00
// skip inner scopes..
/** @todo false negatives: handle inner scopes someday */
if ( scope - > nestedIn - > isClassOrStruct ( ) )
continue ;
2011-09-09 13:16:39 +02:00
2011-09-12 03:51:05 +02:00
std : : vector < std : : string > varname ;
varname . push_back ( " " ) ;
varname . push_back ( arrayInfo . varname ( ) ) ;
2011-09-09 13:16:39 +02:00
2011-09-12 03:51:05 +02:00
// search the function and it's parameters
2011-10-13 20:53:06 +02:00
for ( const Token * tok3 = func_scope - > classDef ; tok3 & & tok3 ! = func_scope - > classEnd ; tok3 = tok3 - > next ( ) ) {
2011-09-12 03:51:05 +02:00
// search for the class/struct name
if ( tok3 - > str ( ) ! = scope - > className )
continue ;
2009-01-31 20:29:27 +01:00
2013-03-14 18:25:28 +01:00
// find all array variables
int posOfSemicolon = - 1 ;
2011-09-12 03:51:05 +02:00
// Declare variable: Fred fred1;
if ( Token : : Match ( tok3 - > next ( ) , " %var% ; " ) )
varname [ 0 ] = tok3 - > strAt ( 1 ) ;
2009-01-31 20:29:27 +01:00
2013-03-14 18:25:28 +01:00
else if ( isArrayOfStruct ( tok3 , posOfSemicolon ) ) {
2013-03-14 06:34:12 +01:00
varname [ 0 ] = tok3 - > strAt ( 1 ) ;
int pos = 2 ;
2013-03-14 18:25:28 +01:00
for ( int k = 0 ; k < posOfSemicolon ; k + + ) {
2013-03-14 06:34:12 +01:00
for ( int index = pos ; index < ( pos + 3 ) ; index + + )
tok3 - > strAt ( index ) ;
pos + = 3 ;
}
}
2011-09-12 03:51:05 +02:00
// Declare pointer or reference: Fred *fred1
else if ( Token : : Match ( tok3 - > next ( ) , " *|& %var% [,) ; = ] " ))
varname [ 0 ] = tok3 - > strAt ( 2 ) ;
2009-01-31 20:29:27 +01:00
2011-09-12 03:51:05 +02:00
else
continue ;
2009-01-31 20:29:27 +01:00
2011-09-12 03:51:05 +02:00
// check for variable sized structure
2011-10-13 20:53:06 +02:00
if ( scope - > type = = Scope : : eStruct & & var - > isPublic ( ) ) {
2011-09-12 03:51:05 +02:00
// last member of a struct with array size of 0 or 1 could be a variable sized structure
if ( var - > dimensions ( ) . size ( ) = = 1 & & var - > dimension ( 0 ) < 2 & &
2011-10-13 20:53:06 +02:00
var - > index ( ) = = ( scope - > varlist . size ( ) - 1 ) ) {
2011-09-12 03:51:05 +02:00
// dynamically allocated so could be variable sized structure
2011-10-13 20:53:06 +02:00
if ( tok3 - > next ( ) - > str ( ) = = " * " ) {
2011-09-12 03:51:05 +02:00
// check for allocation
if ( ( Token : : Match ( tok3 - > tokAt ( 3 ) , " ; %var% = malloc ( %num% ) ; " ) | |
( Token : : Match ( tok3 - > tokAt ( 3 ) , " ; %var% = ( " ) & &
2011-11-20 14:22:39 +01:00
Token : : Match ( tok3 - > linkAt ( 6 ) , " ) malloc ( %num% ) ; " ) ) ) & &
2011-10-13 20:53:06 +02:00
( tok3 - > strAt ( 4 ) = = tok3 - > strAt ( 2 ) ) ) {
2011-09-12 03:51:05 +02:00
MathLib : : bigint size ;
2011-09-09 14:37:24 +02:00
2011-09-12 03:51:05 +02:00
// find size of allocation
if ( tok3 - > strAt ( 3 ) = = " ( " ) // has cast
2011-11-20 14:22:39 +01:00
size = MathLib : : toLongNumber ( tok3 - > linkAt ( 6 ) - > strAt ( 3 ) ) ;
2011-09-09 14:37:24 +02:00
else
2011-09-12 03:51:05 +02:00
size = MathLib : : toLongNumber ( tok3 - > strAt ( 8 ) ) ;
// We don't calculate the size of a structure even when we know
// the size of the members. We just assign a length of 100 for
// any struct. If the size is less than 100, we assume the
// programmer knew the size and specified it rather than using
// sizeof(struct). If the size is greater than 100, we assume
// the programmer specified the size as sizeof(struct) + number.
// Either way, this is just a guess and could be wrong. The
// information to make the right decision has been simplified
// away by the time we get here.
2011-10-13 20:53:06 +02:00
if ( size ! = 100 ) { // magic number for size of struct
2011-09-12 03:51:05 +02:00
// check if a real size was specified and give up
// malloc(10) rather than malloc(sizeof(struct))
if ( size < 100 )
continue ;
// calculate real array size based on allocated size
MathLib : : bigint elements = ( size - 100 ) / arrayInfo . element_size ( ) ;
arrayInfo . num ( 0 , arrayInfo . num ( 0 ) + elements ) ;
}
2011-09-09 14:37:24 +02:00
}
2011-09-12 03:51:05 +02:00
// size unknown so assume it is a variable sized structure
else
continue ;
2011-09-09 13:46:06 +02:00
}
}
2011-09-12 03:51:05 +02:00
}
2011-09-09 13:46:06 +02:00
2011-09-12 03:51:05 +02:00
// Goto end of statement.
const Token * CheckTok = NULL ;
2011-10-13 20:53:06 +02:00
while ( tok3 & & tok3 ! = func_scope - > classEnd ) {
2011-09-12 03:51:05 +02:00
// End of statement.
2011-10-13 20:53:06 +02:00
if ( tok3 - > str ( ) = = " ; " ) {
2011-09-12 03:51:05 +02:00
CheckTok = tok3 ;
break ;
2011-09-09 13:16:39 +02:00
}
2009-01-31 20:29:27 +01:00
2011-09-12 03:51:05 +02:00
// End of function declaration..
if ( Token : : simpleMatch ( tok3 , " ) ; " ) )
break ;
// Function implementation..
2011-10-13 20:53:06 +02:00
if ( Token : : simpleMatch ( tok3 , " ) { " ) ) {
2011-09-12 03:51:05 +02:00
CheckTok = tok3 - > tokAt ( 2 ) ;
2011-09-09 13:16:39 +02:00
break ;
2011-09-12 03:51:05 +02:00
}
2009-01-31 20:29:27 +01:00
2011-09-12 03:51:05 +02:00
tok3 = tok3 - > next ( ) ;
2011-09-09 13:16:39 +02:00
}
2011-09-12 03:51:05 +02:00
if ( ! tok3 )
break ;
if ( ! CheckTok )
continue ;
// Check variable usage..
ArrayInfo temp = arrayInfo ;
temp . varid ( 0 ) ; // do variable lookup by variable and member names rather than varid
std : : string varnames ; // use class and member name for messages
2012-10-13 11:16:48 +02:00
for ( unsigned int k = 0 ; k < varname . size ( ) ; + + k )
varnames + = ( k = = 0 ? " " : " . " ) + varname [ k ] ;
2013-03-14 06:34:12 +01:00
2011-09-12 03:51:05 +02:00
temp . varname ( varnames ) ;
checkScope ( CheckTok , varname , temp ) ;
2009-01-31 20:29:27 +01:00
}
}
}
}
}
}
//---------------------------------------------------------------------------
2009-07-13 16:00:15 +02:00
void CheckBufferOverrun : : bufferOverrun ( )
2009-01-31 20:29:27 +01:00
{
2009-07-05 22:16:43 +02:00
checkGlobalAndLocalVariable ( ) ;
checkStructVariable ( ) ;
2010-05-26 10:56:34 +02:00
checkBufferAllocatedWithStrlen ( ) ;
2010-06-02 07:41:07 +02:00
checkInsecureCmdLineArgs ( ) ;
2009-01-31 20:29:27 +01:00
}
//---------------------------------------------------------------------------
2010-12-31 09:30:56 +01:00
MathLib : : bigint CheckBufferOverrun : : countSprintfLength ( const std : : string & input_string , const std : : list < const Token * > & parameters )
2009-09-25 18:23:44 +02:00
{
2009-10-18 12:58:48 +02:00
bool percentCharFound = false ;
2010-12-31 09:30:56 +01:00
std : : size_t input_string_size = 1 ;
2009-10-18 12:58:48 +02:00
bool handleNextParameter = false ;
2009-09-25 18:23:44 +02:00
std : : string digits_string = " " ;
2009-10-18 12:58:48 +02:00
bool i_d_x_f_found = false ;
2009-10-08 15:27:46 +02:00
std : : list < const Token * > : : const_iterator paramIter = parameters . begin ( ) ;
2010-12-31 09:30:56 +01:00
std : : size_t parameterLength = 0 ;
2011-10-13 20:53:06 +02:00
for ( std : : string : : size_type i = 0 ; i < input_string . length ( ) ; + + i ) {
if ( input_string [ i ] = = ' \\ ' ) {
2010-04-02 07:30:58 +02:00
if ( input_string [ i + 1 ] = = ' 0 ' )
2009-10-18 12:58:48 +02:00
break ;
+ + input_string_size ;
+ + i ;
continue ;
2009-09-25 18:23:44 +02:00
}
2009-10-18 12:58:48 +02:00
2011-10-13 20:53:06 +02:00
if ( percentCharFound ) {
switch ( input_string [ i ] ) {
2009-10-18 12:58:48 +02:00
case ' f ' :
case ' x ' :
case ' X ' :
case ' i ' :
i_d_x_f_found = true ;
case ' c ' :
case ' e ' :
case ' E ' :
case ' g ' :
case ' o ' :
case ' u ' :
case ' p ' :
case ' n ' :
2009-10-19 23:48:29 +02:00
handleNextParameter = true ;
break ;
case ' d ' :
i_d_x_f_found = true ;
2012-04-25 09:56:07 +02:00
if ( paramIter ! = parameters . end ( ) & & * paramIter & & ( * paramIter ) - > type ( ) ! = Token : : eString )
2010-03-09 11:03:45 +01:00
parameterLength = ( * paramIter ) - > str ( ) . length ( ) ;
2009-10-19 23:48:29 +02:00
2009-10-18 12:58:48 +02:00
handleNextParameter = true ;
break ;
case ' s ' :
2012-04-25 09:56:07 +02:00
if ( paramIter ! = parameters . end ( ) & & * paramIter & & ( * paramIter ) - > type ( ) = = Token : : eString )
2010-03-09 11:03:45 +01:00
parameterLength = Token : : getStrLength ( * paramIter ) ;
2009-10-08 15:27:46 +02:00
2009-10-18 12:58:48 +02:00
handleNextParameter = true ;
break ;
2009-10-08 15:27:46 +02:00
}
2009-10-18 12:58:48 +02:00
}
2009-10-08 15:27:46 +02:00
2010-04-02 07:30:58 +02:00
if ( input_string [ i ] = = ' % ' )
2009-10-18 12:58:48 +02:00
percentCharFound = ! percentCharFound ;
2011-10-13 20:53:06 +02:00
else if ( percentCharFound ) {
2009-10-18 12:58:48 +02:00
digits_string . append ( 1 , input_string [ i ] ) ;
2009-09-25 18:23:44 +02:00
}
2010-04-02 07:30:58 +02:00
if ( ! percentCharFound )
2009-10-18 12:58:48 +02:00
input_string_size + + ;
2009-09-25 18:23:44 +02:00
2011-10-13 20:53:06 +02:00
if ( handleNextParameter ) {
2010-08-06 21:02:43 +02:00
unsigned int tempDigits = static_cast < unsigned int > ( std : : abs ( std : : atoi ( digits_string . c_str ( ) ) ) ) ;
2010-04-02 07:30:58 +02:00
if ( i_d_x_f_found )
2010-08-06 21:02:43 +02:00
tempDigits = std : : max ( static_cast < unsigned int > ( tempDigits ) , 1U ) ;
2009-10-08 15:27:46 +02:00
2011-10-13 20:53:06 +02:00
if ( digits_string . find ( ' . ' ) ! = std : : string : : npos ) {
2009-10-11 21:07:18 +02:00
const std : : string endStr = digits_string . substr ( digits_string . find ( ' . ' ) + 1 ) ;
2010-08-06 21:02:43 +02:00
unsigned int maxLen = std : : max ( static_cast < unsigned int > ( std : : abs ( std : : atoi ( endStr . c_str ( ) ) ) ) , 1U ) ;
2009-10-11 21:07:18 +02:00
2011-10-13 20:53:06 +02:00
if ( input_string [ i ] = = ' s ' ) {
2009-10-19 23:48:29 +02:00
// For strings, the length after the dot "%.2s" will limit
// the length of the string.
2010-04-02 07:30:58 +02:00
if ( parameterLength > maxLen )
2009-10-19 23:48:29 +02:00
parameterLength = maxLen ;
2011-10-13 20:53:06 +02:00
} else {
2009-10-19 23:48:29 +02:00
// For integers, the length after the dot "%.2d" can
// increase required length
2010-04-02 07:30:58 +02:00
if ( tempDigits < maxLen )
2009-10-19 23:48:29 +02:00
tempDigits = maxLen ;
}
2009-10-11 21:07:18 +02:00
}
2010-04-02 07:30:58 +02:00
if ( tempDigits < parameterLength )
2009-10-18 12:58:48 +02:00
input_string_size + = parameterLength ;
2009-10-08 15:27:46 +02:00
else
2009-10-19 23:48:29 +02:00
input_string_size + = tempDigits ;
2009-10-08 15:27:46 +02:00
parameterLength = 0 ;
2009-09-26 16:19:18 +02:00
digits_string = " " ;
2009-10-18 12:58:48 +02:00
i_d_x_f_found = false ;
percentCharFound = false ;
handleNextParameter = false ;
2010-04-02 07:30:58 +02:00
if ( paramIter ! = parameters . end ( ) )
2009-10-19 23:55:20 +02:00
+ + paramIter ;
2009-09-25 18:23:44 +02:00
}
}
2010-12-31 09:51:27 +01:00
return ( MathLib : : bigint ) input_string_size ;
2009-09-25 18:23:44 +02:00
}
2010-12-31 09:30:56 +01:00
void CheckBufferOverrun : : checkSprintfCall ( const Token * tok , const MathLib : : bigint size )
2009-10-07 14:37:20 +02:00
{
2010-10-19 18:23:44 +02:00
if ( size = = 0 )
return ;
2009-10-11 20:36:22 +02:00
std : : list < const Token * > parameters ;
2011-10-23 17:47:48 +02:00
const Token * vaArg = tok - > tokAt ( 2 ) - > nextArgument ( ) - > nextArgument ( ) ;
while ( vaArg ) {
2011-12-08 21:28:34 +01:00
if ( Token : : Match ( vaArg - > next ( ) , " [,)] " ) ) {
2012-04-25 09:56:07 +02:00
if ( vaArg - > type ( ) = = Token : : eString )
2011-10-23 17:47:48 +02:00
parameters . push_back ( vaArg ) ;
2009-10-07 14:37:20 +02:00
2012-03-17 21:55:08 +01:00
else if ( vaArg - > isNumber ( ) )
2011-10-23 17:47:48 +02:00
parameters . push_back ( vaArg ) ;
2009-10-11 20:36:22 +02:00
2011-10-23 17:47:48 +02:00
else
parameters . push_back ( 0 ) ;
} else // Parameter is more complex than just a value or variable. Ignore it for now and skip to next token.
parameters . push_back ( 0 ) ;
2009-10-11 20:36:22 +02:00
2011-10-23 17:47:48 +02:00
vaArg = vaArg - > nextArgument ( ) ;
2009-10-07 14:37:20 +02:00
}
2009-10-11 20:36:22 +02:00
2011-10-23 17:47:48 +02:00
MathLib : : bigint len = countSprintfLength ( tok - > tokAt ( 2 ) - > nextArgument ( ) - > strValue ( ) , parameters ) ;
2011-10-13 20:53:06 +02:00
if ( len > size ) {
2011-08-24 13:11:39 +02:00
bufferOverrunError ( tok ) ;
2009-10-11 20:36:22 +02:00
}
2009-10-07 14:37:20 +02:00
}
2010-04-10 21:12:00 +02:00
2010-05-26 10:56:34 +02:00
//---------------------------------------------------------------------------
// Checking for allocating insufficient memory for copying a string by
// allocating only strlen(src) bytes instead of strlen(src) + 1 bytes (one
// extra for the terminating null character).
// Example:
// char *b = malloc(strlen(a)); // Should be malloc(strlen(a) + 1);
// strcpy(b, a); // <== Buffer overrun
//---------------------------------------------------------------------------
void CheckBufferOverrun : : checkBufferAllocatedWithStrlen ( )
{
2012-10-13 11:16:48 +02:00
const SymbolDatabase * const symbolDatabase = _tokenizer - > getSymbolDatabase ( ) ;
2010-05-26 19:21:34 +02:00
2012-10-13 11:16:48 +02:00
const std : : size_t functions = symbolDatabase - > functionScopes . size ( ) ;
for ( std : : size_t i = 0 ; i < functions ; + + i ) {
const Scope * scope = symbolDatabase - > functionScopes [ i ] ;
for ( const Token * tok = scope - > classStart - > next ( ) ; tok & & tok ! = scope - > classEnd ; tok = tok - > next ( ) ) {
unsigned int dstVarId ;
unsigned int srcVarId ;
// Look for allocation of a buffer based on the size of a string
if ( Token : : Match ( tok , " %var% = malloc|g_malloc|g_try_malloc ( strlen ( %var% ) ) " ) ) {
dstVarId = tok - > varId ( ) ;
srcVarId = tok - > tokAt ( 6 ) - > varId ( ) ;
tok = tok - > tokAt ( 8 ) ;
} else if ( Token : : Match ( tok , " %var% = new char [ strlen ( %var% ) ] " )) {
dstVarId = tok - > varId ( ) ;
srcVarId = tok - > tokAt ( 7 ) - > varId ( ) ;
tok = tok - > tokAt ( 9 ) ;
} else if ( Token : : Match ( tok , " %var% = realloc|g_realloc|g_try_realloc ( %var% , strlen ( %var% ) ) " )) {
dstVarId = tok - > varId ( ) ;
srcVarId = tok - > tokAt ( 8 ) - > varId ( ) ;
tok = tok - > tokAt ( 10 ) ;
} else
continue ;
2010-05-26 19:21:34 +02:00
2012-10-13 11:16:48 +02:00
// To avoid false positives and added complexity, we will only look for
// improper usage of the buffer within the block that it was allocated
for ( const Token * const end = tok - > scope ( ) - > classEnd ; tok & & tok - > next ( ) & & tok ! = end ; tok = tok - > next ( ) ) {
// If the buffers are modified, we can't be sure of their sizes
if ( tok - > varId ( ) = = srcVarId | | tok - > varId ( ) = = dstVarId )
break ;
if ( Token : : Match ( tok , " strcpy ( %varid% , %var% ) " , dstVarId ) & &
tok - > tokAt ( 4 ) - > varId ( ) = = srcVarId ) {
bufferOverrunError ( tok ) ;
} else if ( Token : : Match ( tok , " sprintf ( %varid% , %str% , %var% ) " , dstVarId ) & &
tok - > tokAt ( 6 ) - > varId ( ) = = srcVarId & &
tok - > strAt ( 4 ) . find ( " %s " ) ! = std : : string : : npos ) {
bufferOverrunError ( tok ) ;
}
2010-05-26 10:56:34 +02:00
}
2012-10-13 11:16:48 +02:00
if ( ! tok )
return ;
2010-05-26 10:56:34 +02:00
}
}
}
2010-06-02 07:41:07 +02:00
//---------------------------------------------------------------------------
// Checking for buffer overflow caused by copying command line arguments
// into fixed-sized buffers without checking to make sure that the command
// line arguments will not overflow the buffer.
//
// int main(int argc, char* argv[])
// {
// char prog[10];
// strcpy(prog, argv[0]); <-- Possible buffer overrun
// }
//---------------------------------------------------------------------------
void CheckBufferOverrun : : checkInsecureCmdLineArgs ( )
{
2011-12-09 22:28:10 +01:00
const SymbolDatabase * const symbolDatabase = _tokenizer - > getSymbolDatabase ( ) ;
2010-06-02 18:09:25 +02:00
2012-10-13 11:16:48 +02:00
std : : size_t functions = symbolDatabase - > functionScopes . size ( ) ;
for ( std : : size_t i = 0 ; i < functions ; + + i ) {
const Scope * scope = symbolDatabase - > functionScopes [ i ] ;
Function * j = scope - > function ;
if ( j ) {
2011-12-09 22:28:10 +01:00
const Token * tok = j - > token ;
2010-06-02 18:09:25 +02:00
2011-12-09 22:28:10 +01:00
// Get the name of the argv variable
unsigned int varid = 0 ;
if ( Token : : Match ( tok , " main ( int %var% , char * %var% [ ] ,|) " ) ) {
varid = tok - > tokAt ( 7 ) - > varId ( ) ;
2010-06-02 07:41:07 +02:00
2011-12-09 22:28:10 +01:00
} else if ( Token : : Match ( tok , " main ( int %var% , char * * %var% ,|) " )) {
varid = tok - > tokAt ( 8 ) - > varId ( ) ;
2010-06-02 07:41:07 +02:00
}
2011-12-09 22:28:10 +01:00
if ( varid = = 0 )
continue ;
2010-06-02 07:41:07 +02:00
2011-12-09 22:28:10 +01:00
// Jump to the opening curly brace
tok = tok - > next ( ) - > link ( ) ;
if ( ! Token : : simpleMatch ( tok , " ) { " ) )
continue ;
tok = tok - > next ( ) ;
2010-06-02 07:41:07 +02:00
2011-12-09 22:28:10 +01:00
// Search within main() for possible buffer overruns involving argv
2012-03-17 21:55:08 +01:00
for ( const Token * end = tok - > link ( ) ; tok ! = end ; tok = tok - > next ( ) ) {
2011-12-09 22:28:10 +01:00
// If argv is modified or tested, its size may be being limited properly
if ( tok - > varId ( ) = = varid )
break ;
2010-06-02 07:41:07 +02:00
2011-12-09 22:28:10 +01:00
// Match common patterns that can result in a buffer overrun
// e.g. strcpy(buffer, argv[0])
if ( Token : : Match ( tok , " strcpy|strcat ( %var% , * %varid% " , varid ) | |
Token : : Match ( tok , " strcpy|strcat ( %var% , %varid% [ " , varid ) ) {
cmdLineArgsError ( tok ) ;
} else if ( Token : : Match ( tok , " sprintf ( %var% , %str% , %varid% [ " , varid ) & &
tok - > strAt ( 4 ) . find ( " %s " ) ! = std : : string : : npos ) {
cmdLineArgsError ( tok ) ;
} else if ( Token : : Match ( tok , " sprintf ( %var% , %str% , * %varid% " , varid ) & &
tok - > strAt ( 4 ) . find ( " %s " ) ! = std : : string : : npos ) {
cmdLineArgsError ( tok ) ;
}
}
}
2010-06-02 07:41:07 +02:00
}
}
2010-05-26 10:56:34 +02:00
//---------------------------------------------------------------------------
2010-11-21 11:48:27 +01:00
void CheckBufferOverrun : : negativeIndexError ( const Token * tok , MathLib : : bigint index )
2010-04-18 20:51:39 +02:00
{
2010-04-18 21:03:03 +02:00
std : : ostringstream ostr ;
2012-07-08 15:51:24 +02:00
ostr < < " Array index " < < index < < " is out of bounds. " ;
2010-04-18 21:03:03 +02:00
reportError ( tok , Severity : : error , " negativeIndex " , ostr . str ( ) ) ;
2010-04-18 20:51:39 +02:00
}
void CheckBufferOverrun : : negativeIndex ( )
{
2010-04-18 21:03:03 +02:00
const char pattern [ ] = " [ %num% ] " ;
2011-10-13 20:53:06 +02:00
for ( const Token * tok = Token : : findmatch ( _tokenizer - > tokens ( ) , pattern ) ; tok ; tok = Token : : findmatch ( tok - > next ( ) , pattern ) ) {
2010-11-21 11:48:27 +01:00
const MathLib : : bigint index = MathLib : : toLongNumber ( tok - > next ( ) - > str ( ) ) ;
2011-10-13 20:53:06 +02:00
if ( index < 0 ) {
2010-07-14 12:24:07 +02:00
// Negative index. Check if it's an array.
const Token * tok2 = tok ;
2012-03-17 21:55:08 +01:00
while ( tok2 - > strAt ( - 1 ) = = " ] " )
2010-07-14 12:24:07 +02:00
tok2 = tok2 - > previous ( ) - > link ( ) ;
2010-04-20 16:43:51 +02:00
2011-10-13 20:53:06 +02:00
if ( tok2 - > previous ( ) & & tok2 - > previous ( ) - > varId ( ) ) {
2013-01-31 20:08:48 +01:00
const Variable * var = tok2 - > previous ( ) - > variable ( ) ;
2011-06-23 05:11:17 +02:00
if ( var & & var - > isArray ( ) )
2010-04-20 16:43:51 +02:00
negativeIndexError ( tok , index ) ;
}
2010-04-18 21:03:03 +02:00
}
}
2010-04-18 20:51:39 +02:00
}
2010-04-10 21:12:00 +02:00
# include "executionpath.h"
/// @addtogroup Checks
/// @{
2010-04-18 11:08:29 +02:00
CheckBufferOverrun : : ArrayInfo : : ArrayInfo ( )
2012-05-18 16:57:11 +02:00
: _element_size ( 0 ) , _varid ( 0 )
2010-04-10 21:12:00 +02:00
{
2010-04-18 11:08:29 +02:00
}
CheckBufferOverrun : : ArrayInfo : : ArrayInfo ( const CheckBufferOverrun : : ArrayInfo & ai )
{
* this = ai ;
}
2011-06-23 04:44:11 +02:00
CheckBufferOverrun : : ArrayInfo : : ArrayInfo ( const Variable * var , const Tokenizer * tokenizer )
2012-05-18 16:57:11 +02:00
: _varname ( var - > name ( ) ) , _varid ( var - > varId ( ) )
2011-06-23 04:44:11 +02:00
{
2012-07-08 23:39:46 +02:00
for ( std : : size_t i = 0 ; i < var - > dimensions ( ) . size ( ) ; i + + )
2011-06-23 04:44:11 +02:00
_num . push_back ( var - > dimension ( i ) ) ;
if ( var - > typeEndToken ( ) - > str ( ) = = " * " )
_element_size = tokenizer - > sizeOfType ( var - > typeEndToken ( ) ) ;
else if ( var - > typeStartToken ( ) - > str ( ) = = " struct " )
_element_size = 100 ;
else
_element_size = tokenizer - > sizeOfType ( var - > typeEndToken ( ) ) ;
}
2010-09-15 22:25:12 +02:00
CheckBufferOverrun : : ArrayInfo & CheckBufferOverrun : : ArrayInfo : : operator = ( const CheckBufferOverrun : : ArrayInfo & ai )
2010-04-18 11:08:29 +02:00
{
2011-10-13 20:53:06 +02:00
if ( & ai ! = this ) {
2011-06-23 02:35:58 +02:00
_element_size = ai . _element_size ;
_num = ai . _num ;
_varid = ai . _varid ;
_varname = ai . _varname ;
2010-04-18 11:08:29 +02:00
}
return * this ;
}
2010-04-21 18:33:21 +02:00
/**
* Create array info with specified data
* The intention is that this is only a temporary solution . . all
* checking should be based on ArrayInfo from the start and then
* this will not be needed as the declare can be used instead .
*/
2010-12-31 09:30:56 +01:00
CheckBufferOverrun : : ArrayInfo : : ArrayInfo ( unsigned int id , const std : : string & name , MathLib : : bigint size1 , MathLib : : bigint n )
2012-05-18 19:59:19 +02:00
: _varname ( name ) , _element_size ( size1 ) , _varid ( id )
2010-04-21 18:33:21 +02:00
{
_num . push_back ( n ) ;
2010-04-24 21:48:58 +02:00
}
2010-11-21 11:48:27 +01:00
CheckBufferOverrun : : ArrayInfo CheckBufferOverrun : : ArrayInfo : : limit ( MathLib : : bigint value ) const
2010-04-24 21:48:58 +02:00
{
2010-12-31 09:30:56 +01:00
MathLib : : bigint uvalue = std : : max ( MathLib : : bigint ( 0 ) , value ) ;
MathLib : : bigint n = 1 ;
2011-06-23 02:35:58 +02:00
for ( unsigned int i = 0 ; i < _num . size ( ) ; + + i )
n * = _num [ i ] ;
2010-08-06 22:37:48 +02:00
if ( uvalue > n )
n = uvalue ;
2011-06-23 02:35:58 +02:00
return ArrayInfo ( _varid , _varname , _element_size , n - uvalue ) ;
2010-04-21 18:33:21 +02:00
}
2010-04-10 21:12:00 +02:00
/**
* @ brief % Check for buffer overruns ( using ExecutionPath )
*/
2011-10-13 20:53:06 +02:00
class ExecutionPathBufferOverrun : public ExecutionPath {
2010-04-10 21:12:00 +02:00
public :
/** Startup constructor */
2010-04-18 11:08:29 +02:00
ExecutionPathBufferOverrun ( Check * c , const std : : map < unsigned int , CheckBufferOverrun : : ArrayInfo > & arrayinfo )
2011-10-13 20:53:06 +02:00
: ExecutionPath ( c , 0 ) , arrayInfo ( arrayinfo ) , value ( 0 ) {
2010-04-10 21:12:00 +02:00
}
private :
2010-06-14 07:54:41 +02:00
/** @brief Copy this check. Called from the ExecutionPath baseclass. */
2011-10-13 20:53:06 +02:00
ExecutionPath * copy ( ) {
2010-04-10 21:12:00 +02:00
return new ExecutionPathBufferOverrun ( * this ) ;
}
2010-06-14 07:54:41 +02:00
/** @brief is other execution path equal? */
2011-10-13 20:53:06 +02:00
bool is_equal ( const ExecutionPath * e ) const {
2010-04-25 11:55:57 +02:00
const ExecutionPathBufferOverrun * c = static_cast < const ExecutionPathBufferOverrun * > ( e ) ;
return ( value = = c - > value ) ;
}
2010-06-14 07:54:41 +02:00
/** @brief Buffer information */
2010-04-18 11:08:29 +02:00
const std : : map < unsigned int , CheckBufferOverrun : : ArrayInfo > & arrayInfo ;
2010-04-10 21:12:00 +02:00
/** no implementation => compiler error if used by accident */
void operator = ( const ExecutionPathBufferOverrun & ) ;
/** internal constructor for creating extra checks */
2010-04-18 11:08:29 +02:00
ExecutionPathBufferOverrun ( Check * c , const std : : map < unsigned int , CheckBufferOverrun : : ArrayInfo > & arrayinfo , unsigned int varid_ )
2010-04-15 20:08:51 +02:00
: ExecutionPath ( c , varid_ ) ,
2012-05-18 16:57:11 +02:00
arrayInfo ( arrayinfo ) ,
value ( 0 ) { // Pretend that variables are initialized to 0. This checking is not about uninitialized variables.
2010-04-10 21:12:00 +02:00
}
2010-06-14 07:54:41 +02:00
/** @brief Variable value. */
2010-12-31 09:30:56 +01:00
MathLib : : bigint value ;
2010-04-10 21:12:00 +02:00
2010-06-14 07:54:41 +02:00
/**
* @ brief Assign value to a variable
* @ param checks the execution paths
* @ param varid the variable id
* @ param value the assigned value
*/
2011-10-13 20:53:06 +02:00
static void assign_value ( std : : list < ExecutionPath * > & checks , unsigned int varid , const std : : string & value ) {
2010-04-10 21:12:00 +02:00
if ( varid = = 0 )
return ;
std : : list < ExecutionPath * > : : const_iterator it ;
2011-10-13 20:53:06 +02:00
for ( it = checks . begin ( ) ; it ! = checks . end ( ) ; + + it ) {
2010-04-10 21:12:00 +02:00
ExecutionPathBufferOverrun * c = dynamic_cast < ExecutionPathBufferOverrun * > ( * it ) ;
if ( c & & c - > varId = = varid )
2010-12-31 09:30:56 +01:00
c - > value = MathLib : : toLongNumber ( value ) ;
2010-04-10 21:12:00 +02:00
}
}
2010-06-14 07:54:41 +02:00
/**
* @ brief Found array usage , analyse the array usage
* @ param tok token where usage occurs ( only used when reporting the error )
* @ param checks The execution paths
* @ param varid1 variable id for the array
* @ param varid2 variable id for the index
*/
2011-10-13 20:53:06 +02:00
static void array_index ( const Token * tok , std : : list < ExecutionPath * > & checks , unsigned int varid1 , unsigned int varid2 ) {
2010-04-10 21:28:49 +02:00
if ( checks . empty ( ) | | varid1 = = 0 | | varid2 = = 0 )
2010-04-10 21:12:00 +02:00
return ;
// Locate array info corresponding to varid1
2012-03-27 19:40:39 +02:00
ExecutionPathBufferOverrun * c = dynamic_cast < ExecutionPathBufferOverrun * > ( checks . front ( ) ) ;
std : : map < unsigned int , CheckBufferOverrun : : ArrayInfo > : : const_iterator it1 ;
it1 = c - > arrayInfo . find ( varid1 ) ;
if ( it1 = = c - > arrayInfo . end ( ) )
return ;
const CheckBufferOverrun : : ArrayInfo & ai = it1 - > second ;
2010-04-10 21:12:00 +02:00
2012-07-08 15:51:24 +02:00
// Check if varid2 variable has a value that is out of bounds
2010-04-10 21:12:00 +02:00
std : : list < ExecutionPath * > : : const_iterator it ;
2011-10-13 20:53:06 +02:00
for ( it = checks . begin ( ) ; it ! = checks . end ( ) ; + + it ) {
2012-03-27 19:40:39 +02:00
c = dynamic_cast < ExecutionPathBufferOverrun * > ( * it ) ;
2011-10-13 20:53:06 +02:00
if ( c & & c - > varId = = varid2 & & c - > value > = ai . num ( 0 ) ) {
2010-04-10 21:12:00 +02:00
// variable value is out of bounds, report error
CheckBufferOverrun * checkBufferOverrun = dynamic_cast < CheckBufferOverrun * > ( c - > owner ) ;
2011-10-13 20:53:06 +02:00
if ( checkBufferOverrun ) {
2010-12-31 09:30:56 +01:00
std : : vector < MathLib : : bigint > index ;
2010-04-23 16:26:40 +02:00
index . push_back ( c - > value ) ;
2011-08-24 13:11:39 +02:00
checkBufferOverrun - > arrayIndexOutOfBoundsError ( tok , ai , index ) ;
2010-04-10 21:12:00 +02:00
break ;
}
}
}
}
2011-10-13 20:53:06 +02:00
const Token * parse ( const Token & tok , std : : list < ExecutionPath * > & checks ) const {
if ( Token : : Match ( tok . previous ( ) , " [;{}] " ) ) {
2010-04-10 21:12:00 +02:00
// Declaring variable..
2012-06-23 08:15:59 +02:00
if ( Token : : Match ( & tok , " %type% %var% ; " ) /*&& (tok.isStandardType() || isC)*/ ) {
2010-04-10 21:12:00 +02:00
checks . push_back ( new ExecutionPathBufferOverrun ( owner , arrayInfo , tok . next ( ) - > varId ( ) ) ) ;
return tok . tokAt ( 2 ) ;
}
// Assign variable..
2011-10-13 20:53:06 +02:00
if ( Token : : Match ( & tok , " %var% = %num% ; " ) ) {
2010-04-10 21:12:00 +02:00
assign_value ( checks , tok . varId ( ) , tok . strAt ( 2 ) ) ;
return tok . tokAt ( 3 ) ;
}
}
// Assign variable (unknown value = 0)..
2011-10-13 20:53:06 +02:00
if ( Token : : Match ( & tok , " %var% = " ) ) {
2010-04-10 21:12:00 +02:00
assign_value ( checks , tok . varId ( ) , " 0 " ) ;
return & tok ;
}
2010-07-05 22:19:27 +02:00
// Assign variable (unknown value = 0)..
2011-10-13 20:53:06 +02:00
if ( Token : : Match ( tok . tokAt ( - 2 ) , " (|, & %var% ,|) " ) ) {
2010-07-05 22:19:27 +02:00
assign_value ( checks , tok . varId ( ) , " 0 " ) ;
return & tok ;
}
2010-04-10 21:12:00 +02:00
// Array index..
2011-10-13 20:53:06 +02:00
if ( Token : : Match ( & tok , " %var% [ %var% ] " ) ) {
2010-04-10 21:12:00 +02:00
array_index ( & tok , checks , tok . varId ( ) , tok . tokAt ( 2 ) - > varId ( ) ) ;
return tok . tokAt ( 3 ) ;
}
return & tok ;
}
} ;
/// @}
void CheckBufferOverrun : : executionPaths ( )
{
2011-06-23 04:44:11 +02:00
// Parse all variables and extract array info..
2010-04-10 21:12:00 +02:00
std : : map < unsigned int , ArrayInfo > arrayInfo ;
2011-10-31 02:24:59 +01:00
for ( unsigned int i = 1 ; i < = _tokenizer - > varIdCount ( ) ; i + + ) {
2011-06-23 04:44:11 +02:00
const Variable * var = _tokenizer - > getSymbolDatabase ( ) - > getVariableFromVarId ( i ) ;
2011-07-20 07:57:42 +02:00
if ( var & & var - > isArray ( ) & & var - > dimension ( 0 ) > 0 )
2011-06-23 04:44:11 +02:00
arrayInfo [ i ] = ArrayInfo ( var , _tokenizer ) ;
2010-04-10 21:12:00 +02:00
}
// Perform checking - check how the arrayInfo arrays are used
ExecutionPathBufferOverrun c ( this , arrayInfo ) ;
2012-01-26 16:50:59 +01:00
checkExecutionPaths ( _tokenizer - > getSymbolDatabase ( ) , & c ) ;
2010-04-10 21:12:00 +02:00
}
2011-08-04 11:15:14 +02:00
void CheckBufferOverrun : : arrayIndexThenCheck ( )
{
2011-08-07 09:28:08 +02:00
if ( ! _settings - > isEnabled ( " style " ) )
2011-08-04 11:15:14 +02:00
return ;
2012-10-13 11:16:48 +02:00
const SymbolDatabase * symbolDatabase = _tokenizer - > getSymbolDatabase ( ) ;
const std : : size_t functions = symbolDatabase - > functionScopes . size ( ) ;
for ( std : : size_t i = 0 ; i < functions ; + + i ) {
const Scope * scope = symbolDatabase - > functionScopes [ i ] ;
for ( const Token * tok = scope - > classStart ; tok & & tok ! = scope - > classEnd ; tok = tok - > next ( ) ) {
if ( Token : : Match ( tok , " %var% [ %var% ] " ) ) {
const std : : string & indexName ( tok - > strAt ( 2 ) ) ;
// skip array index..
tok = tok - > tokAt ( 4 ) ;
while ( tok & & tok - > str ( ) = = " [ " )
tok = tok - > link ( ) - > next ( ) ;
// syntax error
if ( ! tok )
return ;
// skip comparison
if ( tok - > type ( ) = = Token : : eComparisonOp & & tok - > strAt ( 2 ) = = " && " )
tok = tok - > tokAt ( 2 ) ;
// check if array index is ok
if ( Token : : Match ( tok , ( " && " + indexName + " <|<= " ) . c_str ( ) ) )
arrayIndexThenCheckError ( tok , indexName ) ;
}
2011-08-04 11:15:14 +02:00
}
}
}
void CheckBufferOverrun : : arrayIndexThenCheckError ( const Token * tok , const std : : string & indexName )
{
2011-08-05 09:10:07 +02:00
reportError ( tok , Severity : : style , " arrayIndexThenCheck " ,
2012-07-08 15:51:24 +02:00
" Array index ' " + indexName + " ' is used before limits check. \n "
2012-07-09 11:11:05 +02:00
" Defensive programming: The variable ' " + indexName + " ' is used as an array index before it "
" is check that is within limits. This can mean that the array might be accessed out of bounds. "
" Reorder conditions such as '(a[i] && i < 10)' to '(i < 10 && a[i])'. That way the array will "
" not be accessed if the index is out of limits. " ) ;
2011-08-04 11:15:14 +02:00
}
2013-03-19 08:22:48 +01:00
// -------------------------------------------------------------------------------------
// Check the second and the third parameter of the POSIX function write and validate
// their values.
// The parameters have the following meaning:
// - 1.parameter: file descripter (not required for this check)
// - 2.parameter: is a null terminated character string of the content to write.
// - 3.parameter: the number of bytes to write.
//
// This check is triggered if the size of the string ( 2. parameter) is lower than
// the number of bytes provided at the 3. parameter.
//
// References:
// - http://gd.tuwien.ac.at/languages/c/programming-bbrown/c_075.htm
// - http://codewiki.wikidot.com/c:system-calls:write
// -------------------------------------------------------------------------------------
void CheckBufferOverrun : : writeOutsideBufferSize ( )
{
if ( ! _settings - > standards . posix )
return ;
const SymbolDatabase * symbolDatabase = _tokenizer - > getSymbolDatabase ( ) ;
const std : : size_t functions = symbolDatabase - > functionScopes . size ( ) ;
for ( std : : size_t i = 0 ; i < functions ; + + i ) {
const Scope * scope = symbolDatabase - > functionScopes [ i ] ;
for ( const Token * tok = scope - > classStart ; tok & & tok ! = scope - > classEnd ; tok = tok - > next ( ) ) {
2013-03-20 11:53:25 +01:00
if ( Token : : Match ( tok , " pwrite|write ( " ) & & Token : : Match ( tok - > tokAt ( 2 ) - > nextArgument ( ) , " %str% , %num% " ) ) {
const std : : string & functionName ( tok - > str ( ) ) ;
2013-03-19 08:22:48 +01:00
tok = tok - > tokAt ( 4 ) ; // set tokenptr to %str% parameter
const std : : size_t stringLength = Token : : getStrLength ( tok ) ;
tok = tok - > tokAt ( 2 ) ; // set tokenptr to %num% parameter
const MathLib : : bigint writeLength = MathLib : : toLongNumber ( tok - > str ( ) ) ;
if ( static_cast < unsigned long int > ( writeLength ) > stringLength )
writeOutsideBufferSizeError ( tok , stringLength , writeLength , functionName ) ;
}
}
}
}
void CheckBufferOverrun : : writeOutsideBufferSizeError ( const Token * tok , const std : : size_t stringLength , const MathLib : : bigint writeLength , const std : : string & strFunctionName )
{
reportError ( tok , Severity : : error , " writeOutsideBufferSize " ,
" Writing ' " + MathLib : : longToString ( writeLength - stringLength ) + " ' bytes outside buffer size. \n "
" The number of bytes to write (' " + MathLib : : longToString ( writeLength ) + " ' bytes) are bigger than the source buffer (' " + MathLib : : longToString ( stringLength ) + " ' bytes). "
" Please check the second and the third parameter of the function ' " + strFunctionName + " '. " ) ;
}
// -------------------------------------------------------------------------------------
// -------------------------------------------------------------------------------------