2009-01-31 20:29:27 +01:00
/*
* Cppcheck - A tool for static C / C + + code analysis
2014-02-15 07:45:39 +01:00
* Copyright ( C ) 2007 - 2014 Daniel Marjamäki and Cppcheck team .
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"
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 ( ) ;
2014-07-06 14:48:24 +02:00
for ( std : : size_t i = 0 ; i < arrayInfo . num ( ) . size ( ) ; + + i )
2011-06-23 02:35:58 +02:00
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 ( ) ;
2014-07-06 14:48:24 +02:00
for ( std : : size_t i = 0 ; i < index . size ( ) ; + + i )
2010-04-23 16:26:40 +02:00
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
}
2014-01-22 21:25:37 +01:00
void CheckBufferOverrun : : arrayIndexOutOfBoundsError ( const Token * tok , const ArrayInfo & arrayInfo , const std : : vector < ValueFlow : : Value > & index )
2014-01-17 18:56:46 +01:00
{
std : : ostringstream errmsg ;
2014-01-22 21:25:37 +01:00
errmsg < < " Array ' " < < arrayInfo . varname ( ) ;
2014-07-06 14:48:24 +02:00
for ( std : : size_t i = 0 ; i < arrayInfo . num ( ) . size ( ) ; + + i )
2014-01-22 21:25:37 +01:00
errmsg < < " [ " < < arrayInfo . num ( i ) < < " ] " ;
if ( index . size ( ) = = 1 )
errmsg < < " ' accessed at index " < < index [ 0 ] . intvalue < < " , which is out of bounds. " ;
else {
errmsg < < " ' index " < < arrayInfo . varname ( ) ;
2014-07-06 14:48:24 +02:00
for ( std : : size_t i = 0 ; i < index . size ( ) ; + + i )
2014-01-22 21:25:37 +01:00
errmsg < < " [ " < < index [ i ] . intvalue < < " ] " ;
errmsg < < " out of bounds. " ;
}
2014-02-16 11:47:52 +01:00
const Token * condition = nullptr ;
2014-07-06 14:48:24 +02:00
for ( std : : size_t i = 0 ; i < index . size ( ) ; + + i ) {
2014-02-16 10:32:10 +01:00
if ( condition = = nullptr )
2014-01-22 21:25:37 +01:00
condition = index [ i ] . condition ;
}
2014-01-17 18:56:46 +01:00
2014-02-16 10:32:10 +01:00
if ( condition ! = nullptr ) {
2014-01-22 21:25:37 +01:00
errmsg < < " Otherwise condition ' " < < condition - > expressionString ( ) < < " ' is redundant. " ;
2014-01-17 19:44:45 +01:00
std : : list < const Token * > callstack ;
callstack . push_back ( tok ) ;
2014-01-22 21:25:37 +01:00
callstack . push_back ( condition ) ;
2014-01-17 19:44:45 +01:00
reportError ( callstack , Severity : : warning , " arrayIndexOutOfBoundsCond " , errmsg . str ( ) ) ;
} else {
reportError ( tok , Severity : : error , " arrayIndexOutOfBounds " , errmsg . str ( ) ) ;
}
2014-01-17 18:56:46 +01: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 "
2014-07-31 19:51:29 +02:00
" At most, strncat appends the 3rd parameter's amount of characters and adds a terminating null byte. \n "
" The safe way to use strncat is to subtract one from the remaining space in the buffer and use it as 3rd parameter. "
" Source: http://www.cplusplus.com/reference/cstring/strncat/ \n "
" Source: http://www.opensource.apple.com/source/Libc/Libc-167/gen.subproj/i386.subproj/strncat.c " ) ;
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
}
2014-02-01 22:38:29 +01:00
void CheckBufferOverrun : : negativeMemoryAllocationSizeError ( const Token * tok )
{
reportError ( tok , Severity : : error , " negativeMemoryAllocationSize " ,
2014-04-08 20:23:00 +02:00
" Memory allocation size is negative. \n "
" Memory allocation size is negative. "
" Negative allocation size has no specified behaviour. " ) ;
2014-02-01 22:38:29 +01:00
}
2009-01-31 20:29:27 +01:00
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
// Check array usage..
//---------------------------------------------------------------------------
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 )
{
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 ) ;
2013-11-30 07:40:32 +01:00
if ( Token : : simpleMatch ( end , " { " ) ) // Ticket #5203: Invalid code, bailout
return true ;
2014-07-29 11:59:45 +02:00
// Used later to check if the body belongs to a "if"
const bool is_if = tok - > str ( ) = = " if " ;
2014-03-21 13:20:44 +01:00
for ( ; tok & & 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 ;
}
2014-06-27 06:46:42 +02:00
//---------------------------------------------------------------------------
2010-04-21 18:33:21 +02:00
2014-07-06 08:41:39 +02:00
static bool checkMinSizes ( const std : : list < Library : : ArgumentChecks : : MinSize > & minsizes , const Token * const ftok , const std : : size_t arraySize , const Token * * charSizeToken )
{
if ( charSizeToken )
* charSizeToken = nullptr ;
if ( minsizes . empty ( ) )
return false ;
// All conditions must be true
bool error = true ;
for ( std : : list < Library : : ArgumentChecks : : MinSize > : : const_iterator minsize = minsizes . begin ( ) ; minsize ! = minsizes . end ( ) ; + + minsize ) {
if ( ! error )
return false ;
error = false ;
const Token * argtok = ftok - > tokAt ( 2 ) ;
for ( int argnum = 1 ; argtok & & argnum < minsize - > arg ; argnum + + )
argtok = argtok - > nextArgument ( ) ;
if ( ! argtok )
return false ;
switch ( minsize - > type ) {
case Library : : ArgumentChecks : : MinSize : : ARGVALUE :
if ( Token : : Match ( argtok , " %num% ,|) " ) ) {
const MathLib : : bigint sz = MathLib : : toLongNumber ( argtok - > str ( ) ) ;
2014-07-08 21:47:22 +02:00
if ( ( std : : size_t ) sz > arraySize )
2014-07-06 08:41:39 +02:00
error = true ;
} else if ( argtok - > type ( ) = = Token : : eChar & & Token : : Match ( argtok - > next ( ) , " ,|) " ) & & charSizeToken )
* charSizeToken = argtok ; //sizeArgumentAsCharError(argtok);
break ;
case Library : : ArgumentChecks : : MinSize : : MUL :
// TODO: handle arbitrary arg2
if ( minsize - > arg2 = = minsize - > arg + 1 & & Token : : Match ( argtok , " %num% , %num% ,|) " ) ) {
const MathLib : : bigint sz = MathLib : : toLongNumber ( argtok - > str ( ) ) * MathLib : : toLongNumber ( argtok - > strAt ( 2 ) ) ;
2014-07-08 21:47:22 +02:00
if ( ( std : : size_t ) sz > arraySize )
2014-07-06 08:41:39 +02:00
error = true ;
}
break ;
2014-08-03 20:11:22 +02:00
case Library : : ArgumentChecks : : MinSize : : STRLEN : {
const Token * strtoken = argtok - > getValueTokenMaxStrLength ( ) ;
if ( strtoken & & Token : : getStrLength ( strtoken ) > = arraySize )
2014-07-06 08:41:39 +02:00
error = true ;
2014-08-03 20:11:22 +02:00
}
break ;
2014-07-06 08:41:39 +02:00
case Library : : ArgumentChecks : : MinSize : : SIZEOF :
if ( argtok - > type ( ) = = Token : : eString & & Token : : getStrLength ( argtok ) > = arraySize )
error = true ;
break ;
case Library : : ArgumentChecks : : MinSize : : NONE :
return false ;
} ;
}
return error ;
}
2014-07-05 20:31:43 +02:00
void CheckBufferOverrun : : checkFunctionParameter ( const Token & ftok , unsigned int par , const ArrayInfo & arrayInfo , const std : : list < const Token * > & callstack )
2010-04-21 19:27:28 +02:00
{
2014-07-05 20:31:43 +02:00
const std : : list < Library : : ArgumentChecks : : MinSize > * const minsizes = _settings - > library . argminsizes ( ftok . str ( ) , par ) ;
2010-04-21 20:02:58 +02:00
2014-07-06 08:41:39 +02:00
if ( minsizes & & ( ! ( Token : : simpleMatch ( ftok . previous ( ) , " . " ) | | Token : : Match ( ftok . tokAt ( - 2 ) , " !!std :: " ) ) ) ) {
2011-06-23 02:35:58 +02:00
if ( arrayInfo . element_size ( ) = = 0 )
2010-10-24 11:32:27 +02:00
return ;
2014-07-05 20:31:43 +02:00
MathLib : : bigint arraySize = arrayInfo . element_size ( ) ;
2014-07-06 14:48:24 +02:00
for ( std : : size_t i = 0 ; i < arrayInfo . num ( ) . size ( ) ; + + i )
2014-07-05 20:31:43 +02:00
arraySize * = arrayInfo . num ( i ) ;
2014-07-06 08:41:39 +02:00
const Token * charSizeToken = nullptr ;
2014-07-06 14:48:24 +02:00
if ( checkMinSizes ( * minsizes , & ftok , ( std : : size_t ) arraySize , & charSizeToken ) )
2014-07-05 20:31:43 +02:00
bufferOverrunError ( callstack , arrayInfo . varname ( ) ) ;
2014-07-06 08:41:39 +02:00
if ( charSizeToken )
sizeArgumentAsCharError ( charSizeToken ) ;
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 ) {
2014-07-05 20:31:43 +02:00
const Function * const func = ftok . function ( ) ;
2012-12-22 09:23:34 +01:00
2012-09-11 18:03:47 +02:00
if ( func & & func - > hasBody ) {
// Get corresponding parameter..
2013-11-03 04:48:41 +01:00
const Variable * const 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 *)"
2013-07-20 12:31:04 +02:00
if ( parameter - > declarationId ( ) = = 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..
2014-07-05 20:31:43 +02:00
for ( const Token * ftok2 = func - > functionScope - > classStart ; ftok2 ! = func - > functionScope - > classEnd ; ftok2 = ftok2 - > next ( ) ) {
if ( Token : : Match ( ftok2 , " if|for|switch|while ( " ) ) {
2010-12-31 18:07:46 +01:00
// bailout if there is buffer usage..
2014-07-05 20:31:43 +02:00
if ( bailoutIfSwitch ( ftok2 , parameter - > declarationId ( ) ) ) {
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..
2014-07-05 20:31:43 +02:00
ftok2 = ftok2 - > linkAt ( 1 ) - > linkAt ( 1 ) ;
if ( Token : : simpleMatch ( ftok2 , " } else { " ) )
ftok2 = ftok2 - > linkAt ( 2 ) ;
if ( ! ftok2 )
2011-08-29 19:16:52 +02:00
break ;
2010-12-31 18:07:46 +01:00
continue ;
}
}
2010-08-05 11:01:47 +02:00
2014-07-05 20:31:43 +02:00
if ( ftok2 - > str ( ) = = " } " )
2010-08-05 11:01:47 +02:00
break ;
2014-07-05 20:31:43 +02:00
if ( ftok2 - > varId ( ) = = parameter - > declarationId ( ) ) {
if ( Token : : Match ( ftok2 - > previous ( ) , " -- %var% " ) | |
Token : : Match ( ftok2 , " %var% -- " ) )
2010-10-14 20:00:32 +02:00
break ;
2014-07-05 20:31:43 +02:00
if ( Token : : Match ( ftok2 - > previous ( ) , " ;|{|}|%op% %var% [ %num% ] " ) ) {
const MathLib : : bigint index = MathLib : : toLongNumber ( ftok2 - > 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 ) ;
2014-07-05 20:31:43 +02:00
callstack2 . push_back ( ftok2 ) ;
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..
2014-07-05 20:31:43 +02:00
if ( Token : : Match ( ftok2 , " %var% ( " ) ) {
2011-12-11 08:16:58 +01:00
ArrayInfo ai ( arrayInfo ) ;
2013-07-20 12:31:04 +02:00
ai . declarationId ( parameter - > declarationId ( ) ) ;
2014-07-05 20:31:43 +02:00
checkFunctionCall ( ftok2 , ai , callstack ) ;
2011-12-11 08:16:58 +01:00
}
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 " ) ) {
2014-07-05 20:31:43 +02:00
const Function * const func = ftok . function ( ) ;
2012-12-22 09:23:34 +01:00
// If argument is '%type% a[num]' then check bounds against num
if ( func ) {
2013-11-03 04:48:41 +01:00
const Variable * const 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% ] [,)[] " )
2014-02-16 10:32:10 +01:00
& & ( nameToken = argument - > nameToken ( ) ) ! = nullptr ) {
2012-12-28 11:15:18 +01:00
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 ;
2014-07-06 14:48:24 +02:00
for ( std : : size_t i = 0 ; i < arrayInfo . num ( ) . size ( ) ; i + + )
2012-12-22 09:23:34 +01:00
arraysize * = arrayInfo . num ( i ) ;
if ( Token : : Match ( tok2 , " [,)] " ) & & arraysize > 0 & & argsize > arraysize )
2014-07-05 20:31:43 +02:00
argumentSizeError ( & ftok , ftok . str ( ) , arrayInfo . varname ( ) ) ;
2012-12-22 09:23:34 +01:00
}
}
}
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
2014-05-29 19:58:09 +02:00
const unsigned int declarationId = arrayInfo . declarationId ( ) ;
2011-12-17 19:04:03 +01:00
const Token * tok2 = tok - > tokAt ( 2 ) ;
2011-01-22 21:31:26 +01:00
// 1st parameter..
2014-05-29 19:58:09 +02:00
if ( Token : : Match ( tok2 , " %varid% ,|) " , declarationId ) )
2011-12-11 08:16:58 +01:00
checkFunctionParameter ( * tok , 1 , arrayInfo , callstack ) ;
2014-05-29 19:58:09 +02:00
else if ( Token : : Match ( tok2 , " %varid% + %num% ,|) " , declarationId)) {
2011-12-17 19:04:03 +01:00
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 ( ) ;
2014-05-29 19:58:09 +02:00
if ( Token : : Match ( tok2 , " %varid% ,|) " , declarationId ) )
2011-12-17 19:04:03 +01:00
checkFunctionParameter ( * tok , 2 , arrayInfo , callstack ) ;
2014-05-29 19:58:09 +02:00
else if ( Token : : Match ( tok2 , " %varid% + %num% ,|) " , declarationId)) {
2011-12-17 19:04:03 +01:00
const ArrayInfo ai ( arrayInfo . limit ( MathLib : : toLongNumber ( tok2 - > strAt ( 2 ) ) ) ) ;
checkFunctionParameter ( * tok , 2 , ai , callstack ) ;
2011-01-22 21:31:26 +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 ;
2014-04-23 09:18:09 +02:00
if ( tok - > str ( ) = = " return " ) {
tok = tok - > next ( ) ;
if ( ! tok )
return ;
}
2014-04-24 10:24:40 +02:00
const MathLib : : bigint total_size = arrayInfo . element_size ( ) * size ;
2013-07-20 12:31:04 +02:00
const unsigned int declarationId = arrayInfo . declarationId ( ) ;
2011-09-10 17:21:52 +02:00
2009-01-31 20:29:27 +01:00
std : : string varnames ;
2014-07-07 21:25:30 +02:00
for ( std : : size_t 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
2014-04-23 09:18:09 +02:00
const unsigned char varcount = static_cast < unsigned char > ( varname . empty ( ) ? 0U : ( varname . size ( ) - 1 ) * 2U ) ;
2009-01-31 20:29:27 +01:00
2014-03-29 20:20:22 +01:00
// ValueFlow array index..
if ( ( declarationId > 0 & & Token : : Match ( tok , " %varid% [ " , declarationId ) ) | |
( declarationId = = 0 & & Token : : Match ( tok , ( varnames + " [ " ) . c_str ( ) ) ) ) {
const Token * tok2 = tok ;
while ( tok2 - > str ( ) ! = " [ " )
tok2 = tok2 - > next ( ) ;
valueFlowCheckArrayIndex ( tok2 , arrayInfo ) ;
}
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 ;
2014-05-29 19:58:09 +02:00
const bool isPortabilityEnabled = _settings - > isEnabled ( " portability " ) ;
2014-12-04 20:49:58 +01:00
for ( const Token * const end = tok - > scope ( ) - > classEnd ; tok & & tok ! = end ; tok = tok - > next ( ) ) {
2013-07-20 12:31:04 +02:00
if ( declarationId ! = 0 & & Token : : Match ( tok , " %varid% = new|malloc|realloc " , declarationId ) ) {
2010-02-10 22:11:08 +01:00
// Abort
break ;
}
2011-10-12 20:54:39 +02:00
// reassign buffer
2013-07-20 12:31:04 +02:00
if ( declarationId > 0 & & Token : : Match ( tok , " [;{}] %varid% = %any% " , declarationId ) ) {
2011-10-12 22:06:19 +02:00
// using varid .. bailout
2013-07-20 12:31:04 +02:00
if ( tok - > tokAt ( 3 ) - > varId ( ) ! = declarationId )
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..
2013-07-20 12:31:04 +02:00
if ( ( declarationId > 0 & & ( ( tok - > str ( ) = = " return " | | ( ! tok - > isName ( ) & & ! Token : : Match ( tok , " [.&] " ) ) ) & & Token : : Match ( tok - > next ( ) , " %varid% [ %num% ] " , declarationId ) ) ) | |
( declarationId = = 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 ;
2014-04-23 09:18:09 +02:00
const Token * tok2 = tok - > tokAt ( 2 + varcount ) ;
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..
2014-07-07 21:25:30 +02:00
for ( std : : size_t i = 0 ; i < indexes . size ( ) ; + + i ) {
const std : : size_t ri = indexes . size ( ) - 1 - i ;
2011-09-12 02:42:57 +02:00
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 ) ;
// taking address of 1 past end?
2014-07-29 11:59:45 +02:00
if ( totalIndex = = totalElements ) {
const bool addr = ( tok3 & & ( tok3 - > str ( ) = = " & " | |
2014-08-03 20:11:22 +02:00
Token : : simpleMatch ( tok3 - > previous ( ) , " & ( " ) ) ) ;
2014-07-29 11:59:45 +02:00
if ( addr )
continue ;
}
2011-09-12 02:42:57 +02:00
// Is totalIndex in bounds?
2011-10-13 20:53:06 +02:00
if ( totalIndex > totalElements | | totalIndex < 0 ) {
2014-04-23 09:18:09 +02:00
arrayIndexOutOfBoundsError ( tok - > tokAt ( 1 + varcount ) , arrayInfo , indexes ) ;
2011-09-12 02:42:57 +02:00
}
// 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
2014-07-07 21:25:30 +02:00
for ( std : : size_t i = 0 ; i < indexes . size ( ) ; + + i ) {
2011-10-13 20:53:06 +02:00
if ( indexes [ i ] > = arrayInfo . num ( i ) ) {
2012-03-13 21:30:03 +01:00
if ( indexes . size ( ) = = 1U ) {
2014-04-23 09:18:09 +02:00
arrayIndexOutOfBoundsError ( tok - > tokAt ( 1 + varcount ) , arrayInfo , indexes ) ;
2012-03-13 21:30:03 +01:00
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 ) {
2014-04-23 09:18:09 +02:00
arrayIndexOutOfBoundsError ( tok - > tokAt ( 1 + varcount ) , arrayInfo , indexes ) ;
2011-09-12 02:42:57 +02:00
break ; // only warn about the first one
}
}
}
}
}
tok = tok2 ;
2009-01-31 20:29:27 +01:00
continue ;
}
// memset, memcmp, memcpy, strncpy, fgets..
2013-07-20 12:31:04 +02:00
if ( declarationId = = 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
}
// Writing data into array..
2013-07-20 12:31:04 +02:00
if ( ( declarationId > 0 & & Token : : Match ( tok , " strcpy|strcat ( %varid% , %str% ) " , declarationId ) ) | |
( declarationId = = 0 & & Token : : Match ( tok , ( " strcpy|strcat ( " + varnames + " , %str% ) " ) . c_str ( ) ) ) ) {
2014-04-23 09:18:09 +02:00
const std : : size_t len = Token : : getStrLength ( tok - > tokAt ( varcount + 4 ) ) ;
2011-10-13 20:53:06 +02:00
if ( total_size > 0 & & len > = ( unsigned int ) total_size ) {
2014-06-26 11:44:19 +02:00
bufferOverrunError ( tok , declarationId > 0 ? emptyString : varnames ) ;
2009-08-30 13:44:23 +02:00
continue ;
2009-01-31 20:29:27 +01:00
}
2013-07-20 12:31:04 +02:00
} else if ( ( declarationId > 0 & & Token : : Match ( tok , " strcpy|strcat ( %varid% , %var% ) " , declarationId ) ) | |
( declarationId = = 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
2013-07-20 12:31:04 +02:00
const std : : string strcatPattern = declarationId > 0 ? std : : string ( " strcat ( %varid% , %str% ) ; " ) : ( " strcat ( " + varnames + " , %str% ) ; " ) ;
if ( Token : : Match ( tok , strcatPattern . c_str ( ) , declarationId ) ) {
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 ;
2013-07-20 12:31:04 +02:00
while ( Token : : Match ( tok2 , strcatPattern . c_str ( ) , declarationId ) ) {
2014-04-23 09:18:09 +02:00
charactersAppend + = Token : : getStrLength ( tok2 - > tokAt ( 4 + varcount ) ) ;
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 ;
}
2014-04-23 09:18:09 +02:00
tok2 = tok2 - > tokAt ( 7 + varcount ) ;
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.
2013-07-20 12:31:04 +02:00
const std : : string sprintfPattern = declarationId > 0 ? std : : string ( " sprintf ( %varid% , %str% [,)] " ) : ( " sprintf ( " + varnames + " , %str% [,)] " ) ;
if ( Token : : Match ( tok , sprintfPattern . c_str ( ) , declarationId ) & & 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..
2013-07-20 12:31:04 +02:00
const std : : string snprintfPattern = declarationId > 0 ? std : : string ( " snprintf ( %varid% , %num% , " ) : ( " snprintf ( " + varnames + " , %num% , " ) ;
if ( Token : : Match ( tok , snprintfPattern . c_str ( ) , declarationId ) ) {
2014-04-23 09:18:09 +02:00
const MathLib : : bigint n = MathLib : : toLongNumber ( tok - > strAt ( 4 + varcount ) ) ;
2014-09-27 21:51:11 +02:00
if ( ( n > total_size ) & & total_size > 0 )
2014-04-23 09:18:09 +02:00
outOfBoundsError ( tok - > tokAt ( 4 + varcount ) , " 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
2013-07-20 12:31:04 +02:00
if ( declarationId = = 0 )
2011-01-22 21:31:26 +01:00
continue ;
2013-07-20 12:31:04 +02:00
const ArrayInfo arrayInfo1 ( declarationId , 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
2013-07-20 12:31:04 +02:00
else if ( declarationId & & Token : : Match ( tok , " = %varid% + %num% ; " , declarationId ) ) {
2011-01-01 20:56:21 +01:00
const MathLib : : bigint index = MathLib : : toLongNumber ( tok - > strAt ( 3 ) ) ;
2014-05-29 19:58:09 +02:00
if ( isPortabilityEnabled & & index > size )
2011-08-24 13:11:39 +02:00
pointerOutOfBoundsError ( tok - > next ( ) , " buffer " ) ;
2013-07-20 12:31:04 +02:00
if ( index > = size & & Token : : Match ( tok - > tokAt ( - 2 ) , " [;{}] %varid% = " , declarationId ) )
2011-01-01 20:56:21 +01:00
pointerIsOutOfBounds = true ;
}
2013-07-20 12:31:04 +02:00
else if ( pointerIsOutOfBounds & & Token : : Match ( tok , " [;{}=] * %varid% [;=] " , declarationId ) ) {
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
}
}
2014-03-29 20:20:22 +01:00
void CheckBufferOverrun : : valueFlowCheckArrayIndex ( const Token * const tok , const ArrayInfo & arrayInfo )
{
2014-10-21 22:56:53 +02:00
// Declaration in global scope or namespace?
if ( tok - > scope ( ) - > type = = Scope : : eGlobal | | tok - > scope ( ) - > type = = Scope : : eNamespace )
2014-08-19 07:03:00 +02:00
return ;
2014-08-23 12:28:54 +02:00
/*
{
const Token * parent = tok - > astParent ( ) ;
while ( Token : : Match ( parent , " %var%|::|*|& " ) )
parent = parent - > astParent ( ) ;
if ( parent & & ! Token : : simpleMatch ( parent , " = " ) )
return ;
}
*/
2014-03-29 20:20:22 +01:00
// Taking address?
bool addressOf = false ;
{
const Token * tok2 = tok - > astParent ( ) ;
2014-05-03 18:12:06 +02:00
while ( Token : : Match ( tok2 , " %var%|.|::|[ " ) )
2014-03-29 20:20:22 +01:00
tok2 = tok2 - > astParent ( ) ;
2014-05-18 20:39:52 +02:00
addressOf = tok2 & & tok2 - > str ( ) = = " & " & & ! ( tok2 - > astOperand1 ( ) & & tok2 - > astOperand2 ( ) ) ;
2014-03-29 20:20:22 +01:00
}
// Look for errors first
for ( int warn = 0 ; warn = = 0 | | warn = = 1 ; + + warn ) {
// Negative index..
for ( const Token * tok2 = tok ; tok2 & & tok2 - > str ( ) = = " [ " ; tok2 = tok2 - > link ( ) - > next ( ) ) {
const Token * index = tok2 - > astOperand2 ( ) ;
if ( ! index )
continue ;
2014-04-02 06:49:28 +02:00
const ValueFlow : : Value * value = index - > getValueLE ( - 1LL , _settings ) ;
if ( value )
negativeIndexError ( index , * value ) ;
2014-03-29 20:20:22 +01:00
}
// Index out of bounds..
std : : vector < ValueFlow : : Value > indexes ;
unsigned int valuevarid = 0 ;
for ( const Token * tok2 = tok ; indexes . size ( ) < arrayInfo . num ( ) . size ( ) & & Token : : Match ( tok2 , " [ " ) ; tok2 = tok2 - > link ( ) - > next ( ) ) {
if ( ! tok2 - > astOperand2 ( ) ) {
indexes . clear ( ) ;
break ;
}
const ValueFlow : : Value * value = tok2 - > astOperand2 ( ) - > getMaxValue ( warn = = 1 ) ;
if ( ! value ) {
indexes . clear ( ) ;
break ;
}
if ( valuevarid = = 0U )
valuevarid = value - > varId ;
if ( value - > varId > 0 & & valuevarid ! = value - > varId ) {
indexes . clear ( ) ;
break ;
}
if ( value - > intvalue < 0 ) {
indexes . clear ( ) ;
break ;
}
indexes . push_back ( * value ) ;
}
if ( indexes . size ( ) = = arrayInfo . num ( ) . size ( ) ) {
// 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..
2014-07-07 21:25:30 +02:00
for ( std : : size_t i = 0 ; i < indexes . size ( ) ; + + i ) {
const std : : size_t ri = indexes . size ( ) - 1 - i ;
2014-03-29 20:20:22 +01:00
totalIndex + = indexes [ ri ] . intvalue * totalElements ;
totalElements * = arrayInfo . num ( ri ) ;
}
2014-07-20 11:44:25 +02:00
// totalElements <= 0 => Unknown size
if ( totalElements < = 0 )
2014-03-29 20:20:22 +01:00
continue ;
// taking address of 1 past end?
if ( addressOf & & totalIndex = = totalElements )
continue ;
// Is totalIndex in bounds?
if ( totalIndex > = totalElements ) {
arrayIndexOutOfBoundsError ( tok , arrayInfo , indexes ) ;
break ;
}
// Is any array index out of bounds?
else {
// check each index for overflow
2014-07-07 21:25:30 +02:00
for ( std : : size_t i = 0 ; i < indexes . size ( ) ; + + i ) {
2014-03-29 20:20:22 +01:00
if ( indexes [ i ] . intvalue > = arrayInfo . num ( i ) ) {
// The access is still within the memory range for the array
// so it may be intentional.
if ( _settings - > inconclusive ) {
arrayIndexOutOfBoundsError ( tok , arrayInfo , indexes ) ;
break ; // only warn about the first one
}
}
}
}
}
}
}
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 ) ;
2014-05-29 19:58:09 +02:00
const unsigned int declarationId = arrayInfo . declarationId ( ) ;
const bool isPortabilityEnabled = _settings - > isEnabled ( " portability " ) ;
const bool isWarningEnabled = _settings - > isEnabled ( " warning " ) ;
2012-08-12 12:13:07 +02:00
for ( const Token * const end = tok - > scope ( ) - > classEnd ; tok ! = end ; tok = tok - > next ( ) ) {
2014-06-26 10:35:54 +02:00
if ( tok - > varId ( ) = = declarationId ) {
if ( tok - > strAt ( 1 ) = = " [ " ) {
valueFlowCheckArrayIndex ( tok - > next ( ) , arrayInfo ) ;
}
2011-05-07 11:34:48 +02:00
2014-06-26 10:35:54 +02:00
// undefined behaviour: result of pointer arithmetic is out of bounds
else if ( isPortabilityEnabled & & Token : : Match ( tok - > previous ( ) , " = %varid% + %num% ; " , declarationId)) {
const MathLib : : bigint index = MathLib : : toLongNumber ( tok - > strAt ( 2 ) ) ;
if ( index < 0 | | index > arrayInfo . num ( 0 ) ) {
pointerOutOfBoundsError ( tok , " array " ) ;
}
}
2010-04-21 18:33:21 +02:00
}
2010-05-17 19:51:35 +02:00
2014-06-26 10:35:54 +02:00
else if ( ! tok - > scope ( ) - > isExecutable ( ) ) // No executable code outside of executable scope - continue to increase performance
2010-04-21 18:33:21 +02:00
continue ;
2014-06-26 10:35:54 +02:00
else if ( Token : : Match ( tok , " %var% ( " ) ) {
// Check function call..
2014-06-23 19:06:31 +02:00
checkFunctionCall ( tok , arrayInfo , std : : list < const Token * > ( ) ) ;
2010-04-21 19:27:28 +02:00
2014-07-29 11:59:45 +02:00
if ( _settings - > inconclusive & & Token : : Match ( tok , " strncpy|memcpy|memmove ( %varid% , %str% , %num% ) " , declarationId ) ) {
if ( Token : : getStrLength ( tok - > tokAt ( 4 ) ) > = ( unsigned int ) total_size ) {
const unsigned int num = ( unsigned int ) MathLib : : toLongNumber ( tok - > strAt ( 6 ) ) ;
if ( ( unsigned int ) total_size = = num )
2014-06-23 19:06:31 +02:00
bufferNotZeroTerminatedError ( tok , tok - > strAt ( 2 ) , tok - > str ( ) ) ;
}
2011-09-05 21:19:38 +02:00
}
2014-06-23 19:06:31 +02:00
if ( ( Token : : Match ( tok , " strncpy|strncat ( %varid% , " , declarationId ) & & Token : : Match ( tok - > linkAt ( 1 ) - > tokAt ( - 2 ) , " , %num% ) " ) ) ) {
const Token * param3 = tok - > linkAt ( 1 ) - > previous ( ) ;
2010-04-22 20:07:41 +02:00
2014-06-23 19:06:31 +02:00
// check for strncpy which is not terminated
if ( tok - > str ( ) = = " strncpy " ) {
// strncpy takes entire variable length as input size
unsigned int num = ( unsigned int ) MathLib : : toLongNumber ( param3 - > str ( ) ) ;
// this is currently 'inconclusive'. See TestBufferOverrun::terminateStrncpy3
if ( isWarningEnabled & & num > = total_size & & _settings - > inconclusive ) {
const Token * tok2 = tok - > next ( ) - > link ( ) - > next ( ) ;
for ( ; tok2 ; tok2 = tok2 - > next ( ) ) {
if ( tok2 - > varId ( ) = = tok - > tokAt ( 2 ) - > varId ( ) ) {
if ( ! Token : : Match ( tok2 , " %varid% [ %any% ] = 0 ; " , tok - > tokAt ( 2 ) - > varId ( ) ) ) {
terminateStrncpyError ( tok , tok - > strAt ( 2 ) ) ;
}
break ;
}
2010-04-21 18:33:21 +02:00
}
}
}
2014-06-23 19:06:31 +02:00
// Dangerous usage of strncat..
else if ( tok - > str ( ) = = " strncat " ) {
const MathLib : : bigint n = MathLib : : toLongNumber ( param3 - > str ( ) ) ;
if ( n > = total_size )
strncatUsageError ( tok ) ;
}
2010-04-21 18:33:21 +02:00
2014-06-23 19:06:31 +02:00
// Dangerous usage of strncpy + strncat..
if ( Token : : Match ( param3 - > tokAt ( 2 ) , " ; strncat ( %varid% , " , declarationId ) & & Token : : Match ( param3 - > linkAt ( 4 ) - > tokAt ( - 2 ) , " , %num% ) " ) ) {
const MathLib : : bigint n = MathLib : : toLongNumber ( param3 - > str ( ) ) + MathLib : : toLongNumber ( param3 - > linkAt ( 4 ) - > strAt ( - 1 ) ) ;
if ( n > total_size )
strncatUsageError ( param3 - > tokAt ( 3 ) ) ;
}
2010-04-21 18:33:21 +02:00
}
2014-06-23 19:06:31 +02:00
// Writing data into array..
2014-09-16 11:34:16 +02:00
if ( total_size > 0 & & Token : : Match ( tok , " strcpy|strcat ( %varid% , %str% ) " , declarationId ) ) {
2014-06-23 19:06:31 +02:00
const std : : size_t len = Token : : getStrLength ( tok - > tokAt ( 4 ) ) ;
2014-09-16 11:34:16 +02:00
if ( len > = ( unsigned int ) total_size ) {
2014-06-23 19:06:31 +02:00
bufferOverrunError ( tok , arrayInfo . varname ( ) ) ;
continue ;
}
2010-04-21 18:33:21 +02:00
}
2014-06-23 19:06:31 +02:00
// Detect few strcat() calls
if ( total_size > 0 & & Token : : Match ( tok , " strcat ( %varid% , %str% ) ; " , declarationId ) ) {
std : : size_t charactersAppend = 0 ;
const Token * tok2 = tok ;
2010-04-21 18:33:21 +02:00
2014-06-23 19:06:31 +02:00
while ( tok2 & & Token : : Match ( tok2 , " strcat ( %varid% , %str% ) ; " , declarationId ) ) {
charactersAppend + = Token : : getStrLength ( tok2 - > tokAt ( 4 ) ) ;
if ( charactersAppend > = ( unsigned int ) total_size ) {
bufferOverrunError ( tok2 , arrayInfo . varname ( ) ) ;
break ;
}
tok2 = tok2 - > tokAt ( 7 ) ;
2010-04-21 18:33:21 +02:00
}
}
2014-06-23 19:06:31 +02:00
if ( Token : : Match ( tok , " sprintf ( %varid% , %str% [,)] " , declarationId ) ) {
checkSprintfCall ( tok , total_size ) ;
}
2010-04-21 18:33:21 +02:00
2014-06-23 19:06:31 +02:00
// snprintf..
if ( total_size > 0 & & Token : : Match ( tok , " snprintf ( %varid% , %num% , " , declarationId ) ) {
const MathLib : : bigint n = MathLib : : toLongNumber ( tok - > strAt ( 4 ) ) ;
if ( n > total_size )
outOfBoundsError ( tok - > tokAt ( 4 ) , " snprintf size " , true , n , total_size ) ;
}
2010-12-26 21:23:28 +01:00
2014-06-23 19:06:31 +02:00
// readlink() / readlinkat() buffer usage
if ( _settings - > standards . posix & & Token : : Match ( tok , " readlink|readlinkat ( " ) )
checkReadlinkBufferUsage ( tok , scope_begin , declarationId , total_size ) ;
2011-10-14 19:45:51 +02:00
2014-06-23 19:06:31 +02:00
}
2010-04-21 18:33:21 +02:00
}
}
2013-03-14 06:34:12 +01:00
//---------------------------------------------------------------------------
2014-09-30 14:54:59 +02:00
// Checking member variables of structs.
2013-03-14 06:34:12 +01:00
//---------------------------------------------------------------------------
bool CheckBufferOverrun : : isArrayOfStruct ( const Token * tok , int & position )
{
if ( Token : : Match ( tok - > next ( ) , " %var% [ %num% ] " ) ) {
tok = tok - > tokAt ( 4 ) ;
int i = 1 ;
2013-03-18 19:50:24 +01:00
for ( ; ; ) {
2013-03-14 06:34:12 +01:00
if ( Token : : Match ( tok - > next ( ) , " [ %num% ] " ) ) {
i + + ;
tok = tok - > tokAt ( 4 ) ;
} else
break ;
}
2014-03-14 16:26:37 +01:00
if ( Token : : simpleMatch ( tok - > next ( ) , " ; " ) ) {
2013-03-14 06:34:12 +01:00
position = i ;
return true ;
}
}
return false ;
}
2014-01-02 10:46:19 +01:00
void CheckBufferOverrun : : checkReadlinkBufferUsage ( const Token * ftok , const Token * scope_begin , const unsigned int varid , const MathLib : : bigint total_size )
2011-10-24 21:22:04 +02:00
{
2014-03-27 16:06:30 +01:00
const std : : string & funcname = ftok - > str ( ) ;
2014-01-02 10:46:19 +01:00
const Token * bufParam = ftok - > tokAt ( 2 ) - > nextArgument ( ) ;
if ( funcname = = " readlinkat " )
2014-02-16 10:32:10 +01:00
bufParam = bufParam ? bufParam - > nextArgument ( ) : nullptr ;
2014-01-02 10:46:19 +01:00
if ( ! Token : : Match ( bufParam , " %varid% , %num% ) " , varid ) )
return ;
2011-10-24 21:22:04 +02:00
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 ;
2014-01-02 10:46:19 +01:00
// only writing a part of the buffer
if ( n < total_size )
return ;
2011-10-24 21:22:04 +02:00
// 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 ) {
2014-01-02 10:46:19 +01:00
bufferNotZeroTerminatedError ( ftok , bufParam - > str ( ) , funcname ) ;
2011-10-24 21:22:04 +02:00
} else if ( n = = total_size ) {
2014-01-02 10:46:19 +01:00
possibleReadlinkBufferOverrunError ( ftok , 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% ] " ) ) {
2014-05-27 16:21:13 +02:00
const std : : size_t strLen = tok - > str ( ) . size ( ) - 2 ; // Don't count enclosing quotes
const std : : size_t index = ( std : : size_t ) std : : atoi ( tok - > strAt ( 2 ) . c_str ( ) ) ;
if ( index > strLen )
2011-12-01 04:17:09 +01:00
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 + + ) {
2014-09-11 09:26:35 +02:00
const Variable * const var = symbolDatabase - > getVariableFromVarId ( i ) ;
2011-10-13 20:53:06 +02:00
if ( var & & var - > isArray ( ) & & var - > dimension ( 0 ) > 0 ) {
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 ( ) ;
2014-09-11 09:26:35 +02:00
const ArrayInfo arrayInfo ( var , _tokenizer , i ) ;
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
2014-02-16 11:47:52 +01:00
const Variable * var = nullptr ;
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 ;
2014-06-04 18:00:22 +02:00
_errorLogger - > reportProgress ( _tokenizer - > list . 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 ;
2014-02-01 22:38:29 +01:00
if ( size < 0 ) {
negativeMemoryAllocationSizeError ( tok - > next ( ) - > next ( ) ) ;
}
2012-03-17 21:55:08 +01:00
} 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% ; " ) & &
2014-04-12 23:26:13 +02:00
tok - > next ( ) - > variable ( ) & &
tok - > next ( ) - > variable ( ) - > isPointer ( ) ) {
2012-03-17 21:55:08 +01:00
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 ;
2014-02-01 22:38:29 +01:00
if ( size < 0 ) {
negativeMemoryAllocationSizeError ( tok - > next ( ) - > next ( ) ) ;
}
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
2014-02-01 22:38:29 +01:00
const unsigned int sizeOfType = _tokenizer - > sizeOfType ( var - > typeStartToken ( ) ) ;
if ( sizeOfType > 0 ) {
2013-02-06 06:39:58 +01:00
size / = static_cast < int > ( sizeOfType ) ;
2014-02-01 22:38:29 +01:00
}
if ( size < 0 ) {
negativeMemoryAllocationSizeError ( tok - > next ( ) - > next ( ) ) ;
}
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 ) ;
2014-12-01 16:22:56 +01:00
const MathLib : : bigint totalSize = size * static_cast < int > ( _tokenizer - > sizeOfType ( & sizeTok ) ) ;
if ( totalSize = = 0 )
2012-03-17 21:55:08 +01:00
continue ;
2009-01-31 20:29:27 +01:00
2012-03-17 21:55:08 +01:00
std : : vector < std : : string > v ;
2014-12-01 16:22:56 +01:00
ArrayInfo temp ( var - > declarationId ( ) , tok - > next ( ) - > str ( ) , totalSize / size , size ) ;
2012-03-17 21:55:08 +01:00
checkScope ( tok - > tokAt ( nextTok ) , v , temp ) ;
}
2009-01-31 20:29:27 +01:00
}
}
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
2014-09-30 14:54:59 +02:00
// Checking member variables of structs.
2009-01-31 20:29:27 +01:00
//---------------------------------------------------------------------------
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))
2014-01-31 06:19:36 +01:00
if ( size < 100 | | arrayInfo . element_size ( ) = = 0 )
2011-09-12 03:51:05 +02:00
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.
2014-07-14 10:20:00 +02:00
const Token * checkTok = nullptr ;
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 ( ) = = " ; " ) {
2014-07-14 10:20:00 +02:00
checkTok = tok3 ;
2011-09-12 03:51:05 +02:00
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 , " ) { " ) ) {
2014-07-14 10:20:00 +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 ;
2014-07-14 10:20:00 +02:00
if ( ! checkTok )
2011-09-12 03:51:05 +02:00
continue ;
// Check variable usage..
ArrayInfo temp = arrayInfo ;
2013-07-20 12:31:04 +02:00
temp . declarationId ( 0 ) ; // do variable lookup by variable and member names rather than varid
2011-09-12 03:51:05 +02:00
std : : string varnames ; // use class and member name for messages
2014-07-06 17:50:21 +02:00
for ( std : : size_t k = 0 ; k < varname . size ( ) ; + + k )
2012-10-13 11:16:48 +02:00
varnames + = ( k = = 0 ? " " : " . " ) + varname [ k ] ;
2013-03-14 06:34:12 +01:00
2011-09-12 03:51:05 +02:00
temp . varname ( varnames ) ;
2014-07-14 10:20:00 +02:00
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 ( ) ;
2014-07-06 08:41:39 +02:00
checkStringArgument ( ) ;
2010-06-02 07:41:07 +02:00
checkInsecureCmdLineArgs ( ) ;
2009-01-31 20:29:27 +01:00
}
//---------------------------------------------------------------------------
2014-06-26 17:36:20 +02:00
void CheckBufferOverrun : : bufferOverrun2 ( )
{
// singlepass checking using ast, symboldatabase and valueflow
for ( const Token * tok = _tokenizer - > tokens ( ) ; tok ; tok = tok - > next ( ) ) {
2014-08-04 08:25:10 +02:00
// Array index
if ( ! Token : : Match ( tok , " %var% [ " ) )
continue ;
// TODO: what to do about negative index..
const Token * index = tok - > next ( ) - > astOperand2 ( ) ;
if ( index & & index - > getValueLE ( - 1LL , _settings ) )
continue ;
// Set full varname..
std : : string varname ( tok - > str ( ) ) ;
if ( tok - > astParent ( ) & & tok - > astParent ( ) - > str ( ) = = " . " ) {
const Token * parent = tok - > astParent ( ) ;
while ( parent - > astParent ( ) & & parent - > astParent ( ) - > str ( ) = = " . " )
parent = parent - > astParent ( ) ;
varname = parent - > expressionString ( ) ;
}
const Token * const strtoken = tok - > getValueTokenMinStrSize ( ) ;
if ( strtoken ) {
ArrayInfo arrayInfo ( tok - > varId ( ) , varname , 1U , Token : : getStrSize ( strtoken ) ) ;
valueFlowCheckArrayIndex ( tok - > next ( ) , arrayInfo ) ;
}
else {
const Variable * const var = tok - > variable ( ) ;
2014-06-26 17:36:20 +02:00
if ( ! var | | var - > nameToken ( ) = = tok | | ! var - > isArray ( ) )
continue ;
// TODO: last array in struct..
if ( var - > dimension ( 0 ) < = 1 & & Token : : simpleMatch ( var - > nameToken ( ) - > linkAt ( 1 ) , " ] ; } " ) )
continue ;
ArrayInfo arrayInfo ( var , _tokenizer ) ;
2014-08-04 08:25:10 +02:00
arrayInfo . varname ( varname ) ;
2014-06-26 17:36:20 +02:00
valueFlowCheckArrayIndex ( tok - > next ( ) , arrayInfo ) ;
}
}
}
//---------------------------------------------------------------------------
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 ] = = ' \\ ' ) {
2014-08-12 15:44:20 +02:00
if ( i < input_string . length ( ) - 1 & & 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 ) ;
2014-07-07 21:25:30 +02:00
const 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
2014-02-28 16:18:36 +01:00
parameters . push_back ( nullptr ) ;
2011-10-23 17:47:48 +02:00
} else // Parameter is more complex than just a value or variable. Ignore it for now and skip to next token.
2014-02-28 16:18:36 +01:00
parameters . push_back ( nullptr ) ;
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
2014-07-06 08:41:39 +02:00
//---------------------------------------------------------------------------
// memcpy(temp, "hello world", 50);
//---------------------------------------------------------------------------
void CheckBufferOverrun : : checkStringArgument ( )
{
const SymbolDatabase * const symbolDatabase = _tokenizer - > getSymbolDatabase ( ) ;
std : : size_t functions = symbolDatabase - > functionScopes . size ( ) ;
for ( std : : size_t functionIndex = 0 ; functionIndex < functions ; + + functionIndex ) {
const Scope * const scope = symbolDatabase - > functionScopes [ functionIndex ] ;
for ( const Token * tok = scope - > classStart ; tok ! = scope - > classEnd ; tok = tok - > next ( ) ) {
if ( ! Token : : Match ( tok , " %var% ( " ) | | ! _settings - > library . hasminsize ( tok - > str ( ) ) )
continue ;
unsigned int argnr = 1 ;
for ( const Token * argtok = tok - > tokAt ( 2 ) ; argtok ; argtok = argtok - > nextArgument ( ) , argnr + + ) {
2014-08-04 08:25:10 +02:00
if ( ! Token : : Match ( argtok , " %var%|%str% ,|) " ) )
continue ;
const Token * strtoken = argtok - > getValueTokenMinStrSize ( ) ;
if ( ! strtoken )
2014-07-06 08:41:39 +02:00
continue ;
const std : : list < Library : : ArgumentChecks : : MinSize > * minsizes = _settings - > library . argminsizes ( tok - > str ( ) , argnr ) ;
if ( ! minsizes )
continue ;
2014-08-04 08:25:10 +02:00
if ( checkMinSizes ( * minsizes , tok , Token : : getStrSize ( strtoken ) , nullptr ) )
2014-07-08 07:08:51 +02:00
bufferOverrunError ( argtok ) ;
2014-07-06 08:41:39 +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 ) {
2014-09-02 16:10:51 +02:00
const Function * function = symbolDatabase - > functionScopes [ i ] - > function ;
if ( function ) {
const Token * tok = function - > 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
2014-09-29 15:38:33 +02:00
tok = symbolDatabase - > functionScopes [ i ] - > classStart ;
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 ) ;
2014-09-29 15:38:33 +02:00
tok = tok - > linkAt ( 1 ) ;
2011-12-09 22:28:10 +01:00
} else if ( Token : : Match ( tok , " sprintf ( %var% , %str% , %varid% [ " , varid ) & &
tok - > strAt ( 4 ) . find ( " %s " ) ! = std : : string : : npos ) {
cmdLineArgsError ( tok ) ;
2014-09-29 15:38:33 +02:00
tok = tok - > linkAt ( 1 ) ;
2011-12-09 22:28:10 +01:00
} else if ( Token : : Match ( tok , " sprintf ( %var% , %str% , * %varid% " , varid ) & &
tok - > strAt ( 4 ) . find ( " %s " ) ! = std : : string : : npos ) {
cmdLineArgsError ( tok ) ;
2014-09-29 15:38:33 +02:00
tok = tok - > linkAt ( 1 ) ;
2011-12-09 22:28:10 +01:00
}
}
}
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
}
2014-04-02 06:49:28 +02:00
void CheckBufferOverrun : : negativeIndexError ( const Token * tok , const ValueFlow : : Value & index )
{
std : : ostringstream ostr ;
ostr < < " Array index " < < index . intvalue < < " is out of bounds. " ;
if ( index . condition )
ostr < < " Otherwise there is useless condition at line " < < index . condition - > linenr ( ) < < " . " ;
reportError ( tok , index . condition ? Severity : : warning : Severity : : error , " negativeIndex " , ostr . str ( ) , index . inconclusive ) ;
}
2010-04-18 11:08:29 +02:00
CheckBufferOverrun : : ArrayInfo : : ArrayInfo ( )
2013-07-20 12:31:04 +02:00
: _element_size ( 0 ) , _declarationId ( 0 )
2010-04-10 21:12:00 +02:00
{
2010-04-18 11:08:29 +02:00
}
2013-07-20 12:31:04 +02:00
CheckBufferOverrun : : ArrayInfo : : ArrayInfo ( const Variable * var , const Tokenizer * tokenizer , const unsigned int forcedeclid )
: _varname ( var - > name ( ) ) , _declarationId ( ( forcedeclid = = 0U ) ? var - > declarationId ( ) : forcedeclid )
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-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 )
2013-07-20 12:31:04 +02:00
: _varname ( name ) , _element_size ( size1 ) , _declarationId ( 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 ;
2014-07-06 17:50:21 +02:00
for ( std : : size_t i = 0 ; i < _num . size ( ) ; + + i )
2011-06-23 02:35:58 +02:00
n * = _num [ i ] ;
2010-08-06 22:37:48 +02:00
if ( uvalue > n )
n = uvalue ;
2013-07-20 12:31:04 +02:00
return ArrayInfo ( _declarationId , _varname , _element_size , n - uvalue ) ;
2010-04-21 18:33:21 +02:00
}
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 ;
2013-11-10 05:05:31 +01:00
const SymbolDatabase * const symbolDatabase = _tokenizer - > getSymbolDatabase ( ) ;
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 ) {
2013-11-10 05:05:31 +01:00
const Scope * const scope = symbolDatabase - > functionScopes [ i ] ;
2012-10-13 11:16:48 +02:00
for ( const Token * tok = scope - > classStart ; tok & & tok ! = scope - > classEnd ; tok = tok - > next ( ) ) {
if ( Token : : Match ( tok , " %var% [ %var% ] " ) ) {
2014-05-03 09:30:30 +02:00
tok = tok - > tokAt ( 2 ) ;
unsigned int indexID = tok - > varId ( ) ;
if ( ! indexID )
continue ;
const std : : string & indexName ( tok - > str ( ) ) ;
2012-10-13 11:16:48 +02:00
// skip array index..
2014-05-03 09:30:30 +02:00
tok = tok - > tokAt ( 2 ) ;
2012-10-13 11:16:48 +02:00
while ( tok & & tok - > str ( ) = = " [ " )
tok = tok - > link ( ) - > next ( ) ;
// syntax error
if ( ! tok )
return ;
// skip comparison
2013-08-23 17:04:01 +02:00
if ( tok - > type ( ) = = Token : : eComparisonOp )
2012-10-13 11:16:48 +02:00
tok = tok - > tokAt ( 2 ) ;
2014-09-30 14:54:59 +02:00
// skip close parentheses
2014-05-03 09:30:30 +02:00
if ( tok - > str ( ) = = " ) " )
2013-08-23 17:04:01 +02:00
tok = tok - > next ( ) ;
2012-10-13 11:16:48 +02:00
// check if array index is ok
2013-08-23 17:04:01 +02:00
// statement can be closed in parentheses, so "(| " is using
2014-05-03 09:30:30 +02:00
if ( Token : : Match ( tok , " && (| %varid% <|<= " , indexID ) )
arrayIndexThenCheckError ( tok , indexName ) ;
2014-05-24 17:48:08 +02:00
else if ( Token : : Match ( tok , " && (| %any% >|>= %varid% !!+ " , indexID ) )
2012-10-13 11:16:48 +02:00
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 "
2013-10-23 08:03:37 +02:00
" is checked that is within limits. This can mean that the array might be accessed out of bounds. "
2012-07-09 11:11:05 +02:00
" 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:
2013-08-25 18:46:07 +02:00
// - http://pubs.opengroup.org/onlinepubs/9699919799/functions/write.html
2013-03-19 08:22:48 +01:00
// - 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 ;
2013-11-10 05:05:31 +01:00
const SymbolDatabase * const symbolDatabase = _tokenizer - > getSymbolDatabase ( ) ;
2013-03-19 08:22:48 +01:00
const std : : size_t functions = symbolDatabase - > functionScopes . size ( ) ;
for ( std : : size_t i = 0 ; i < functions ; + + i ) {
2013-11-10 05:05:31 +01:00
const Scope * const scope = symbolDatabase - > functionScopes [ i ] ;
2013-03-19 08:22:48 +01:00
for ( const Token * tok = scope - > classStart ; tok & & tok ! = scope - > classEnd ; tok = tok - > next ( ) ) {
2013-04-09 17:30:53 +02:00
if ( Token : : Match ( tok , " pwrite|write ( " ) & & Token : : Match ( tok - > tokAt ( 2 ) - > nextArgument ( ) , " %str% , %num% " ) ) {
2013-03-20 11:53:25 +01:00
const std : : string & functionName ( tok - > str ( ) ) ;
2013-04-09 17:30:53 +02:00
tok = tok - > tokAt ( 2 ) - > nextArgument ( ) ; // set tokenptr to %str% parameter
2013-08-25 18:46:07 +02:00
const std : : size_t stringLength = Token : : getStrLength ( tok ) + 1 ; // zero-terminated string!
2013-03-19 08:22:48 +01:00
tok = tok - > tokAt ( 2 ) ; // set tokenptr to %num% parameter
const MathLib : : bigint writeLength = MathLib : : toLongNumber ( tok - > str ( ) ) ;
2013-04-09 17:30:53 +02:00
if ( static_cast < std : : size_t > ( writeLength ) > stringLength )
writeOutsideBufferSizeError ( tok , stringLength , writeLength , functionName ) ;
2013-03-19 08:22:48 +01:00
}
}
}
}
void CheckBufferOverrun : : writeOutsideBufferSizeError ( const Token * tok , const std : : size_t stringLength , const MathLib : : bigint writeLength , const std : : string & strFunctionName )
{
reportError ( tok , Severity : : error , " writeOutsideBufferSize " ,
2013-09-21 18:48:48 +02:00
" Writing " + MathLib : : toString ( writeLength - stringLength ) + " bytes outside buffer size. \n "
" The number of bytes to write ( " + MathLib : : toString ( writeLength ) + " bytes) are bigger than the source buffer ( " + MathLib : : toString ( stringLength ) + " bytes). "
2013-03-19 08:22:48 +01:00
" Please check the second and the third parameter of the function ' " + strFunctionName + " '. " ) ;
}
2014-11-15 10:43:49 +01:00
2014-12-02 06:41:18 +01:00
Check : : FileInfo * CheckBufferOverrun : : getFileInfo ( const Tokenizer * tokenizer , const Settings * settings ) const
2014-11-15 10:43:49 +01:00
{
2014-12-02 06:41:18 +01:00
( void ) settings ;
2014-11-15 10:43:49 +01:00
MyFileInfo * fileInfo = new MyFileInfo ;
// Array usage..
const SymbolDatabase * const symbolDatabase = tokenizer - > getSymbolDatabase ( ) ;
const std : : size_t functions = symbolDatabase - > functionScopes . size ( ) ;
for ( std : : size_t i = 0 ; i < functions ; + + i ) {
const Scope * const scope = symbolDatabase - > functionScopes [ i ] ;
for ( const Token * tok = scope - > classStart ; tok & & tok ! = scope - > classEnd ; tok = tok - > next ( ) ) {
if ( Token : : Match ( tok , " %var% [ " ) & &
Token : : Match ( tok - > linkAt ( 1 ) , " ] !![ " ) & &
tok - > variable ( ) & &
tok - > variable ( ) - > isExtern ( ) & &
tok - > variable ( ) - > isGlobal ( ) & &
tok - > next ( ) - > astOperand2 ( ) ) {
const ValueFlow : : Value * value = tok - > next ( ) - > astOperand2 ( ) - > getMaxValue ( false ) ;
if ( value & & value - > intvalue > 0 ) {
struct MyFileInfo : : ArrayUsage arrayUsage ;
arrayUsage . index = value - > intvalue ;
arrayUsage . fileName = tokenizer - > list . file ( tok ) ;
arrayUsage . linenr = tok - > linenr ( ) ;
std : : map < std : : string , struct MyFileInfo : : ArrayUsage > : : iterator it = fileInfo - > arrayUsage . find ( tok - > str ( ) ) ;
if ( it = = fileInfo - > arrayUsage . end ( ) | | it - > second . index < arrayUsage . index )
fileInfo - > arrayUsage [ tok - > str ( ) ] = arrayUsage ;
}
}
}
}
// Arrays..
const std : : list < Variable > & varlist = symbolDatabase - > scopeList . front ( ) . varlist ;
for ( std : : list < Variable > : : const_iterator it = varlist . begin ( ) ; it ! = varlist . end ( ) ; + + it ) {
const Variable & var = * it ;
if ( ! var . isStatic ( ) & & var . isArray ( ) & & var . dimensions ( ) . size ( ) = = 1U )
fileInfo - > arraySize [ var . name ( ) ] = var . dimension ( 0U ) ;
}
return fileInfo ;
}
void CheckBufferOverrun : : analyseWholeProgram ( const std : : list < Check : : FileInfo * > & fileInfo , ErrorLogger & errorLogger )
{
// Merge all fileInfo
MyFileInfo all ;
for ( std : : list < Check : : FileInfo * > : : const_iterator it = fileInfo . begin ( ) ; it ! = fileInfo . end ( ) ; + + it ) {
const MyFileInfo * fi = dynamic_cast < MyFileInfo * > ( * it ) ;
if ( ! fi )
continue ;
// merge array usage
for ( std : : map < std : : string , struct MyFileInfo : : ArrayUsage > : : const_iterator it2 = fi - > arrayUsage . begin ( ) ; it2 ! = fi - > arrayUsage . end ( ) ; + + it2 ) {
std : : map < std : : string , struct MyFileInfo : : ArrayUsage > : : const_iterator allit = all . arrayUsage . find ( it2 - > first ) ;
if ( allit = = all . arrayUsage . end ( ) | | it2 - > second . index > allit - > second . index )
all . arrayUsage [ it2 - > first ] = it2 - > second ;
}
// merge array info
for ( std : : map < std : : string , MathLib : : bigint > : : const_iterator it2 = fi - > arraySize . begin ( ) ; it2 ! = fi - > arraySize . end ( ) ; + + it2 ) {
std : : map < std : : string , MathLib : : bigint > : : const_iterator allit = all . arraySize . find ( it2 - > first ) ;
if ( allit = = all . arraySize . end ( ) )
all . arraySize [ it2 - > first ] = it2 - > second ;
else
all . arraySize [ it2 - > first ] = - 1 ;
}
}
// Check buffer usage
for ( std : : map < std : : string , struct MyFileInfo : : ArrayUsage > : : const_iterator it = all . arrayUsage . begin ( ) ; it ! = all . arrayUsage . end ( ) ; + + it ) {
std : : map < std : : string , MathLib : : bigint > : : const_iterator sz = all . arraySize . find ( it - > first ) ;
if ( sz ! = all . arraySize . end ( ) & & sz - > second > 0 & & sz - > second < it - > second . index ) {
ErrorLogger : : ErrorMessage : : FileLocation fileLoc ;
fileLoc . setfile ( it - > second . fileName ) ;
fileLoc . line = it - > second . linenr ;
std : : list < ErrorLogger : : ErrorMessage : : FileLocation > locationList ;
locationList . push_back ( fileLoc ) ;
std : : ostringstream ostr ;
ostr < < " Array " < < it - > first < < ' [ ' < < sz - > second < < " ] accessed at index " < < it - > second . index < < " which is out of bounds " ;
const ErrorLogger : : ErrorMessage errmsg ( locationList ,
Severity : : error ,
ostr . str ( ) ,
" arrayIndexOutOfBounds " ,
false ) ;
errorLogger . reportErr ( errmsg ) ;
}
}
}