2018-12-25 21:11:23 +01:00
/*
* Cppcheck - A tool for static C / C + + code analysis
* Copyright ( C ) 2007 - 2018 Cppcheck team .
*
* 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"
# include "astutils.h"
# include "symboldatabase.h"
# include <tinyxml2.h>
//---------------------------------------------------------------------------
std : : string CTU : : getFunctionId ( const Tokenizer * tokenizer , const Function * function )
{
return tokenizer - > list . file ( function - > tokenDef ) + ' : ' + MathLib : : toString ( function - > tokenDef - > linenr ( ) ) ;
}
CTU : : FileInfo : : Location : : Location ( const Tokenizer * tokenizer , const Token * tok )
{
fileName = tokenizer - > list . file ( tok ) ;
linenr = tok - > linenr ( ) ;
}
std : : string CTU : : FileInfo : : toString ( ) const
{
std : : ostringstream out ;
// Function calls..
for ( const CTU : : FileInfo : : FunctionCall & functionCall : functionCalls ) {
out < < " <function-call "
< < " id= \" " < < functionCall . functionId < < ' \" '
< < " functionName= \" " < < functionCall . functionName < < ' \" '
< < " argnr= \" " < < functionCall . argnr < < ' \" '
< < " argExpr= \" " < < functionCall . argumentExpression < < ' \" '
< < " argvalue= \" " < < functionCall . argvalue < < ' \" '
< < " valueType= \" " < < functionCall . valueType < < ' \" '
< < " fileName= \" " < < functionCall . location . fileName < < ' \" '
< < " linenr= \" " < < functionCall . location . linenr < < ' \" '
< < " /> \n " ;
}
// Nested calls..
for ( const CTU : : FileInfo : : NestedCall & nestedCall : nestedCalls ) {
out < < " <nested-call "
< < " id= \" " < < nestedCall . id < < ' \" '
< < " id2= \" " < < nestedCall . id2 < < ' \" '
< < " functionName= \" " < < nestedCall . functionName < < ' \" '
< < " argnr= \" " < < nestedCall . argnr < < ' \" '
< < " argnr2= \" " < < nestedCall . argnr2 < < ' \" '
< < " fileName= \" " < < nestedCall . location . fileName < < ' \" '
< < " linenr= \" " < < nestedCall . location . linenr < < ' \" '
< < " /> \n " ;
}
return out . str ( ) ;
}
std : : string CTU : : FileInfo : : UnsafeUsage : : toString ( ) const
{
std : : ostringstream out ;
out < < " <unsafe-usage "
< < " id= \" " < < functionId < < ' \" '
< < " argnr= \" " < < argnr < < ' \" '
< < " argname= \" " < < argumentName < < ' \" '
< < " fileName= \" " < < location . fileName < < ' \" '
< < " linenr= \" " < < location . linenr < < ' \" '
< < " /> \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-25 21:11:23 +01:00
CTU : : FileInfo : : NestedCall : : NestedCall ( const Tokenizer * tokenizer , const Scope * scope , unsigned int argnr_ , const Token * tok )
:
id ( getFunctionId ( tokenizer , scope - > function ) ) ,
functionName ( scope - > className ) ,
argnr ( argnr_ ) ,
argnr2 ( 0 ) ,
location ( CTU : : FileInfo : : Location ( tokenizer , tok ) )
{
}
void CTU : : FileInfo : : loadFromXml ( const tinyxml2 : : XMLElement * xmlElement )
{
for ( const tinyxml2 : : XMLElement * e = xmlElement - > FirstChildElement ( ) ; e ; e = e - > NextSiblingElement ( ) ) {
const char * id = e - > Attribute ( " id " ) ;
if ( ! id )
continue ;
const char * functionName = e - > Attribute ( " functionName " ) ;
if ( ! functionName )
continue ;
const char * argnr = e - > Attribute ( " argnr " ) ;
if ( ! argnr | | ! MathLib : : isInt ( argnr ) )
continue ;
const char * fileName = e - > Attribute ( " fileName " ) ;
if ( ! fileName )
continue ;
const char * linenr = e - > Attribute ( " linenr " ) ;
if ( ! linenr | | ! MathLib : : isInt ( linenr ) )
continue ;
if ( std : : strcmp ( e - > Name ( ) , " function-call " ) = = 0 ) {
FunctionCall functionCall ;
functionCall . functionId = id ;
functionCall . functionName = functionName ;
functionCall . argnr = std : : atoi ( argnr ) ;
const char * argExpr = e - > Attribute ( " argExpr " ) ;
if ( ! argExpr )
continue ;
functionCall . argumentExpression =
functionCall . valueType = ( ValueFlow : : Value : : ValueType ) std : : atoi ( e - > Attribute ( " valueType " ) ) ;
functionCall . argvalue = MathLib : : toLongNumber ( e - > Attribute ( " argvalue " ) ) ;
functionCall . location . fileName = fileName ;
functionCall . location . linenr = std : : atoi ( linenr ) ;
functionCalls . push_back ( functionCall ) ;
} else if ( std : : strcmp ( e - > Name ( ) , " nested-call " ) = = 0 ) {
NestedCall nestedCall ;
nestedCall . functionName = functionName ;
nestedCall . id = id ;
const char * id2 = e - > Attribute ( " id2 " ) ;
if ( ! id2 )
continue ;
nestedCall . id2 = id2 ;
nestedCall . argnr = std : : atoi ( argnr ) ;
const char * argnr2 = e - > Attribute ( " argnr2 " ) ;
if ( ! argnr2 | | ! MathLib : : isInt ( argnr2 ) )
continue ;
nestedCall . argnr2 = std : : atoi ( argnr2 ) ;
nestedCall . location . fileName = fileName ;
nestedCall . location . linenr = std : : atoi ( linenr ) ;
nestedCalls . push_back ( nestedCall ) ;
}
}
}
std : : map < std : : string , std : : list < CTU : : FileInfo : : NestedCall > > CTU : : FileInfo : : getNestedCallsMap ( ) const
{
std : : map < std : : string , std : : list < CTU : : FileInfo : : NestedCall > > ret ;
for ( const CTU : : FileInfo : : NestedCall & nc : nestedCalls )
ret [ nc . id ] . push_back ( nc ) ;
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 ;
const char * id = e - > Attribute ( " id " ) ;
if ( ! id )
continue ;
const char * argnr = e - > Attribute ( " argnr " ) ;
if ( ! argnr | | ! MathLib : : isInt ( argnr ) )
continue ;
const char * argname = e - > Attribute ( " argname " ) ;
if ( ! argname )
continue ;
const char * fileName = e - > Attribute ( " fileName " ) ;
if ( ! fileName )
continue ;
const char * linenr = e - > Attribute ( " linenr " ) ;
if ( ! linenr | | ! MathLib : : isInt ( linenr ) )
continue ;
ret . push_back ( FileInfo : : UnsafeUsage ( id , std : : atoi ( argnr ) , argname , FileInfo : : Location ( fileName , std : : atoi ( linenr ) ) ) ) ;
}
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 ;
const Function * const function = scope . function ;
// source function calls
for ( const Token * tok = scope . bodyStart ; tok ! = scope . bodyEnd ; tok = tok - > next ( ) ) {
if ( tok - > str ( ) ! = " ( " | | ! tok - > astOperand1 ( ) | | ! tok - > astOperand2 ( ) )
continue ;
if ( ! tok - > astOperand1 ( ) - > function ( ) )
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 ;
if ( argtok - > hasKnownIntValue ( ) ) {
struct FileInfo : : FunctionCall functionCall ;
functionCall . valueType = ValueFlow : : Value : : INT ;
functionCall . functionId = getFunctionId ( tokenizer , tok - > astOperand1 ( ) - > function ( ) ) ;
functionCall . functionName = tok - > astOperand1 ( ) - > expressionString ( ) ;
functionCall . location . fileName = tokenizer - > list . file ( tok ) ;
functionCall . location . linenr = tok - > linenr ( ) ;
functionCall . argnr = argnr + 1 ;
functionCall . argumentExpression = argtok - > expressionString ( ) ;
functionCall . argvalue = argtok - > values ( ) . front ( ) . intvalue ;
fileInfo - > functionCalls . push_back ( functionCall ) ;
continue ;
}
// pointer to uninitialized data..
if ( ! argtok - > isUnaryOp ( " & " ) )
continue ;
argtok = argtok - > astOperand1 ( ) ;
if ( ! argtok | | ! argtok - > valueType ( ) | | argtok - > valueType ( ) - > pointer ! = 0 )
continue ;
if ( argtok - > values ( ) . size ( ) ! = 1U )
continue ;
const ValueFlow : : Value & v = argtok - > values ( ) . front ( ) ;
if ( v . valueType = = ValueFlow : : Value : : UNINIT & & ! v . isInconclusive ( ) ) {
struct FileInfo : : FunctionCall functionCall ;
functionCall . valueType = ValueFlow : : Value : : UNINIT ;
functionCall . functionId = getFunctionId ( tokenizer , tok - > astOperand1 ( ) - > function ( ) ) ;
functionCall . functionName = tok - > astOperand1 ( ) - > expressionString ( ) ;
functionCall . location . fileName = tokenizer - > list . file ( tok ) ;
functionCall . location . linenr = tok - > linenr ( ) ;
functionCall . argnr = argnr + 1 ;
functionCall . argvalue = 0 ;
functionCall . argumentExpression = argtok - > expressionString ( ) ;
fileInfo - > functionCalls . push_back ( functionCall ) ;
continue ;
}
}
}
// Nested function calls
for ( int argnr = 0 ; argnr < function - > argCount ( ) ; + + argnr ) {
const Token * tok ;
int argnr2 = isCallFunction ( & scope , argnr , & tok ) ;
if ( argnr2 > 0 ) {
FileInfo : : NestedCall nestedCall ( tokenizer , & scope , argnr + 1 , tok ) ;
nestedCall . id = getFunctionId ( tokenizer , function ) ;
nestedCall . id2 = getFunctionId ( tokenizer , tok - > function ( ) ) ;
nestedCall . argnr2 = argnr2 ;
fileInfo - > nestedCalls . push_back ( nestedCall ) ;
}
}
}
return fileInfo ;
}
2018-12-26 11:36:26 +01:00
2018-12-26 19:17:49 +01:00
static bool isUnsafeFunction ( const Tokenizer * tokenizer , const Settings * settings , const Scope * scope , int argnr , const Token * * tok , const Check * check , bool ( * isUnsafeUsage ) ( const Check * check , const Token * argtok ) )
{
const Variable * const argvar = scope - > function - > getArgumentVar ( argnr ) ;
if ( ! argvar - > isPointer ( ) )
return false ;
for ( const Token * tok2 = scope - > bodyStart ; tok2 ! = scope - > bodyEnd ; tok2 = tok2 - > next ( ) ) {
if ( Token : : simpleMatch ( tok2 , " ) { " ) ) {
tok2 = tok2 - > linkAt ( 1 ) ;
if ( Token : : findmatch ( tok2 - > link ( ) , " return|throw " , tok2 ) )
return false ;
if ( isVariableChanged ( tok2 - > link ( ) , tok2 , argvar - > declarationId ( ) , false , settings , tokenizer - > isCPP ( ) ) )
return false ;
}
if ( tok2 - > variable ( ) ! = argvar )
continue ;
if ( ! isUnsafeUsage ( check , tok2 ) )
return false ;
* tok = tok2 ;
return true ;
}
return false ;
}
std : : list < CTU : : FileInfo : : UnsafeUsage > CTU : : getUnsafeUsage ( const Tokenizer * tokenizer , const Settings * settings , const Check * check , bool ( * isUnsafeUsage ) ( const Check * check , const Token * argtok ) )
{
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 ) {
const Token * tok ;
if ( isUnsafeFunction ( tokenizer , settings , & scope , argnr , & tok , check , isUnsafeUsage ) )
unsafeUsage . push_back ( CTU : : FileInfo : : UnsafeUsage ( CTU : : getFunctionId ( tokenizer , function ) , argnr + 1 , tok - > str ( ) , CTU : : FileInfo : : Location ( tokenizer , tok ) ) ) ;
}
}
return unsafeUsage ;
}
2018-12-26 11:36:26 +01:00
static bool findPath ( const CTU : : FileInfo : : FunctionCall & from ,
const CTU : : FileInfo : : UnsafeUsage & to ,
const std : : map < std : : string , std : : list < CTU : : FileInfo : : NestedCall > > & nestedCalls )
{
if ( from . functionId = = to . functionId & & from . argnr = = to . argnr )
return true ;
const std : : map < std : : string , std : : list < CTU : : FileInfo : : NestedCall > > : : const_iterator nc = nestedCalls . find ( from . functionId ) ;
if ( nc = = nestedCalls . end ( ) )
return false ;
for ( const CTU : : FileInfo : : NestedCall & nestedCall : nc - > second ) {
if ( from . functionId = = nestedCall . id & & from . argnr = = nestedCall . argnr & & nestedCall . id2 = = to . functionId & & nestedCall . argnr2 = = to . argnr )
return true ;
}
return false ;
}
static std : : string replacestr ( std : : string s , const std : : string & from , const std : : string & to )
{
std : : string : : size_type pos1 = 0 ;
while ( pos1 < s . size ( ) ) {
pos1 = s . find ( from , pos1 ) ;
if ( pos1 = = std : : string : : npos )
return s ;
if ( pos1 > 0 & & ( s [ pos1 - 1 ] = = ' _ ' | | std : : isalnum ( s [ pos1 - 1 ] ) ) ) {
pos1 + + ;
continue ;
}
const std : : string : : size_type pos2 = pos1 + from . size ( ) ;
if ( pos2 > = s . size ( ) )
return s . substr ( 0 , pos1 ) + to ;
if ( s [ pos2 ] = = ' _ ' | | std : : isalnum ( s [ pos2 ] ) ) {
pos1 + + ;
continue ;
}
s = s . substr ( 0 , pos1 ) + to + s . substr ( pos2 ) ;
pos1 + = to . size ( ) ;
}
return s ;
}
2018-12-26 15:56:10 +01:00
std : : list < ErrorLogger : : ErrorMessage : : FileLocation > CTU : : FileInfo : : getErrorPath ( InvalidValueType invalidValue ,
2018-12-26 11:36:26 +01:00
const CTU : : FileInfo : : UnsafeUsage & unsafeUsage ,
const std : : map < std : : string , std : : list < CTU : : FileInfo : : NestedCall > > & nestedCallsMap ,
2018-12-26 15:56:10 +01:00
const char info [ ] ,
const FunctionCall * * const functionCallPtr ) const
2018-12-26 11:36:26 +01:00
{
std : : list < ErrorLogger : : ErrorMessage : : FileLocation > locationList ;
2018-12-26 15:56:10 +01:00
for ( const FunctionCall & functionCall : functionCalls ) {
2018-12-26 11:36:26 +01:00
2018-12-26 15:56:10 +01:00
if ( invalidValue = = CTU : : FileInfo : : InvalidValueType : : null & &
( functionCall . valueType ! = ValueFlow : : Value : : ValueType : : INT | | functionCall . argvalue ! = 0 ) ) {
continue ;
}
if ( invalidValue = = CTU : : FileInfo : : InvalidValueType : : uninit & &
functionCall . valueType ! = ValueFlow : : Value : : ValueType : : UNINIT ) {
continue ;
}
2018-12-26 11:36:26 +01:00
2018-12-26 15:56:10 +01:00
if ( ! findPath ( functionCall , unsafeUsage , nestedCallsMap ) )
continue ;
if ( functionCallPtr )
* functionCallPtr = & functionCall ;
std : : string value1 ;
if ( functionCall . valueType = = ValueFlow : : Value : : ValueType : : INT )
value1 = " null " ;
else if ( functionCall . valueType = = ValueFlow : : Value : : ValueType : : UNINIT )
value1 = " uninitialized " ;
ErrorLogger : : ErrorMessage : : FileLocation fileLoc1 ;
fileLoc1 . setfile ( functionCall . location . fileName ) ;
fileLoc1 . line = functionCall . location . linenr ;
fileLoc1 . setinfo ( " Calling function " + functionCall . functionName + " , " + MathLib : : toString ( functionCall . argnr ) + getOrdinalText ( functionCall . argnr ) + " argument is " + value1 ) ;
2018-12-26 11:36:26 +01:00
2018-12-26 15:56:10 +01:00
ErrorLogger : : ErrorMessage : : FileLocation fileLoc2 ;
fileLoc2 . setfile ( unsafeUsage . location . fileName ) ;
fileLoc2 . line = unsafeUsage . location . linenr ;
fileLoc2 . setinfo ( replacestr ( info , " ARG " , unsafeUsage . argumentName ) ) ;
2018-12-26 11:36:26 +01:00
2018-12-26 15:56:10 +01:00
locationList . push_back ( fileLoc1 ) ;
locationList . push_back ( fileLoc2 ) ;
return locationList ;
}
2018-12-26 11:36:26 +01:00
return locationList ;
}