2009-01-31 20:29:27 +01:00
/*
* Cppcheck - A tool for static C / C + + code analysis
2019-02-09 07:24:06 +01:00
* Copyright ( C ) 2007 - 2019 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"
2017-05-27 04:33:47 +02:00
# include "astutils.h"
# include "library.h"
2009-08-02 14:26:26 +02:00
# include "mathlib.h"
2017-05-27 04:33:47 +02:00
# include "settings.h"
2011-06-23 04:44:11 +02:00
# include "symboldatabase.h"
2017-05-27 04:33:47 +02:00
# include "token.h"
# include "tokenize.h"
# include "tokenlist.h"
2017-05-23 14:58:43 +02:00
# include "utils.h"
2017-05-27 04:33:47 +02:00
# include "valueflow.h"
2009-02-11 06:16:10 +01:00
2017-05-27 04:33:47 +02:00
# include <tinyxml2.h>
2009-01-31 20:29:27 +01:00
# include <algorithm>
2011-12-23 22:31:48 +01:00
# include <cstdlib>
2017-05-27 04:33:47 +02:00
# include <sstream>
2015-07-04 09:42:42 +02:00
# include <stack>
2017-05-27 04:33:47 +02:00
# include <utility>
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
2016-01-25 20:01:48 +01:00
// CWE ids used:
2016-08-15 18:04:55 +02:00
static const CWE CWE131 ( 131U ) ; // Incorrect Calculation of Buffer Size
static const CWE CWE170 ( 170U ) ; // Improper Null Termination
2019-03-31 09:00:52 +02:00
static const CWE CWE_ARRAY_INDEX_THEN_CHECK ( 398U ) ; // Indicator of Poor Code Quality
2016-08-15 18:04:55 +02:00
static const CWE CWE682 ( 682U ) ; // Incorrect Calculation
static const CWE CWE758 ( 758U ) ; // Reliance on Undefined, Unspecified, or Implementation-Defined Behavior
2019-03-31 09:00:52 +02:00
static const CWE CWE_POINTER_ARITHMETIC_OVERFLOW ( 758U ) ; // Reliance on Undefined, Unspecified, or Implementation-Defined Behavior
2019-03-23 08:41:20 +01:00
static const CWE CWE_BUFFER_UNDERRUN ( 786U ) ; // Access of Memory Location Before Start of Buffer
static const CWE CWE_BUFFER_OVERRUN ( 788U ) ; // Access of Memory Location After End of Buffer
2016-01-25 20:01:48 +01:00
//---------------------------------------------------------------------------
2019-03-17 19:02:36 +01:00
static const ValueFlow : : Value * getBufferSizeValue ( const Token * tok )
2019-03-17 13:09:15 +01:00
{
for ( const ValueFlow : : Value & value : tok - > values ( ) ) {
2019-03-17 19:02:36 +01:00
if ( value . isBufferSizeValue ( ) )
return & value ;
2019-03-17 13:09:15 +01:00
}
2019-03-17 19:02:36 +01:00
return nullptr ;
2019-03-17 13:09:15 +01:00
}
2019-03-09 22:14:02 +01:00
static size_t getMinFormatStringOutputLength ( const std : : vector < const Token * > & parameters , unsigned int formatStringArgNr )
2009-09-25 18:23:44 +02:00
{
2019-03-09 22:14:02 +01:00
if ( formatStringArgNr = = 0 | | formatStringArgNr > parameters . size ( ) )
return 0 ;
if ( parameters [ formatStringArgNr - 1 ] - > tokType ( ) ! = Token : : eString )
return 0 ;
const std : : string & formatString = parameters [ formatStringArgNr - 1 ] - > str ( ) ;
2009-10-18 12:58:48 +02:00
bool percentCharFound = false ;
2019-03-09 22:14:02 +01:00
std : : size_t outputStringSize = 0 ;
2009-10-18 12:58:48 +02:00
bool handleNextParameter = false ;
2015-02-18 20:56:44 +01:00
std : : string digits_string ;
2009-10-18 12:58:48 +02:00
bool i_d_x_f_found = false ;
2010-12-31 09:30:56 +01:00
std : : size_t parameterLength = 0 ;
2019-03-09 22:14:02 +01:00
unsigned int inputArgNr = formatStringArgNr ;
for ( std : : string : : size_type i = 1 ; i + 1 < formatString . length ( ) ; + + i ) {
if ( formatString [ i ] = = ' \\ ' ) {
if ( i < formatString . length ( ) - 1 & & formatString [ i + 1 ] = = ' 0 ' )
2009-10-18 12:58:48 +02:00
break ;
2019-03-09 22:14:02 +01:00
+ + outputStringSize ;
2009-10-18 12:58:48 +02:00
+ + 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 ) {
2019-03-09 22:14:02 +01:00
switch ( formatString [ i ] ) {
2009-10-18 12:58:48 +02:00
case ' f ' :
case ' x ' :
case ' X ' :
case ' i ' :
i_d_x_f_found = true ;
2015-06-28 18:07:31 +02:00
handleNextParameter = true ;
2019-03-09 22:14:02 +01:00
parameterLength = 1 ; // TODO
2015-06-28 18:07:31 +02:00
break ;
2009-10-18 12:58:48 +02:00
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 ;
2019-03-09 22:14:02 +01:00
parameterLength = 1 ; // TODO
2009-10-19 23:48:29 +02:00
break ;
case ' d ' :
i_d_x_f_found = true ;
2019-03-09 22:14:02 +01:00
parameterLength = 1 ;
if ( inputArgNr < parameters . size ( ) & & parameters [ inputArgNr ] - > hasKnownIntValue ( ) )
parameterLength = MathLib : : toString ( parameters [ inputArgNr ] - > getKnownIntValue ( ) ) . length ( ) ;
2009-10-19 23:48:29 +02:00
2009-10-18 12:58:48 +02:00
handleNextParameter = true ;
break ;
case ' s ' :
2019-03-09 22:14:02 +01:00
parameterLength = 0 ;
if ( inputArgNr < parameters . size ( ) & & parameters [ inputArgNr ] - > tokType ( ) = = Token : : eString )
parameterLength = Token : : getStrLength ( parameters [ inputArgNr ] ) ;
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
2019-03-09 22:14:02 +01:00
if ( formatString [ i ] = = ' % ' )
2009-10-18 12:58:48 +02:00
percentCharFound = ! percentCharFound ;
2011-10-13 20:53:06 +02:00
else if ( percentCharFound ) {
2019-03-09 22:14:02 +01:00
digits_string . append ( 1 , formatString [ i ] ) ;
2009-09-25 18:23:44 +02:00
}
2010-04-02 07:30:58 +02:00
if ( ! percentCharFound )
2019-03-09 22:14:02 +01:00
outputStringSize + + ;
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
2019-03-09 22:14:02 +01:00
if ( formatString [ 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 )
2019-03-09 22:14:02 +01:00
outputStringSize + = parameterLength ;
2009-10-08 15:27:46 +02:00
else
2019-03-09 22:14:02 +01:00
outputStringSize + = tempDigits ;
2009-10-08 15:27:46 +02:00
parameterLength = 0 ;
2017-06-02 20:38:00 +02:00
digits_string . clear ( ) ;
2009-10-18 12:58:48 +02:00
i_d_x_f_found = false ;
percentCharFound = false ;
handleNextParameter = false ;
2019-03-09 22:14:02 +01:00
+ + inputArgNr ;
2009-09-25 18:23:44 +02:00
}
}
2019-03-09 22:14:02 +01:00
return outputStringSize ;
2009-09-25 18:23:44 +02:00
}
2010-05-26 10:56:34 +02:00
//---------------------------------------------------------------------------
2019-03-09 22:14:02 +01:00
2019-03-31 09:00:52 +02:00
static bool getDimensionsEtc ( const Token * const arrayToken , const Settings * settings , std : : vector < Dimension > * const dimensions , ErrorPath * const errorPath , bool * const mightBeLarger )
{
const Token * array = arrayToken ;
while ( Token : : Match ( array , " .|:: " ) )
array = array - > astOperand2 ( ) ;
if ( ! array - > variable ( ) )
return false ;
if ( array - > variable ( ) - > isArray ( ) & & ! array - > variable ( ) - > dimensions ( ) . empty ( ) ) {
* dimensions = array - > variable ( ) - > dimensions ( ) ;
if ( dimensions - > size ( ) > = 1 & & ( ( * dimensions ) [ 0 ] . num < = 1 | | ! ( * dimensions ) [ 0 ] . tok ) ) {
visitAstNodes ( arrayToken ,
[ & ] ( const Token * child ) {
if ( child - > originalName ( ) = = " -> " ) {
* mightBeLarger = true ;
return ChildrenToVisit : : none ;
}
return ChildrenToVisit : : op1_and_op2 ;
} ) ;
}
} else if ( const Token * stringLiteral = array - > getValueTokenMinStrSize ( ) ) {
Dimension dim ;
dim . tok = nullptr ;
dim . num = Token : : getStrSize ( stringLiteral ) ;
dim . known = array - > hasKnownValue ( ) ;
dimensions - > emplace_back ( dim ) ;
} else if ( array - > valueType ( ) & & array - > valueType ( ) - > pointer > = 1 & & array - > valueType ( ) - > isIntegral ( ) ) {
const ValueFlow : : Value * value = getBufferSizeValue ( array ) ;
if ( ! value )
return false ;
* errorPath = value - > errorPath ;
Dimension dim ;
dim . known = value - > isKnown ( ) ;
dim . tok = nullptr ;
dim . num = value - > intvalue / array - > valueType ( ) - > typeSize ( * settings ) ;
dimensions - > emplace_back ( dim ) ;
}
return ! dimensions - > empty ( ) ;
}
static std : : vector < const ValueFlow : : Value * > getOverrunIndexValues ( const Token * tok , const Token * arrayToken , const std : : vector < Dimension > & dimensions , const std : : vector < const Token * > & indexTokens )
{
const Token * array = arrayToken ;
while ( Token : : Match ( array , " .|:: " ) )
array = array - > astOperand2 ( ) ;
for ( int cond = 0 ; cond < 2 ; cond + + ) {
bool equal = false ;
bool overflow = false ;
bool allKnown = true ;
std : : vector < const ValueFlow : : Value * > indexValues ;
for ( size_t i = 0 ; i < dimensions . size ( ) & & i < indexTokens . size ( ) ; + + i ) {
const ValueFlow : : Value * value = indexTokens [ i ] - > getMaxValue ( cond = = 1 ) ;
indexValues . push_back ( value ) ;
if ( ! value )
continue ;
if ( ! value - > isKnown ( ) ) {
if ( ! allKnown )
continue ;
allKnown = false ;
}
if ( array - > variable ( ) - > isArray ( ) & & dimensions [ i ] . num = = 0 )
continue ;
if ( value - > intvalue = = dimensions [ i ] . num )
equal = true ;
else if ( value - > intvalue > dimensions [ i ] . num )
overflow = true ;
}
if ( equal & & tok - > str ( ) ! = " [ " )
continue ;
if ( ! overflow & & equal ) {
const Token * parent = tok ;
while ( Token : : simpleMatch ( parent , " [ " ) )
parent = parent - > astParent ( ) ;
if ( ! parent | | parent - > isUnaryOp ( " & " ) )
continue ;
}
if ( overflow | | equal )
return indexValues ;
}
return std : : vector < const ValueFlow : : Value * > ( ) ;
}
2019-03-09 22:14:02 +01:00
void CheckBufferOverrun : : arrayIndex ( )
2010-05-26 10:56:34 +02:00
{
2019-03-09 22:14:02 +01:00
for ( const Token * tok = mTokenizer - > tokens ( ) ; tok ; tok = tok - > next ( ) ) {
2019-03-12 06:46:38 +01:00
if ( tok - > str ( ) ! = " [ " )
2019-03-09 22:14:02 +01:00
continue ;
2019-03-12 06:46:38 +01:00
const Token * array = tok - > astOperand1 ( ) ;
while ( Token : : Match ( array , " .|:: " ) )
array = array - > astOperand2 ( ) ;
if ( ! array | | ! array - > variable ( ) | | array - > variable ( ) - > nameToken ( ) = = array )
continue ;
if ( ! array - > scope ( ) - > isExecutable ( ) ) {
2019-03-09 22:14:02 +01:00
// LHS in non-executable scope => This is just a definition
2019-03-12 06:46:38 +01:00
const Token * parent = tok ;
2019-03-09 22:14:02 +01:00
while ( parent & & ! Token : : simpleMatch ( parent - > astParent ( ) , " = " ) )
parent = parent - > astParent ( ) ;
2019-03-13 06:39:09 +01:00
if ( ! parent | | parent = = parent - > astParent ( ) - > astOperand1 ( ) )
2017-03-27 11:48:34 +02:00
continue ;
2019-03-09 22:14:02 +01:00
}
2019-03-12 06:46:38 +01:00
2019-03-19 13:16:22 +01:00
std : : vector < const Token * > indexTokens ;
for ( const Token * tok2 = tok ; tok2 & & tok2 - > str ( ) = = " [ " ; tok2 = tok2 - > link ( ) - > next ( ) ) {
if ( ! tok2 - > astOperand2 ( ) ) {
indexTokens . clear ( ) ;
break ;
}
indexTokens . emplace_back ( tok2 - > astOperand2 ( ) ) ;
}
if ( indexTokens . empty ( ) )
2019-03-09 22:14:02 +01:00
continue ;
2017-03-27 11:48:34 +02:00
2019-03-17 13:09:15 +01:00
std : : vector < Dimension > dimensions ;
2019-03-17 19:02:36 +01:00
ErrorPath errorPath ;
2019-03-19 13:17:27 +01:00
bool mightBeLarger = false ;
2019-03-31 09:00:52 +02:00
if ( ! getDimensionsEtc ( tok - > astOperand1 ( ) , mSettings , & dimensions , & errorPath , & mightBeLarger ) )
2019-03-17 13:09:15 +01:00
continue ;
2019-03-09 22:14:02 +01:00
// Positive index
2019-03-17 13:09:15 +01:00
if ( ! mightBeLarger ) { // TODO check arrays with dim 1 also
2019-03-31 09:00:52 +02:00
const std : : vector < const ValueFlow : : Value * > & indexValues = getOverrunIndexValues ( tok , tok - > astOperand1 ( ) , dimensions , indexTokens ) ;
if ( ! indexValues . empty ( ) )
arrayIndexError ( tok , dimensions , indexValues ) ;
2014-07-06 08:41:39 +02:00
}
2010-06-02 07:41:07 +02:00
2019-03-09 22:14:02 +01:00
// Negative index
2019-03-19 13:16:22 +01:00
bool neg = false ;
std : : vector < const ValueFlow : : Value * > negativeIndexes ;
for ( size_t i = 0 ; i < indexTokens . size ( ) ; + + i ) {
const ValueFlow : : Value * negativeValue = indexTokens [ i ] - > getValueLE ( - 1 , mSettings ) ;
negativeIndexes . emplace_back ( negativeValue ) ;
if ( negativeValue )
neg = true ;
}
if ( neg ) {
negativeIndexError ( tok , dimensions , negativeIndexes ) ;
2011-12-09 22:28:10 +01:00
}
2010-06-02 07:41:07 +02:00
}
}
2010-05-26 10:56:34 +02:00
2019-03-19 13:16:22 +01:00
static std : : string stringifyIndexes ( const std : : string & array , const std : : vector < const ValueFlow : : Value * > & indexValues )
{
if ( indexValues . size ( ) = = 1 )
return MathLib : : toString ( indexValues [ 0 ] - > intvalue ) ;
std : : ostringstream ret ;
ret < < array ;
for ( const ValueFlow : : Value * index : indexValues ) {
ret < < " [ " ;
if ( index )
ret < < index - > intvalue ;
else
ret < < " * " ;
ret < < " ] " ;
}
return ret . str ( ) ;
}
static std : : string arrayIndexMessage ( const Token * tok , const std : : vector < Dimension > & dimensions , const std : : vector < const ValueFlow : : Value * > & indexValues , const Token * condition )
2019-03-11 15:32:30 +01:00
{
2019-03-09 22:14:02 +01:00
std : : string array = tok - > astOperand1 ( ) - > expressionString ( ) ;
2019-03-17 13:09:15 +01:00
for ( const Dimension & dim : dimensions )
2019-03-09 22:14:02 +01:00
array + = " [ " + MathLib : : toString ( dim . num ) + " ] " ;
2010-05-26 10:56:34 +02:00
2017-05-16 19:08:47 +02:00
std : : ostringstream errmsg ;
2019-03-19 13:16:22 +01:00
if ( condition )
errmsg < < ValueFlow : : eitherTheConditionIsRedundant ( condition )
< < " or the array ' " + array + " ' is accessed at index " < < stringifyIndexes ( tok - > astOperand1 ( ) - > expressionString ( ) , indexValues ) < < " , which is out of bounds. " ;
2017-05-16 19:08:47 +02:00
else
2019-03-19 13:16:22 +01:00
errmsg < < " Array ' " < < array < < " ' accessed at index " < < stringifyIndexes ( tok - > astOperand1 ( ) - > expressionString ( ) , indexValues ) < < " , which is out of bounds. " ;
2014-04-02 06:49:28 +02:00
2019-03-09 22:14:02 +01:00
return errmsg . str ( ) ;
2010-04-18 11:08:29 +02:00
}
2019-03-19 13:16:22 +01:00
void CheckBufferOverrun : : arrayIndexError ( const Token * tok , const std : : vector < Dimension > & dimensions , const std : : vector < const ValueFlow : : Value * > & indexes )
2011-06-23 04:44:11 +02:00
{
2019-03-09 22:14:02 +01:00
if ( ! tok ) {
2019-03-23 08:41:20 +01:00
reportError ( tok , Severity : : error , " arrayIndexOutOfBounds " , " Array 'arr[16]' accessed at index 16, which is out of bounds. " , CWE_BUFFER_OVERRUN , false ) ;
reportError ( tok , Severity : : warning , " arrayIndexOutOfBoundsCond " , " Array 'arr[16]' accessed at index 16, which is out of bounds. " , CWE_BUFFER_OVERRUN , false ) ;
2019-03-09 22:14:02 +01:00
return ;
2015-01-30 20:27:48 +01:00
}
2011-06-23 04:44:11 +02:00
2019-03-19 13:16:22 +01:00
const Token * condition = nullptr ;
const ValueFlow : : Value * index = nullptr ;
for ( const ValueFlow : : Value * indexValue : indexes ) {
if ( ! indexValue )
continue ;
if ( ! indexValue - > errorSeverity ( ) & & ! mSettings - > isEnabled ( Settings : : WARNING ) )
return ;
if ( indexValue - > condition )
condition = indexValue - > condition ;
if ( ! index | | ! indexValue - > errorPath . empty ( ) )
index = indexValue ;
}
2019-03-09 22:14:02 +01:00
reportError ( getErrorPath ( tok , index , " Array index out of bounds " ) ,
index - > errorSeverity ( ) ? Severity : : error : Severity : : warning ,
2019-03-11 21:39:39 +01:00
index - > condition ? " arrayIndexOutOfBoundsCond " : " arrayIndexOutOfBounds " ,
2019-03-19 13:16:22 +01:00
arrayIndexMessage ( tok , dimensions , indexes , condition ) ,
2019-03-23 08:41:20 +01:00
CWE_BUFFER_OVERRUN ,
2019-03-09 22:14:02 +01:00
index - > isInconclusive ( ) ) ;
2015-11-08 12:39:08 +01:00
}
2019-03-19 13:16:22 +01:00
void CheckBufferOverrun : : negativeIndexError ( const Token * tok , const std : : vector < Dimension > & dimensions , const std : : vector < const ValueFlow : : Value * > & indexes )
2015-11-08 12:39:08 +01:00
{
2019-03-19 13:16:22 +01:00
if ( ! tok ) {
2019-03-23 08:41:20 +01:00
reportError ( tok , Severity : : error , " negativeIndex " , " Negative array index " , CWE_BUFFER_UNDERRUN , false ) ;
2019-03-09 22:14:02 +01:00
return ;
2015-11-08 12:39:08 +01:00
}
2011-08-04 11:15:14 +02:00
2019-03-19 13:16:22 +01:00
const Token * condition = nullptr ;
const ValueFlow : : Value * negativeValue = nullptr ;
for ( const ValueFlow : : Value * indexValue : indexes ) {
if ( ! indexValue )
continue ;
if ( ! indexValue - > errorSeverity ( ) & & ! mSettings - > isEnabled ( Settings : : WARNING ) )
return ;
if ( indexValue - > condition )
condition = indexValue - > condition ;
if ( ! negativeValue | | ! indexValue - > errorPath . empty ( ) )
negativeValue = indexValue ;
}
2011-08-04 11:15:14 +02:00
2019-03-09 22:14:02 +01:00
reportError ( getErrorPath ( tok , negativeValue , " Negative array index " ) ,
negativeValue - > errorSeverity ( ) ? Severity : : error : Severity : : warning ,
" negativeIndex " ,
2019-03-19 13:16:22 +01:00
arrayIndexMessage ( tok , dimensions , indexes , condition ) ,
2019-03-23 08:41:20 +01:00
CWE_BUFFER_UNDERRUN ,
2019-03-09 22:14:02 +01:00
negativeValue - > isInconclusive ( ) ) ;
2011-08-04 11:15:14 +02:00
}
2019-03-11 19:20:06 +01:00
//---------------------------------------------------------------------------
2013-03-19 08:22:48 +01:00
2019-03-31 09:00:52 +02:00
void CheckBufferOverrun : : pointerArithmetic ( )
{
if ( ! mSettings - > isEnabled ( Settings : : PORTABILITY ) )
return ;
for ( const Token * tok = mTokenizer - > tokens ( ) ; tok ; tok = tok - > next ( ) ) {
if ( ! Token : : Match ( tok , " +|- " ) )
continue ;
if ( ! tok - > valueType ( ) | | tok - > valueType ( ) - > pointer = = 0 )
continue ;
if ( ! tok - > astOperand1 ( ) | | ! tok - > astOperand2 ( ) )
continue ;
if ( ! tok - > astOperand1 ( ) - > valueType ( ) | | ! tok - > astOperand2 ( ) - > valueType ( ) )
continue ;
const Token * arrayToken , * indexToken ;
if ( tok - > astOperand1 ( ) - > valueType ( ) - > pointer > 0 ) {
arrayToken = tok - > astOperand1 ( ) ;
indexToken = tok - > astOperand2 ( ) ;
} else {
arrayToken = tok - > astOperand2 ( ) ;
indexToken = tok - > astOperand1 ( ) ;
}
if ( ! indexToken | | ! indexToken - > valueType ( ) | | indexToken - > valueType ( ) - > pointer > 0 | | ! indexToken - > valueType ( ) - > isIntegral ( ) )
continue ;
std : : vector < Dimension > dimensions ;
ErrorPath errorPath ;
bool mightBeLarger = false ;
if ( ! getDimensionsEtc ( arrayToken , mSettings , & dimensions , & errorPath , & mightBeLarger ) )
continue ;
if ( tok - > str ( ) = = " + " ) {
// Positive index
if ( ! mightBeLarger ) { // TODO check arrays with dim 1 also
const std : : vector < const Token * > indexTokens { indexToken } ;
const std : : vector < const ValueFlow : : Value * > & indexValues = getOverrunIndexValues ( tok , arrayToken , dimensions , indexTokens ) ;
if ( ! indexValues . empty ( ) )
pointerArithmeticError ( tok , indexToken , indexValues . front ( ) ) ;
}
if ( const ValueFlow : : Value * neg = indexToken - > getValueLE ( - 1 , mSettings ) )
pointerArithmeticError ( tok , indexToken , neg ) ;
} else if ( tok - > str ( ) = = " - " ) {
// TODO
}
}
}
void CheckBufferOverrun : : pointerArithmeticError ( const Token * tok , const Token * indexToken , const ValueFlow : : Value * indexValue )
{
if ( ! tok ) {
reportError ( tok , Severity : : portability , " pointerOutOfBounds " , " Pointer arithmetic overflow. " , CWE_POINTER_ARITHMETIC_OVERFLOW , false ) ;
reportError ( tok , Severity : : portability , " pointerOutOfBoundsCond " , " Pointer arithmetic overflow. " , CWE_POINTER_ARITHMETIC_OVERFLOW , false ) ;
return ;
}
std : : string errmsg ;
if ( indexValue - > condition )
errmsg = " Undefined behaviour, when ' " + indexToken - > expressionString ( ) + " ' is " + MathLib : : toString ( indexValue - > intvalue ) + " the pointer arithmetic ' " + tok - > expressionString ( ) + " ' is out of bounds. " ;
else
errmsg = " Undefined behaviour, pointer arithmetic ' " + tok - > expressionString ( ) + " ' is out of bounds. " ;
reportError ( getErrorPath ( tok , indexValue , " Pointer arithmetic overflow " ) ,
Severity : : portability ,
indexValue - > condition ? " pointerOutOfBoundsCond " : " pointerOutOfBounds " ,
errmsg ,
CWE_POINTER_ARITHMETIC_OVERFLOW ,
indexValue - > isInconclusive ( ) ) ;
}
//---------------------------------------------------------------------------
2019-03-17 20:12:02 +01:00
ValueFlow : : Value CheckBufferOverrun : : getBufferSize ( const Token * bufTok ) const
2016-10-29 12:18:11 +02:00
{
2019-03-09 22:14:02 +01:00
if ( ! bufTok - > valueType ( ) )
2019-03-17 20:12:02 +01:00
return ValueFlow : : Value ( - 1 ) ;
2019-03-09 22:14:02 +01:00
const Variable * var = bufTok - > variable ( ) ;
2019-03-17 19:02:36 +01:00
if ( ! var | | var - > dimensions ( ) . empty ( ) ) {
const ValueFlow : : Value * value = getBufferSizeValue ( bufTok ) ;
if ( value )
2019-03-17 20:12:02 +01:00
return * value ;
2019-03-17 19:02:36 +01:00
}
2019-03-09 22:14:02 +01:00
if ( ! var )
2019-03-17 20:12:02 +01:00
return ValueFlow : : Value ( - 1 ) ;
2019-03-17 13:40:56 +01:00
MathLib : : bigint dim = 1 ;
2019-03-17 19:02:36 +01:00
for ( const Dimension & d : var - > dimensions ( ) )
2019-03-17 13:40:56 +01:00
dim * = d . num ;
2019-03-17 19:02:36 +01:00
2019-03-17 20:12:02 +01:00
ValueFlow : : Value v ;
v . setKnown ( ) ;
v . valueType = ValueFlow : : Value : : ValueType : : BUFFER_SIZE ;
2019-03-17 13:40:56 +01:00
if ( var - > isPointerArray ( ) )
2019-03-17 20:12:02 +01:00
v . intvalue = dim * mSettings - > sizeof_pointer ;
2019-03-19 21:06:37 +01:00
else if ( var - > isPointer ( ) )
return ValueFlow : : Value ( - 1 ) ;
2019-03-17 20:12:02 +01:00
else {
const MathLib : : bigint typeSize = bufTok - > valueType ( ) - > typeSize ( * mSettings ) ;
v . intvalue = dim * typeSize ;
}
2019-03-17 19:02:36 +01:00
2019-03-17 20:12:02 +01:00
return v ;
2016-10-29 12:18:11 +02:00
}
2019-03-11 19:20:06 +01:00
//---------------------------------------------------------------------------
2016-10-29 12:18:11 +02:00
2019-03-09 22:14:02 +01:00
static bool checkBufferSize ( const Token * ftok , const Library : : ArgumentChecks : : MinSize & minsize , const std : : vector < const Token * > & args , const MathLib : : bigint bufferSize , const Settings * settings )
2014-11-15 10:43:49 +01:00
{
2019-03-09 22:14:02 +01:00
const Token * const arg = ( minsize . arg > 0 & & minsize . arg - 1 < args . size ( ) ) ? args [ minsize . arg - 1 ] : nullptr ;
const Token * const arg2 = ( minsize . arg2 > 0 & & minsize . arg2 - 1 < args . size ( ) ) ? args [ minsize . arg2 - 1 ] : nullptr ;
2014-12-02 06:41:18 +01:00
2019-03-09 22:14:02 +01:00
switch ( minsize . type ) {
case Library : : ArgumentChecks : : MinSize : : Type : : STRLEN :
if ( settings - > library . isargformatstr ( ftok , minsize . arg ) ) {
return getMinFormatStringOutputLength ( args , minsize . arg ) < bufferSize ;
} else if ( arg ) {
const Token * strtoken = arg - > getValueTokenMaxStrLength ( ) ;
if ( strtoken )
return Token : : getStrLength ( strtoken ) < bufferSize ;
2014-11-15 10:43:49 +01:00
}
2019-03-09 22:14:02 +01:00
break ;
case Library : : ArgumentChecks : : MinSize : : Type : : ARGVALUE :
if ( arg & & arg - > hasKnownIntValue ( ) )
return arg - > getKnownIntValue ( ) < = bufferSize ;
break ;
case Library : : ArgumentChecks : : MinSize : : Type : : SIZEOF :
// TODO
break ;
case Library : : ArgumentChecks : : MinSize : : Type : : MUL :
if ( arg & & arg2 & & arg - > hasKnownIntValue ( ) & & arg2 - > hasKnownIntValue ( ) )
return ( arg - > getKnownIntValue ( ) * arg2 - > getKnownIntValue ( ) ) < = bufferSize ;
break ;
2019-03-17 20:34:49 +01:00
case Library : : ArgumentChecks : : MinSize : : Type : : VALUE :
return minsize . value < = bufferSize ;
2019-03-09 22:14:02 +01:00
case Library : : ArgumentChecks : : MinSize : : Type : : NONE :
break ;
} ;
return true ;
2014-11-15 10:43:49 +01:00
}
2017-03-30 10:14:17 +02:00
2019-03-09 22:14:02 +01:00
void CheckBufferOverrun : : bufferOverflow ( )
{
const SymbolDatabase * symbolDatabase = mTokenizer - > getSymbolDatabase ( ) ;
for ( const Scope * scope : symbolDatabase - > functionScopes ) {
for ( const Token * tok = scope - > bodyStart ; tok ! = scope - > bodyEnd ; tok = tok - > next ( ) ) {
if ( ! Token : : Match ( tok , " %name% ( " ) | | Token : : simpleMatch ( tok , " ) { " ) )
2017-03-30 10:14:17 +02:00
continue ;
2019-03-09 22:14:02 +01:00
if ( ! mSettings - > library . hasminsize ( tok ) )
2017-03-30 10:14:17 +02:00
continue ;
2019-03-09 22:14:02 +01:00
const std : : vector < const Token * > args = getArguments ( tok ) ;
for ( unsigned int argnr = 0 ; argnr < args . size ( ) ; + + argnr ) {
if ( ! args [ argnr ] - > valueType ( ) | | args [ argnr ] - > valueType ( ) - > pointer = = 0 )
continue ;
const std : : vector < Library : : ArgumentChecks : : MinSize > * minsizes = mSettings - > library . argminsizes ( tok , argnr + 1 ) ;
if ( ! minsizes | | minsizes - > empty ( ) )
continue ;
// Get buffer size..
const Token * argtok = args [ argnr ] ;
while ( argtok & & argtok - > isCast ( ) )
argtok = argtok - > astOperand2 ( ) ? argtok - > astOperand2 ( ) : argtok - > astOperand1 ( ) ;
while ( Token : : Match ( argtok , " .|:: " ) )
argtok = argtok - > astOperand2 ( ) ;
if ( ! argtok | | ! argtok - > variable ( ) )
continue ;
// TODO: strcpy(buf+10, "hello");
2019-03-17 20:12:02 +01:00
const ValueFlow : : Value bufferSize = getBufferSize ( argtok ) ;
if ( bufferSize . intvalue < = 1 )
2019-03-09 22:14:02 +01:00
continue ;
bool error = true ;
for ( const Library : : ArgumentChecks : : MinSize & minsize : * minsizes ) {
2019-03-17 20:12:02 +01:00
if ( checkBufferSize ( tok , minsize , args , bufferSize . intvalue , mSettings ) ) {
2019-03-09 22:14:02 +01:00
error = false ;
break ;
}
}
if ( error )
2019-03-17 20:12:02 +01:00
bufferOverflowError ( args [ argnr ] , & bufferSize ) ;
2019-03-09 22:14:02 +01:00
}
2014-11-15 10:43:49 +01:00
}
}
}
2016-07-08 20:53:08 +02:00
2019-03-17 20:12:02 +01:00
void CheckBufferOverrun : : bufferOverflowError ( const Token * tok , const ValueFlow : : Value * value )
2016-07-08 20:53:08 +02:00
{
2019-03-23 08:41:20 +01:00
reportError ( getErrorPath ( tok , value , " Buffer overrun " ) , Severity : : error , " bufferAccessOutOfBounds " , " Buffer is accessed out of bounds: " + ( tok ? tok - > expressionString ( ) : " buf " ) , CWE_BUFFER_OVERRUN , false ) ;
2016-07-08 20:53:08 +02:00
}
2019-03-11 19:20:06 +01:00
//---------------------------------------------------------------------------
void CheckBufferOverrun : : arrayIndexThenCheck ( )
{
if ( ! mSettings - > isEnabled ( Settings : : PORTABILITY ) )
return ;
const SymbolDatabase * symbolDatabase = mTokenizer - > getSymbolDatabase ( ) ;
for ( const Scope * const scope : symbolDatabase - > functionScopes ) {
for ( const Token * tok = scope - > bodyStart ; tok & & tok ! = scope - > bodyEnd ; tok = tok - > next ( ) ) {
if ( Token : : simpleMatch ( tok , " sizeof ( " ) ) {
tok = tok - > linkAt ( 1 ) ;
continue ;
}
if ( Token : : Match ( tok , " %name% [ %var% ] " ) ) {
tok = tok - > next ( ) ;
const unsigned int indexID = tok - > next ( ) - > varId ( ) ;
const std : : string & indexName ( tok - > strAt ( 1 ) ) ;
// Iterate AST upwards
const Token * tok2 = tok ;
const Token * tok3 = tok2 ;
while ( tok2 - > astParent ( ) & & tok2 - > tokType ( ) ! = Token : : eLogicalOp ) {
tok3 = tok2 ;
tok2 = tok2 - > astParent ( ) ;
}
// Ensure that we ended at a logical operator and that we came from its left side
if ( tok2 - > tokType ( ) ! = Token : : eLogicalOp | | tok2 - > astOperand1 ( ) ! = tok3 )
continue ;
// check if array index is ok
// statement can be closed in parentheses, so "(| " is using
if ( Token : : Match ( tok2 , " && (| %varid% <|<= " , indexID ) )
arrayIndexThenCheckError ( tok , indexName ) ;
else if ( Token : : Match ( tok2 , " && (| %any% >|>= %varid% !!+ " , indexID ) )
arrayIndexThenCheckError ( tok , indexName ) ;
}
}
}
}
void CheckBufferOverrun : : arrayIndexThenCheckError ( const Token * tok , const std : : string & indexName )
{
reportError ( tok , Severity : : style , " arrayIndexThenCheck " ,
" $symbol: " + indexName + " \n "
" Array index '$symbol' is used before limits check. \n "
" Defensive programming: The variable '$symbol' is used as an array index before it "
" is checked that is within limits. This can mean that the array might be accessed out of bounds. "
" Reorder conditions such as '(a[i] && i < 10)' to '(i < 10 && a[i])'. That way the array will "
2019-03-31 09:00:52 +02:00
" not be accessed if the index is out of limits. " , CWE_ARRAY_INDEX_THEN_CHECK , false ) ;
2019-03-11 19:20:06 +01:00
}
2019-03-12 21:15:26 +01:00
//---------------------------------------------------------------------------
void CheckBufferOverrun : : stringNotZeroTerminated ( )
{
// this is currently 'inconclusive'. See TestBufferOverrun::terminateStrncpy3
if ( ! mSettings - > isEnabled ( Settings : : WARNING ) | | ! mSettings - > inconclusive )
return ;
const SymbolDatabase * symbolDatabase = mTokenizer - > getSymbolDatabase ( ) ;
for ( const Scope * const scope : symbolDatabase - > functionScopes ) {
for ( const Token * tok = scope - > bodyStart ; tok & & tok ! = scope - > bodyEnd ; tok = tok - > next ( ) ) {
2019-03-13 06:45:01 +01:00
if ( ! Token : : simpleMatch ( tok , " strncpy ( " ) )
2019-03-12 21:15:26 +01:00
continue ;
const std : : vector < const Token * > args = getArguments ( tok ) ;
if ( args . size ( ) ! = 3 )
continue ;
const Token * sizeToken = args [ 2 ] ;
if ( ! sizeToken - > hasKnownIntValue ( ) )
continue ;
2019-03-17 20:12:02 +01:00
const ValueFlow : : Value & bufferSize = getBufferSize ( args [ 0 ] ) ;
if ( bufferSize . intvalue < 0 | | sizeToken - > getKnownIntValue ( ) < bufferSize . intvalue )
2019-03-12 21:15:26 +01:00
continue ;
const Token * srcValue = args [ 1 ] - > getValueTokenMaxStrLength ( ) ;
if ( srcValue & & Token : : getStrLength ( srcValue ) < sizeToken - > getKnownIntValue ( ) )
continue ;
// Is the buffer zero terminated after the call?
bool isZeroTerminated = false ;
for ( const Token * tok2 = tok - > next ( ) - > link ( ) ; tok2 ! = scope - > bodyEnd ; tok2 = tok2 - > next ( ) ) {
if ( ! Token : : Match ( tok2 , " ] = " ) )
continue ;
const Token * rhs = tok2 - > next ( ) - > astOperand2 ( ) ;
if ( ! rhs | | ! rhs - > hasKnownIntValue ( ) | | rhs - > getKnownIntValue ( ) ! = 0 )
continue ;
if ( isSameExpression ( mTokenizer - > isCPP ( ) , false , args [ 0 ] , tok2 - > link ( ) - > astOperand1 ( ) , mSettings - > library , false , false ) )
isZeroTerminated = true ;
}
if ( isZeroTerminated )
continue ;
// TODO: Locate unsafe string usage..
2019-03-13 06:45:01 +01:00
terminateStrncpyError ( tok , args [ 0 ] - > expressionString ( ) ) ;
2019-03-12 21:15:26 +01:00
}
}
}
void CheckBufferOverrun : : terminateStrncpyError ( const Token * tok , const std : : string & varname )
{
const std : : string shortMessage = " The buffer '$symbol' may not be null-terminated after the call to strncpy(). " ;
reportError ( tok , Severity : : warning , " terminateStrncpy " ,
" $symbol: " + varname + ' \n ' +
shortMessage + ' \n ' +
shortMessage + ' ' +
" 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 "
" assumes buffer is null-terminated. " , CWE170 , true ) ;
}
void CheckBufferOverrun : : bufferNotZeroTerminatedError ( const Token * tok , const std : : string & varname , const std : : string & function )
{
const std : : string errmsg = " $symbol: " + varname + ' \n ' +
" $symbol: " + function + ' \n ' +
" 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. " ;
reportError ( tok , Severity : : warning , " bufferNotZeroTerminated " , errmsg , CWE170 , true ) ;
}
2019-03-23 08:36:10 +01:00
//---------------------------------------------------------------------------
// CTU..
//---------------------------------------------------------------------------
2019-03-23 19:02:05 +01:00
std : : string CheckBufferOverrun : : MyFileInfo : : toString ( ) const
2019-03-23 08:36:10 +01:00
{
return CTU : : toString ( unsafeUsage ) ;
}
bool CheckBufferOverrun : : isCtuUnsafeBufferUsage ( const Check * check , const Token * argtok , MathLib : : bigint * offset )
{
const CheckBufferOverrun * c = dynamic_cast < const CheckBufferOverrun * > ( check ) ;
if ( ! c )
return false ;
if ( ! argtok - > valueType ( ) )
return false ;
if ( ! Token : : Match ( argtok , " %name% [ " ) | | argtok - > astParent ( ) ! = argtok - > next ( ) | | Token : : simpleMatch ( argtok - > linkAt ( 1 ) , " ] [ " ) )
return false ;
if ( ! argtok - > next ( ) - > astOperand2 ( ) )
return false ;
if ( ! argtok - > next ( ) - > astOperand2 ( ) - > hasKnownIntValue ( ) )
return false ;
if ( ! offset )
return false ;
* offset = argtok - > next ( ) - > astOperand2 ( ) - > getKnownIntValue ( ) * argtok - > valueType ( ) - > typeSize ( * c - > mSettings ) ;
return true ;
}
/** @brief Parse current TU and extract file info */
2019-03-23 19:02:05 +01:00
Check : : FileInfo * CheckBufferOverrun : : getFileInfo ( const Tokenizer * tokenizer , const Settings * settings ) const
2019-03-23 08:36:10 +01:00
{
2019-03-23 15:26:13 +01:00
CheckBufferOverrun checkBufferOverrun ( tokenizer , settings , nullptr ) ;
const std : : list < CTU : : FileInfo : : UnsafeUsage > & unsafeUsage = CTU : : getUnsafeUsage ( tokenizer , settings , & checkBufferOverrun , isCtuUnsafeBufferUsage ) ;
2019-03-23 08:36:10 +01:00
if ( unsafeUsage . empty ( ) )
return nullptr ;
MyFileInfo * fileInfo = new MyFileInfo ;
fileInfo - > unsafeUsage = unsafeUsage ;
return fileInfo ;
}
2019-03-23 19:02:05 +01:00
Check : : FileInfo * CheckBufferOverrun : : loadFileInfoFromXml ( const tinyxml2 : : XMLElement * xmlElement ) const
2019-03-23 08:36:10 +01:00
{
const std : : list < CTU : : FileInfo : : UnsafeUsage > & unsafeUsage = CTU : : loadUnsafeUsageListFromXml ( xmlElement ) ;
if ( unsafeUsage . empty ( ) )
return nullptr ;
MyFileInfo * fileInfo = new MyFileInfo ;
fileInfo - > unsafeUsage = unsafeUsage ;
return fileInfo ;
}
/** @brief Analyse all file infos for all TU */
2019-03-23 19:03:57 +01:00
bool CheckBufferOverrun : : analyseWholeProgram ( const CTU : : FileInfo * ctu , const std : : list < Check : : FileInfo * > & fileInfo , const Settings & settings , ErrorLogger & errorLogger )
{
2019-03-23 08:36:10 +01:00
if ( ! ctu )
return false ;
bool foundErrors = false ;
( void ) settings ; // This argument is unused
const std : : map < std : : string , std : : list < const CTU : : FileInfo : : CallBase * > > callsMap = ctu - > getCallsMap ( ) ;
2019-03-23 19:03:57 +01:00
for ( Check : : FileInfo * fi1 : fileInfo ) {
2019-03-23 08:36:10 +01:00
const MyFileInfo * fi = dynamic_cast < MyFileInfo * > ( fi1 ) ;
if ( ! fi )
continue ;
for ( const CTU : : FileInfo : : UnsafeUsage & unsafeUsage : fi - > unsafeUsage ) {
const CTU : : FileInfo : : FunctionCall * functionCall = nullptr ;
const std : : list < ErrorLogger : : ErrorMessage : : FileLocation > & locationList =
2019-03-23 19:03:57 +01:00
ctu - > getErrorPath ( CTU : : FileInfo : : InvalidValueType : : bufferOverflow ,
unsafeUsage ,
callsMap ,
" Using argument ARG " ,
& functionCall ,
false ) ;
2019-03-23 08:36:10 +01:00
if ( locationList . empty ( ) )
continue ;
if ( unsafeUsage . value > 0 ) {
const ErrorLogger : : ErrorMessage errmsg ( locationList ,
emptyString ,
Severity : : error ,
" Buffer access out of bounds; ' " + unsafeUsage . myArgumentName + " ' buffer size is " + MathLib : : toString ( functionCall - > callArgValue ) + " and it is accessed at offset " + MathLib : : toString ( unsafeUsage . value ) + " . " ,
" ctubufferoverrun " ,
2019-03-23 08:41:20 +01:00
CWE_BUFFER_OVERRUN , false ) ;
2019-03-23 08:36:10 +01:00
errorLogger . reportErr ( errmsg ) ;
} else {
const ErrorLogger : : ErrorMessage errmsg ( locationList ,
emptyString ,
Severity : : error ,
" Buffer access out of bounds; buffer ' " + unsafeUsage . myArgumentName + " ' is accessed at offset " + MathLib : : toString ( unsafeUsage . value ) + " . " ,
" ctubufferunderrun " ,
2019-03-23 08:41:20 +01:00
CWE_BUFFER_UNDERRUN , false ) ;
2019-03-23 08:36:10 +01:00
errorLogger . reportErr ( errmsg ) ;
}
foundErrors = true ;
}
}
return foundErrors ;
}