2020-06-19 13:16:48 +02:00
/*
* Cppcheck - A tool for static C / C + + code analysis
2021-03-21 20:58:32 +01:00
* Copyright ( C ) 2007 - 2021 Cppcheck team .
2020-06-19 13:16:48 +02:00
*
* This program is free software : you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation , either version 3 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program . If not , see < http : //www.gnu.org/licenses/>.
*/
# include "bughuntingchecks.h"
2022-01-27 19:03:20 +01:00
2020-06-19 13:16:48 +02:00
# include "astutils.h"
# include "errorlogger.h"
2022-01-27 19:03:20 +01:00
# include "errortypes.h"
# include "library.h"
# include "mathlib.h"
2020-06-19 13:16:48 +02:00
# include "settings.h"
# include "symboldatabase.h"
# include "token.h"
2022-01-27 19:03:20 +01:00
# include "utils.h"
# include "valueflow.h"
# include <algorithm>
2020-06-19 13:16:48 +02:00
# include <cstring>
2022-01-27 19:03:20 +01:00
# include <list>
# include <map>
# include <memory>
# include <string>
# include <utility>
2020-06-19 13:16:48 +02:00
2020-12-05 11:46:07 +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
2020-06-19 13:16:48 +02:00
static float getKnownFloatValue ( const Token * tok , float def )
{
for ( const auto & value : tok - > values ( ) ) {
if ( value . isKnown ( ) & & value . valueType = = ValueFlow : : Value : : ValueType : : FLOAT )
return value . floatValue ;
}
return def ;
}
2020-12-05 11:47:57 +01:00
static bool isLessThan ( ExprEngine : : DataBase * dataBase , ExprEngine : : ValuePtr lhs , ExprEngine : : ValuePtr rhs )
{
return ExprEngine : : BinOpResult ( " < " , lhs , rhs ) . isTrue ( dataBase ) ;
}
2020-12-05 11:46:07 +01:00
static void arrayIndex ( const Token * tok , const ExprEngine : : Value & value , ExprEngine : : DataBase * dataBase )
{
if ( ! Token : : simpleMatch ( tok - > astParent ( ) , " [ " ) )
return ;
2020-12-18 21:32:23 +01:00
int nr = 0 ;
2020-12-05 11:46:07 +01:00
const Token * buf = tok - > astParent ( ) - > astOperand1 ( ) ;
2020-12-18 21:32:23 +01:00
while ( Token : : simpleMatch ( buf , " [ " ) ) {
+ + nr ;
buf = buf - > astOperand1 ( ) ;
}
if ( ! buf | | ! buf - > variable ( ) | | ! buf - > variable ( ) - > isArray ( ) | | buf = = buf - > variable ( ) - > nameToken ( ) )
2020-12-05 11:46:07 +01:00
// TODO
return ;
const Token * index = tok - > astParent ( ) - > astOperand2 ( ) ;
if ( tok ! = index )
// TODO
return ;
2020-12-18 21:32:23 +01:00
if ( buf - > variable ( ) - > dimensions ( ) . size ( ) > nr & & buf - > variable ( ) - > dimensions ( ) [ nr ] . known ) {
const MathLib : : bigint bufSize = buf - > variable ( ) - > dimensions ( ) [ nr ] . num ;
2020-12-05 11:46:07 +01:00
if ( value . isGreaterThan ( dataBase , bufSize - 1 ) ) {
const bool bailout = ( value . type = = ExprEngine : : ValueType : : BailoutValue ) ;
dataBase - > reportError ( tok ,
Severity : : SeverityType : : error ,
" bughuntingArrayIndexOutOfBounds " ,
" Array index out of bounds, cannot determine that " + index - > expressionString ( ) + " is less than " + std : : to_string ( bufSize ) ,
CWE_BUFFER_OVERRUN ,
false ,
bailout ) ;
}
}
2021-01-01 08:57:35 +01:00
bool isUnsigned = tok - > valueType ( ) & & tok - > valueType ( ) - > sign = = : : ValueType : : Sign : : UNSIGNED ;
if ( ! isUnsigned & & value . isLessThan ( dataBase , 0 ) ) {
2020-12-05 11:46:07 +01:00
const bool bailout = ( value . type = = ExprEngine : : ValueType : : BailoutValue ) ;
dataBase - > reportError ( tok ,
Severity : : SeverityType : : error ,
" bughuntingArrayIndexNegative " ,
" Array index out of bounds, cannot determine that " + index - > expressionString ( ) + " is not negative " ,
CWE_BUFFER_UNDERRUN ,
false ,
bailout ) ;
}
}
2020-06-19 13:16:48 +02:00
static void bufferOverflow ( const Token * tok , const ExprEngine : : Value & value , ExprEngine : : DataBase * dataBase )
{
2020-12-05 11:47:57 +01:00
if ( value . type ! = ExprEngine : : ValueType : : FunctionCallArgumentValues )
return ;
if ( ! Token : : simpleMatch ( tok , " ( " ) | | ! Token : : Match ( tok - > previous ( ) , " %name% ( " ) )
2020-06-19 13:16:48 +02:00
return ;
2020-12-05 11:47:57 +01:00
const Library : : Function * func = dataBase - > settings - > library . getFunction ( tok - > previous ( ) ) ;
if ( ! func )
2020-06-19 13:16:48 +02:00
return ;
2020-12-05 11:47:57 +01:00
const ExprEngine : : FunctionCallArgumentValues * functionCallArguments = dynamic_cast < const ExprEngine : : FunctionCallArgumentValues * > ( & value ) ;
if ( ! functionCallArguments )
return ;
const std : : vector < const Token * > arguments = getArguments ( tok ) ;
if ( functionCallArguments - > argValues . size ( ) ! = arguments . size ( ) )
// TODO investigate what to do
2020-06-19 13:16:48 +02:00
return ;
int overflowArgument = 0 ;
2020-12-05 11:47:57 +01:00
bool bailout = false ;
for ( auto argNrChecks : func - > argumentChecks ) {
const int argnr = argNrChecks . first ;
2020-12-05 14:56:06 +01:00
const Library : : ArgumentChecks & checks = argNrChecks . second ;
if ( argnr < = 0 | | argnr > arguments . size ( ) | | checks . minsizes . empty ( ) )
2020-12-05 11:47:57 +01:00
continue ;
2020-12-05 14:56:06 +01:00
2020-12-05 11:47:57 +01:00
ExprEngine : : ValuePtr argValue = functionCallArguments - > argValues [ argnr - 1 ] ;
if ( ! argValue | | argValue - > type = = ExprEngine : : ValueType : : BailoutValue ) {
overflowArgument = argnr ;
bailout = true ;
break ;
}
2020-06-19 13:16:48 +02:00
2020-12-05 11:47:57 +01:00
std : : shared_ptr < ExprEngine : : ArrayValue > arrayValue = std : : dynamic_pointer_cast < ExprEngine : : ArrayValue > ( argValue ) ;
2020-12-05 13:51:12 +01:00
if ( ! arrayValue | | arrayValue - > size . size ( ) ! = 1 ) {
// TODO: implement this properly.
overflowArgument = argnr ;
bailout = true ;
break ;
}
2020-12-05 11:47:57 +01:00
for ( const Library : : ArgumentChecks : : MinSize & minsize : checks . minsizes ) {
2021-01-08 10:29:01 +01:00
if ( minsize . type = = Library : : ArgumentChecks : : MinSize : : Type : : ARGVALUE & & minsize . arg > 0 & & minsize . arg < = arguments . size ( ) ) {
2020-12-05 11:47:57 +01:00
ExprEngine : : ValuePtr otherValue = functionCallArguments - > argValues [ minsize . arg - 1 ] ;
if ( ! otherValue | | otherValue - > type = = ExprEngine : : ValueType : : BailoutValue ) {
overflowArgument = argnr ;
bailout = true ;
break ;
}
if ( isLessThan ( dataBase , arrayValue - > size [ 0 ] , otherValue ) ) {
overflowArgument = argnr ;
break ;
}
2021-01-08 10:29:01 +01:00
} else if ( minsize . type = = Library : : ArgumentChecks : : MinSize : : Type : : STRLEN & & minsize . arg > 0 & & minsize . arg < = arguments . size ( ) ) {
2020-12-05 13:51:12 +01:00
if ( func - > formatstr ) {
// TODO: implement this properly. check if minsize refers to a format string and check max length of that..
overflowArgument = argnr ;
bailout = true ;
break ;
}
2020-12-05 11:47:57 +01:00
if ( Token : : Match ( arguments [ minsize . arg - 1 ] , " %str% " ) ) {
const Token * const str = arguments [ minsize . arg - 1 ] ;
if ( arrayValue - > size [ 0 ] - > isLessThan ( dataBase , Token : : getStrLength ( str ) ) ) {
overflowArgument = argnr ;
break ;
}
} else {
ExprEngine : : ValuePtr otherValue = functionCallArguments - > argValues [ minsize . arg - 1 ] ;
if ( ! otherValue | | otherValue - > type = = ExprEngine : : ValueType : : BailoutValue ) {
overflowArgument = argnr ;
bailout = true ;
break ;
}
std : : shared_ptr < ExprEngine : : ArrayValue > arrayValue2 = std : : dynamic_pointer_cast < ExprEngine : : ArrayValue > ( otherValue ) ;
2020-12-05 18:52:18 +01:00
if ( ! arrayValue2 | | arrayValue2 - > size . size ( ) ! = 1 ) {
overflowArgument = argnr ;
bailout = true ;
break ;
}
if ( isLessThan ( dataBase , arrayValue - > size [ 0 ] , arrayValue2 - > size [ 0 ] ) ) {
2020-12-05 11:47:57 +01:00
overflowArgument = argnr ;
break ;
}
}
2020-06-19 13:16:48 +02:00
}
}
2020-12-05 11:47:57 +01:00
if ( overflowArgument > 0 )
break ;
2020-06-19 13:16:48 +02:00
}
2020-12-05 11:47:57 +01:00
if ( overflowArgument = = 0 )
2020-06-19 13:16:48 +02:00
return ;
dataBase - > reportError ( tok ,
Severity : : SeverityType : : error ,
" bughuntingBufferOverflow " ,
2020-12-05 11:47:57 +01:00
" Buffer read/write, when calling ' " + tok - > previous ( ) - > str ( ) + " ' it cannot be determined that " + std : : to_string ( overflowArgument ) + getOrdinalText ( overflowArgument ) + " argument is not overflowed " ,
2020-06-19 13:16:48 +02:00
CWE ( 120 ) ,
false ,
bailout ) ;
}
static void divByZero ( const Token * tok , const ExprEngine : : Value & value , ExprEngine : : DataBase * dataBase )
{
if ( ! tok - > astParent ( ) | | ! std : : strchr ( " /% " , tok - > astParent ( ) - > str ( ) [ 0 ] ) )
return ;
if ( tok - > hasKnownIntValue ( ) & & tok - > getKnownIntValue ( ) ! = 0 )
return ;
if ( tok - > isImpossibleIntValue ( 0 ) )
return ;
2022-01-03 12:46:33 +01:00
if ( value . isUninit ( dataBase ) & & value . type ! = ExprEngine : : ValueType : : BailoutValue )
2020-06-19 13:16:48 +02:00
return ;
float f = getKnownFloatValue ( tok , 0.0f ) ;
if ( f > 0.0f | | f < 0.0f )
return ;
if ( value . type = = ExprEngine : : ValueType : : BailoutValue ) {
if ( Token : : simpleMatch ( tok - > previous ( ) , " sizeof ( " ) )
return ;
}
if ( tok - > astParent ( ) - > astOperand2 ( ) = = tok & & value . isEqual ( dataBase , 0 ) ) {
const char * const id = ( tok - > valueType ( ) & & tok - > valueType ( ) - > isFloat ( ) ) ? " bughuntingDivByZeroFloat " : " bughuntingDivByZero " ;
const bool bailout = ( value . type = = ExprEngine : : ValueType : : BailoutValue ) ;
2021-04-30 16:47:02 +02:00
dataBase - > reportError ( dataBase - > settings - > clang ? tok : tok - > astParent ( ) ,
2020-06-19 13:16:48 +02:00
Severity : : SeverityType : : error ,
id ,
" There is division, cannot determine that there can't be a division by zero. " ,
CWE ( 369 ) ,
false ,
bailout ) ;
}
}
# ifdef BUG_HUNTING_INTEGEROVERFLOW
static void integerOverflow ( const Token * tok , const ExprEngine : : Value & value , ExprEngine : : DataBase * dataBase )
{
if ( ! tok - > isArithmeticalOp ( ) | | ! tok - > valueType ( ) | | ! tok - > valueType ( ) - > isIntegral ( ) | | tok - > valueType ( ) - > pointer > 0 )
return ;
const ExprEngine : : BinOpResult * b = dynamic_cast < const ExprEngine : : BinOpResult * > ( & value ) ;
if ( ! b )
return ;
int bits = getIntBitsFromValueType ( tok - > valueType ( ) , * dataBase - > settings ) ;
if ( bits = = 0 | | bits > = 60 )
return ;
std : : string errorMessage ;
if ( tok - > valueType ( ) - > sign = = : : ValueType : : Sign : : SIGNED ) {
MathLib : : bigint v = 1LL < < ( bits - 1 ) ;
if ( b - > isGreaterThan ( dataBase , v - 1 ) )
errorMessage = " greater than " + std : : to_string ( v - 1 ) ;
if ( b - > isLessThan ( dataBase , - v ) ) {
if ( ! errorMessage . empty ( ) )
errorMessage + = " or " ;
errorMessage + = " less than " + std : : to_string ( - v ) ;
}
} else {
MathLib : : bigint maxValue = ( 1LL < < bits ) - 1 ;
if ( b - > isGreaterThan ( dataBase , maxValue ) )
errorMessage = " greater than " + std : : to_string ( maxValue ) ;
if ( b - > isLessThan ( dataBase , 0 ) ) {
if ( ! errorMessage . empty ( ) )
errorMessage + = " or " ;
errorMessage + = " less than 0 " ;
}
}
if ( errorMessage . empty ( ) )
return ;
errorMessage = " There is integer arithmetic, cannot determine that there can't be overflow (if result is " + errorMessage + " ). " ;
if ( tok - > valueType ( ) - > sign = = : : ValueType : : Sign : : UNSIGNED )
errorMessage + = " Note that unsigned integer overflow is defined and will wrap around. " ;
dataBase - > reportError ( tok , Severity : : SeverityType : : error , " bughuntingIntegerOverflow " , errorMessage , false , value . type = = ExprEngine : : ValueType : : BailoutValue ) ;
}
# endif
2020-07-17 09:05:38 +02:00
/** check if variable is unconditionally assigned */
static bool isVariableAssigned ( const Variable * var , const Token * tok , const Token * scopeStart = nullptr )
{
const Token * const start = scopeStart & & precedes ( var - > nameToken ( ) , scopeStart ) ? scopeStart : var - > nameToken ( ) ;
for ( const Token * prev = tok - > previous ( ) ; prev ; prev = prev - > previous ( ) ) {
if ( ! precedes ( start , prev ) )
break ;
if ( prev - > str ( ) = = " } " ) {
if ( Token : : simpleMatch ( prev - > link ( ) - > tokAt ( - 2 ) , " } else { " ) ) {
const Token * elseEnd = prev ;
const Token * elseStart = prev - > link ( ) ;
const Token * ifEnd = elseStart - > tokAt ( - 2 ) ;
const Token * ifStart = ifEnd - > link ( ) ;
if ( isVariableAssigned ( var , ifEnd , ifStart ) & & isVariableAssigned ( var , elseEnd , elseStart ) ) {
return true ;
}
}
prev = prev - > link ( ) ;
}
if ( scopeStart & & Token : : Match ( prev , " return|throw|continue|break " ) )
return true ;
2020-07-17 11:02:46 +02:00
if ( Token : : Match ( prev , " %varid% = " , var - > declarationId ( ) ) ) {
bool usedInRhs = false ;
visitAstNodes ( prev - > next ( ) - > astOperand2 ( ) , [ & usedInRhs , var ] ( const Token * tok ) {
if ( tok - > varId ( ) = = var - > declarationId ( ) ) {
usedInRhs = true ;
return ChildrenToVisit : : done ;
}
return ChildrenToVisit : : op1_and_op2 ;
} ) ;
if ( ! usedInRhs )
return true ;
}
2020-07-17 09:05:38 +02:00
}
return false ;
}
2020-06-19 13:16:48 +02:00
static void uninit ( const Token * tok , const ExprEngine : : Value & value , ExprEngine : : DataBase * dataBase )
{
if ( ! tok - > astParent ( ) )
return ;
std : : string uninitStructMember ;
if ( const auto * structValue = dynamic_cast < const ExprEngine : : StructValue * > ( & value ) ) {
2022-01-03 12:46:33 +01:00
uninitStructMember = structValue - > getUninitStructMember ( dataBase ) ;
2020-06-19 13:16:48 +02:00
// uninitialized struct member => is there data copy of struct..
if ( ! uninitStructMember . empty ( ) ) {
if ( ! Token : : Match ( tok - > astParent ( ) , " [=,(] " ) )
return ;
}
}
2020-06-27 14:15:53 +02:00
bool uninitData = false ;
2022-01-03 12:46:33 +01:00
if ( ! value . isUninit ( dataBase ) & & uninitStructMember . empty ( ) ) {
2020-06-27 14:15:53 +02:00
if ( Token : : Match ( tok - > astParent ( ) , " [(,] " ) ) {
if ( const auto * arrayValue = dynamic_cast < const ExprEngine : : ArrayValue * > ( & value ) ) {
2022-01-03 12:46:33 +01:00
uninitData = arrayValue - > data . size ( ) > = 1 & & arrayValue - > data [ 0 ] . value - > isUninit ( dataBase ) ;
2020-06-27 14:15:53 +02:00
}
}
if ( ! uninitData )
return ;
}
2020-06-19 13:16:48 +02:00
2020-06-28 22:49:51 +02:00
// container is not uninitialized
if ( tok - > valueType ( ) & & tok - > valueType ( ) - > pointer = = 0 & & tok - > valueType ( ) - > container )
return ;
// container element is not uninitialized
if ( tok - > str ( ) = = " [ " & &
tok - > astOperand1 ( ) & &
tok - > astOperand1 ( ) - > valueType ( ) & &
tok - > astOperand1 ( ) - > valueType ( ) - > pointer = = 0 & &
tok - > astOperand1 ( ) - > valueType ( ) - > container ) {
if ( tok - > astOperand1 ( ) - > valueType ( ) - > container - > stdStringLike )
return ;
bool pointerType = false ;
for ( const Token * typeTok = tok - > astOperand1 ( ) - > valueType ( ) - > containerTypeToken ; Token : : Match ( typeTok , " %name%|*|::|< " ) ; typeTok = typeTok - > next ( ) ) {
if ( typeTok - > str ( ) = = " < " & & typeTok - > link ( ) )
typeTok = typeTok - > link ( ) ;
if ( typeTok - > str ( ) = = " * " )
pointerType = true ;
}
if ( ! pointerType )
return ;
}
2020-09-07 21:19:07 +02:00
// variable that is not uninitialized..
if ( tok - > variable ( ) & & ! tok - > variable ( ) - > isPointer ( ) & & ! tok - > variable ( ) - > isReference ( ) ) {
// smart pointer is not uninitialized
if ( tok - > variable ( ) - > isSmartPointer ( ) )
return ;
2020-09-07 09:13:31 +02:00
2020-09-07 21:19:07 +02:00
// struct
if ( tok - > variable ( ) - > type ( ) & & tok - > variable ( ) - > type ( ) - > needInitialization = = Type : : NeedInitialization : : False )
return ;
// template variable is not uninitialized
if ( Token : : findmatch ( tok - > variable ( ) - > typeStartToken ( ) , " %name% < " , tok - > variable ( ) - > typeEndToken ( ) ) )
return ;
}
2020-09-07 16:25:37 +02:00
2020-06-19 13:16:48 +02:00
// lhs in assignment
if ( tok - > astParent ( ) - > str ( ) = = " = " & & tok = = tok - > astParent ( ) - > astOperand1 ( ) )
return ;
// Avoid FP when there is bailout..
if ( value . type = = ExprEngine : : ValueType : : BailoutValue ) {
if ( tok - > hasKnownValue ( ) )
return ;
2020-07-13 20:23:44 +02:00
if ( ! tok - > variable ( ) )
// FIXME
2020-06-19 13:16:48 +02:00
return ;
2020-07-13 20:23:44 +02:00
2020-06-19 13:16:48 +02:00
// lhs for scope operator
if ( Token : : Match ( tok , " %name% :: " ) )
return ;
if ( tok - > astParent ( ) - > str ( ) = = " :: " & & tok = = tok - > astParent ( ) - > astOperand1 ( ) )
return ;
2020-07-14 08:12:40 +02:00
// Object allocated on the stack
2020-07-14 11:15:26 +02:00
if ( Token : : Match ( tok , " %var% . " ) & & tok - > next ( ) - > originalName ( ) ! = " -> " )
2020-07-14 08:12:40 +02:00
return ;
2020-07-19 16:54:44 +02:00
// Assume that stream object is initialized
if ( Token : : Match ( tok - > previous ( ) , " [;{}] %var% <<|>> " ) & & ! tok - > next ( ) - > astParent ( ) )
return ;
2020-06-19 13:16:48 +02:00
// Containers are not uninitialized
std : : vector < const Token * > tokens { tok , tok - > astOperand1 ( ) , tok - > astOperand2 ( ) } ;
if ( Token : : Match ( tok - > previous ( ) , " . %name% " ) )
tokens . push_back ( tok - > previous ( ) - > astOperand1 ( ) ) ;
for ( const Token * t : tokens ) {
if ( t & & t - > valueType ( ) & & t - > valueType ( ) - > pointer = = 0 & & t - > valueType ( ) - > container )
return ;
}
const Variable * var = tok - > variable ( ) ;
2020-07-13 20:23:44 +02:00
if ( var & & var - > nameToken ( ) = = tok )
return ;
if ( var & & ! var - > isLocal ( ) )
return ; // FIXME
2020-06-19 13:16:48 +02:00
if ( var & & ! var - > isPointer ( ) ) {
if ( ! var - > isLocal ( ) | | var - > isStatic ( ) )
return ;
}
2020-09-09 16:22:36 +02:00
if ( var & & ( Token : : Match ( var - > nameToken ( ) , " %name% [=:({)] " ) | | var - > isInit ( ) ) )
2020-06-19 13:16:48 +02:00
return ;
if ( var & & var - > nameToken ( ) = = tok )
return ;
2020-07-13 20:23:44 +02:00
// Are there unconditional assignment?
if ( var & & Token : : Match ( var - > nameToken ( ) , " %varid% ;| %varid%| = " , tok - > varId ( ) ) )
return ;
2020-06-19 13:16:48 +02:00
2020-07-19 16:27:56 +02:00
// Arrays are allocated on the stack
if ( var & & Token : : Match ( tok , " %var% [ " ) & & var - > isArray ( ) )
return ;
2020-07-17 13:20:31 +02:00
if ( tok - > variable ( ) & & isVariableAssigned ( tok - > variable ( ) , tok ) )
return ;
}
2020-07-17 09:05:38 +02:00
2020-06-19 13:16:48 +02:00
// Uninitialized function argument
2020-07-13 11:43:11 +02:00
bool inconclusive = false ;
2020-06-19 13:16:48 +02:00
if ( Token : : Match ( tok - > astParent ( ) , " [,(] " ) ) {
const Token * parent = tok - > astParent ( ) ;
int count = 0 ;
if ( Token : : simpleMatch ( parent , " , " ) ) {
if ( tok = = parent - > astOperand2 ( ) )
2020-06-20 23:00:39 +02:00
count = 1 ;
parent = parent - > astParent ( ) ;
2020-06-19 13:16:48 +02:00
while ( Token : : simpleMatch ( parent , " , " ) ) {
count + + ;
parent = parent - > astParent ( ) ;
}
}
if ( Token : : simpleMatch ( parent , " ( " ) & & parent - > astOperand1 ( ) ! = tok ) {
if ( parent - > astOperand1 ( ) - > function ( ) ) {
const Variable * argvar = parent - > astOperand1 ( ) - > function ( ) - > getArgumentVar ( count ) ;
if ( argvar & & argvar - > isReference ( ) & & ! argvar - > isConst ( ) )
return ;
2020-07-13 11:43:11 +02:00
if ( uninitData & & argvar & & ! argvar - > isConst ( ) ) {
if ( parent - > astOperand1 ( ) - > function ( ) - > hasBody ( ) )
return ;
inconclusive = true ;
}
if ( ! uninitStructMember . empty ( ) & & dataBase - > isC ( ) & & argvar & & ! argvar - > isConst ( ) ) {
if ( parent - > astOperand1 ( ) - > function ( ) - > hasBody ( ) )
return ;
inconclusive = true ;
}
2020-06-27 14:29:29 +02:00
} else if ( uninitData ) {
if ( dataBase - > settings - > library . getFunction ( parent - > astOperand1 ( ) ) )
return ;
2020-06-27 14:59:02 +02:00
if ( parent - > astOperand1 ( ) - > isKeyword ( ) )
return ;
2020-06-19 13:16:48 +02:00
}
2020-06-27 14:15:53 +02:00
} else if ( uninitData )
return ;
2020-06-19 13:16:48 +02:00
}
2021-02-24 22:00:06 +01:00
if ( inconclusive & & ! dataBase - > settings - > certainty . isEnabled ( Certainty : : inconclusive ) )
2020-07-13 11:43:11 +02:00
return ;
2020-06-19 13:16:48 +02:00
// Avoid FP for array declaration
const Token * parent = tok - > astParent ( ) ;
while ( parent & & parent - > str ( ) = = " [ " )
parent = parent - > astParent ( ) ;
if ( ! parent )
return ;
2020-07-13 11:43:11 +02:00
const std : : string inconclusiveMessage ( inconclusive ? " . It is inconclusive if there would be a problem in the function call. " : " " ) ;
2020-06-19 13:16:48 +02:00
if ( ! uninitStructMember . empty ( ) ) {
2020-07-18 18:14:55 +02:00
const std : : string symbol = tok - > expressionString ( ) + " . " + uninitStructMember ;
2020-06-19 13:16:48 +02:00
dataBase - > reportError ( tok ,
Severity : : SeverityType : : error ,
" bughuntingUninitStructMember " ,
2020-07-18 18:14:55 +02:00
" $symbol: " + symbol + " \n Cannot determine that '$symbol' is initialized " + inconclusiveMessage ,
2020-06-19 13:16:48 +02:00
CWE_USE_OF_UNINITIALIZED_VARIABLE ,
2020-07-13 11:43:11 +02:00
inconclusive ,
2020-06-19 13:16:48 +02:00
value . type = = ExprEngine : : ValueType : : BailoutValue ) ;
return ;
}
2020-06-27 14:59:02 +02:00
std : : string uninitexpr = tok - > expressionString ( ) ;
if ( uninitData )
uninitexpr + = " [0] " ;
2020-07-18 18:14:55 +02:00
const std : : string symbol = ( tok - > varId ( ) > 0 ) ? ( " $symbol: " + tok - > str ( ) + " \n " ) : std : : string ( ) ;
2020-12-08 11:16:26 +01:00
std : : string constMessage ;
2020-12-08 10:28:40 +01:00
std : : string errorId = " bughuntingUninit " ;
{
const Token * vartok = tok ;
2020-12-08 11:16:26 +01:00
while ( Token : : Match ( vartok , " .|[ " ) )
vartok = vartok - > astOperand1 ( ) ;
2020-12-08 10:28:40 +01:00
const Variable * var = vartok ? vartok - > variable ( ) : nullptr ;
2020-12-08 11:16:26 +01:00
if ( var & & var - > isArgument ( ) ) {
errorId + = " NonConstArg " ;
constMessage = " (you can use 'const' to say data must be initialized) " ;
}
2020-12-08 10:28:40 +01:00
}
2020-06-19 13:16:48 +02:00
dataBase - > reportError ( tok ,
Severity : : SeverityType : : error ,
2020-12-08 10:28:40 +01:00
errorId . c_str ( ) ,
2020-12-08 11:16:26 +01:00
symbol + " Cannot determine that ' " + uninitexpr + " ' is initialized " + constMessage + inconclusiveMessage ,
2020-06-19 13:16:48 +02:00
CWE_USE_OF_UNINITIALIZED_VARIABLE ,
2020-07-13 11:43:11 +02:00
inconclusive ,
2020-06-19 13:16:48 +02:00
value . type = = ExprEngine : : ValueType : : BailoutValue ) ;
}
static void checkFunctionCall ( const Token * tok , const ExprEngine : : Value & value , ExprEngine : : DataBase * dataBase )
{
if ( ! Token : : Match ( tok - > astParent ( ) , " [(,] " ) )
return ;
const Token * parent = tok - > astParent ( ) ;
while ( Token : : simpleMatch ( parent , " , " ) )
parent = parent - > astParent ( ) ;
if ( ! parent | | parent - > str ( ) ! = " ( " )
return ;
int num = 0 ;
for ( const Token * argTok : getArguments ( parent - > astOperand1 ( ) ) ) {
- - num ;
if ( argTok = = tok ) {
num = - num ;
break ;
}
}
if ( num < = 0 )
return ;
if ( parent - > astOperand1 ( ) - > function ( ) ) {
const Variable * arg = parent - > astOperand1 ( ) - > function ( ) - > getArgumentVar ( num - 1 ) ;
if ( arg & & arg - > nameToken ( ) ) {
std : : string bad ;
MathLib : : bigint low ;
if ( arg - > nameToken ( ) - > getCppcheckAttribute ( TokenImpl : : CppcheckAttributes : : Type : : LOW , & low ) ) {
if ( ! ( tok - > hasKnownIntValue ( ) & & tok - > getKnownIntValue ( ) > = low ) & & value . isLessThan ( dataBase , low ) )
bad = " __cppcheck_low__( " + std : : to_string ( low ) + " ) " ;
}
MathLib : : bigint high ;
if ( arg - > nameToken ( ) - > getCppcheckAttribute ( TokenImpl : : CppcheckAttributes : : Type : : HIGH , & high ) ) {
if ( ! ( tok - > hasKnownIntValue ( ) & & tok - > getKnownIntValue ( ) < = high ) & & value . isGreaterThan ( dataBase , high ) )
bad = " __cppcheck_high__( " + std : : to_string ( high ) + " ) " ;
}
if ( ! bad . empty ( ) ) {
dataBase - > reportError ( tok ,
Severity : : SeverityType : : error ,
" bughuntingInvalidArgValue " ,
" There is function call, cannot determine that " + std : : to_string ( num ) + getOrdinalText ( num ) + " argument value meets the attribute " + bad ,
CWE ( 0 ) ,
false ) ;
return ;
}
}
}
// Check invalid function argument values..
for ( const Library : : InvalidArgValue & invalidArgValue : Library : : getInvalidArgValues ( dataBase - > settings - > library . validarg ( parent - > astOperand1 ( ) , num ) ) ) {
bool err = false ;
std : : string bad ;
switch ( invalidArgValue . type ) {
2021-01-08 10:29:01 +01:00
case Library : : InvalidArgValue : : Type : : eq :
2020-06-19 13:16:48 +02:00
if ( ! tok - > hasKnownIntValue ( ) | | tok - > getKnownIntValue ( ) = = MathLib : : toLongNumber ( invalidArgValue . op1 ) )
err = value . isEqual ( dataBase , MathLib : : toLongNumber ( invalidArgValue . op1 ) ) ;
bad = " equals " + invalidArgValue . op1 ;
break ;
2021-01-08 10:29:01 +01:00
case Library : : InvalidArgValue : : Type : : le :
2020-06-19 13:16:48 +02:00
if ( ! tok - > hasKnownIntValue ( ) | | tok - > getKnownIntValue ( ) < = MathLib : : toLongNumber ( invalidArgValue . op1 ) )
err = value . isLessThan ( dataBase , MathLib : : toLongNumber ( invalidArgValue . op1 ) + 1 ) ;
bad = " less equal " + invalidArgValue . op1 ;
break ;
2021-01-08 10:29:01 +01:00
case Library : : InvalidArgValue : : Type : : lt :
2020-06-19 13:16:48 +02:00
if ( ! tok - > hasKnownIntValue ( ) | | tok - > getKnownIntValue ( ) < MathLib : : toLongNumber ( invalidArgValue . op1 ) )
err = value . isLessThan ( dataBase , MathLib : : toLongNumber ( invalidArgValue . op1 ) ) ;
bad = " less than " + invalidArgValue . op1 ;
break ;
2021-01-08 10:29:01 +01:00
case Library : : InvalidArgValue : : Type : : ge :
2020-06-19 13:16:48 +02:00
if ( ! tok - > hasKnownIntValue ( ) | | tok - > getKnownIntValue ( ) > = MathLib : : toLongNumber ( invalidArgValue . op1 ) )
err = value . isGreaterThan ( dataBase , MathLib : : toLongNumber ( invalidArgValue . op1 ) - 1 ) ;
bad = " greater equal " + invalidArgValue . op1 ;
break ;
2021-01-08 10:29:01 +01:00
case Library : : InvalidArgValue : : Type : : gt :
2020-06-19 13:16:48 +02:00
if ( ! tok - > hasKnownIntValue ( ) | | tok - > getKnownIntValue ( ) > MathLib : : toLongNumber ( invalidArgValue . op1 ) )
err = value . isGreaterThan ( dataBase , MathLib : : toLongNumber ( invalidArgValue . op1 ) ) ;
bad = " greater than " + invalidArgValue . op1 ;
break ;
2021-01-08 10:29:01 +01:00
case Library : : InvalidArgValue : : Type : : range :
2020-06-19 13:16:48 +02:00
// TODO
err = value . isEqual ( dataBase , MathLib : : toLongNumber ( invalidArgValue . op1 ) ) ;
err | = value . isEqual ( dataBase , MathLib : : toLongNumber ( invalidArgValue . op2 ) ) ;
bad = " range " + invalidArgValue . op1 + " - " + invalidArgValue . op2 ;
break ;
}
if ( err ) {
dataBase - > reportError ( tok , Severity : : SeverityType : : error , " bughuntingInvalidArgValue " , " There is function call, cannot determine that " + std : : to_string ( num ) + getOrdinalText ( num ) + " argument value is valid. Bad value: " + bad , CWE ( 0 ) , false ) ;
break ;
}
}
// Uninitialized function argument..
if ( dataBase - > settings - > library . isuninitargbad ( parent - > astOperand1 ( ) , num ) & & dataBase - > settings - > library . isnullargbad ( parent - > astOperand1 ( ) , num ) & & value . type = = ExprEngine : : ValueType : : ArrayValue ) {
const ExprEngine : : ArrayValue & arrayValue = static_cast < const ExprEngine : : ArrayValue & > ( value ) ;
auto index0 = std : : make_shared < ExprEngine : : IntRange > ( " 0 " , 0 , 0 ) ;
for ( const auto & v : arrayValue . read ( index0 ) ) {
2022-01-03 12:46:33 +01:00
if ( v . second - > isUninit ( dataBase ) ) {
2020-06-19 13:16:48 +02:00
dataBase - > reportError ( tok , Severity : : SeverityType : : error , " bughuntingUninitArg " , " There is function call, cannot determine that " + std : : to_string ( num ) + getOrdinalText ( num ) + " argument is initialized. " , CWE_USE_OF_UNINITIALIZED_VARIABLE , false ) ;
break ;
}
}
}
}
static void checkAssignment ( const Token * tok , const ExprEngine : : Value & value , ExprEngine : : DataBase * dataBase )
{
if ( ! Token : : simpleMatch ( tok - > astParent ( ) , " = " ) )
return ;
const Token * lhs = tok - > astParent ( ) - > astOperand1 ( ) ;
while ( Token : : simpleMatch ( lhs , " . " ) )
lhs = lhs - > astOperand2 ( ) ;
if ( ! lhs | | ! lhs - > variable ( ) | | ! lhs - > variable ( ) - > nameToken ( ) )
return ;
const Token * vartok = lhs - > variable ( ) - > nameToken ( ) ;
2020-08-22 11:37:44 +02:00
bool executable = false ;
std : : string fullName = lhs - > variable ( ) - > name ( ) ;
for ( const Scope * s = lhs - > variable ( ) - > nameToken ( ) - > scope ( ) ; s - > type ! = Scope : : ScopeType : : eGlobal ; s = s - > nestedIn ) {
if ( s - > isExecutable ( ) ) {
executable = true ;
break ;
}
fullName = s - > className + " :: " + fullName ;
}
auto getMinMaxValue = [ = ] ( TokenImpl : : CppcheckAttributes : : Type type , MathLib : : bigint * val ) {
if ( vartok - > getCppcheckAttribute ( type , val ) )
return true ;
if ( ! executable ) {
const auto it = dataBase - > settings - > variableContracts . find ( fullName ) ;
if ( it ! = dataBase - > settings - > variableContracts . end ( ) ) {
const std : : string * str ;
if ( type = = TokenImpl : : CppcheckAttributes : : Type : : LOW )
str = & it - > second . minValue ;
else if ( type = = TokenImpl : : CppcheckAttributes : : Type : : HIGH )
str = & it - > second . maxValue ;
else
return false ;
* val = MathLib : : toLongNumber ( * str ) ;
return ! str - > empty ( ) ;
}
}
return false ;
} ;
2020-06-19 13:16:48 +02:00
MathLib : : bigint low ;
2020-08-22 11:37:44 +02:00
if ( getMinMaxValue ( TokenImpl : : CppcheckAttributes : : Type : : LOW , & low ) ) {
2020-06-19 13:16:48 +02:00
if ( value . isLessThan ( dataBase , low ) )
dataBase - > reportError ( tok , Severity : : SeverityType : : error , " bughuntingAssign " , " There is assignment, cannot determine that value is greater or equal with " + std : : to_string ( low ) , CWE_INCORRECT_CALCULATION , false ) ;
}
MathLib : : bigint high ;
2020-08-22 11:37:44 +02:00
if ( getMinMaxValue ( TokenImpl : : CppcheckAttributes : : Type : : HIGH , & high ) ) {
2020-06-19 13:16:48 +02:00
if ( value . isGreaterThan ( dataBase , high ) )
dataBase - > reportError ( tok , Severity : : SeverityType : : error , " bughuntingAssign " , " There is assignment, cannot determine that value is lower or equal with " + std : : to_string ( high ) , CWE_INCORRECT_CALCULATION , false ) ;
}
}
void addBughuntingChecks ( std : : vector < ExprEngine : : Callback > * callbacks )
{
2020-12-05 11:46:07 +01:00
callbacks - > push_back ( arrayIndex ) ;
2020-06-19 13:16:48 +02:00
callbacks - > push_back ( bufferOverflow ) ;
callbacks - > push_back ( divByZero ) ;
callbacks - > push_back ( checkFunctionCall ) ;
callbacks - > push_back ( checkAssignment ) ;
# ifdef BUG_HUNTING_INTEGEROVERFLOW
callbacks - > push_back ( integerOverflow ) ;
# endif
callbacks - > push_back ( uninit ) ;
}