2018-12-25 21:11:23 +01:00
/*
* Cppcheck - A tool for static C / C + + code analysis
2023-01-28 10:16:34 +01:00
* Copyright ( C ) 2007 - 2023 Cppcheck team .
2018-12-25 21:11:23 +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
* along with this program . If not , see < http : //www.gnu.org/licenses/>.
*/
//---------------------------------------------------------------------------
# include "ctu.h"
2022-01-27 19:03:20 +01:00
2018-12-25 21:11:23 +01:00
# include "astutils.h"
2022-01-27 19:03:20 +01:00
# include "errortypes.h"
2020-04-13 13:44:48 +02:00
# include "settings.h"
2018-12-25 21:11:23 +01:00
# include "symboldatabase.h"
2022-01-27 19:03:20 +01:00
# include "token.h"
2020-05-23 07:16:49 +02:00
# include "tokenize.h"
2022-01-27 19:03:20 +01:00
# include "tokenlist.h"
2022-02-11 19:44:08 +01:00
# include "utils.h"
2020-05-23 07:16:49 +02:00
2022-07-08 16:42:57 +02:00
# include <algorithm>
2022-01-27 19:03:20 +01:00
# include <cstdint>
# include <cstring>
2019-05-09 06:55:36 +02:00
# include <iterator> // back_inserter
2022-08-19 10:16:23 +02:00
# include <sstream>
2022-01-27 19:03:20 +01:00
# include <utility>
# include <tinyxml2.h>
2018-12-25 21:11:23 +01:00
//---------------------------------------------------------------------------
2018-12-30 11:55:39 +01:00
static const char ATTR_CALL_ID [ ] = " call-id " ;
static const char ATTR_CALL_FUNCNAME [ ] = " call-funcname " ;
static const char ATTR_CALL_ARGNR [ ] = " call-argnr " ;
static const char ATTR_CALL_ARGEXPR [ ] = " call-argexpr " ;
static const char ATTR_CALL_ARGVALUETYPE [ ] = " call-argvaluetype " ;
static const char ATTR_CALL_ARGVALUE [ ] = " call-argvalue " ;
2018-12-30 18:31:37 +01:00
static const char ATTR_WARNING [ ] = " warning " ;
2019-09-12 10:46:33 +02:00
static const char ATTR_LOC_FILENAME [ ] = " file " ;
static const char ATTR_LOC_LINENR [ ] = " line " ;
static const char ATTR_LOC_COLUMN [ ] = " col " ;
2018-12-30 18:31:37 +01:00
static const char ATTR_INFO [ ] = " info " ;
2018-12-30 11:55:39 +01:00
static const char ATTR_MY_ID [ ] = " my-id " ;
static const char ATTR_MY_ARGNR [ ] = " my-argnr " ;
static const char ATTR_MY_ARGNAME [ ] = " my-argname " ;
2019-03-23 08:36:10 +01:00
static const char ATTR_VALUE [ ] = " value " ;
2018-12-30 11:55:39 +01:00
2019-02-03 17:51:02 +01:00
int CTU : : maxCtuDepth = 2 ;
2018-12-30 11:55:39 +01:00
2018-12-25 21:11:23 +01:00
std : : string CTU : : getFunctionId ( const Tokenizer * tokenizer , const Function * function )
{
2019-08-18 12:19:05 +02:00
return tokenizer - > list . file ( function - > tokenDef ) + ' : ' + MathLib : : toString ( function - > tokenDef - > linenr ( ) ) + ' : ' + MathLib : : toString ( function - > tokenDef - > column ( ) ) ;
2018-12-25 21:11:23 +01:00
}
CTU : : FileInfo : : Location : : Location ( const Tokenizer * tokenizer , const Token * tok )
2019-01-12 18:29:08 +01:00
: fileName ( tokenizer - > list . file ( tok ) )
2019-09-12 10:46:33 +02:00
, lineNumber ( tok - > linenr ( ) )
, column ( tok - > column ( ) )
2021-08-07 20:51:18 +02:00
{ }
2018-12-25 21:11:23 +01:00
std : : string CTU : : FileInfo : : toString ( ) const
{
std : : ostringstream out ;
// Function calls..
for ( const CTU : : FileInfo : : FunctionCall & functionCall : functionCalls ) {
2018-12-30 11:55:39 +01:00
out < < functionCall . toXmlString ( ) ;
2018-12-25 21:11:23 +01:00
}
// Nested calls..
for ( const CTU : : FileInfo : : NestedCall & nestedCall : nestedCalls ) {
2018-12-30 11:55:39 +01:00
out < < nestedCall . toXmlString ( ) < < " \n " ;
2018-12-25 21:11:23 +01:00
}
return out . str ( ) ;
}
2018-12-30 11:55:39 +01:00
std : : string CTU : : FileInfo : : CallBase : : toBaseXmlString ( ) const
{
std : : ostringstream out ;
out < < " " < < ATTR_CALL_ID < < " = \" " < < callId < < " \" "
2021-08-06 07:57:03 +02:00
< < " " < < ATTR_CALL_FUNCNAME < < " = \" " < < ErrorLogger : : toxml ( callFunctionName ) < < " \" "
2018-12-30 11:55:39 +01:00
< < " " < < ATTR_CALL_ARGNR < < " = \" " < < callArgNr < < " \" "
2021-08-06 07:57:03 +02:00
< < " " < < ATTR_LOC_FILENAME < < " = \" " < < ErrorLogger : : toxml ( location . fileName ) < < " \" "
2019-09-12 10:46:33 +02:00
< < " " < < ATTR_LOC_LINENR < < " = \" " < < location . lineNumber < < " \" "
< < " " < < ATTR_LOC_COLUMN < < " = \" " < < location . column < < " \" " ;
2018-12-30 11:55:39 +01:00
return out . str ( ) ;
}
std : : string CTU : : FileInfo : : FunctionCall : : toXmlString ( ) const
{
std : : ostringstream out ;
out < < " <function-call "
< < toBaseXmlString ( )
2021-08-04 21:39:13 +02:00
< < " " < < ATTR_CALL_ARGEXPR < < " = \" " < < ErrorLogger : : toxml ( callArgumentExpression ) < < " \" "
2021-01-02 09:30:00 +01:00
< < " " < < ATTR_CALL_ARGVALUETYPE < < " = \" " < < static_cast < int > ( callValueType ) < < " \" "
2018-12-30 18:31:37 +01:00
< < " " < < ATTR_CALL_ARGVALUE < < " = \" " < < callArgValue < < " \" " ;
if ( warning )
out < < " " < < ATTR_WARNING < < " = \" true \" " ;
if ( callValuePath . empty ( ) )
out < < " /> " ;
else {
out < < " > \n " ;
2020-05-23 07:16:49 +02:00
for ( const ErrorMessage : : FileLocation & loc : callValuePath )
2018-12-30 18:31:37 +01:00
out < < " <path "
2021-08-04 21:39:13 +02:00
< < " " < < ATTR_LOC_FILENAME < < " = \" " < < ErrorLogger : : toxml ( loc . getfile ( ) ) < < " \" "
2018-12-30 18:31:37 +01:00
< < " " < < ATTR_LOC_LINENR < < " = \" " < < loc . line < < " \" "
2019-09-12 10:46:33 +02:00
< < " " < < ATTR_LOC_COLUMN < < " = \" " < < loc . column < < " \" "
2021-08-04 21:39:13 +02:00
< < " " < < ATTR_INFO < < " = \" " < < ErrorLogger : : toxml ( loc . getinfo ( ) ) < < " \" /> \n " ;
2018-12-30 18:31:37 +01:00
out < < " </function-call> " ;
}
2018-12-30 11:55:39 +01:00
return out . str ( ) ;
}
std : : string CTU : : FileInfo : : NestedCall : : toXmlString ( ) const
{
std : : ostringstream out ;
out < < " <function-call "
< < toBaseXmlString ( )
< < " " < < ATTR_MY_ID < < " = \" " < < myId < < " \" "
< < " " < < ATTR_MY_ARGNR < < " = \" " < < myArgNr < < " \" "
< < " /> " ;
return out . str ( ) ;
}
2018-12-25 21:11:23 +01:00
std : : string CTU : : FileInfo : : UnsafeUsage : : toString ( ) const
{
std : : ostringstream out ;
out < < " <unsafe-usage "
2018-12-30 11:55:39 +01:00
< < " " < < ATTR_MY_ID < < " = \" " < < myId < < ' \" '
< < " " < < ATTR_MY_ARGNR < < " = \" " < < myArgNr < < ' \" '
< < " " < < ATTR_MY_ARGNAME < < " = \" " < < myArgumentName < < ' \" '
2021-08-04 21:39:13 +02:00
< < " " < < ATTR_LOC_FILENAME < < " = \" " < < ErrorLogger : : toxml ( location . fileName ) < < ' \" '
2019-09-12 10:46:33 +02:00
< < " " < < ATTR_LOC_LINENR < < " = \" " < < location . lineNumber < < ' \" '
< < " " < < ATTR_LOC_COLUMN < < " = \" " < < location . column < < ' \" '
2019-03-23 08:36:10 +01:00
< < " " < < ATTR_VALUE < < " = \" " < < value < < " \" "
2018-12-25 21:11:23 +01:00
< < " /> \n " ;
return out . str ( ) ;
}
2018-12-26 15:56:10 +01:00
std : : string CTU : : toString ( const std : : list < CTU : : FileInfo : : UnsafeUsage > & unsafeUsage )
{
std : : ostringstream ret ;
for ( const CTU : : FileInfo : : UnsafeUsage & u : unsafeUsage )
ret < < u . toString ( ) ;
return ret . str ( ) ;
}
2018-12-30 11:55:39 +01:00
CTU : : FileInfo : : CallBase : : CallBase ( const Tokenizer * tokenizer , const Token * callToken )
: callId ( getFunctionId ( tokenizer , callToken - > function ( ) ) )
, callFunctionName ( callToken - > next ( ) - > astOperand1 ( ) - > expressionString ( ) )
, location ( CTU : : FileInfo : : Location ( tokenizer , callToken ) )
2021-08-07 20:51:18 +02:00
{ }
2018-12-30 11:55:39 +01:00
CTU : : FileInfo : : NestedCall : : NestedCall ( const Tokenizer * tokenizer , const Function * myFunction , const Token * callToken )
: CallBase ( tokenizer , callToken )
, myId ( getFunctionId ( tokenizer , myFunction ) )
2021-08-07 20:51:18 +02:00
{ }
2018-12-30 11:55:39 +01:00
static std : : string readAttrString ( const tinyxml2 : : XMLElement * e , const char * attr , bool * error )
{
const char * value = e - > Attribute ( attr ) ;
if ( ! value & & error )
* error = true ;
return value ? value : " " ;
}
static long long readAttrInt ( const tinyxml2 : : XMLElement * e , const char * attr , bool * error )
2018-12-25 21:11:23 +01:00
{
2021-05-01 18:40:20 +02:00
int64_t value = 0 ;
2022-10-02 07:12:40 +02:00
const bool err = ( e - > QueryInt64Attribute ( attr , & value ) ! = tinyxml2 : : XML_SUCCESS ) ;
2021-05-01 18:40:20 +02:00
if ( error )
* error = err ;
return value ;
2018-12-30 11:55:39 +01:00
}
2019-06-15 13:28:01 +02:00
bool CTU : : FileInfo : : CallBase : : loadBaseFromXml ( const tinyxml2 : : XMLElement * xmlElement )
2018-12-30 11:55:39 +01:00
{
bool error = false ;
2019-06-15 13:28:01 +02:00
callId = readAttrString ( xmlElement , ATTR_CALL_ID , & error ) ;
callFunctionName = readAttrString ( xmlElement , ATTR_CALL_FUNCNAME , & error ) ;
callArgNr = readAttrInt ( xmlElement , ATTR_CALL_ARGNR , & error ) ;
location . fileName = readAttrString ( xmlElement , ATTR_LOC_FILENAME , & error ) ;
2019-09-12 10:46:33 +02:00
location . lineNumber = readAttrInt ( xmlElement , ATTR_LOC_LINENR , & error ) ;
location . column = readAttrInt ( xmlElement , ATTR_LOC_COLUMN , & error ) ;
2018-12-30 11:55:39 +01:00
return ! error ;
}
2019-06-15 13:28:01 +02:00
bool CTU : : FileInfo : : FunctionCall : : loadFromXml ( const tinyxml2 : : XMLElement * xmlElement )
2018-12-30 11:55:39 +01:00
{
2019-06-15 13:28:01 +02:00
if ( ! loadBaseFromXml ( xmlElement ) )
2018-12-30 11:55:39 +01:00
return false ;
bool error = false ;
2019-06-15 13:28:01 +02:00
callArgumentExpression = readAttrString ( xmlElement , ATTR_CALL_ARGEXPR , & error ) ;
callValueType = ( ValueFlow : : Value : : ValueType ) readAttrInt ( xmlElement , ATTR_CALL_ARGVALUETYPE , & error ) ;
callArgValue = readAttrInt ( xmlElement , ATTR_CALL_ARGVALUE , & error ) ;
const char * w = xmlElement - > Attribute ( ATTR_WARNING ) ;
2018-12-30 18:31:37 +01:00
warning = w & & std : : strcmp ( w , " true " ) = = 0 ;
2019-06-15 13:28:01 +02:00
for ( const tinyxml2 : : XMLElement * e2 = xmlElement - > FirstChildElement ( ) ; ! error & & e2 ; e2 = e2 - > NextSiblingElement ( ) ) {
2018-12-30 18:31:37 +01:00
if ( std : : strcmp ( e2 - > Name ( ) , " path " ) ! = 0 )
continue ;
2020-05-23 07:16:49 +02:00
ErrorMessage : : FileLocation loc ;
2018-12-30 18:31:37 +01:00
loc . setfile ( readAttrString ( e2 , ATTR_LOC_FILENAME , & error ) ) ;
loc . line = readAttrInt ( e2 , ATTR_LOC_LINENR , & error ) ;
2019-09-12 10:46:33 +02:00
loc . column = readAttrInt ( e2 , ATTR_LOC_COLUMN , & error ) ;
2018-12-30 18:31:37 +01:00
loc . setinfo ( readAttrString ( e2 , ATTR_INFO , & error ) ) ;
}
2018-12-30 11:55:39 +01:00
return ! error ;
}
2019-06-15 13:28:01 +02:00
bool CTU : : FileInfo : : NestedCall : : loadFromXml ( const tinyxml2 : : XMLElement * xmlElement )
2018-12-30 11:55:39 +01:00
{
2019-06-15 13:28:01 +02:00
if ( ! loadBaseFromXml ( xmlElement ) )
2018-12-30 11:55:39 +01:00
return false ;
bool error = false ;
2019-06-15 13:28:01 +02:00
myId = readAttrString ( xmlElement , ATTR_MY_ID , & error ) ;
myArgNr = readAttrInt ( xmlElement , ATTR_MY_ARGNR , & error ) ;
2018-12-30 11:55:39 +01:00
return ! error ;
2018-12-25 21:11:23 +01:00
}
void CTU : : FileInfo : : loadFromXml ( const tinyxml2 : : XMLElement * xmlElement )
{
for ( const tinyxml2 : : XMLElement * e = xmlElement - > FirstChildElement ( ) ; e ; e = e - > NextSiblingElement ( ) ) {
if ( std : : strcmp ( e - > Name ( ) , " function-call " ) = = 0 ) {
FunctionCall functionCall ;
2018-12-30 11:55:39 +01:00
if ( functionCall . loadFromXml ( e ) )
2022-09-08 09:21:35 +02:00
functionCalls . push_back ( std : : move ( functionCall ) ) ;
2018-12-25 21:11:23 +01:00
} else if ( std : : strcmp ( e - > Name ( ) , " nested-call " ) = = 0 ) {
NestedCall nestedCall ;
2018-12-30 11:55:39 +01:00
if ( nestedCall . loadFromXml ( e ) )
2022-09-08 09:21:35 +02:00
nestedCalls . push_back ( std : : move ( nestedCall ) ) ;
2018-12-25 21:11:23 +01:00
}
}
}
2018-12-30 16:23:25 +01:00
std : : map < std : : string , std : : list < const CTU : : FileInfo : : CallBase * > > CTU : : FileInfo : : getCallsMap ( ) const
2018-12-25 21:11:23 +01:00
{
2018-12-30 16:23:25 +01:00
std : : map < std : : string , std : : list < const CTU : : FileInfo : : CallBase * > > ret ;
2018-12-25 21:11:23 +01:00
for ( const CTU : : FileInfo : : NestedCall & nc : nestedCalls )
2018-12-30 16:23:25 +01:00
ret [ nc . callId ] . push_back ( & nc ) ;
for ( const CTU : : FileInfo : : FunctionCall & fc : functionCalls )
ret [ fc . callId ] . push_back ( & fc ) ;
2018-12-25 21:11:23 +01:00
return ret ;
}
std : : list < CTU : : FileInfo : : UnsafeUsage > CTU : : loadUnsafeUsageListFromXml ( const tinyxml2 : : XMLElement * xmlElement )
{
std : : list < CTU : : FileInfo : : UnsafeUsage > ret ;
for ( const tinyxml2 : : XMLElement * e = xmlElement - > FirstChildElement ( ) ; e ; e = e - > NextSiblingElement ( ) ) {
if ( std : : strcmp ( e - > Name ( ) , " unsafe-usage " ) ! = 0 )
continue ;
2018-12-30 11:55:39 +01:00
bool error = false ;
FileInfo : : UnsafeUsage unsafeUsage ;
unsafeUsage . myId = readAttrString ( e , ATTR_MY_ID , & error ) ;
unsafeUsage . myArgNr = readAttrInt ( e , ATTR_MY_ARGNR , & error ) ;
unsafeUsage . myArgumentName = readAttrString ( e , ATTR_MY_ARGNAME , & error ) ;
unsafeUsage . location . fileName = readAttrString ( e , ATTR_LOC_FILENAME , & error ) ;
2019-09-12 10:46:33 +02:00
unsafeUsage . location . lineNumber = readAttrInt ( e , ATTR_LOC_LINENR , & error ) ;
unsafeUsage . location . column = readAttrInt ( e , ATTR_LOC_COLUMN , & error ) ;
2019-03-23 08:36:10 +01:00
unsafeUsage . value = readAttrInt ( e , ATTR_VALUE , & error ) ;
2018-12-30 11:55:39 +01:00
if ( ! error )
2022-09-08 09:21:35 +02:00
ret . push_back ( std : : move ( unsafeUsage ) ) ;
2018-12-25 21:11:23 +01:00
}
return ret ;
}
static int isCallFunction ( const Scope * scope , int argnr , const Token * * tok )
{
const Variable * const argvar = scope - > function - > getArgumentVar ( argnr ) ;
if ( ! argvar - > isPointer ( ) )
return - 1 ;
for ( const Token * tok2 = scope - > bodyStart ; tok2 ! = scope - > bodyEnd ; tok2 = tok2 - > next ( ) ) {
if ( tok2 - > variable ( ) ! = argvar )
continue ;
if ( ! Token : : Match ( tok2 - > previous ( ) , " [(,] %var% [,)] " ) )
break ;
int argnr2 = 1 ;
const Token * prev = tok2 ;
while ( prev & & prev - > str ( ) ! = " ( " ) {
if ( Token : : Match ( prev , " ]|) " ) )
prev = prev - > link ( ) ;
else if ( prev - > str ( ) = = " , " )
+ + argnr2 ;
prev = prev - > previous ( ) ;
}
if ( ! prev | | ! Token : : Match ( prev - > previous ( ) , " %name% ( " ) )
break ;
if ( ! prev - > astOperand1 ( ) | | ! prev - > astOperand1 ( ) - > function ( ) )
break ;
* tok = prev - > previous ( ) ;
return argnr2 ;
}
return - 1 ;
}
CTU : : FileInfo * CTU : : getFileInfo ( const Tokenizer * tokenizer )
{
const SymbolDatabase * const symbolDatabase = tokenizer - > getSymbolDatabase ( ) ;
FileInfo * fileInfo = new FileInfo ;
// Parse all functions in TU
for ( const Scope & scope : symbolDatabase - > scopeList ) {
if ( ! scope . isExecutable ( ) | | scope . type ! = Scope : : eFunction | | ! scope . function )
continue ;
2022-01-17 20:35:30 +01:00
const Function * const scopeFunction = scope . function ;
2018-12-25 21:11:23 +01:00
// source function calls
for ( const Token * tok = scope . bodyStart ; tok ! = scope . bodyEnd ; tok = tok - > next ( ) ) {
if ( tok - > str ( ) ! = " ( " | | ! tok - > astOperand1 ( ) | | ! tok - > astOperand2 ( ) )
continue ;
2022-01-17 20:35:30 +01:00
const Function * tokFunction = tok - > astOperand1 ( ) - > function ( ) ;
if ( ! tokFunction )
2018-12-25 21:11:23 +01:00
continue ;
const std : : vector < const Token * > args ( getArguments ( tok - > previous ( ) ) ) ;
for ( int argnr = 0 ; argnr < args . size ( ) ; + + argnr ) {
const Token * argtok = args [ argnr ] ;
if ( ! argtok )
continue ;
2018-12-30 18:31:37 +01:00
for ( const ValueFlow : : Value & value : argtok - > values ( ) ) {
2019-03-23 08:36:10 +01:00
if ( ( ! value . isIntValue ( ) | | value . intvalue ! = 0 | | value . isInconclusive ( ) ) & & ! value . isBufferSizeValue ( ) )
2018-12-30 18:31:37 +01:00
continue ;
2019-09-20 15:06:37 +02:00
// Skip impossible values since they cannot be represented
if ( value . isImpossible ( ) )
continue ;
2018-12-30 22:17:21 +01:00
FileInfo : : FunctionCall functionCall ;
2019-03-23 08:36:10 +01:00
functionCall . callValueType = value . valueType ;
2022-01-17 20:35:30 +01:00
functionCall . callId = getFunctionId ( tokenizer , tokFunction ) ;
2018-12-30 11:55:39 +01:00
functionCall . callFunctionName = tok - > astOperand1 ( ) - > expressionString ( ) ;
2019-09-12 10:46:33 +02:00
functionCall . location = FileInfo : : Location ( tokenizer , tok ) ;
2018-12-30 11:55:39 +01:00
functionCall . callArgNr = argnr + 1 ;
functionCall . callArgumentExpression = argtok - > expressionString ( ) ;
2018-12-30 18:31:37 +01:00
functionCall . callArgValue = value . intvalue ;
2018-12-31 07:50:02 +01:00
functionCall . warning = ! value . errorSeverity ( ) ;
2018-12-30 18:31:37 +01:00
for ( const ErrorPathItem & i : value . errorPath ) {
2020-05-23 07:16:49 +02:00
ErrorMessage : : FileLocation loc ;
2018-12-30 18:31:37 +01:00
loc . setfile ( tokenizer - > list . file ( i . first ) ) ;
loc . line = i . first - > linenr ( ) ;
2019-09-12 10:46:33 +02:00
loc . column = i . first - > column ( ) ;
2018-12-30 18:31:37 +01:00
loc . setinfo ( i . second ) ;
2022-09-08 09:21:35 +02:00
functionCall . callValuePath . push_back ( std : : move ( loc ) ) ;
2018-12-30 18:31:37 +01:00
}
2022-09-08 09:21:35 +02:00
fileInfo - > functionCalls . push_back ( std : : move ( functionCall ) ) ;
2018-12-25 21:11:23 +01:00
}
2019-03-23 11:20:35 +01:00
// array
2022-11-23 19:11:47 +01:00
if ( argtok - > variable ( ) & & argtok - > variable ( ) - > isArray ( ) & & argtok - > variable ( ) - > dimensions ( ) . size ( ) = = 1 & & argtok - > variable ( ) - > dimensionKnown ( 0 ) ) {
2019-03-23 11:20:35 +01:00
FileInfo : : FunctionCall functionCall ;
functionCall . callValueType = ValueFlow : : Value : : ValueType : : BUFFER_SIZE ;
2022-01-17 20:35:30 +01:00
functionCall . callId = getFunctionId ( tokenizer , tokFunction ) ;
2019-03-23 11:20:35 +01:00
functionCall . callFunctionName = tok - > astOperand1 ( ) - > expressionString ( ) ;
2019-09-12 10:46:33 +02:00
functionCall . location = FileInfo : : Location ( tokenizer , tok ) ;
2019-03-23 11:20:35 +01:00
functionCall . callArgNr = argnr + 1 ;
functionCall . callArgumentExpression = argtok - > expressionString ( ) ;
2023-03-03 18:36:27 +01:00
const auto typeSize = argtok - > valueType ( ) - > typeSize ( tokenizer - > getSettings ( ) - > platform ) ;
2023-03-02 21:19:53 +01:00
functionCall . callArgValue = typeSize > 0 ? argtok - > variable ( ) - > dimension ( 0 ) * typeSize : - 1 ;
2019-03-23 11:20:35 +01:00
functionCall . warning = false ;
2022-09-08 09:21:35 +02:00
fileInfo - > functionCalls . push_back ( std : : move ( functionCall ) ) ;
2019-03-23 11:20:35 +01:00
}
2019-03-23 15:57:17 +01:00
// &var => buffer
if ( argtok - > isUnaryOp ( " & " ) & & argtok - > astOperand1 ( ) - > variable ( ) & & argtok - > astOperand1 ( ) - > valueType ( ) & & ! argtok - > astOperand1 ( ) - > variable ( ) - > isArray ( ) ) {
FileInfo : : FunctionCall functionCall ;
functionCall . callValueType = ValueFlow : : Value : : ValueType : : BUFFER_SIZE ;
2022-01-17 20:35:30 +01:00
functionCall . callId = getFunctionId ( tokenizer , tokFunction ) ;
2019-03-23 15:57:17 +01:00
functionCall . callFunctionName = tok - > astOperand1 ( ) - > expressionString ( ) ;
2019-09-12 10:46:33 +02:00
functionCall . location = FileInfo : : Location ( tokenizer , tok ) ;
2019-03-23 15:57:17 +01:00
functionCall . callArgNr = argnr + 1 ;
functionCall . callArgumentExpression = argtok - > expressionString ( ) ;
2023-03-03 18:36:27 +01:00
functionCall . callArgValue = argtok - > astOperand1 ( ) - > valueType ( ) - > typeSize ( tokenizer - > getSettings ( ) - > platform ) ;
2019-03-23 15:57:17 +01:00
functionCall . warning = false ;
2022-09-08 09:21:35 +02:00
fileInfo - > functionCalls . push_back ( std : : move ( functionCall ) ) ;
2019-03-23 15:57:17 +01:00
}
2022-01-17 20:35:30 +01:00
// pointer/reference to uninitialized data
auto isAddressOfArg = [ ] ( const Token * argtok ) - > const Token * {
if ( ! argtok - > isUnaryOp ( " & " ) )
return nullptr ;
argtok = argtok - > astOperand1 ( ) ;
if ( ! argtok | | ! argtok - > valueType ( ) | | argtok - > valueType ( ) - > pointer ! = 0 )
return nullptr ;
return argtok ;
} ;
auto isReferenceArg = [ & ] ( const Token * argtok ) - > const Token * {
const Variable * argvar = tokFunction - > getArgumentVar ( argnr ) ;
if ( ! argvar | | ! argvar - > valueType ( ) | | argvar - > valueType ( ) - > reference = = Reference : : None )
return nullptr ;
return argtok ;
} ;
const Token * addr = isAddressOfArg ( argtok ) ;
argtok = addr ? addr : isReferenceArg ( argtok ) ;
if ( ! argtok | | argtok - > values ( ) . size ( ) ! = 1U )
2018-12-25 21:11:23 +01:00
continue ;
2023-03-13 20:55:11 +01:00
if ( argtok - > variable ( ) & & argtok - > variable ( ) - > isClass ( ) )
continue ;
2022-01-17 20:35:30 +01:00
2018-12-25 21:11:23 +01:00
const ValueFlow : : Value & v = argtok - > values ( ) . front ( ) ;
2019-07-10 14:04:56 +02:00
if ( v . valueType = = ValueFlow : : Value : : ValueType : : UNINIT & & ! v . isInconclusive ( ) ) {
2018-12-30 22:17:21 +01:00
FileInfo : : FunctionCall functionCall ;
2019-07-10 14:04:56 +02:00
functionCall . callValueType = ValueFlow : : Value : : ValueType : : UNINIT ;
2022-01-17 20:35:30 +01:00
functionCall . callId = getFunctionId ( tokenizer , tokFunction ) ;
2018-12-30 11:55:39 +01:00
functionCall . callFunctionName = tok - > astOperand1 ( ) - > expressionString ( ) ;
2019-09-12 10:46:33 +02:00
functionCall . location = FileInfo : : Location ( tokenizer , tok ) ;
2018-12-30 11:55:39 +01:00
functionCall . callArgNr = argnr + 1 ;
functionCall . callArgValue = 0 ;
functionCall . callArgumentExpression = argtok - > expressionString ( ) ;
2018-12-30 18:31:37 +01:00
functionCall . warning = false ;
2022-09-08 09:21:35 +02:00
fileInfo - > functionCalls . push_back ( std : : move ( functionCall ) ) ;
2018-12-25 21:11:23 +01:00
continue ;
}
}
}
// Nested function calls
2022-01-17 20:35:30 +01:00
for ( int argnr = 0 ; argnr < scopeFunction - > argCount ( ) ; + + argnr ) {
2018-12-25 21:11:23 +01:00
const Token * tok ;
2018-12-30 22:17:21 +01:00
const int argnr2 = isCallFunction ( & scope , argnr , & tok ) ;
2018-12-25 21:11:23 +01:00
if ( argnr2 > 0 ) {
2022-01-17 20:35:30 +01:00
FileInfo : : NestedCall nestedCall ( tokenizer , scopeFunction , tok ) ;
2018-12-30 11:55:39 +01:00
nestedCall . myArgNr = argnr + 1 ;
nestedCall . callArgNr = argnr2 ;
2022-09-08 09:21:35 +02:00
fileInfo - > nestedCalls . push_back ( std : : move ( nestedCall ) ) ;
2018-12-25 21:11:23 +01:00
}
}
}
return fileInfo ;
}
2018-12-26 11:36:26 +01:00
2019-03-23 08:36:10 +01:00
static std : : list < std : : pair < const Token * , MathLib : : bigint > > getUnsafeFunction ( const Tokenizer * tokenizer , const Settings * settings , const Scope * scope , int argnr , const Check * check , bool ( * isUnsafeUsage ) ( const Check * check , const Token * argtok , MathLib : : bigint * value ) )
2018-12-26 19:17:49 +01:00
{
2019-03-23 08:36:10 +01:00
std : : list < std : : pair < const Token * , MathLib : : bigint > > ret ;
2018-12-26 19:17:49 +01:00
const Variable * const argvar = scope - > function - > getArgumentVar ( argnr ) ;
2022-07-13 21:08:51 +02:00
if ( ! argvar - > isArrayOrPointer ( ) & & ! argvar - > isReference ( ) )
2019-03-23 08:36:10 +01:00
return ret ;
2018-12-26 19:17:49 +01:00
for ( const Token * tok2 = scope - > bodyStart ; tok2 ! = scope - > bodyEnd ; tok2 = tok2 - > next ( ) ) {
2018-12-31 08:24:39 +01:00
if ( Token : : Match ( tok2 , " )|else { " ) ) {
2018-12-26 19:17:49 +01:00
tok2 = tok2 - > linkAt ( 1 ) ;
if ( Token : : findmatch ( tok2 - > link ( ) , " return|throw " , tok2 ) )
2019-03-23 08:36:10 +01:00
return ret ;
2020-07-20 06:25:35 +02:00
int indirect = 0 ;
2020-07-23 10:09:06 +02:00
if ( argvar - > valueType ( ) )
2020-07-20 06:25:35 +02:00
indirect = argvar - > valueType ( ) - > pointer ;
if ( isVariableChanged ( tok2 - > link ( ) , tok2 , indirect , argvar - > declarationId ( ) , false , settings , tokenizer - > isCPP ( ) ) )
2019-03-23 08:36:10 +01:00
return ret ;
2018-12-26 19:17:49 +01:00
}
2018-12-31 08:16:21 +01:00
if ( Token : : Match ( tok2 , " %oror%|&&|? " ) ) {
tok2 = tok2 - > findExpressionStartEndTokens ( ) . second ;
continue ;
}
2018-12-26 19:17:49 +01:00
if ( tok2 - > variable ( ) ! = argvar )
continue ;
2019-03-23 08:36:10 +01:00
MathLib : : bigint value = 0 ;
if ( ! isUnsafeUsage ( check , tok2 , & value ) )
return ret ; // TODO: Is this a read? then continue..
ret . emplace_back ( tok2 , value ) ;
return ret ;
2018-12-26 19:17:49 +01:00
}
2019-03-23 08:36:10 +01:00
return ret ;
2018-12-26 19:17:49 +01:00
}
2021-01-02 23:10:27 +01:00
std : : list < CTU : : FileInfo : : UnsafeUsage > CTU : : getUnsafeUsage ( const Tokenizer * tokenizer , const Settings * settings , const Check * check , bool ( * isUnsafeUsage ) ( const Check * check , const Token * argtok , MathLib : : bigint * value ) )
2018-12-26 19:17:49 +01:00
{
std : : list < CTU : : FileInfo : : UnsafeUsage > unsafeUsage ;
// Parse all functions in TU
const SymbolDatabase * const symbolDatabase = tokenizer - > getSymbolDatabase ( ) ;
for ( const Scope & scope : symbolDatabase - > scopeList ) {
if ( ! scope . isExecutable ( ) | | scope . type ! = Scope : : eFunction | | ! scope . function )
continue ;
const Function * const function = scope . function ;
// "Unsafe" functions unconditionally reads data before it is written..
for ( int argnr = 0 ; argnr < function - > argCount ( ) ; + + argnr ) {
2019-03-23 08:36:10 +01:00
for ( const std : : pair < const Token * , MathLib : : bigint > & v : getUnsafeFunction ( tokenizer , settings , & scope , argnr , check , isUnsafeUsage ) ) {
const Token * tok = v . first ;
2022-10-02 07:12:40 +02:00
const MathLib : : bigint val = v . second ;
2021-01-02 23:10:27 +01:00
unsafeUsage . emplace_back ( CTU : : getFunctionId ( tokenizer , function ) , argnr + 1 , tok - > str ( ) , CTU : : FileInfo : : Location ( tokenizer , tok ) , val ) ;
2019-03-23 08:36:10 +01:00
}
2018-12-26 19:17:49 +01:00
}
}
return unsafeUsage ;
}
2018-12-26 11:36:26 +01:00
2018-12-30 16:23:25 +01:00
static bool findPath ( const std : : string & callId ,
2019-07-14 12:22:33 +02:00
nonneg int callArgNr ,
2019-03-23 08:36:10 +01:00
MathLib : : bigint unsafeValue ,
2018-12-30 16:23:25 +01:00
CTU : : FileInfo : : InvalidValueType invalidValue ,
const std : : map < std : : string , std : : list < const CTU : : FileInfo : : CallBase * > > & callsMap ,
const CTU : : FileInfo : : CallBase * path [ 10 ] ,
2018-12-30 18:31:37 +01:00
int index ,
bool warning )
2018-12-26 11:36:26 +01:00
{
2019-02-03 17:51:02 +01:00
if ( index > = CTU : : maxCtuDepth | | index > = 10 )
2018-12-30 16:23:25 +01:00
return false ;
2018-12-26 11:36:26 +01:00
2018-12-30 16:23:25 +01:00
const std : : map < std : : string , std : : list < const CTU : : FileInfo : : CallBase * > > : : const_iterator it = callsMap . find ( callId ) ;
if ( it = = callsMap . end ( ) )
2018-12-26 11:36:26 +01:00
return false ;
2018-12-30 16:23:25 +01:00
for ( const CTU : : FileInfo : : CallBase * c : it - > second ) {
if ( c - > callArgNr ! = callArgNr )
continue ;
const CTU : : FileInfo : : FunctionCall * functionCall = dynamic_cast < const CTU : : FileInfo : : FunctionCall * > ( c ) ;
if ( functionCall ) {
2018-12-30 18:31:37 +01:00
if ( ! warning & & functionCall - > warning )
continue ;
2018-12-30 16:23:25 +01:00
switch ( invalidValue ) {
case CTU : : FileInfo : : InvalidValueType : : null :
2019-07-10 14:04:56 +02:00
if ( functionCall - > callValueType ! = ValueFlow : : Value : : ValueType : : INT | | functionCall - > callArgValue ! = 0 )
2018-12-30 16:23:25 +01:00
continue ;
break ;
case CTU : : FileInfo : : InvalidValueType : : uninit :
2019-07-10 14:04:56 +02:00
if ( functionCall - > callValueType ! = ValueFlow : : Value : : ValueType : : UNINIT )
2018-12-30 16:23:25 +01:00
continue ;
break ;
2019-03-23 08:36:10 +01:00
case CTU : : FileInfo : : InvalidValueType : : bufferOverflow :
2019-07-10 14:04:56 +02:00
if ( functionCall - > callValueType ! = ValueFlow : : Value : : ValueType : : BUFFER_SIZE )
2019-03-23 08:36:10 +01:00
continue ;
2023-03-02 21:19:53 +01:00
if ( unsafeValue < 0 | | ( unsafeValue > = functionCall - > callArgValue & & functionCall - > callArgValue > = 0 ) )
2019-03-23 08:36:10 +01:00
break ;
continue ;
2020-04-21 17:27:51 +02:00
}
2018-12-30 16:23:25 +01:00
path [ index ] = functionCall ;
return true ;
}
const CTU : : FileInfo : : NestedCall * nestedCall = dynamic_cast < const CTU : : FileInfo : : NestedCall * > ( c ) ;
if ( ! nestedCall )
continue ;
2019-03-23 08:36:10 +01:00
if ( findPath ( nestedCall - > myId , nestedCall - > myArgNr , unsafeValue , invalidValue , callsMap , path , index + 1 , warning ) ) {
2018-12-30 16:23:25 +01:00
path [ index ] = nestedCall ;
2018-12-26 11:36:26 +01:00
return true ;
2018-12-30 16:23:25 +01:00
}
2018-12-26 11:36:26 +01:00
}
return false ;
}
2020-05-23 07:16:49 +02:00
std : : list < ErrorMessage : : FileLocation > CTU : : FileInfo : : getErrorPath ( InvalidValueType invalidValue ,
2021-08-07 20:51:18 +02:00
const CTU : : FileInfo : : UnsafeUsage & unsafeUsage ,
const std : : map < std : : string , std : : list < const CTU : : FileInfo : : CallBase * > > & callsMap ,
const char info [ ] ,
const FunctionCall * * const functionCallPtr ,
bool warning )
2018-12-26 11:36:26 +01:00
{
2020-05-23 07:16:49 +02:00
std : : list < ErrorMessage : : FileLocation > locationList ;
2018-12-26 11:36:26 +01:00
2019-06-30 21:39:22 +02:00
const CTU : : FileInfo : : CallBase * path [ 10 ] = { nullptr } ;
2018-12-26 11:36:26 +01:00
2019-03-23 08:36:10 +01:00
if ( ! findPath ( unsafeUsage . myId , unsafeUsage . myArgNr , unsafeUsage . value , invalidValue , callsMap , path , 0 , warning ) )
2018-12-30 16:23:25 +01:00
return locationList ;
2018-12-26 15:56:10 +01:00
2018-12-30 16:23:25 +01:00
const std : : string value1 = ( invalidValue = = InvalidValueType : : null ) ? " null " : " uninitialized " ;
2018-12-26 11:36:26 +01:00
2018-12-30 16:23:25 +01:00
for ( int index = 9 ; index > = 0 ; index - - ) {
if ( ! path [ index ] )
continue ;
2018-12-26 11:36:26 +01:00
2018-12-30 18:31:37 +01:00
const CTU : : FileInfo : : FunctionCall * functionCall = dynamic_cast < const CTU : : FileInfo : : FunctionCall * > ( path [ index ] ) ;
if ( functionCall ) {
if ( functionCallPtr )
* functionCallPtr = functionCall ;
2019-05-08 20:23:32 +02:00
std : : copy ( functionCall - > callValuePath . cbegin ( ) , functionCall - > callValuePath . cend ( ) , std : : back_inserter ( locationList ) ) ;
2018-12-30 18:31:37 +01:00
}
2018-12-26 15:56:10 +01:00
2020-05-23 07:16:49 +02:00
ErrorMessage : : FileLocation fileLoc ( path [ index ] - > location . fileName , path [ index ] - > location . lineNumber , path [ index ] - > location . column ) ;
2018-12-30 16:23:25 +01:00
fileLoc . setinfo ( " Calling function " + path [ index ] - > callFunctionName + " , " + MathLib : : toString ( path [ index ] - > callArgNr ) + getOrdinalText ( path [ index ] - > callArgNr ) + " argument is " + value1 ) ;
2022-09-08 09:21:35 +02:00
locationList . push_back ( std : : move ( fileLoc ) ) ;
2018-12-26 15:56:10 +01:00
}
2018-12-26 11:36:26 +01:00
2020-05-23 07:16:49 +02:00
ErrorMessage : : FileLocation fileLoc2 ( unsafeUsage . location . fileName , unsafeUsage . location . lineNumber , unsafeUsage . location . column ) ;
2018-12-30 16:23:25 +01:00
fileLoc2 . setinfo ( replaceStr ( info , " ARG " , unsafeUsage . myArgumentName ) ) ;
2022-09-08 09:21:35 +02:00
locationList . push_back ( std : : move ( fileLoc2 ) ) ;
2018-12-30 16:23:25 +01:00
2018-12-26 11:36:26 +01:00
return locationList ;
}