2009-01-31 20:29:27 +01:00
/*
* Cppcheck - A tool for static C / C + + code analysis
2021-03-21 20:58:32 +01:00
* Copyright ( C ) 2007 - 2021 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"
2020-05-23 07:16:49 +02:00
# include "errorlogger.h"
2017-05-27 04:33:47 +02:00
# 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"
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
2009-01-31 20:29:27 +01:00
# include <algorithm>
2011-12-23 22:31:48 +01:00
# include <cstdlib>
2021-01-12 10:24:28 +01:00
# include <iterator>
2019-04-28 11:25:43 +02:00
# include <numeric> // std::accumulate
2017-05-27 04:33:47 +02:00
# include <sstream>
2021-01-12 10:24:28 +01:00
# include <tinyxml2.h>
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
2021-08-22 16:37:41 +02:00
static const CWE CWE_ARGUMENT_SIZE ( 398U ) ; // Indicator of Poor Code Quality
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
{
2019-04-28 10:07:11 +02:00
const std : : list < ValueFlow : : Value > & tokenValues = tok - > values ( ) ;
const auto it = std : : find_if ( tokenValues . begin ( ) , tokenValues . end ( ) , std : : mem_fn ( & ValueFlow : : Value : : isBufferSizeValue ) ) ;
return it = = tokenValues . end ( ) ? nullptr : & * it ;
2019-03-17 13:09:15 +01:00
}
2019-07-16 07:59:35 +02:00
static int getMinFormatStringOutputLength ( const std : : vector < const Token * > & parameters , nonneg int formatStringArgNr )
2009-09-25 18:23:44 +02:00
{
2019-07-16 07:59:35 +02:00
if ( formatStringArgNr < = 0 | | formatStringArgNr > parameters . size ( ) )
2019-03-09 22:14:02 +01:00
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-07-16 07:59:35 +02:00
int 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 ;
2019-07-16 07:59:35 +02:00
int parameterLength = 0 ;
int inputArgNr = formatStringArgNr ;
for ( int i = 1 ; i + 1 < formatString . length ( ) ; + + i ) {
2019-03-09 22:14:02 +01:00
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 ) {
2019-07-16 07:59:35 +02:00
int tempDigits = std : : abs ( std : : atoi ( digits_string . c_str ( ) ) ) ;
2010-04-02 07:30:58 +02:00
if ( i_d_x_f_found )
2019-07-16 07:59:35 +02:00
tempDigits = std : : max ( tempDigits , 1 ) ;
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 ) ;
2019-07-16 07:59:35 +02:00
const int maxLen = std : : max ( std : : abs ( std : : atoi ( endStr . c_str ( ) ) ) , 1 ) ;
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
2020-02-17 10:31:08 +01:00
static bool getDimensionsEtc ( const Token * const arrayToken , const Settings * settings , std : : vector < Dimension > * const dimensions , ErrorPath * const errorPath , bool * const mightBeLarger , MathLib : : bigint * path )
2019-03-31 09:00:52 +02:00
{
const Token * array = arrayToken ;
while ( Token : : Match ( array , " .|:: " ) )
array = array - > astOperand2 ( ) ;
2019-10-21 07:11:22 +02:00
if ( array - > variable ( ) & & array - > variable ( ) - > isArray ( ) & & ! array - > variable ( ) - > dimensions ( ) . empty ( ) ) {
2019-03-31 09:00:52 +02:00
* dimensions = array - > variable ( ) - > dimensions ( ) ;
if ( dimensions - > size ( ) > = 1 & & ( ( * dimensions ) [ 0 ] . num < = 1 | | ! ( * dimensions ) [ 0 ] . tok ) ) {
visitAstNodes ( arrayToken ,
2021-08-07 20:51:18 +02:00
[ & ] ( const Token * child ) {
2019-03-31 09:00:52 +02:00
if ( child - > originalName ( ) = = " -> " ) {
* mightBeLarger = true ;
return ChildrenToVisit : : none ;
}
return ChildrenToVisit : : op1_and_op2 ;
} ) ;
}
Set correct type and size of string and char literals (#2275)
* Set correct type and size of string and char literals
Use that string and char literal tokens store the prefix. This makes
it possible to distinghuish between different type of string literals
(i.e., utf8 encoded strings, utf16, wide strings, etc) which have
different type.
When the tokens holding the string and character values have the correct
type, it is possible to improve Token::getStrSize() to give the correct
result for all string types. Previously, it would return the number of
characters in the string, i.e., it would give the wrong size unless
the type of the string was char*.
Since strings now can have different size (in number of bytes) and
length (in number of elements), add a new helper function that returns
the number of characters. Checkers have been updated to use the correct
functions.
Having the size makes it possible to find more problems with prefixed
strings, and to reduce false positives, for example in the buffer
overflow checker.
Also, improve the stringLiteralWrite error message to also print the
prefix of the string (if there is one).
* Add comment and update string length
2019-10-20 07:11:57 +02:00
} else if ( const Token * stringLiteral = array - > getValueTokenMinStrSize ( settings ) ) {
2019-03-31 09:00:52 +02:00
Dimension dim ;
dim . tok = nullptr ;
Set correct type and size of string and char literals (#2275)
* Set correct type and size of string and char literals
Use that string and char literal tokens store the prefix. This makes
it possible to distinghuish between different type of string literals
(i.e., utf8 encoded strings, utf16, wide strings, etc) which have
different type.
When the tokens holding the string and character values have the correct
type, it is possible to improve Token::getStrSize() to give the correct
result for all string types. Previously, it would return the number of
characters in the string, i.e., it would give the wrong size unless
the type of the string was char*.
Since strings now can have different size (in number of bytes) and
length (in number of elements), add a new helper function that returns
the number of characters. Checkers have been updated to use the correct
functions.
Having the size makes it possible to find more problems with prefixed
strings, and to reduce false positives, for example in the buffer
overflow checker.
Also, improve the stringLiteralWrite error message to also print the
prefix of the string (if there is one).
* Add comment and update string length
2019-10-20 07:11:57 +02:00
dim . num = Token : : getStrArraySize ( stringLiteral ) ;
2019-03-31 09:00:52 +02:00
dim . known = array - > hasKnownValue ( ) ;
dimensions - > emplace_back ( dim ) ;
2021-05-22 15:39:20 +02:00
} else if ( array - > valueType ( ) & & array - > valueType ( ) - > pointer > = 1 & & ( array - > valueType ( ) - > isIntegral ( ) | | array - > valueType ( ) - > isFloat ( ) ) ) {
2019-03-31 09:00:52 +02:00
const ValueFlow : : Value * value = getBufferSizeValue ( array ) ;
if ( ! value )
return false ;
2020-02-17 10:31:08 +01:00
if ( path )
* path = value - > path ;
2019-03-31 09:00:52 +02:00
* errorPath = value - > errorPath ;
Dimension dim ;
dim . known = value - > isKnown ( ) ;
dim . tok = nullptr ;
2021-11-27 12:15:36 +01:00
const int typeSize = array - > valueType ( ) - > typeSize ( * settings , array - > valueType ( ) - > pointer > 1 ) ;
2020-01-18 12:29:03 +01:00
if ( typeSize = = 0 )
return false ;
2020-01-19 09:17:38 +01:00
dim . num = value - > intvalue / typeSize ;
2019-03-31 09:00:52 +02:00
dimensions - > emplace_back ( dim ) ;
}
return ! dimensions - > empty ( ) ;
}
2021-08-16 09:19:07 +02:00
static ValueFlow : : Value makeSizeValue ( MathLib : : bigint size , MathLib : : bigint path )
{
ValueFlow : : Value v ( size ) ;
v . path = path ;
return v ;
}
static std : : vector < ValueFlow : : Value > getOverrunIndexValues ( const Token * tok ,
const Token * arrayToken ,
const std : : vector < Dimension > & dimensions ,
const std : : vector < const Token * > & indexTokens ,
MathLib : : bigint path )
2019-03-31 09:00:52 +02:00
{
const Token * array = arrayToken ;
while ( Token : : Match ( array , " .|:: " ) )
array = array - > astOperand2 ( ) ;
2021-08-16 09:19:07 +02:00
bool isArrayIndex = tok - > str ( ) = = " [ " ;
if ( isArrayIndex ) {
const Token * parent = tok ;
while ( Token : : simpleMatch ( parent , " [ " ) )
parent = parent - > astParent ( ) ;
if ( ! parent | | parent - > isUnaryOp ( " & " ) )
isArrayIndex = false ;
}
bool overflow = false ;
std : : vector < ValueFlow : : Value > indexValues ;
for ( int i = 0 ; i < dimensions . size ( ) & & i < indexTokens . size ( ) ; + + i ) {
MathLib : : bigint size = dimensions [ i ] . num ;
if ( ! isArrayIndex )
size + + ;
const bool zeroArray = array - > variable ( ) & & array - > variable ( ) - > isArray ( ) & & dimensions [ i ] . num = = 0 ;
std : : vector < ValueFlow : : Value > values = ! zeroArray
? ValueFlow : : isOutOfBounds ( makeSizeValue ( size , path ) , indexTokens [ i ] )
: std : : vector < ValueFlow : : Value > { } ;
if ( values . empty ( ) ) {
if ( indexTokens [ i ] - > hasKnownIntValue ( ) )
indexValues . push_back ( indexTokens [ i ] - > values ( ) . front ( ) ) ;
else
indexValues . push_back ( ValueFlow : : Value : : unknown ( ) ) ;
2019-03-31 09:00:52 +02:00
continue ;
}
2021-08-16 09:19:07 +02:00
overflow = true ;
indexValues . push_back ( values . front ( ) ) ;
2019-03-31 09:00:52 +02:00
}
2021-08-16 09:19:07 +02:00
if ( overflow )
return indexValues ;
return { } ;
2019-03-31 09:00:52 +02:00
}
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 ( ) ;
2019-10-21 07:11:22 +02:00
if ( ! array | | ( ( ! array - > variable ( ) | | array - > variable ( ) - > nameToken ( ) = = array ) & & array - > tokType ( ) ! = Token : : eString ) )
2019-03-12 06:46:38 +01:00
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-10-05 16:14:30 +02:00
if ( astIsContainer ( array ) )
continue ;
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 ;
2020-02-17 10:31:08 +01:00
MathLib : : bigint path = 0 ;
if ( ! getDimensionsEtc ( tok - > astOperand1 ( ) , mSettings , & dimensions , & errorPath , & mightBeLarger , & path ) )
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
2021-08-16 09:19:07 +02:00
const std : : vector < ValueFlow : : Value > & indexValues =
getOverrunIndexValues ( tok , tok - > astOperand1 ( ) , dimensions , indexTokens , path ) ;
2019-03-31 09:00:52 +02:00
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 ;
2021-08-16 09:19:07 +02:00
std : : vector < ValueFlow : : Value > negativeIndexes ;
2019-09-19 20:29:33 +02:00
for ( const Token * indexToken : indexTokens ) {
const ValueFlow : : Value * negativeValue = indexToken - > getValueLE ( - 1 , mSettings ) ;
2021-08-16 09:19:07 +02:00
if ( negativeValue ) {
negativeIndexes . emplace_back ( * negativeValue ) ;
2019-03-19 13:16:22 +01:00
neg = true ;
2021-08-16 09:19:07 +02:00
} else {
negativeIndexes . emplace_back ( ValueFlow : : Value : : unknown ( ) ) ;
}
2019-03-19 13:16:22 +01:00
}
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
2021-08-16 09:19:07 +02:00
static std : : string stringifyIndexes ( const std : : string & array , const std : : vector < ValueFlow : : Value > & indexValues )
2019-03-19 13:16:22 +01:00
{
if ( indexValues . size ( ) = = 1 )
2021-08-16 09:19:07 +02:00
return MathLib : : toString ( indexValues [ 0 ] . intvalue ) ;
2019-03-19 13:16:22 +01:00
std : : ostringstream ret ;
ret < < array ;
2021-08-16 09:19:07 +02:00
for ( const ValueFlow : : Value & index : indexValues ) {
2019-03-19 13:16:22 +01:00
ret < < " [ " ;
2021-08-16 09:19:07 +02:00
if ( index . isNonValue ( ) )
2019-03-19 13:16:22 +01:00
ret < < " * " ;
2021-08-16 09:19:07 +02:00
else
ret < < index . intvalue ;
2019-03-19 13:16:22 +01:00
ret < < " ] " ;
}
return ret . str ( ) ;
}
2021-08-16 09:19:07 +02:00
static std : : string arrayIndexMessage ( const Token * tok ,
const std : : vector < Dimension > & dimensions ,
const std : : vector < ValueFlow : : Value > & indexValues ,
const Token * condition )
2019-03-11 15:32:30 +01:00
{
2019-04-28 11:17:11 +02:00
auto add_dim = [ ] ( const std : : string & s , const Dimension & dim ) {
2019-04-28 11:25:43 +02:00
return s + " [ " + MathLib : : toString ( dim . num ) + " ] " ;
} ;
2019-04-28 11:17:11 +02:00
const std : : string array = std : : accumulate ( dimensions . begin ( ) , dimensions . end ( ) , tok - > astOperand1 ( ) - > expressionString ( ) , add_dim ) ;
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
}
2021-08-16 09:19:07 +02:00
void CheckBufferOverrun : : arrayIndexError ( const Token * tok ,
const std : : vector < Dimension > & dimensions ,
const std : : vector < ValueFlow : : Value > & indexes )
2011-06-23 04:44:11 +02:00
{
2019-03-09 22:14:02 +01:00
if ( ! tok ) {
2021-02-24 22:00:06 +01:00
reportError ( tok , Severity : : error , " arrayIndexOutOfBounds " , " Array 'arr[16]' accessed at index 16, which is out of bounds. " , CWE_BUFFER_OVERRUN , Certainty : : normal ) ;
reportError ( tok , Severity : : warning , " arrayIndexOutOfBoundsCond " , " Array 'arr[16]' accessed at index 16, which is out of bounds. " , CWE_BUFFER_OVERRUN , Certainty : : normal ) ;
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 ;
2021-08-16 09:19:07 +02:00
for ( const ValueFlow : : Value & indexValue : indexes ) {
if ( ! indexValue . errorSeverity ( ) & & ! mSettings - > severity . isEnabled ( Severity : : warning ) )
2019-03-19 13:16:22 +01:00
return ;
2021-08-16 09:19:07 +02:00
if ( indexValue . condition )
condition = indexValue . condition ;
if ( ! index | | ! indexValue . errorPath . empty ( ) )
index = & indexValue ;
2019-03-19 13:16:22 +01:00
}
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 ,
2021-02-24 22:00:06 +01:00
index - > isInconclusive ( ) ? Certainty : : inconclusive : Certainty : : normal ) ;
2015-11-08 12:39:08 +01:00
}
2021-08-16 09:19:07 +02:00
void CheckBufferOverrun : : negativeIndexError ( const Token * tok ,
const std : : vector < Dimension > & dimensions ,
const std : : vector < ValueFlow : : Value > & indexes )
2015-11-08 12:39:08 +01:00
{
2019-03-19 13:16:22 +01:00
if ( ! tok ) {
2021-02-24 22:00:06 +01:00
reportError ( tok , Severity : : error , " negativeIndex " , " Negative array index " , CWE_BUFFER_UNDERRUN , Certainty : : normal ) ;
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 ;
2021-08-16 09:19:07 +02:00
for ( const ValueFlow : : Value & indexValue : indexes ) {
if ( ! indexValue . errorSeverity ( ) & & ! mSettings - > severity . isEnabled ( Severity : : warning ) )
2019-03-19 13:16:22 +01:00
return ;
2021-08-16 09:19:07 +02:00
if ( indexValue . condition )
condition = indexValue . condition ;
if ( ! negativeValue | | ! indexValue . errorPath . empty ( ) )
negativeValue = & indexValue ;
2019-03-19 13:16:22 +01:00
}
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 ,
2021-02-24 22:00:06 +01:00
negativeValue - > isInconclusive ( ) ? Certainty : : inconclusive : Certainty : : normal ) ;
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 ( )
{
2021-02-24 22:00:06 +01:00
if ( ! mSettings - > severity . isEnabled ( Severity : : portability ) )
2019-03-31 09:00:52 +02:00
return ;
for ( const Token * tok = mTokenizer - > tokens ( ) ; tok ; tok = tok - > next ( ) ) {
if ( ! Token : : Match ( tok , " +|- " ) )
continue ;
if ( ! tok - > valueType ( ) | | tok - > valueType ( ) - > pointer = = 0 )
continue ;
2021-05-15 20:32:46 +02:00
if ( ! tok - > isBinaryOp ( ) )
2019-03-31 09:00:52 +02:00
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 ;
2020-02-17 10:31:08 +01:00
MathLib : : bigint path = 0 ;
if ( ! getDimensionsEtc ( arrayToken , mSettings , & dimensions , & errorPath , & mightBeLarger , & path ) )
2019-03-31 09:00:52 +02:00
continue ;
if ( tok - > str ( ) = = " + " ) {
// Positive index
if ( ! mightBeLarger ) { // TODO check arrays with dim 1 also
const std : : vector < const Token * > indexTokens { indexToken } ;
2021-08-16 09:19:07 +02:00
const std : : vector < ValueFlow : : Value > & indexValues =
getOverrunIndexValues ( tok , arrayToken , dimensions , indexTokens , path ) ;
2019-03-31 09:00:52 +02:00
if ( ! indexValues . empty ( ) )
2021-08-16 09:19:07 +02:00
pointerArithmeticError ( tok , indexToken , & indexValues . front ( ) ) ;
2019-03-31 09:00:52 +02:00
}
if ( const ValueFlow : : Value * neg = indexToken - > getValueLE ( - 1 , mSettings ) )
pointerArithmeticError ( tok , indexToken , neg ) ;
} else if ( tok - > str ( ) = = " - " ) {
2021-09-15 08:28:50 +02:00
if ( arrayToken - > variable ( ) & & arrayToken - > variable ( ) - > isArgument ( ) )
continue ;
2021-05-15 20:32:46 +02:00
const Token * array = arrayToken ;
while ( Token : : Match ( array , " .|:: " ) )
array = array - > astOperand2 ( ) ;
if ( array - > variable ( ) & & array - > variable ( ) - > isArray ( ) ) {
const ValueFlow : : Value * v = indexToken - > getValueGE ( 1 , mSettings ) ;
if ( v )
pointerArithmeticError ( tok , indexToken , v ) ;
}
2019-03-31 09:00:52 +02:00
}
}
}
void CheckBufferOverrun : : pointerArithmeticError ( const Token * tok , const Token * indexToken , const ValueFlow : : Value * indexValue )
{
if ( ! tok ) {
2021-02-24 22:00:06 +01:00
reportError ( tok , Severity : : portability , " pointerOutOfBounds " , " Pointer arithmetic overflow. " , CWE_POINTER_ARITHMETIC_OVERFLOW , Certainty : : normal ) ;
reportError ( tok , Severity : : portability , " pointerOutOfBoundsCond " , " Pointer arithmetic overflow. " , CWE_POINTER_ARITHMETIC_OVERFLOW , Certainty : : normal ) ;
2019-03-31 09:00:52 +02:00
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 ,
2021-02-24 22:00:06 +01:00
indexValue - > isInconclusive ( ) ? Certainty : : inconclusive : Certainty : : normal ) ;
2019-03-31 09:00:52 +02:00
}
//---------------------------------------------------------------------------
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
2019-04-28 11:25:43 +02:00
MathLib : : bigint dim = std : : accumulate ( var - > dimensions ( ) . begin ( ) , var - > dimensions ( ) . end ( ) , 1LL , [ ] ( MathLib : : bigint i1 , const Dimension & dim ) {
return i1 * dim . 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 ;
2020-04-21 17:27:51 +02:00
}
2019-03-09 22:14:02 +01:00
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 ) ;
2019-07-16 07:59:35 +02:00
for ( int argnr = 0 ; argnr < args . size ( ) ; + + argnr ) {
2019-03-09 22:14:02 +01:00
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 ;
2019-11-10 16:42:48 +01:00
if ( argtok - > valueType ( ) & & argtok - > valueType ( ) - > pointer = = 0 )
continue ;
2019-03-09 22:14:02 +01:00
// TODO: strcpy(buf+10, "hello");
2019-03-17 20:12:02 +01:00
const ValueFlow : : Value bufferSize = getBufferSize ( argtok ) ;
2021-04-13 16:40:38 +02:00
if ( bufferSize . intvalue < = 0 )
2019-03-09 22:14:02 +01:00
continue ;
2021-05-22 12:13:39 +02:00
// buffer size == 1 => do not warn for dynamic memory
if ( bufferSize . intvalue = = 1 ) {
const Token * tok2 = argtok ;
while ( Token : : simpleMatch ( tok2 - > astParent ( ) , " . " ) )
tok2 = tok2 - > astParent ( ) ;
while ( Token : : Match ( tok2 , " [|. " ) )
tok2 = tok2 - > astOperand1 ( ) ;
const Variable * var = tok2 ? tok2 - > variable ( ) : nullptr ;
if ( var ) {
if ( var - > isPointer ( ) )
continue ;
2021-05-22 15:34:39 +02:00
if ( var - > isArgument ( ) & & var - > isReference ( ) )
2021-05-22 12:13:39 +02:00
continue ;
}
}
2021-04-13 16:40:38 +02:00
const bool error = std : : none_of ( minsizes - > begin ( ) , minsizes - > end ( ) , [ = ] ( const Library : : ArgumentChecks : : MinSize & minsize ) {
2019-04-28 10:30:20 +02:00
return checkBufferSize ( tok , minsize , args , bufferSize . intvalue , mSettings ) ;
} ) ;
2019-03-09 22:14:02 +01:00
if ( error )
2021-04-13 16:40:38 +02:00
bufferOverflowError ( args [ argnr ] , & bufferSize , ( bufferSize . intvalue = = 1 ) ? Certainty : : inconclusive : Certainty : : normal ) ;
2019-03-09 22:14:02 +01:00
}
2014-11-15 10:43:49 +01:00
}
}
}
2016-07-08 20:53:08 +02:00
2021-04-13 16:40:38 +02:00
void CheckBufferOverrun : : bufferOverflowError ( const Token * tok , const ValueFlow : : Value * value , const Certainty : : CertaintyLevel & certainty )
2016-07-08 20:53:08 +02:00
{
2021-04-13 16:40:38 +02:00
reportError ( getErrorPath ( tok , value , " Buffer overrun " ) , Severity : : error , " bufferAccessOutOfBounds " , " Buffer is accessed out of bounds: " + ( tok ? tok - > expressionString ( ) : " buf " ) , CWE_BUFFER_OVERRUN , certainty ) ;
2016-07-08 20:53:08 +02:00
}
2019-03-11 19:20:06 +01:00
//---------------------------------------------------------------------------
void CheckBufferOverrun : : arrayIndexThenCheck ( )
{
2021-02-24 22:00:06 +01:00
if ( ! mSettings - > severity . isEnabled ( Severity : : portability ) )
2019-03-11 19:20:06 +01:00
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 ( ) ;
2019-07-16 07:59:35 +02:00
const int indexID = tok - > next ( ) - > varId ( ) ;
2019-03-11 19:20:06 +01:00
const std : : string & indexName ( tok - > strAt ( 1 ) ) ;
// Iterate AST upwards
const Token * tok2 = tok ;
const Token * tok3 = tok2 ;
2019-12-20 09:46:01 +01:00
while ( tok2 - > astParent ( ) & & tok2 - > tokType ( ) ! = Token : : eLogicalOp & & tok2 - > str ( ) ! = " ? " ) {
2019-03-11 19:20:06 +01:00
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 "
2021-02-24 22:00:06 +01:00
" not be accessed if the index is out of limits. " , CWE_ARRAY_INDEX_THEN_CHECK , Certainty : : normal ) ;
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
2021-02-24 22:00:06 +01:00
if ( ! mSettings - > severity . isEnabled ( Severity : : warning ) | | ! mSettings - > certainty . isEnabled ( Certainty : : inconclusive ) )
2019-03-12 21:15:26 +01:00
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 ( ) ) {
2019-04-06 06:55:46 +02:00
if ( ! Token : : simpleMatch ( tok2 , " ] = " ) )
2019-03-12 21:15:26 +01:00
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 "
2021-02-24 22:00:06 +01:00
" assumes buffer is null-terminated. " , CWE170 , Certainty : : inconclusive ) ;
2019-03-12 21:15:26 +01:00
}
2021-08-22 16:37:41 +02:00
//---------------------------------------------------------------------------
void CheckBufferOverrun : : argumentSize ( )
{
// Check '%type% x[10]' arguments
if ( ! mSettings - > severity . isEnabled ( Severity : : warning ) )
return ;
2019-03-12 21:15:26 +01:00
2021-08-22 16:37:41 +02:00
const SymbolDatabase * symbolDatabase = mTokenizer - > getSymbolDatabase ( ) ;
for ( const Scope * const scope : symbolDatabase - > functionScopes ) {
for ( const Token * tok = scope - > bodyStart ; tok ! = scope - > bodyEnd ; tok = tok - > next ( ) ) {
if ( ! tok - > function ( ) | | ! Token : : Match ( tok , " %name% ( " ) )
continue ;
// If argument is '%type% a[num]' then check bounds against num
const Function * callfunc = tok - > function ( ) ;
const std : : vector < const Token * > callargs = getArguments ( tok ) ;
for ( nonneg int paramIndex = 0 ; paramIndex < callargs . size ( ) & & paramIndex < callfunc - > argCount ( ) ; + + paramIndex ) {
const Variable * const argument = callfunc - > getArgumentVar ( paramIndex ) ;
if ( ! argument | | ! argument - > nameToken ( ) | | ! argument - > isArray ( ) )
continue ;
if ( ! argument - > valueType ( ) | | ! callargs [ paramIndex ] - > valueType ( ) )
continue ;
if ( argument - > valueType ( ) - > type ! = callargs [ paramIndex ] - > valueType ( ) - > type )
continue ;
const Token * calldata = callargs [ paramIndex ] ;
while ( Token : : Match ( calldata , " ::|. " ) )
calldata = calldata - > astOperand2 ( ) ;
if ( ! calldata - > variable ( ) | | ! calldata - > variable ( ) - > isArray ( ) )
continue ;
if ( calldata - > variable ( ) - > dimensions ( ) . size ( ) ! = argument - > dimensions ( ) . size ( ) )
continue ;
bool err = false ;
for ( int d = 0 ; d < argument - > dimensions ( ) . size ( ) ; + + d ) {
const auto & dim1 = calldata - > variable ( ) - > dimensions ( ) [ d ] ;
const auto & dim2 = argument - > dimensions ( ) [ d ] ;
if ( ! dim1 . known | | ! dim2 . known )
break ;
if ( dim1 . num < dim2 . num )
err = true ;
}
if ( err )
2021-08-29 15:38:58 +02:00
argumentSizeError ( tok , tok - > str ( ) , paramIndex , callargs [ paramIndex ] - > expressionString ( ) , calldata - > variable ( ) , argument ) ;
2021-08-22 16:37:41 +02:00
}
}
}
}
2019-03-12 21:15:26 +01:00
2021-08-29 15:38:58 +02:00
void CheckBufferOverrun : : argumentSizeError ( const Token * tok , const std : : string & functionName , nonneg int paramIndex , const std : : string & paramExpression , const Variable * paramVar , const Variable * functionArg )
2021-08-22 16:37:41 +02:00
{
2021-08-29 15:38:58 +02:00
const std : : string strParamNum = std : : to_string ( paramIndex + 1 ) + getOrdinalText ( paramIndex + 1 ) ;
ErrorPath errorPath ;
errorPath . emplace_back ( tok , " Function ' " + functionName + " ' is called " ) ;
if ( functionArg )
errorPath . emplace_back ( functionArg - > nameToken ( ) , " Declaration of " + strParamNum + " function argument. " ) ;
if ( paramVar )
errorPath . emplace_back ( paramVar - > nameToken ( ) , " Passing buffer ' " + paramVar - > name ( ) + " ' to function that is declared here " ) ;
errorPath . emplace_back ( tok , " " ) ;
reportError ( errorPath , Severity : : warning , " argumentSize " ,
2021-08-22 16:37:41 +02:00
" $symbol: " + functionName + ' \n ' +
2021-08-29 15:38:58 +02:00
" Buffer ' " + paramExpression + " ' is too small, the function ' " + functionName + " ' expects a bigger buffer in " + strParamNum + " argument " , CWE_ARGUMENT_SIZE , Certainty : : normal ) ;
2021-08-22 16:37:41 +02:00
}
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
{
2019-04-03 06:43:56 +02:00
std : : string xml ;
if ( ! unsafeArrayIndex . empty ( ) )
xml = " <array-index> \n " + CTU : : toString ( unsafeArrayIndex ) + " </array-index> \n " ;
if ( ! unsafePointerArith . empty ( ) )
xml + = " <pointer-arith> \n " + CTU : : toString ( unsafePointerArith ) + " </pointer-arith> \n " ;
return xml ;
2019-03-23 08:36:10 +01:00
}
2019-04-03 06:43:56 +02:00
bool CheckBufferOverrun : : isCtuUnsafeBufferUsage ( const Check * check , const Token * argtok , MathLib : : bigint * offset , int type )
2019-03-23 08:36:10 +01:00
{
const CheckBufferOverrun * c = dynamic_cast < const CheckBufferOverrun * > ( check ) ;
if ( ! c )
return false ;
2019-05-01 13:00:14 +02:00
if ( ! argtok - > valueType ( ) | | argtok - > valueType ( ) - > typeSize ( * c - > mSettings ) = = 0 )
2019-03-23 08:36:10 +01:00
return false ;
2019-04-03 06:43:56 +02:00
const Token * indexTok = nullptr ;
if ( type = = 1 & & Token : : Match ( argtok , " %name% [ " ) & & argtok - > astParent ( ) = = argtok - > next ( ) & & ! Token : : simpleMatch ( argtok - > linkAt ( 1 ) , " ] [ " ) )
indexTok = argtok - > next ( ) - > astOperand2 ( ) ;
else if ( type = = 2 & & Token : : simpleMatch ( argtok - > astParent ( ) , " + " ) )
indexTok = ( argtok = = argtok - > astParent ( ) - > astOperand1 ( ) ) ?
argtok - > astParent ( ) - > astOperand2 ( ) :
argtok - > astParent ( ) - > astOperand1 ( ) ;
if ( ! indexTok )
2019-03-23 08:36:10 +01:00
return false ;
2019-04-03 06:43:56 +02:00
if ( ! indexTok - > hasKnownIntValue ( ) )
2019-03-23 08:36:10 +01:00
return false ;
if ( ! offset )
return false ;
2019-04-03 06:43:56 +02:00
* offset = indexTok - > getKnownIntValue ( ) * argtok - > valueType ( ) - > typeSize ( * c - > mSettings ) ;
2019-03-23 08:36:10 +01:00
return true ;
}
2019-04-03 06:43:56 +02:00
bool CheckBufferOverrun : : isCtuUnsafeArrayIndex ( const Check * check , const Token * argtok , MathLib : : bigint * offset )
{
return CheckBufferOverrun : : isCtuUnsafeBufferUsage ( check , argtok , offset , 1 ) ;
}
bool CheckBufferOverrun : : isCtuUnsafePointerArith ( const Check * check , const Token * argtok , MathLib : : bigint * offset )
{
return CheckBufferOverrun : : isCtuUnsafeBufferUsage ( check , argtok , offset , 2 ) ;
}
2019-03-23 08:36:10 +01:00
/** @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 ) ;
2019-03-23 08:36:10 +01:00
MyFileInfo * fileInfo = new MyFileInfo ;
2019-04-03 06:43:56 +02:00
fileInfo - > unsafeArrayIndex = CTU : : getUnsafeUsage ( tokenizer , settings , & checkBufferOverrun , isCtuUnsafeArrayIndex ) ;
fileInfo - > unsafePointerArith = CTU : : getUnsafeUsage ( tokenizer , settings , & checkBufferOverrun , isCtuUnsafePointerArith ) ;
if ( fileInfo - > unsafeArrayIndex . empty ( ) & & fileInfo - > unsafePointerArith . empty ( ) ) {
delete fileInfo ;
return nullptr ;
}
2019-03-23 08:36:10 +01:00
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
{
2019-04-03 06:43:56 +02:00
const std : : string arrayIndex ( " array-index " ) ;
const std : : string pointerArith ( " pointer-arith " ) ;
2019-03-23 08:36:10 +01:00
MyFileInfo * fileInfo = new MyFileInfo ;
2019-04-03 06:43:56 +02:00
for ( const tinyxml2 : : XMLElement * e = xmlElement - > FirstChildElement ( ) ; e ; e = e - > NextSiblingElement ( ) ) {
if ( e - > Name ( ) = = arrayIndex )
fileInfo - > unsafeArrayIndex = CTU : : loadUnsafeUsageListFromXml ( e ) ;
else if ( e - > Name ( ) = = pointerArith )
fileInfo - > unsafePointerArith = CTU : : loadUnsafeUsageListFromXml ( e ) ;
}
if ( fileInfo - > unsafeArrayIndex . empty ( ) & & fileInfo - > unsafePointerArith . empty ( ) ) {
delete fileInfo ;
return nullptr ;
}
2019-03-23 08:36:10 +01:00
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 ;
2019-04-03 06:43:56 +02:00
for ( const CTU : : FileInfo : : UnsafeUsage & unsafeUsage : fi - > unsafeArrayIndex )
2021-01-09 20:32:38 +01:00
foundErrors | = analyseWholeProgram1 ( callsMap , unsafeUsage , 1 , errorLogger ) ;
2019-04-03 06:43:56 +02:00
for ( const CTU : : FileInfo : : UnsafeUsage & unsafeUsage : fi - > unsafePointerArith )
2021-01-09 20:32:38 +01:00
foundErrors | = analyseWholeProgram1 ( callsMap , unsafeUsage , 2 , errorLogger ) ;
2019-04-03 06:43:56 +02:00
}
return foundErrors ;
}
2019-03-23 08:36:10 +01:00
2021-01-09 20:32:38 +01:00
bool CheckBufferOverrun : : analyseWholeProgram1 ( const std : : map < std : : string , std : : list < const CTU : : FileInfo : : CallBase * > > & callsMap , const CTU : : FileInfo : : UnsafeUsage & unsafeUsage , int type , ErrorLogger & errorLogger )
2019-04-03 06:43:56 +02:00
{
const CTU : : FileInfo : : FunctionCall * functionCall = nullptr ;
2020-05-23 07:16:49 +02:00
const std : : list < ErrorMessage : : FileLocation > & locationList =
2021-01-09 20:32:38 +01:00
CTU : : FileInfo : : getErrorPath ( CTU : : FileInfo : : InvalidValueType : : bufferOverflow ,
2021-01-10 14:46:19 +01:00
unsafeUsage ,
callsMap ,
" Using argument ARG " ,
& functionCall ,
false ) ;
2019-04-03 06:43:56 +02:00
if ( locationList . empty ( ) )
return false ;
2019-03-23 08:36:10 +01:00
2019-04-03 06:43:56 +02:00
const char * errorId = nullptr ;
std : : string errmsg ;
CWE cwe ( 0 ) ;
if ( type = = 1 ) {
errorId = " ctuArrayIndex " ;
if ( unsafeUsage . value > 0 )
errmsg = " Array index out of bounds; ' " + unsafeUsage . myArgumentName + " ' buffer size is " + MathLib : : toString ( functionCall - > callArgValue ) + " and it is accessed at offset " + MathLib : : toString ( unsafeUsage . value ) + " . " ;
else
errmsg = " Array index out of bounds; buffer ' " + unsafeUsage . myArgumentName + " ' is accessed at offset " + MathLib : : toString ( unsafeUsage . value ) + " . " ;
cwe = ( unsafeUsage . value > 0 ) ? CWE_BUFFER_OVERRUN : CWE_BUFFER_UNDERRUN ;
} else {
errorId = " ctuPointerArith " ;
errmsg = " Pointer arithmetic overflow; ' " + unsafeUsage . myArgumentName + " ' buffer size is " + MathLib : : toString ( functionCall - > callArgValue ) ;
cwe = CWE_POINTER_ARITHMETIC_OVERFLOW ;
2019-03-23 08:36:10 +01:00
}
2019-04-03 06:43:56 +02:00
2020-05-23 07:16:49 +02:00
const ErrorMessage errorMessage ( locationList ,
emptyString ,
Severity : : error ,
errmsg ,
errorId ,
2021-02-24 22:00:06 +01:00
cwe , Certainty : : normal ) ;
2019-04-03 06:43:56 +02:00
errorLogger . reportErr ( errorMessage ) ;
return true ;
2019-03-23 08:36:10 +01:00
}
2019-05-31 11:16:04 +02:00
void CheckBufferOverrun : : objectIndex ( )
{
const SymbolDatabase * symbolDatabase = mTokenizer - > getSymbolDatabase ( ) ;
for ( const Scope * functionScope : symbolDatabase - > functionScopes ) {
for ( const Token * tok = functionScope - > bodyStart ; tok ! = functionScope - > bodyEnd ; tok = tok - > next ( ) ) {
if ( ! Token : : simpleMatch ( tok , " [ " ) )
continue ;
const Token * obj = tok - > astOperand1 ( ) ;
const Token * idx = tok - > astOperand2 ( ) ;
if ( ! idx | | ! obj )
continue ;
2021-01-12 10:24:28 +01:00
if ( idx - > hasKnownIntValue ( ) ) {
if ( idx - > getKnownIntValue ( ) = = 0 )
continue ;
}
2019-05-31 11:16:04 +02:00
if ( idx - > hasKnownIntValue ( ) & & idx - > getKnownIntValue ( ) = = 0 )
continue ;
2022-01-18 14:48:02 +01:00
std : : vector < ValueFlow : : Value > values = getLifetimeObjValues ( obj , false , - 1 ) ;
2021-01-06 17:13:44 +01:00
for ( const ValueFlow : : Value & v : values ) {
2021-01-05 16:56:38 +01:00
if ( v . lifetimeKind ! = ValueFlow : : Value : : LifetimeKind : : Address )
continue ;
const Variable * var = v . tokvalue - > variable ( ) ;
2021-11-16 06:49:13 +01:00
if ( ! var )
continue ;
2021-01-05 16:56:38 +01:00
if ( var - > isReference ( ) )
continue ;
if ( var - > isRValueReference ( ) )
continue ;
if ( var - > isArray ( ) )
continue ;
if ( var - > isPointer ( ) ) {
if ( ! var - > valueType ( ) )
continue ;
if ( ! obj - > valueType ( ) )
continue ;
if ( var - > valueType ( ) - > pointer > obj - > valueType ( ) - > pointer )
continue ;
}
2022-01-04 10:37:16 +01:00
if ( obj - > valueType ( ) & & var - > valueType ( ) & & ( obj - > isCast ( ) | | ( mTokenizer - > isCPP ( ) & & isCPPCast ( obj ) ) | | obj - > valueType ( ) - > pointer ) ) { // allow cast to a different type
const auto varSize = var - > valueType ( ) - > typeSize ( * mSettings ) ;
if ( varSize = = 0 )
continue ;
if ( obj - > valueType ( ) - > type ! = var - > valueType ( ) - > type ) {
if ( ValueFlow : : isOutOfBounds ( makeSizeValue ( varSize , v . path ) , idx ) . empty ( ) )
continue ;
}
}
2021-01-12 10:24:28 +01:00
if ( v . path ! = 0 ) {
std : : vector < ValueFlow : : Value > idxValues ;
std : : copy_if ( idx - > values ( ) . begin ( ) ,
idx - > values ( ) . end ( ) ,
std : : back_inserter ( idxValues ) ,
2021-08-07 20:51:18 +02:00
[ & ] ( const ValueFlow : : Value & vidx ) {
2021-01-12 21:28:56 +01:00
if ( ! vidx . isIntValue ( ) )
return false ;
return vidx . path = = v . path | | vidx . path = = 0 ;
} ) ;
2021-09-06 07:15:46 +02:00
if ( std : : any_of ( idxValues . begin ( ) , idxValues . end ( ) , [ & ] ( const ValueFlow : : Value & vidx ) {
2021-08-07 20:51:18 +02:00
if ( vidx . isImpossible ( ) )
2021-01-12 21:28:56 +01:00
return ( vidx . intvalue = = 0 ) ;
else
return ( vidx . intvalue ! = 0 ) ;
} ) ) {
2021-01-12 10:24:28 +01:00
objectIndexError ( tok , & v , idx - > hasKnownIntValue ( ) ) ;
}
} else {
objectIndexError ( tok , & v , idx - > hasKnownIntValue ( ) ) ;
}
2021-01-05 16:56:38 +01:00
}
2019-05-31 11:16:04 +02:00
}
}
}
void CheckBufferOverrun : : objectIndexError ( const Token * tok , const ValueFlow : : Value * v , bool known )
{
ErrorPath errorPath ;
std : : string name ;
if ( v ) {
name = v - > tokvalue - > variable ( ) - > name ( ) ;
errorPath = v - > errorPath ;
}
errorPath . emplace_back ( tok , " " ) ;
std : : string verb = known ? " is " : " might be " ;
reportError ( errorPath ,
known ? Severity : : error : Severity : : warning ,
" objectIndex " ,
" The address of local variable ' " + name + " ' " + verb + " accessed at non-zero index. " ,
CWE758 ,
2021-02-24 22:00:06 +01:00
Certainty : : normal ) ;
2019-05-31 11:16:04 +02:00
}