2019-09-17 21:00:59 +02:00
/*
* Cppcheck - A tool for static C / C + + code analysis
2020-05-10 11:16:32 +02:00
* Copyright ( C ) 2007 - 2020 Cppcheck team .
2019-09-17 21:00:59 +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/>.
*/
2020-04-30 12:18:49 +02:00
/**
* @ brief This is the ExprEngine component in Cppcheck . Its job is to
* convert the C / C + + code into expressions that the Z3 prover understands .
* We can then ask Z3 prover for instance if variable " x " can be 123 and
* the Z3 prover can tell us that .
*
* Overview
* = = = = = = = =
*
* The ExprEngine performs a " abstract execution " of each function .
* - ExprEngine performs " forward " analysis only . It starts at the top
* of the functions .
* - There is a abstract program state ` Data : : memory ` .
* - The constraints are stored in the vector ` Data : : constraints ` .
*
* Abstract program state
* = = = = = = = = = = = = = = = = = = = = = =
*
* The map ` Data : : memory ` contains the abstract values of all variables
* that are used in the current function scope .
*
* Use ` - - debug - bug - hunting - - verbose ` to dump out ` Data : : memory ` .
* Example output :
* 2 : 5 : { x = $ 1 y = $ 2 }
* Explanation :
* At line 2 , column 5 : The memory has two variables . Variable x has the
* value $ 1. Variable y has the value $ 2.
*
* Different value names :
* - Typical abstract value has name that starts with " $ " . The number is
* just a incremented value .
* - If a variable has a known value then the concrete value is written .
* Example : ` { x = 1 } ` .
* - For an uninitialized value the output says " ? " . For example : ` { a = ? } `
* - For buffers the output is something like ` { buf = ( $ 3 , size = 10 , [ : ] = ? , [ $ 1 ] = $ 2 ) } `
* The first item " $3 " is the name of the buffer value .
* The second item says that the size of this buffer is 10.
* After that comes ` [ index ] = value ` items that show what values buffer items have :
* ` [ : ] = ? ` means that all items are uninitialized .
* ` [ $ 1 ] = $ 2 ` means that the buffer item at index " $1 " has value " $2 " .
*
* Abstract execution
* = = = = = = = = = = = = = = = = = =
*
* The function :
* static void execute ( const Token * start , const Token * end , Data & data )
*
* Perform abstract execution of the code from ` start ` to ` end ` . The
* ` data ` is modified during the abstract execution .
*
* Each astTop token is executed . From that , operands are executed
* recursively in the " execute.. " functions . The result of an operand is
* a abstract value .
*
* Branches
* - - - - - - - -
*
* Imagine :
* code1
* if ( x > 0 )
* code2
* else
* code3
* code4
*
* When " if " is reached . . the current ` data ` is branched into ` thenData `
* and ` elseData ` .
* For " thenData " a constraint is added : x > 0
* For " elseData " a constraint is added : ! ( x > 0 )
*
* Then analysis of ` thenData ` and ` elseData ` will continue separately ,
* by recursive execution . The " code4 " block will be analysed both with
* ` thenData ` and ` elseData ` .
*
* Z3
* = =
*
* The ExprEngine will not execute Z3 unless a check wants it to .
*
* The abstract values and all their constraints is added to a Z3 solver
* object and after that Z3 can tell us if some condition can be true .
*
* Z3 is a SMT solver :
* https : //en.wikipedia.org/wiki/Satisfiability_modulo_theories
*
* In SMT :
* - all variables are " constant " . A variable can not be changed or assigned .
* - There is no " execution " . The solver considers all equations simultaneously .
*
* Simple example ( TestExpr : : expr6 ) :
*
* void f ( unsigned char x )
* {
* unsigned char y = 8 - x ; \ n "
* y > 1000 ;
* }
*
* If a check wants to know if " y > 1000 " can be true , ExprEngine will
* generate this Z3 input :
*
* ( declare - fun $ 1 ( ) Int )
* ( assert ( and ( > = $ 1 0 ) ( < = $ 1 255 ) ) )
* ( assert ( > ( - 8 $ 1 ) 1000 ) )
*
* A symbol " $1 " is created .
* assert that " $1 " is a value 0 - 255.
* assert that " 8-$1 " is greater than 1000.
*
* Z3 can now determine if these assertions are possible or not . In this
* case these assertions are not possible , there is no value for $ 1 between
* 0 - 255 that means that " 8-$1 " is greater than 1000.
*/
2019-09-17 21:00:59 +02:00
# include "exprengine.h"
2019-09-19 20:18:39 +02:00
# include "astutils.h"
2019-09-17 21:00:59 +02:00
# include "settings.h"
# include "symboldatabase.h"
# include "tokenize.h"
2019-10-02 17:59:04 +02:00
# include <cstdlib>
2019-10-27 15:35:04 +01:00
# include <cstring>
2019-09-22 21:14:20 +02:00
# include <limits>
2019-09-17 21:00:59 +02:00
# include <memory>
# include <iostream>
2019-10-02 17:59:04 +02:00
# ifdef USE_Z3
# include <z3++.h>
2020-05-12 16:57:07 +02:00
# include <z3_version.h>
# define GET_VERSION_INT(A,B,C) ((A) * 10000 + (B) * 100 + (C))
# define Z3_VERSION_INT GET_VERSION_INT(Z3_MAJOR_VERSION, Z3_MINOR_VERSION, Z3_BUILD_NUMBER)
2019-10-02 17:59:04 +02:00
# endif
2019-09-17 21:00:59 +02:00
2019-10-08 20:13:25 +02:00
namespace {
struct VerifyException {
VerifyException ( const Token * tok , const std : : string & what ) : tok ( tok ) , what ( what ) { }
const Token * tok ;
const std : : string what ;
} ;
}
2019-09-17 21:00:59 +02:00
std : : string ExprEngine : : str ( int128_t value )
{
std : : ostringstream ostr ;
2019-09-17 22:28:36 +02:00
# ifdef __GNUC__
2019-09-17 21:00:59 +02:00
if ( value = = ( int ) value ) {
ostr < < ( int ) value ;
return ostr . str ( ) ;
}
if ( value < 0 ) {
ostr < < " - " ;
value = - value ;
}
uint64_t high = value > > 64 ;
uint64_t low = value ;
if ( high > 0 )
ostr < < " h " < < std : : hex < < high < < " l " ;
ostr < < std : : hex < < low ;
2019-09-17 22:28:36 +02:00
# else
ostr < < value ;
# endif
2019-09-17 21:00:59 +02:00
return ostr . str ( ) ;
}
static ExprEngine : : ValuePtr getValueRangeFromValueType ( const std : : string & name , const ValueType * vt , const cppcheck : : Platform & platform ) ;
namespace {
2019-09-19 20:18:39 +02:00
class TrackExecution {
public :
2019-12-22 21:00:53 +01:00
TrackExecution ( ) : mDataIndex ( 0 ) , mAbortLine ( - 1 ) { }
2020-04-28 17:16:13 +02:00
2019-09-19 20:18:39 +02:00
int getNewDataIndex ( ) {
2019-09-20 07:04:58 +02:00
return mDataIndex + + ;
2019-09-19 20:18:39 +02:00
}
2019-09-26 20:48:33 +02:00
void symbolRange ( const Token * tok , ExprEngine : : ValuePtr value ) {
2019-09-26 21:43:29 +02:00
if ( ! tok | | ! value )
2019-09-19 20:18:39 +02:00
return ;
2020-04-28 17:16:13 +02:00
if ( tok - > index ( ) = = 0 )
return ;
2019-09-26 20:48:33 +02:00
const std : : string & symbolicExpression = value - > getSymbolicExpression ( ) ;
if ( symbolicExpression [ 0 ] ! = ' $ ' )
return ;
2019-09-27 08:17:46 +02:00
if ( mSymbols . find ( symbolicExpression ) ! = mSymbols . end ( ) )
2019-09-26 20:48:33 +02:00
return ;
2019-09-27 08:17:46 +02:00
mSymbols . insert ( symbolicExpression ) ;
2020-04-28 22:18:02 +02:00
mMap [ tok ] . push_back ( symbolicExpression + " = " + value - > getRange ( ) ) ;
2019-09-26 20:48:33 +02:00
2019-09-19 20:18:39 +02:00
}
void state ( const Token * tok , const std : : string & s ) {
2020-04-28 22:18:02 +02:00
mMap [ tok ] . push_back ( s ) ;
2019-09-19 20:18:39 +02:00
}
2019-09-29 15:00:54 +02:00
void print ( std : : ostream & out ) {
2019-09-19 20:18:39 +02:00
std : : set < std : : pair < int , int > > locations ;
2020-04-28 22:18:02 +02:00
for ( auto it : mMap ) {
2019-09-20 07:04:58 +02:00
locations . insert ( std : : pair < int , int > ( it . first - > linenr ( ) , it . first - > column ( ) ) ) ;
2019-09-19 20:18:39 +02:00
}
for ( const std : : pair < int , int > & loc : locations ) {
int lineNumber = loc . first ;
int column = loc . second ;
2020-04-28 22:18:02 +02:00
for ( auto & it : mMap ) {
2019-09-19 20:18:39 +02:00
const Token * tok = it . first ;
if ( lineNumber ! = tok - > linenr ( ) )
continue ;
2019-09-26 21:00:36 +02:00
if ( column ! = tok - > column ( ) )
continue ;
2019-09-19 20:18:39 +02:00
const std : : vector < std : : string > & dumps = it . second ;
for ( const std : : string & dump : dumps )
2019-09-29 15:00:54 +02:00
out < < lineNumber < < " : " < < column < < " : " < < dump < < " \n " ;
2019-09-19 20:18:39 +02:00
}
}
}
2019-12-22 21:00:53 +01:00
void report ( std : : ostream & out , const Scope * functionScope ) {
int linenr = - 1 ;
std : : string code ;
for ( const Token * tok = functionScope - > bodyStart - > next ( ) ; tok ! = functionScope - > bodyEnd ; tok = tok - > next ( ) ) {
if ( tok - > linenr ( ) > linenr ) {
if ( ! code . empty ( ) )
out < < getStatus ( linenr ) < < " " < < code < < std : : endl ;
linenr = tok - > linenr ( ) ;
code . clear ( ) ;
}
code + = " " + tok - > str ( ) ;
}
out < < getStatus ( linenr ) < < " " < < code < < std : : endl ;
}
void setAbortLine ( int linenr ) {
if ( linenr > 0 & & ( mAbortLine = = - 1 | | linenr < mAbortLine ) )
mAbortLine = linenr ;
}
void addError ( int linenr ) {
mErrors . insert ( linenr ) ;
}
bool isAllOk ( ) const {
return mErrors . empty ( ) ;
}
2020-04-28 22:09:01 +02:00
void addMissingContract ( const std : : string & f ) {
mMissingContracts . insert ( f ) ;
}
const std : : set < std : : string > getMissingContracts ( ) const {
return mMissingContracts ;
}
2019-09-19 20:18:39 +02:00
private :
2019-12-22 21:00:53 +01:00
const char * getStatus ( int linenr ) const {
if ( mErrors . find ( linenr ) ! = mErrors . end ( ) )
return " ERROR " ;
if ( mAbortLine > 0 & & linenr > = mAbortLine )
return " -- " ;
return " ok " ;
}
2020-04-28 22:18:02 +02:00
std : : map < const Token * , std : : vector < std : : string > > mMap ;
2020-04-28 17:16:13 +02:00
2019-09-20 07:04:58 +02:00
int mDataIndex ;
2019-12-22 21:00:53 +01:00
int mAbortLine ;
2019-09-27 08:17:46 +02:00
std : : set < std : : string > mSymbols ;
2019-12-22 21:00:53 +01:00
std : : set < int > mErrors ;
2020-04-28 22:09:01 +02:00
std : : set < std : : string > mMissingContracts ;
2019-09-19 20:18:39 +02:00
} ;
2019-09-29 15:00:54 +02:00
class Data : public ExprEngine : : DataBase {
2019-09-17 21:00:59 +02:00
public :
2020-04-28 17:16:13 +02:00
Data ( int * symbolValueIndex , ErrorLogger * errorLogger , const Tokenizer * tokenizer , const Settings * settings , const std : : string & currentFunction , const std : : vector < ExprEngine : : Callback > & callbacks , TrackExecution * trackExecution )
2020-04-27 09:08:50 +02:00
: DataBase ( currentFunction , settings )
2019-09-29 15:00:54 +02:00
, symbolValueIndex ( symbolValueIndex )
2020-04-28 17:16:13 +02:00
, errorLogger ( errorLogger )
2019-09-17 21:00:59 +02:00
, tokenizer ( tokenizer )
2019-09-19 20:18:39 +02:00
, callbacks ( callbacks )
, mTrackExecution ( trackExecution )
2019-09-20 06:12:20 +02:00
, mDataIndex ( trackExecution - > getNewDataIndex ( ) ) { }
2020-01-09 18:50:29 +01:00
typedef std : : map < nonneg int , ExprEngine : : ValuePtr > Memory ;
2019-09-17 21:00:59 +02:00
Memory memory ;
2019-09-19 20:18:39 +02:00
int * const symbolValueIndex ;
2020-04-28 17:16:13 +02:00
ErrorLogger * errorLogger ;
2019-09-19 20:18:39 +02:00
const Tokenizer * const tokenizer ;
2019-09-17 21:00:59 +02:00
const std : : vector < ExprEngine : : Callback > & callbacks ;
2019-10-03 08:48:05 +02:00
std : : vector < ExprEngine : : ValuePtr > constraints ;
2019-09-17 21:00:59 +02:00
2020-04-28 17:16:13 +02:00
ExprEngine : : ValuePtr executeContract ( const Function * function , ExprEngine : : ValuePtr ( * executeExpression ) ( const Token * , Data & ) ) {
const auto it = settings - > functionContracts . find ( function - > fullName ( ) ) ;
2020-04-27 09:08:50 +02:00
if ( it = = settings - > functionContracts . end ( ) )
2020-04-28 17:16:13 +02:00
return ExprEngine : : ValuePtr ( ) ;
2020-04-27 09:08:50 +02:00
const std : : string & expects = it - > second ;
TokenList tokenList ( settings ) ;
std : : istringstream istr ( expects ) ;
tokenList . createTokens ( istr ) ;
tokenList . createAst ( ) ;
SymbolDatabase * symbolDatabase = const_cast < SymbolDatabase * > ( tokenizer - > getSymbolDatabase ( ) ) ;
for ( Token * tok = tokenList . front ( ) ; tok ; tok = tok - > next ( ) ) {
for ( const Variable & arg : function - > argumentList ) {
if ( arg . name ( ) = = tok - > str ( ) ) {
tok - > variable ( & arg ) ;
tok - > varId ( arg . declarationId ( ) ) ;
}
}
}
symbolDatabase - > setValueTypeInTokenList ( false , tokenList . front ( ) ) ;
2020-04-28 17:16:13 +02:00
return executeExpression ( tokenList . front ( ) - > astTop ( ) , * this ) ;
}
void contractConstraints ( const Function * function , ExprEngine : : ValuePtr ( * executeExpression ) ( const Token * , Data & ) ) {
auto value = executeContract ( function , executeExpression ) ;
if ( value )
constraints . push_back ( value ) ;
2020-04-27 09:08:50 +02:00
}
2019-12-22 21:00:53 +01:00
void addError ( int linenr ) OVERRIDE {
mTrackExecution - > addError ( linenr ) ;
}
2019-09-26 20:48:33 +02:00
void assignValue ( const Token * tok , unsigned int varId , ExprEngine : : ValuePtr value ) {
2019-10-07 17:44:26 +02:00
if ( varId = = 0 )
return ;
2019-09-26 20:48:33 +02:00
mTrackExecution - > symbolRange ( tok , value ) ;
2019-09-27 13:12:16 +02:00
if ( value ) {
if ( auto arr = std : : dynamic_pointer_cast < ExprEngine : : ArrayValue > ( value ) ) {
mTrackExecution - > symbolRange ( tok , arr - > size ) ;
for ( const auto & indexAndValue : arr - > data )
mTrackExecution - > symbolRange ( tok , indexAndValue . value ) ;
2019-09-27 13:30:09 +02:00
} else if ( auto s = std : : dynamic_pointer_cast < ExprEngine : : StructValue > ( value ) ) {
for ( const auto & m : s - > member )
mTrackExecution - > symbolRange ( tok , m . second ) ;
2019-09-27 13:12:16 +02:00
}
}
2019-09-26 20:48:33 +02:00
memory [ varId ] = value ;
}
2019-09-26 21:35:29 +02:00
void assignStructMember ( const Token * tok , ExprEngine : : StructValue * structVal , const std : : string & memberName , ExprEngine : : ValuePtr value ) {
mTrackExecution - > symbolRange ( tok , value ) ;
structVal - > member [ memberName ] = value ;
}
2020-01-09 18:50:29 +01:00
void functionCall ( ) {
// Remove values for global variables
const SymbolDatabase * symbolDatabase = tokenizer - > getSymbolDatabase ( ) ;
for ( std : : map < nonneg int , ExprEngine : : ValuePtr > : : iterator it = memory . begin ( ) ; it ! = memory . end ( ) ; ) {
unsigned int varid = it - > first ;
const Variable * var = symbolDatabase - > getVariableFromVarId ( varid ) ;
2020-01-15 18:35:55 +01:00
if ( var & & var - > isGlobal ( ) )
2020-01-09 18:50:29 +01:00
it = memory . erase ( it ) ;
else
+ + it ;
}
}
2019-12-27 16:26:44 +01:00
std : : string getNewSymbolName ( ) OVERRIDE {
2019-09-17 21:00:59 +02:00
return " $ " + std : : to_string ( + + ( * symbolValueIndex ) ) ;
}
std : : shared_ptr < ExprEngine : : ArrayValue > getArrayValue ( const Token * tok ) {
const Memory : : iterator it = memory . find ( tok - > varId ( ) ) ;
if ( it ! = memory . end ( ) )
return std : : dynamic_pointer_cast < ExprEngine : : ArrayValue > ( it - > second ) ;
2020-01-02 06:16:36 +01:00
if ( tok - > varId ( ) = = 0 | | ! tok - > variable ( ) )
2019-09-21 14:17:16 +02:00
return std : : shared_ptr < ExprEngine : : ArrayValue > ( ) ;
2019-09-29 15:00:54 +02:00
auto val = std : : make_shared < ExprEngine : : ArrayValue > ( this , tok - > variable ( ) ) ;
assignValue ( tok , tok - > varId ( ) , val ) ;
2019-09-21 14:17:16 +02:00
return val ;
2019-09-17 21:00:59 +02:00
}
2019-09-19 20:18:39 +02:00
ExprEngine : : ValuePtr getValue ( unsigned int varId , const ValueType * valueType , const Token * tok ) {
2019-09-17 21:00:59 +02:00
const Memory : : const_iterator it = memory . find ( varId ) ;
if ( it ! = memory . end ( ) )
return it - > second ;
if ( ! valueType )
return ExprEngine : : ValuePtr ( ) ;
ExprEngine : : ValuePtr value = getValueRangeFromValueType ( getNewSymbolName ( ) , valueType , * settings ) ;
2019-09-19 20:18:39 +02:00
if ( value ) {
2020-01-21 18:55:07 +01:00
if ( tok - > variable ( ) & & tok - > variable ( ) - > nameToken ( ) )
addConstraints ( value , tok - > variable ( ) - > nameToken ( ) ) ;
2019-09-26 20:48:33 +02:00
assignValue ( tok , varId , value ) ;
2019-09-19 20:18:39 +02:00
}
2019-09-17 21:00:59 +02:00
return value ;
}
2019-09-19 20:18:39 +02:00
2020-04-28 17:16:13 +02:00
void trackCheckContract ( const Token * tok , const std : : string & solverOutput ) {
std : : ostringstream os ;
os < < " checkContract:{ \n " ;
std : : string line ;
std : : istringstream istr ( solverOutput ) ;
while ( std : : getline ( istr , line ) )
os < < " " < < line < < " \n " ;
os < < " } " ;
mTrackExecution - > state ( tok , os . str ( ) ) ;
}
2019-09-19 20:18:39 +02:00
void trackProgramState ( const Token * tok ) {
if ( memory . empty ( ) )
return ;
const SymbolDatabase * const symbolDatabase = tokenizer - > getSymbolDatabase ( ) ;
std : : ostringstream s ;
2019-09-27 21:03:47 +02:00
s < < " { " ; // << mDataIndex << ":";
2019-09-19 20:18:39 +02:00
for ( auto mem : memory ) {
ExprEngine : : ValuePtr value = mem . second ;
2019-09-27 14:36:33 +02:00
const Variable * var = symbolDatabase - > getVariableFromVarId ( mem . first ) ;
if ( ! var )
continue ;
s < < " " < < var - > name ( ) < < " = " ;
2019-09-20 21:27:51 +02:00
if ( ! value )
s < < " (null) " ;
2019-09-26 10:07:31 +02:00
else if ( value - > name [ 0 ] = = ' $ ' & & value - > getSymbolicExpression ( ) ! = value - > name )
2019-09-26 10:03:58 +02:00
s < < " ( " < < value - > name < < " , " < < value - > getSymbolicExpression ( ) < < " ) " ;
2019-09-19 20:18:39 +02:00
else
s < < value - > name ;
}
s < < " } " ;
mTrackExecution - > state ( tok , s . str ( ) ) ;
}
2019-10-02 21:47:00 +02:00
2020-04-28 22:09:01 +02:00
void addMissingContract ( const std : : string & f ) {
mTrackExecution - > addMissingContract ( f ) ;
}
const std : : set < std : : string > getMissingContracts ( ) const {
return mTrackExecution - > getMissingContracts ( ) ;
}
2019-10-02 21:47:00 +02:00
ExprEngine : : ValuePtr notValue ( ExprEngine : : ValuePtr v ) {
auto b = std : : dynamic_pointer_cast < ExprEngine : : BinOpResult > ( v ) ;
if ( b ) {
std : : string binop ;
if ( b - > binop = = " == " )
binop = " != " ;
else if ( b - > binop = = " != " )
binop = " == " ;
else if ( b - > binop = = " >= " )
binop = " < " ;
else if ( b - > binop = = " <= " )
binop = " > " ;
else if ( b - > binop = = " > " )
binop = " <= " ;
else if ( b - > binop = = " < " )
binop = " >= " ;
if ( ! binop . empty ( ) )
return std : : make_shared < ExprEngine : : BinOpResult > ( binop , b - > op1 , b - > op2 ) ;
}
auto zero = std : : make_shared < ExprEngine : : IntRange > ( " 0 " , 0 , 0 ) ;
return std : : make_shared < ExprEngine : : BinOpResult > ( " == " , v , zero ) ;
}
2019-10-03 08:48:05 +02:00
void addConstraint ( ExprEngine : : ValuePtr condValue , bool trueCond ) {
2019-10-06 14:47:34 +02:00
if ( ! condValue )
return ;
2019-10-02 21:47:00 +02:00
if ( trueCond )
2019-10-03 08:48:05 +02:00
constraints . push_back ( condValue ) ;
2019-10-02 21:47:00 +02:00
else
2019-10-03 08:48:05 +02:00
constraints . push_back ( notValue ( condValue ) ) ;
2019-10-02 21:47:00 +02:00
}
2019-10-06 19:58:51 +02:00
void addConstraint ( ExprEngine : : ValuePtr lhsValue , ExprEngine : : ValuePtr rhsValue , bool equals ) {
if ( ! lhsValue | | ! rhsValue )
return ;
constraints . push_back ( std : : make_shared < ExprEngine : : BinOpResult > ( equals ? " == " : " != " , lhsValue , rhsValue ) ) ;
}
2019-12-24 21:14:14 +01:00
void addConstraints ( ExprEngine : : ValuePtr value , const Token * tok ) {
MathLib : : bigint low ;
if ( tok - > getCppcheckAttribute ( TokenImpl : : CppcheckAttributes : : Type : : LOW , & low ) )
addConstraint ( std : : make_shared < ExprEngine : : BinOpResult > ( " >= " , value , std : : make_shared < ExprEngine : : IntRange > ( std : : to_string ( low ) , low , low ) ) , true ) ;
MathLib : : bigint high ;
if ( tok - > getCppcheckAttribute ( TokenImpl : : CppcheckAttributes : : Type : : HIGH , & high ) )
addConstraint ( std : : make_shared < ExprEngine : : BinOpResult > ( " <= " , value , std : : make_shared < ExprEngine : : IntRange > ( std : : to_string ( high ) , high , high ) ) , true ) ;
}
2019-09-19 20:18:39 +02:00
private :
TrackExecution * const mTrackExecution ;
2019-09-20 06:12:20 +02:00
const int mDataIndex ;
2019-09-17 21:00:59 +02:00
} ;
}
2020-04-24 21:17:06 +02:00
# ifdef __clang__
// work around "undefined reference to `__muloti4'" linker error - see https://bugs.llvm.org/show_bug.cgi?id=16404
__attribute__ ( ( no_sanitize ( " undefined " ) ) )
# endif
2019-09-25 18:33:21 +02:00
static ExprEngine : : ValuePtr simplifyValue ( ExprEngine : : ValuePtr origValue )
{
auto b = std : : dynamic_pointer_cast < ExprEngine : : BinOpResult > ( origValue ) ;
if ( ! b )
return origValue ;
if ( ! b - > op1 | | ! b - > op2 )
return origValue ;
auto intRange1 = std : : dynamic_pointer_cast < ExprEngine : : IntRange > ( b - > op1 ) ;
2019-10-02 17:59:04 +02:00
auto intRange2 = std : : dynamic_pointer_cast < ExprEngine : : IntRange > ( b - > op2 ) ;
2019-09-25 18:33:21 +02:00
if ( intRange1 & & intRange2 & & intRange1 - > minValue = = intRange1 - > maxValue & & intRange2 - > minValue = = intRange2 - > maxValue ) {
const std : : string & binop = b - > binop ;
int128_t v ;
if ( binop = = " + " )
v = intRange1 - > minValue + intRange2 - > minValue ;
else if ( binop = = " - " )
v = intRange1 - > minValue - intRange2 - > minValue ;
else if ( binop = = " * " )
v = intRange1 - > minValue * intRange2 - > minValue ;
else if ( binop = = " / " & & intRange2 - > minValue ! = 0 )
v = intRange1 - > minValue / intRange2 - > minValue ;
else if ( binop = = " % " & & intRange2 - > minValue ! = 0 )
v = intRange1 - > minValue % intRange2 - > minValue ;
else
return origValue ;
return std : : make_shared < ExprEngine : : IntRange > ( ExprEngine : : str ( v ) , v , v ) ;
}
return origValue ;
}
2019-09-28 16:16:36 +02:00
static ExprEngine : : ValuePtr translateUninitValueToRange ( ExprEngine : : ValuePtr value , const : : ValueType * valueType , Data & data )
{
if ( ! value )
return value ;
if ( value - > type = = ExprEngine : : ValueType : : UninitValue ) {
auto rangeValue = getValueRangeFromValueType ( data . getNewSymbolName ( ) , valueType , * data . settings ) ;
if ( rangeValue )
return rangeValue ;
}
if ( auto conditionalValue = std : : dynamic_pointer_cast < ExprEngine : : ConditionalValue > ( value ) ) {
if ( conditionalValue - > values . size ( ) = = 1 & & conditionalValue - > values [ 0 ] . second & & conditionalValue - > values [ 0 ] . second - > type = = ExprEngine : : ValueType : : UninitValue ) {
auto rangeValue = getValueRangeFromValueType ( data . getNewSymbolName ( ) , valueType , * data . settings ) ;
if ( rangeValue )
return rangeValue ;
}
}
return value ;
}
2019-09-25 18:33:21 +02:00
2019-09-28 19:28:12 +02:00
static int128_t truncateInt ( int128_t value , int bits , char sign )
{
value = value & ( ( ( int128_t ) 1 < < bits ) - 1 ) ;
// Sign extension
if ( sign = = ' s ' & & value & ( 1ULL < < ( bits - 1 ) ) )
value | = ~ ( ( ( int128_t ) 1 < < bits ) - 1 ) ;
return value ;
}
2019-12-29 16:26:11 +01:00
ExprEngine : : ArrayValue : : ArrayValue ( const std : : string & name , ExprEngine : : ValuePtr size , ExprEngine : : ValuePtr value , bool pointer , bool nullPointer , bool uninitPointer )
2019-09-26 10:03:58 +02:00
: Value ( name , ExprEngine : : ValueType : : ArrayValue )
2019-12-29 16:26:11 +01:00
, pointer ( pointer ) , nullPointer ( nullPointer ) , uninitPointer ( uninitPointer )
2019-09-25 18:33:21 +02:00
, size ( size )
{
assign ( ExprEngine : : ValuePtr ( ) , value ) ;
}
2019-09-29 15:00:54 +02:00
ExprEngine : : ArrayValue : : ArrayValue ( DataBase * data , const Variable * var )
: Value ( data - > getNewSymbolName ( ) , ExprEngine : : ValueType : : ArrayValue )
2019-12-29 16:26:11 +01:00
, pointer ( var - > isPointer ( ) ) , nullPointer ( var - > isPointer ( ) ) , uninitPointer ( var - > isPointer ( ) )
2019-09-25 18:33:21 +02:00
{
if ( var ) {
int sz = 1 ;
for ( const auto & dim : var - > dimensions ( ) ) {
if ( ! dim . known ) {
sz = - 1 ;
break ;
}
sz * = dim . num ;
}
if ( sz > = 1 )
size = std : : make_shared < ExprEngine : : IntRange > ( std : : to_string ( sz ) , sz , sz ) ;
}
2019-09-29 15:00:54 +02:00
ValuePtr val ;
2019-09-29 17:32:26 +02:00
if ( var & & ! var - > isGlobal ( ) & & ! var - > isStatic ( ) )
2019-09-29 15:00:54 +02:00
val = std : : make_shared < ExprEngine : : UninitValue > ( ) ;
2019-09-29 17:32:26 +02:00
else if ( var & & var - > valueType ( ) ) {
: : ValueType vt ( * var - > valueType ( ) ) ;
vt . pointer = 0 ;
val = getValueRangeFromValueType ( data - > getNewSymbolName ( ) , & vt , * data - > settings ) ;
2019-09-29 15:00:54 +02:00
}
assign ( ExprEngine : : ValuePtr ( ) , val ) ;
2019-09-25 18:33:21 +02:00
}
2019-12-29 16:26:11 +01:00
std : : string ExprEngine : : ArrayValue : : getRange ( ) const
{
std : : string r = getSymbolicExpression ( ) ;
if ( nullPointer )
r + = std : : string ( r . empty ( ) ? " " : " , " ) + " null " ;
if ( uninitPointer )
r + = std : : string ( r . empty ( ) ? " " : " , " ) + " ->? " ;
return r ;
}
2019-09-17 21:00:59 +02:00
void ExprEngine : : ArrayValue : : assign ( ExprEngine : : ValuePtr index , ExprEngine : : ValuePtr value )
{
2019-09-25 18:33:21 +02:00
if ( ! index )
data . clear ( ) ;
if ( value ) {
ExprEngine : : ArrayValue : : IndexAndValue indexAndValue = { index , value } ;
data . push_back ( indexAndValue ) ;
2019-09-17 21:00:59 +02:00
}
}
2019-09-24 22:22:16 +02:00
void ExprEngine : : ArrayValue : : clear ( )
{
2019-09-25 18:33:21 +02:00
data . clear ( ) ;
ExprEngine : : ArrayValue : : IndexAndValue indexAndValue = {
ExprEngine : : ValuePtr ( ) , std : : make_shared < ExprEngine : : IntRange > ( " 0 " , 0 , 0 )
} ;
data . push_back ( indexAndValue ) ;
}
static bool isEqual ( ExprEngine : : ValuePtr v1 , ExprEngine : : ValuePtr v2 )
{
if ( ! v1 | | ! v2 )
return ! v1 & & ! v2 ;
2019-09-26 10:03:58 +02:00
return v1 - > name = = v2 - > name ;
2019-09-24 22:22:16 +02:00
}
2019-09-25 18:33:21 +02:00
static bool isNonOverlapping ( ExprEngine : : ValuePtr v1 , ExprEngine : : ValuePtr v2 )
2019-09-17 21:00:59 +02:00
{
2019-09-25 18:33:21 +02:00
if ( ! v1 | | ! v2 )
return false ; // Don't know!
auto intRange1 = std : : dynamic_pointer_cast < ExprEngine : : IntRange > ( v1 ) ;
auto intRange2 = std : : dynamic_pointer_cast < ExprEngine : : IntRange > ( v2 ) ;
if ( intRange1 & & intRange2 & & ( intRange1 - > minValue > intRange2 - > maxValue | | intRange1 - > maxValue < intRange2 - > maxValue ) )
return true ;
return false ;
}
2019-09-26 10:03:58 +02:00
ExprEngine : : ConditionalValue : : Vector ExprEngine : : ArrayValue : : read ( ExprEngine : : ValuePtr index ) const
2019-09-25 18:33:21 +02:00
{
2019-09-26 10:03:58 +02:00
ExprEngine : : ConditionalValue : : Vector ret ;
2019-10-04 17:58:00 +02:00
if ( ! index )
return ret ;
2020-04-04 11:44:59 +02:00
for ( const auto & indexAndValue : data ) {
2019-10-08 20:13:25 +02:00
if ( : : isEqual ( index , indexAndValue . index ) )
2019-09-25 18:33:21 +02:00
ret . clear ( ) ;
if ( isNonOverlapping ( index , indexAndValue . index ) )
continue ;
2019-09-26 10:03:58 +02:00
// Array contains string literal data...
if ( ! indexAndValue . index & & indexAndValue . value - > type = = ExprEngine : : ValueType : : StringLiteralValue ) {
2019-09-25 18:33:21 +02:00
auto stringLiteral = std : : dynamic_pointer_cast < ExprEngine : : StringLiteralValue > ( indexAndValue . value ) ;
if ( ! stringLiteral ) {
2019-09-26 10:03:58 +02:00
ret . push_back ( std : : pair < ValuePtr , ValuePtr > ( indexAndValue . index , std : : make_shared < ExprEngine : : IntRange > ( " " , - 128 , 128 ) ) ) ;
2019-09-25 18:33:21 +02:00
continue ;
}
if ( auto i = std : : dynamic_pointer_cast < ExprEngine : : IntRange > ( index ) ) {
2020-02-13 16:27:06 +01:00
if ( i - > minValue > = 0 & & i - > minValue = = i - > maxValue ) {
2019-09-25 18:33:21 +02:00
int c = 0 ;
if ( i - > minValue < stringLiteral - > size ( ) )
c = stringLiteral - > string [ i - > minValue ] ;
2019-09-26 10:03:58 +02:00
ret . push_back ( std : : pair < ValuePtr , ValuePtr > ( indexAndValue . index , std : : make_shared < ExprEngine : : IntRange > ( std : : to_string ( c ) , c , c ) ) ) ;
2019-09-25 18:33:21 +02:00
continue ;
}
}
int cmin = 0 , cmax = 0 ;
for ( char c : stringLiteral - > string ) {
if ( c < cmin )
cmin = c ;
else if ( c > cmax )
cmax = c ;
}
2019-09-26 10:03:58 +02:00
ret . push_back ( std : : pair < ValuePtr , ValuePtr > ( indexAndValue . index , std : : make_shared < ExprEngine : : IntRange > ( " " , cmin , cmax ) ) ) ;
2019-09-25 18:33:21 +02:00
continue ;
}
2019-09-29 21:19:21 +02:00
// Rename IntRange
if ( auto i = std : : dynamic_pointer_cast < ExprEngine : : IntRange > ( indexAndValue . value ) ) {
ret . push_back ( std : : pair < ValuePtr , ValuePtr > ( indexAndValue . index , std : : make_shared < ExprEngine : : IntRange > ( indexAndValue . value - > name + " : " + index - > name , i - > minValue , i - > maxValue ) ) ) ;
continue ;
}
2019-09-26 10:03:58 +02:00
ret . push_back ( std : : pair < ValuePtr , ValuePtr > ( indexAndValue . index , indexAndValue . value ) ) ;
2019-09-17 21:00:59 +02:00
}
2019-09-26 10:03:58 +02:00
if ( ret . size ( ) = = 1 )
ret [ 0 ] . first = ExprEngine : : ValuePtr ( ) ;
else if ( ret . size ( ) = = 2 & & ! ret [ 0 ] . first ) {
ret [ 0 ] . first = std : : make_shared < ExprEngine : : BinOpResult > ( " != " , index , ret [ 1 ] . first ) ;
ret [ 1 ] . first = std : : make_shared < ExprEngine : : BinOpResult > ( " == " , index , ret [ 1 ] . first ) ;
} else {
// FIXME!!
ret . clear ( ) ;
}
2019-09-25 18:33:21 +02:00
return ret ;
2019-09-17 21:00:59 +02:00
}
2019-09-26 10:03:58 +02:00
std : : string ExprEngine : : ConditionalValue : : getSymbolicExpression ( ) const
{
std : : ostringstream ostr ;
ostr < < " { " ;
bool first = true ;
for ( auto condvalue : values ) {
ValuePtr cond = condvalue . first ;
ValuePtr value = condvalue . second ;
if ( ! first )
ostr < < " , " ;
first = false ;
ostr < < " { "
< < ( cond ? cond - > getSymbolicExpression ( ) : std : : string ( " (null) " ) )
< < " , "
< < value - > getSymbolicExpression ( )
< < " } " ;
}
ostr < < " } " ;
return ostr . str ( ) ;
}
std : : string ExprEngine : : ArrayValue : : getSymbolicExpression ( ) const
2019-09-21 14:17:16 +02:00
{
2019-09-25 18:33:21 +02:00
std : : ostringstream ostr ;
ostr < < " size= " < < ( size ? size - > name : std : : string ( " (null) " ) ) ;
2020-04-04 11:44:59 +02:00
for ( const auto & indexAndValue : data ) {
2019-09-25 18:33:21 +02:00
ostr < < " ,[ "
< < ( ! indexAndValue . index ? std : : string ( " : " ) : indexAndValue . index - > name )
< < " ]= "
< < indexAndValue . value - > name ;
2019-09-21 14:17:16 +02:00
}
2019-09-25 18:33:21 +02:00
return ostr . str ( ) ;
2019-09-21 14:17:16 +02:00
}
2019-09-26 19:39:12 +02:00
std : : string ExprEngine : : StructValue : : getSymbolicExpression ( ) const
{
std : : ostringstream ostr ;
ostr < < " { " ;
bool first = true ;
for ( const auto & m : member ) {
const std : : string & memberName = m . first ;
auto memberValue = m . second ;
if ( ! first )
ostr < < " , " ;
first = false ;
ostr < < memberName < < " = " < < ( memberValue ? memberValue - > getSymbolicExpression ( ) : std : : string ( " (null) " ) ) ;
}
ostr < < " } " ;
return ostr . str ( ) ;
}
2019-10-02 17:59:04 +02:00
std : : string ExprEngine : : IntegerTruncation : : getSymbolicExpression ( ) const
2019-09-17 21:00:59 +02:00
{
2019-10-02 17:59:04 +02:00
return sign + std : : to_string ( bits ) + " ( " + inputValue - > getSymbolicExpression ( ) + " ) " ;
2019-09-17 21:00:59 +02:00
}
2019-10-02 17:59:04 +02:00
# ifdef USE_Z3
2019-10-06 14:47:34 +02:00
2019-10-02 17:59:04 +02:00
struct ExprData {
2019-10-05 16:33:40 +02:00
typedef std : : map < std : : string , z3 : : expr > ValueExpr ;
2019-10-02 17:59:04 +02:00
typedef std : : vector < z3 : : expr > AssertionList ;
2019-09-17 21:00:59 +02:00
2019-10-05 16:33:40 +02:00
z3 : : context context ;
2019-10-02 17:59:04 +02:00
ValueExpr valueExpr ;
AssertionList assertionList ;
2019-09-17 21:00:59 +02:00
2019-10-02 17:59:04 +02:00
void addAssertions ( z3 : : solver & solver ) const {
for ( auto assertExpr : assertionList )
solver . add ( assertExpr ) ;
2019-09-17 21:00:59 +02:00
}
2019-12-30 12:53:59 +01:00
z3 : : expr addInt ( const std : : string & name , int128_t minValue , int128_t maxValue ) {
z3 : : expr e = context . int_const ( name . c_str ( ) ) ;
valueExpr . emplace ( name , e ) ;
if ( minValue > = INT_MIN & & maxValue < = INT_MAX )
assertionList . push_back ( e > = int ( minValue ) & & e < = int ( maxValue ) ) ;
else if ( maxValue < = INT_MAX )
assertionList . push_back ( e < = int ( maxValue ) ) ;
else if ( minValue > = INT_MIN )
assertionList . push_back ( e > = int ( minValue ) ) ;
return e ;
}
2019-12-30 19:47:18 +01:00
z3 : : expr addFloat ( const std : : string & name ) {
2020-05-12 16:57:07 +02:00
# if Z3_VERSION_INT >= GET_VERSION_INT(4,8,0)
2019-12-30 19:47:18 +01:00
z3 : : expr e = context . fpa_const ( name . c_str ( ) , 11 , 53 ) ;
2020-01-16 18:59:28 +01:00
# else
2020-01-16 19:35:05 +01:00
z3 : : expr e = context . real_const ( name . c_str ( ) ) ;
2020-01-15 19:46:00 +01:00
# endif
2019-12-30 19:47:18 +01:00
valueExpr . emplace ( name , e ) ;
return e ;
}
2019-10-06 14:47:34 +02:00
z3 : : expr getExpr ( const ExprEngine : : BinOpResult * b ) {
auto op1 = getExpr ( b - > op1 ) ;
auto op2 = getExpr ( b - > op2 ) ;
if ( b - > binop = = " + " )
return op1 + op2 ;
if ( b - > binop = = " - " )
return op1 - op2 ;
if ( b - > binop = = " * " )
return op1 * op2 ;
if ( b - > binop = = " / " )
return op1 / op2 ;
if ( b - > binop = = " % " )
2020-05-12 17:01:16 +02:00
# if Z3_VERSION_INT >= GET_VERSION_INT(4,8,5)
2019-10-06 14:47:34 +02:00
return op1 % op2 ;
2020-01-16 18:59:28 +01:00
# else
return op1 - ( op1 / op2 ) * op2 ;
2020-01-15 19:46:00 +01:00
# endif
2019-10-06 14:47:34 +02:00
if ( b - > binop = = " == " )
2019-10-09 20:17:26 +02:00
return int_expr ( op1 ) = = int_expr ( op2 ) ;
2019-10-06 14:47:34 +02:00
if ( b - > binop = = " != " )
return op1 ! = op2 ;
if ( b - > binop = = " >= " )
return op1 > = op2 ;
if ( b - > binop = = " <= " )
return op1 < = op2 ;
if ( b - > binop = = " > " )
return op1 > op2 ;
if ( b - > binop = = " < " )
return op1 < op2 ;
if ( b - > binop = = " && " )
2019-10-09 20:17:26 +02:00
return bool_expr ( op1 ) & & bool_expr ( op2 ) ;
2019-10-06 14:47:34 +02:00
if ( b - > binop = = " || " )
2019-10-09 20:17:26 +02:00
return bool_expr ( op1 ) | | bool_expr ( op2 ) ;
2019-10-14 17:20:20 +02:00
if ( b - > binop = = " << " )
return op1 * z3 : : pw ( context . int_val ( 2 ) , op2 ) ;
if ( b - > binop = = " >> " )
return op1 / z3 : : pw ( context . int_val ( 2 ) , op2 ) ;
2019-10-14 11:56:39 +02:00
throw VerifyException ( nullptr , " Internal error: Unhandled operator " + b - > binop ) ;
2019-10-06 14:47:34 +02:00
}
2019-09-23 20:27:13 +02:00
2019-10-06 14:47:34 +02:00
z3 : : expr getExpr ( ExprEngine : : ValuePtr v ) {
2019-10-09 22:16:30 +02:00
if ( ! v )
throw VerifyException ( nullptr , " Can not solve expressions, operand value is null " ) ;
2019-10-06 14:47:34 +02:00
if ( auto intRange = std : : dynamic_pointer_cast < ExprEngine : : IntRange > ( v ) ) {
if ( intRange - > name [ 0 ] ! = ' $ ' )
2020-05-12 17:01:16 +02:00
# if Z3_VERSION_INT >= GET_VERSION_INT(4,7,1)
2019-10-06 14:47:34 +02:00
return context . int_val ( int64_t ( intRange - > minValue ) ) ;
2020-01-16 18:59:28 +01:00
# else
return context . int_val ( ( long long ) ( intRange - > minValue ) ) ;
2020-01-15 19:46:00 +01:00
# endif
2019-10-06 14:47:34 +02:00
auto it = valueExpr . find ( v - > name ) ;
if ( it ! = valueExpr . end ( ) )
return it - > second ;
2019-12-30 12:53:59 +01:00
return addInt ( v - > name , intRange - > minValue , intRange - > maxValue ) ;
2019-10-06 14:47:34 +02:00
}
2019-09-17 21:00:59 +02:00
2019-12-30 19:47:18 +01:00
if ( auto floatRange = std : : dynamic_pointer_cast < ExprEngine : : FloatRange > ( v ) ) {
auto it = valueExpr . find ( v - > name ) ;
if ( it ! = valueExpr . end ( ) )
return it - > second ;
return addFloat ( v - > name ) ;
}
2019-10-06 14:47:34 +02:00
if ( auto b = std : : dynamic_pointer_cast < ExprEngine : : BinOpResult > ( v ) ) {
return getExpr ( b . get ( ) ) ;
}
2019-09-22 21:14:20 +02:00
2019-10-06 14:47:34 +02:00
if ( auto c = std : : dynamic_pointer_cast < ExprEngine : : ConditionalValue > ( v ) ) {
2019-10-10 20:29:36 +02:00
if ( c - > values . empty ( ) )
throw VerifyException ( nullptr , " ConditionalValue is empty " ) ;
2019-10-06 14:47:34 +02:00
if ( c - > values . size ( ) = = 1 )
return getExpr ( c - > values [ 0 ] . second ) ;
2019-09-22 21:14:20 +02:00
2019-10-06 14:47:34 +02:00
return z3 : : ite ( getExpr ( c - > values [ 1 ] . first ) ,
getExpr ( c - > values [ 1 ] . second ) ,
getExpr ( c - > values [ 0 ] . second ) ) ;
}
2019-10-03 20:16:06 +02:00
2019-10-06 17:43:30 +02:00
if ( auto integerTruncation = std : : dynamic_pointer_cast < ExprEngine : : IntegerTruncation > ( v ) ) {
return getExpr ( integerTruncation - > inputValue ) ;
//return getExpr(integerTruncation->inputValue) & ((1 << integerTruncation->bits) - 1);
}
2019-10-06 14:47:34 +02:00
if ( v - > type = = ExprEngine : : ValueType : : UninitValue )
return context . int_val ( 0 ) ;
2019-10-03 20:16:06 +02:00
2019-10-09 22:16:30 +02:00
throw VerifyException ( nullptr , " Internal error: Unhandled value type " ) ;
2019-10-06 14:47:34 +02:00
}
2019-10-03 20:16:06 +02:00
2019-10-06 14:47:34 +02:00
z3 : : expr getConstraintExpr ( ExprEngine : : ValuePtr v ) {
if ( v - > type = = ExprEngine : : ValueType : : IntRange )
return ( getExpr ( v ) ! = 0 ) ;
return getExpr ( v ) ;
}
2019-10-09 20:17:26 +02:00
private :
z3 : : expr bool_expr ( z3 : : expr e ) {
if ( e . is_bool ( ) )
return e ;
return e ! = 0 ;
}
z3 : : expr int_expr ( z3 : : expr e ) {
if ( e . is_bool ( ) )
return z3 : : ite ( e , context . int_val ( 1 ) , context . int_val ( 0 ) ) ;
return e ;
}
2019-10-06 14:47:34 +02:00
} ;
2019-10-02 17:59:04 +02:00
# endif
2019-10-05 16:33:40 +02:00
2019-10-08 20:13:25 +02:00
bool ExprEngine : : IntRange : : isEqual ( DataBase * dataBase , int value ) const
2019-10-05 16:33:40 +02:00
{
if ( value < minValue | | value > maxValue )
return false ;
const Data * data = dynamic_cast < Data * > ( dataBase ) ;
if ( data - > constraints . empty ( ) )
return true ;
# ifdef USE_Z3
// Check the value against the constraints
ExprData exprData ;
z3 : : solver solver ( exprData . context ) ;
2019-10-06 18:26:40 +02:00
try {
2019-12-30 12:53:59 +01:00
z3 : : expr e = exprData . addInt ( name , minValue , maxValue ) ;
2019-10-06 18:26:40 +02:00
for ( auto constraint : dynamic_cast < const Data * > ( dataBase ) - > constraints )
solver . add ( exprData . getConstraintExpr ( constraint ) ) ;
2019-12-30 12:53:59 +01:00
exprData . addAssertions ( solver ) ;
2019-10-06 18:26:40 +02:00
solver . add ( e = = value ) ;
return solver . check ( ) = = z3 : : sat ;
} catch ( const z3 : : exception & exception ) {
2019-10-09 09:42:00 +02:00
std : : cerr < < " z3: " < < exception < < std : : endl ;
2019-10-06 18:26:40 +02:00
return true ; // Safe option is to return true
}
2019-10-05 16:33:40 +02:00
# else
// The value may or may not be in range
return false ;
# endif
}
2019-12-26 15:39:08 +01:00
bool ExprEngine : : IntRange : : isGreaterThan ( DataBase * dataBase , int value ) const
{
2020-01-21 20:19:51 +01:00
if ( maxValue < = value )
2019-12-26 15:39:08 +01:00
return false ;
const Data * data = dynamic_cast < Data * > ( dataBase ) ;
if ( data - > constraints . empty ( ) )
return true ;
# ifdef USE_Z3
// Check the value against the constraints
ExprData exprData ;
z3 : : solver solver ( exprData . context ) ;
try {
2019-12-30 19:47:18 +01:00
z3 : : expr e = exprData . addInt ( name , minValue , maxValue ) ;
2019-12-26 15:39:08 +01:00
for ( auto constraint : dynamic_cast < const Data * > ( dataBase ) - > constraints )
solver . add ( exprData . getConstraintExpr ( constraint ) ) ;
2019-12-30 12:53:59 +01:00
exprData . addAssertions ( solver ) ;
2019-12-26 15:39:08 +01:00
solver . add ( e > value ) ;
return solver . check ( ) = = z3 : : sat ;
} catch ( const z3 : : exception & exception ) {
std : : cerr < < " z3: " < < exception < < std : : endl ;
return true ; // Safe option is to return true
}
# else
// The value may or may not be in range
return false ;
# endif
}
bool ExprEngine : : IntRange : : isLessThan ( DataBase * dataBase , int value ) const
{
2020-01-21 20:19:51 +01:00
if ( minValue > = value )
2019-12-26 15:39:08 +01:00
return false ;
const Data * data = dynamic_cast < Data * > ( dataBase ) ;
if ( data - > constraints . empty ( ) )
return true ;
# ifdef USE_Z3
// Check the value against the constraints
ExprData exprData ;
z3 : : solver solver ( exprData . context ) ;
try {
2019-12-30 19:47:18 +01:00
z3 : : expr e = exprData . addInt ( name , minValue , maxValue ) ;
2019-12-26 15:39:08 +01:00
for ( auto constraint : dynamic_cast < const Data * > ( dataBase ) - > constraints )
solver . add ( exprData . getConstraintExpr ( constraint ) ) ;
2019-12-30 12:53:59 +01:00
exprData . addAssertions ( solver ) ;
2019-12-26 15:39:08 +01:00
solver . add ( e < value ) ;
return solver . check ( ) = = z3 : : sat ;
} catch ( const z3 : : exception & exception ) {
std : : cerr < < " z3: " < < exception < < std : : endl ;
return true ; // Safe option is to return true
}
# else
// The value may or may not be in range
return false ;
# endif
}
2019-12-30 19:47:18 +01:00
bool ExprEngine : : FloatRange : : isEqual ( DataBase * dataBase , int value ) const
{
const Data * data = dynamic_cast < Data * > ( dataBase ) ;
if ( data - > constraints . empty ( ) )
return true ;
2019-12-31 09:01:58 +01:00
if ( MathLib : : isFloat ( name ) ) {
float f = MathLib : : toDoubleNumber ( name ) ;
return value > = f - 0.00001 & & value < = f + 0.00001 ;
}
2019-12-30 19:47:18 +01:00
# ifdef USE_Z3
// Check the value against the constraints
ExprData exprData ;
z3 : : solver solver ( exprData . context ) ;
try {
z3 : : expr e = exprData . addFloat ( name ) ;
for ( auto constraint : dynamic_cast < const Data * > ( dataBase ) - > constraints )
solver . add ( exprData . getConstraintExpr ( constraint ) ) ;
exprData . addAssertions ( solver ) ;
2019-12-31 09:01:58 +01:00
solver . add ( e > = value & & e < = value ) ;
2019-12-30 19:47:18 +01:00
return solver . check ( ) ! = z3 : : unsat ;
} catch ( const z3 : : exception & exception ) {
std : : cerr < < " z3: " < < exception < < std : : endl ;
return true ; // Safe option is to return true
}
# else
// The value may or may not be in range
return false ;
# endif
}
bool ExprEngine : : FloatRange : : isGreaterThan ( DataBase * dataBase , int value ) const
{
if ( value < minValue | | value > maxValue )
return false ;
const Data * data = dynamic_cast < Data * > ( dataBase ) ;
if ( data - > constraints . empty ( ) )
return true ;
2019-12-31 09:01:58 +01:00
if ( MathLib : : isFloat ( name ) )
return value > MathLib : : toDoubleNumber ( name ) ;
2019-12-30 19:47:18 +01:00
# ifdef USE_Z3
// Check the value against the constraints
ExprData exprData ;
z3 : : solver solver ( exprData . context ) ;
try {
z3 : : expr e = exprData . addFloat ( name ) ;
for ( auto constraint : dynamic_cast < const Data * > ( dataBase ) - > constraints )
solver . add ( exprData . getConstraintExpr ( constraint ) ) ;
exprData . addAssertions ( solver ) ;
solver . add ( e > value ) ;
return solver . check ( ) = = z3 : : sat ;
} catch ( const z3 : : exception & exception ) {
std : : cerr < < " z3: " < < exception < < std : : endl ;
return true ; // Safe option is to return true
}
# else
// The value may or may not be in range
return false ;
# endif
}
bool ExprEngine : : FloatRange : : isLessThan ( DataBase * dataBase , int value ) const
{
if ( value < minValue | | value > maxValue )
return false ;
const Data * data = dynamic_cast < Data * > ( dataBase ) ;
if ( data - > constraints . empty ( ) )
return true ;
2019-12-31 09:01:58 +01:00
if ( MathLib : : isFloat ( name ) )
return value < MathLib : : toDoubleNumber ( name ) ;
2019-12-30 19:47:18 +01:00
# ifdef USE_Z3
// Check the value against the constraints
ExprData exprData ;
z3 : : solver solver ( exprData . context ) ;
try {
z3 : : expr e = exprData . addFloat ( name ) ;
for ( auto constraint : dynamic_cast < const Data * > ( dataBase ) - > constraints )
solver . add ( exprData . getConstraintExpr ( constraint ) ) ;
exprData . addAssertions ( solver ) ;
solver . add ( e < value ) ;
return solver . check ( ) = = z3 : : sat ;
} catch ( const z3 : : exception & exception ) {
std : : cerr < < " z3: " < < exception < < std : : endl ;
return true ; // Safe option is to return true
}
# else
// The value may or may not be in range
return false ;
# endif
}
2019-10-08 20:13:25 +02:00
bool ExprEngine : : BinOpResult : : isEqual ( ExprEngine : : DataBase * dataBase , int value ) const
2019-09-17 21:00:59 +02:00
{
2019-10-02 17:59:04 +02:00
# ifdef USE_Z3
ExprData exprData ;
2019-10-05 16:33:40 +02:00
z3 : : solver solver ( exprData . context ) ;
2019-10-06 14:47:34 +02:00
z3 : : expr e = exprData . getExpr ( this ) ;
2019-10-03 08:48:05 +02:00
for ( auto constraint : dynamic_cast < const Data * > ( dataBase ) - > constraints )
2019-10-06 14:47:34 +02:00
solver . add ( exprData . getConstraintExpr ( constraint ) ) ;
2019-12-30 12:53:59 +01:00
exprData . addAssertions ( solver ) ;
2019-10-02 21:47:00 +02:00
solver . add ( e = = value ) ;
return solver . check ( ) = = z3 : : sat ;
2019-10-02 17:59:04 +02:00
# else
2019-10-02 21:47:00 +02:00
( void ) dataBase ;
2019-10-02 17:59:04 +02:00
( void ) value ;
return false ;
# endif
2019-09-17 21:00:59 +02:00
}
2019-10-08 20:13:25 +02:00
bool ExprEngine : : BinOpResult : : isGreaterThan ( ExprEngine : : DataBase * dataBase , int value ) const
{
# ifdef USE_Z3
2019-10-09 09:42:00 +02:00
try {
ExprData exprData ;
z3 : : solver solver ( exprData . context ) ;
z3 : : expr e = exprData . getExpr ( this ) ;
for ( auto constraint : dynamic_cast < const Data * > ( dataBase ) - > constraints )
solver . add ( exprData . getConstraintExpr ( constraint ) ) ;
2019-12-30 12:53:59 +01:00
exprData . addAssertions ( solver ) ;
2019-10-09 09:42:00 +02:00
solver . add ( e > value ) ;
return solver . check ( ) = = z3 : : sat ;
} catch ( const z3 : : exception & exception ) {
std : : cerr < < " z3: " < < exception < < std : : endl ;
return true ; // Safe option is to return true
}
2019-10-08 20:13:25 +02:00
# else
( void ) dataBase ;
( void ) value ;
return false ;
# endif
}
bool ExprEngine : : BinOpResult : : isLessThan ( ExprEngine : : DataBase * dataBase , int value ) const
{
# ifdef USE_Z3
2019-10-09 09:42:00 +02:00
try {
ExprData exprData ;
z3 : : solver solver ( exprData . context ) ;
z3 : : expr e = exprData . getExpr ( this ) ;
for ( auto constraint : dynamic_cast < const Data * > ( dataBase ) - > constraints )
solver . add ( exprData . getConstraintExpr ( constraint ) ) ;
2019-12-30 12:53:59 +01:00
exprData . addAssertions ( solver ) ;
2019-10-09 09:42:00 +02:00
solver . add ( e < value ) ;
return solver . check ( ) = = z3 : : sat ;
} catch ( const z3 : : exception & exception ) {
std : : cerr < < " z3: " < < exception < < std : : endl ;
return true ; // Safe option is to return true
}
2019-10-08 20:13:25 +02:00
# else
( void ) dataBase ;
( void ) value ;
return false ;
# endif
}
2019-10-02 21:47:00 +02:00
std : : string ExprEngine : : BinOpResult : : getExpr ( ExprEngine : : DataBase * dataBase ) const
2019-09-17 21:00:59 +02:00
{
2019-10-02 17:59:04 +02:00
# ifdef USE_Z3
2019-10-09 20:17:26 +02:00
try {
ExprData exprData ;
z3 : : solver solver ( exprData . context ) ;
z3 : : expr e = exprData . getExpr ( this ) ;
for ( auto constraint : dynamic_cast < const Data * > ( dataBase ) - > constraints )
solver . add ( exprData . getConstraintExpr ( constraint ) ) ;
2019-12-30 12:53:59 +01:00
exprData . addAssertions ( solver ) ;
2019-10-09 20:17:26 +02:00
solver . add ( e ) ;
std : : ostringstream os ;
os < < solver ;
switch ( solver . check ( ) ) {
case z3 : : sat :
2020-05-08 12:21:10 +02:00
os < < " \n z3::sat \n " ;
2019-10-09 20:17:26 +02:00
break ;
case z3 : : unsat :
2020-05-08 12:21:10 +02:00
os < < " \n z3::unsat \n " ;
2019-10-09 20:17:26 +02:00
break ;
case z3 : : unknown :
2020-05-08 12:21:10 +02:00
os < < " \n z3::unknown \n " ;
2019-10-09 20:17:26 +02:00
break ;
}
return os . str ( ) ;
} catch ( const z3 : : exception & exception ) {
std : : ostringstream os ;
2020-05-08 12:21:10 +02:00
os < < " \n z3: " < < exception < < " \n " ;
2019-10-09 20:17:26 +02:00
return os . str ( ) ;
2019-10-09 11:24:57 +02:00
}
2019-10-02 17:59:04 +02:00
# else
2019-10-02 21:47:00 +02:00
( void ) dataBase ;
2019-10-02 17:59:04 +02:00
return " " ;
# endif
2019-09-17 21:00:59 +02:00
}
2019-10-02 17:59:04 +02:00
2019-09-17 21:00:59 +02:00
// Todo: This is taken from ValueFlow and modified.. we should reuse it
2019-09-23 20:27:13 +02:00
static int getIntBitsFromValueType ( const ValueType * vt , const cppcheck : : Platform & platform )
2019-09-17 21:00:59 +02:00
{
2019-09-23 20:27:13 +02:00
if ( ! vt )
return 0 ;
2019-09-17 21:00:59 +02:00
switch ( vt - > type ) {
case ValueType : : Type : : BOOL :
2019-09-23 20:27:13 +02:00
return 1 ;
2019-09-17 21:00:59 +02:00
case ValueType : : Type : : CHAR :
2019-09-23 20:27:13 +02:00
return platform . char_bit ;
2019-09-17 21:00:59 +02:00
case ValueType : : Type : : SHORT :
2019-09-23 20:27:13 +02:00
return platform . short_bit ;
2019-09-17 21:00:59 +02:00
case ValueType : : Type : : INT :
2019-09-23 20:27:13 +02:00
return platform . int_bit ;
2019-09-17 21:00:59 +02:00
case ValueType : : Type : : LONG :
2019-09-23 20:27:13 +02:00
return platform . long_bit ;
2019-09-17 21:00:59 +02:00
case ValueType : : Type : : LONGLONG :
2019-09-23 20:27:13 +02:00
return platform . long_long_bit ;
2019-09-17 21:00:59 +02:00
default :
2019-09-23 20:27:13 +02:00
return 0 ;
2020-04-21 17:27:51 +02:00
}
2019-09-23 20:27:13 +02:00
}
static ExprEngine : : ValuePtr getValueRangeFromValueType ( const std : : string & name , const ValueType * vt , const cppcheck : : Platform & platform )
{
if ( ! vt | | ! ( vt - > isIntegral ( ) | | vt - > isFloat ( ) ) | | vt - > pointer )
return ExprEngine : : ValuePtr ( ) ;
2019-09-17 21:00:59 +02:00
2019-09-23 20:27:13 +02:00
int bits = getIntBitsFromValueType ( vt , platform ) ;
2019-09-17 21:00:59 +02:00
if ( bits = = 1 ) {
return std : : make_shared < ExprEngine : : IntRange > ( name , 0 , 1 ) ;
2019-09-23 20:27:13 +02:00
} else if ( bits > 1 ) {
2019-09-17 21:00:59 +02:00
if ( vt - > sign = = ValueType : : Sign : : UNSIGNED ) {
return std : : make_shared < ExprEngine : : IntRange > ( name , 0 , ( ( int128_t ) 1 < < bits ) - 1 ) ;
} else {
return std : : make_shared < ExprEngine : : IntRange > ( name , - ( ( int128_t ) 1 < < ( bits - 1 ) ) , ( ( int128_t ) 1 < < ( bits - 1 ) ) - 1 ) ;
}
}
2019-12-30 19:47:18 +01:00
if ( vt - > isFloat ( ) )
return std : : make_shared < ExprEngine : : FloatRange > ( name , - std : : numeric_limits < float > : : infinity ( ) , std : : numeric_limits < float > : : infinity ( ) ) ;
return ExprEngine : : ValuePtr ( ) ;
2019-09-17 21:00:59 +02:00
}
2019-10-02 21:47:00 +02:00
static void call ( const std : : vector < ExprEngine : : Callback > & callbacks , const Token * tok , ExprEngine : : ValuePtr value , Data * dataBase )
2019-09-17 21:00:59 +02:00
{
if ( value ) {
for ( ExprEngine : : Callback f : callbacks ) {
2019-10-09 22:16:30 +02:00
try {
f ( tok , * value , dataBase ) ;
} catch ( const VerifyException & e ) {
throw VerifyException ( tok , e . what ) ;
}
2019-09-17 21:00:59 +02:00
}
}
}
static ExprEngine : : ValuePtr executeExpression ( const Token * tok , Data & data ) ;
2020-04-28 17:16:13 +02:00
static ExprEngine : : ValuePtr executeExpression1 ( const Token * tok , Data & data ) ;
2019-09-17 21:00:59 +02:00
static ExprEngine : : ValuePtr executeReturn ( const Token * tok , Data & data )
{
ExprEngine : : ValuePtr retval = executeExpression ( tok - > astOperand1 ( ) , data ) ;
2019-10-02 21:47:00 +02:00
call ( data . callbacks , tok , retval , & data ) ;
2019-09-17 21:00:59 +02:00
return retval ;
}
2019-09-23 20:27:13 +02:00
static ExprEngine : : ValuePtr truncateValue ( ExprEngine : : ValuePtr val , const ValueType * valueType , Data & data )
{
2019-09-24 13:27:53 +02:00
if ( ! valueType )
return val ;
2019-09-23 20:27:13 +02:00
if ( valueType - > pointer ! = 0 )
return val ;
if ( ! valueType - > isIntegral ( ) )
return val ; // TODO
int bits = getIntBitsFromValueType ( valueType , * data . settings ) ;
if ( bits = = 0 )
// TODO
return val ;
if ( auto range = std : : dynamic_pointer_cast < ExprEngine : : IntRange > ( val ) ) {
if ( range - > minValue = = range - > maxValue ) {
2019-09-28 19:28:12 +02:00
int128_t newValue = truncateInt ( range - > minValue , bits , valueType - > sign = = ValueType : : Sign : : SIGNED ? ' s ' : ' u ' ) ;
2019-09-23 20:27:13 +02:00
if ( newValue = = range - > minValue )
return val ;
return std : : make_shared < ExprEngine : : IntRange > ( ExprEngine : : str ( newValue ) , newValue , newValue ) ;
}
if ( auto typeRange = getValueRangeFromValueType ( " " , valueType , * data . settings ) ) {
auto typeIntRange = std : : dynamic_pointer_cast < ExprEngine : : IntRange > ( typeRange ) ;
if ( typeIntRange ) {
if ( range - > minValue > = typeIntRange - > minValue & & range - > maxValue < = typeIntRange - > maxValue )
return val ;
}
}
return std : : make_shared < ExprEngine : : IntegerTruncation > ( data . getNewSymbolName ( ) , val , bits , valueType - > sign = = ValueType : : Sign : : SIGNED ? ' s ' : ' u ' ) ;
}
// TODO
return val ;
}
2019-09-17 21:00:59 +02:00
static ExprEngine : : ValuePtr executeAssign ( const Token * tok , Data & data )
{
ExprEngine : : ValuePtr rhsValue = executeExpression ( tok - > astOperand2 ( ) , data ) ;
2019-10-23 18:42:40 +02:00
2020-04-30 21:05:34 +02:00
if ( ! rhsValue ) {
2020-04-30 21:49:27 +02:00
const ValueType * const vt1 = tok - > astOperand1 ( ) ? tok - > astOperand1 ( ) - > valueType ( ) : nullptr ;
const ValueType * const vt2 = tok - > astOperand2 ( ) ? tok - > astOperand2 ( ) - > valueType ( ) : nullptr ;
if ( vt1 & & vt1 - > pointer = = 0 & & vt1 - > isIntegral ( ) )
rhsValue = getValueRangeFromValueType ( data . getNewSymbolName ( ) , vt1 , * data . settings ) ;
else if ( vt2 & & vt2 - > container & & vt2 - > container - > stdStringLike ) {
auto size = std : : make_shared < ExprEngine : : IntRange > ( data . getNewSymbolName ( ) , 0 , ~ 0ULL ) ;
auto value = std : : make_shared < ExprEngine : : IntRange > ( data . getNewSymbolName ( ) , - 128 , 127 ) ;
rhsValue = std : : make_shared < ExprEngine : : ArrayValue > ( data . getNewSymbolName ( ) , size , value , false , false , false ) ;
call ( data . callbacks , tok - > astOperand2 ( ) , rhsValue , & data ) ;
2020-04-30 21:05:34 +02:00
}
2019-10-23 18:42:40 +02:00
}
if ( ! rhsValue )
throw VerifyException ( tok , " Expression ' " + tok - > expressionString ( ) + " ' ; Failed to evaluate RHS " );
2019-09-24 13:27:53 +02:00
ExprEngine : : ValuePtr assignValue ;
2019-09-21 21:15:51 +02:00
if ( tok - > str ( ) = = " = " )
2019-09-24 13:27:53 +02:00
assignValue = rhsValue ;
2019-09-21 21:15:51 +02:00
else {
// "+=" => "+"
std : : string binop ( tok - > str ( ) ) ;
binop = binop . substr ( 0 , binop . size ( ) - 1 ) ;
ExprEngine : : ValuePtr lhsValue = executeExpression ( tok - > astOperand1 ( ) , data ) ;
2019-09-25 18:33:21 +02:00
assignValue = simplifyValue ( std : : make_shared < ExprEngine : : BinOpResult > ( binop , lhsValue , rhsValue ) ) ;
2019-09-21 21:15:51 +02:00
}
2019-09-17 21:00:59 +02:00
const Token * lhsToken = tok - > astOperand1 ( ) ;
2019-09-24 13:27:53 +02:00
assignValue = truncateValue ( assignValue , lhsToken - > valueType ( ) , data ) ;
2019-10-02 21:47:00 +02:00
call ( data . callbacks , tok , assignValue , & data ) ;
2019-09-24 13:27:53 +02:00
2019-09-17 21:00:59 +02:00
if ( lhsToken - > varId ( ) > 0 ) {
2019-09-26 20:48:33 +02:00
data . assignValue ( lhsToken , lhsToken - > varId ( ) , assignValue ) ;
2019-09-17 21:00:59 +02:00
} else if ( lhsToken - > str ( ) = = " [ " ) {
auto arrayValue = data . getArrayValue ( lhsToken - > astOperand1 ( ) ) ;
if ( arrayValue ) {
2019-09-21 11:36:34 +02:00
// Is it array initialization?
const Token * arrayInit = lhsToken - > astOperand1 ( ) ;
if ( arrayInit & & arrayInit - > variable ( ) & & arrayInit - > variable ( ) - > nameToken ( ) = = arrayInit ) {
2019-09-26 10:03:58 +02:00
if ( assignValue - > type = = ExprEngine : : ValueType : : StringLiteralValue )
2019-09-25 18:33:21 +02:00
arrayValue - > assign ( ExprEngine : : ValuePtr ( ) , assignValue ) ;
2019-09-21 11:36:34 +02:00
} else {
auto indexValue = executeExpression ( lhsToken - > astOperand2 ( ) , data ) ;
2019-09-24 13:27:53 +02:00
arrayValue - > assign ( indexValue , assignValue ) ;
2019-09-21 11:36:34 +02:00
}
2019-09-17 21:00:59 +02:00
}
} else if ( lhsToken - > isUnaryOp ( " * " ) ) {
auto pval = executeExpression ( lhsToken - > astOperand1 ( ) , data ) ;
2019-09-26 10:03:58 +02:00
if ( pval & & pval - > type = = ExprEngine : : ValueType : : AddressOfValue ) {
2019-09-17 21:00:59 +02:00
auto val = std : : dynamic_pointer_cast < ExprEngine : : AddressOfValue > ( pval ) ;
if ( val )
2019-09-26 20:48:33 +02:00
data . assignValue ( lhsToken , val - > varId , assignValue ) ;
2019-09-26 10:03:58 +02:00
} else if ( pval & & pval - > type = = ExprEngine : : ValueType : : BinOpResult ) {
2019-09-22 15:58:55 +02:00
auto b = std : : dynamic_pointer_cast < ExprEngine : : BinOpResult > ( pval ) ;
if ( b & & b - > binop = = " + " ) {
std : : shared_ptr < ExprEngine : : ArrayValue > arr ;
ExprEngine : : ValuePtr offset ;
2019-09-26 10:03:58 +02:00
if ( b - > op1 - > type = = ExprEngine : : ValueType : : ArrayValue ) {
2019-09-22 15:58:55 +02:00
arr = std : : dynamic_pointer_cast < ExprEngine : : ArrayValue > ( b - > op1 ) ;
offset = b - > op2 ;
} else {
arr = std : : dynamic_pointer_cast < ExprEngine : : ArrayValue > ( b - > op2 ) ;
offset = b - > op1 ;
}
if ( arr & & offset ) {
2019-09-24 13:27:53 +02:00
arr - > assign ( offset , assignValue ) ;
2019-09-22 15:58:55 +02:00
}
}
2019-09-17 21:00:59 +02:00
}
2019-09-26 21:35:29 +02:00
} else if ( Token : : Match ( lhsToken , " . %name% " ) ) {
auto structVal = executeExpression ( lhsToken - > astOperand1 ( ) , data ) ;
if ( structVal & & structVal - > type = = ExprEngine : : ValueType : : StructValue )
data . assignStructMember ( tok , & * std : : static_pointer_cast < ExprEngine : : StructValue > ( structVal ) , lhsToken - > strAt ( 1 ) , assignValue ) ;
2019-09-17 21:00:59 +02:00
}
2019-09-24 13:27:53 +02:00
return assignValue ;
2019-09-17 21:00:59 +02:00
}
2020-04-28 17:16:13 +02:00
# ifdef USE_Z3
2020-04-28 22:29:16 +02:00
static void checkContract ( Data & data , const Token * tok , const Function * function , const std : : vector < ExprEngine : : ValuePtr > & argValues )
{
2020-04-28 17:16:13 +02:00
ExprData exprData ;
z3 : : solver solver ( exprData . context ) ;
try {
// Invert contract, we want to know if the contract might not be met
solver . add ( z3 : : ite ( exprData . getConstraintExpr ( data . executeContract ( function , executeExpression1 ) ) , exprData . context . bool_val ( false ) , exprData . context . bool_val ( true ) ) ) ;
bool bailoutValue = false ;
for ( nonneg int i = 0 ; i < argValues . size ( ) ; + + i ) {
const Variable * argvar = function - > getArgumentVar ( i ) ;
if ( ! argvar | | ! argvar - > nameToken ( ) )
continue ;
ExprEngine : : ValuePtr argValue = argValues [ i ] ;
if ( ! argValue | | argValue - > type = = ExprEngine : : ValueType : : BailoutValue ) {
bailoutValue = true ;
break ;
}
if ( argValue & & argValue - > type = = ExprEngine : : ValueType : : IntRange ) {
solver . add ( exprData . getExpr ( data . getValue ( argvar - > declarationId ( ) , nullptr , nullptr ) ) = = exprData . getExpr ( argValue ) ) ;
}
}
if ( ! bailoutValue ) {
for ( auto constraint : data . constraints )
solver . add ( exprData . getConstraintExpr ( constraint ) ) ;
exprData . addAssertions ( solver ) ;
// Log solver expressions for debugging/testing purposes
std : : ostringstream os ;
os < < solver ;
data . trackCheckContract ( tok , os . str ( ) ) ;
}
if ( bailoutValue | | solver . check ( ) = = z3 : : sat ) {
data . addError ( tok - > linenr ( ) ) ;
std : : list < const Token * > callstack { tok } ;
const char * const id = " bughuntingFunctionCall " ;
const auto contractIt = data . settings - > functionContracts . find ( function - > fullName ( ) ) ;
const std : : string functionName = contractIt - > first ;
const std : : string functionExpects = contractIt - > second ;
ErrorLogger : : ErrorMessage errmsg ( callstack ,
& data . tokenizer - > list ,
Severity : : SeverityType : : error ,
id ,
2020-04-28 22:09:01 +02:00
" Function ' " + function - > name ( ) + " ' is called, can not determine that its contract ' " + functionExpects + " ' is always met. " ,
2020-04-28 17:16:13 +02:00
CWE ( 0 ) ,
2020-05-03 17:20:38 +02:00
false ) ;
errmsg . incomplete = bailoutValue ;
2020-04-28 22:09:01 +02:00
errmsg . function = functionName ;
2020-04-28 17:16:13 +02:00
data . errorLogger - > reportErr ( errmsg ) ;
}
} catch ( const z3 : : exception & exception ) {
std : : cerr < < " z3: " < < exception < < std : : endl ;
2020-04-28 22:09:01 +02:00
} catch ( const VerifyException & e ) {
std : : list < const Token * > callstack { tok } ;
const char * const id = " internalErrorInExprEngine " ;
const auto contractIt = data . settings - > functionContracts . find ( function - > fullName ( ) ) ;
const std : : string functionExpects = contractIt - > second ;
ErrorLogger : : ErrorMessage errmsg ( callstack ,
& data . tokenizer - > list ,
2020-04-29 18:30:12 +02:00
Severity : : SeverityType : : error ,
2020-04-28 22:09:01 +02:00
id ,
2020-04-29 18:30:12 +02:00
" Function ' " + function - > name ( ) + " ' is called, can not determine that its contract is always met. " ,
2020-04-28 22:09:01 +02:00
CWE ( 0 ) ,
2020-05-03 17:20:38 +02:00
false ) ;
errmsg . incomplete = true ;
2020-04-28 22:09:01 +02:00
data . errorLogger - > reportErr ( errmsg ) ;
2020-04-28 17:16:13 +02:00
}
}
2020-04-28 22:29:16 +02:00
# endif
2020-04-28 17:16:13 +02:00
2019-09-17 21:00:59 +02:00
static ExprEngine : : ValuePtr executeFunctionCall ( const Token * tok , Data & data )
{
2020-01-15 15:23:58 +01:00
if ( Token : : simpleMatch ( tok - > previous ( ) , " sizeof ( " ) ) {
ExprEngine : : ValuePtr retVal ;
if ( tok - > hasKnownIntValue ( ) ) {
const MathLib : : bigint value = tok - > getKnownIntValue ( ) ;
retVal = std : : make_shared < ExprEngine : : IntRange > ( std : : to_string ( value ) , value , value ) ;
} else {
retVal = std : : make_shared < ExprEngine : : IntRange > ( data . getNewSymbolName ( ) , 1 , 0x7fffffff ) ;
}
call ( data . callbacks , tok , retVal , & data ) ;
return retVal ;
}
2019-09-24 22:22:16 +02:00
std : : vector < ExprEngine : : ValuePtr > argValues ;
2019-09-21 19:34:06 +02:00
for ( const Token * argtok : getArguments ( tok ) ) {
auto val = executeExpression ( argtok , data ) ;
2019-09-24 22:22:16 +02:00
argValues . push_back ( val ) ;
2019-09-24 19:53:33 +02:00
if ( ! argtok - > valueType ( ) | | ( argtok - > valueType ( ) - > constness & 1 ) = = 1 )
continue ;
2019-09-21 19:34:06 +02:00
if ( auto arrayValue = std : : dynamic_pointer_cast < ExprEngine : : ArrayValue > ( val ) ) {
ValueType vt ( * argtok - > valueType ( ) ) ;
vt . pointer = 0 ;
auto anyVal = getValueRangeFromValueType ( data . getNewSymbolName ( ) , & vt , * data . settings ) ;
2019-09-25 18:33:21 +02:00
arrayValue - > assign ( ExprEngine : : ValuePtr ( ) , anyVal ) ;
2019-09-24 19:53:33 +02:00
} else if ( auto addressOf = std : : dynamic_pointer_cast < ExprEngine : : AddressOfValue > ( val ) ) {
ValueType vt ( * argtok - > valueType ( ) ) ;
vt . pointer = 0 ;
if ( vt . isIntegral ( ) & & argtok - > valueType ( ) - > pointer = = 1 )
2019-09-26 20:48:33 +02:00
data . assignValue ( argtok , addressOf - > varId , getValueRangeFromValueType ( data . getNewSymbolName ( ) , & vt , * data . settings ) ) ;
2019-09-21 19:34:06 +02:00
}
}
2019-09-24 22:22:16 +02:00
2020-04-28 17:16:13 +02:00
if ( tok - > astOperand1 ( ) - > function ( ) ) {
2020-04-28 22:09:01 +02:00
const std : : string & functionName = tok - > astOperand1 ( ) - > function ( ) - > fullName ( ) ;
const auto contractIt = data . settings - > functionContracts . find ( functionName ) ;
if ( contractIt ! = data . settings - > functionContracts . end ( ) ) {
2020-04-28 22:29:16 +02:00
# ifdef USE_Z3
2020-04-28 17:16:13 +02:00
checkContract ( data , tok , tok - > astOperand1 ( ) - > function ( ) , argValues ) ;
2020-04-28 22:29:16 +02:00
# endif
2020-04-28 22:09:01 +02:00
} else if ( ! argValues . empty ( ) ) {
bool bailout = false ;
for ( const auto v : argValues )
bailout | = ( v & & v - > type = = ExprEngine : : ValueType : : BailoutValue ) ;
if ( ! bailout )
data . addMissingContract ( functionName ) ;
}
2020-04-28 17:16:13 +02:00
}
2019-09-17 21:00:59 +02:00
auto val = getValueRangeFromValueType ( data . getNewSymbolName ( ) , tok - > valueType ( ) , * data . settings ) ;
2019-10-02 21:47:00 +02:00
call ( data . callbacks , tok , val , & data ) ;
2020-01-09 18:50:29 +01:00
data . functionCall ( ) ;
2019-09-17 21:00:59 +02:00
return val ;
}
static ExprEngine : : ValuePtr executeArrayIndex ( const Token * tok , Data & data )
{
auto arrayValue = data . getArrayValue ( tok - > astOperand1 ( ) ) ;
if ( arrayValue ) {
auto indexValue = executeExpression ( tok - > astOperand2 ( ) , data ) ;
2019-09-26 10:03:58 +02:00
auto conditionalValues = arrayValue - > read ( indexValue ) ;
for ( auto value : conditionalValues )
2019-10-02 21:47:00 +02:00
call ( data . callbacks , tok , value . second , & data ) ;
2019-09-29 21:19:21 +02:00
if ( conditionalValues . size ( ) = = 1 & & ! conditionalValues [ 0 ] . first )
return conditionalValues [ 0 ] . second ;
2019-09-26 10:03:58 +02:00
return std : : make_shared < ExprEngine : : ConditionalValue > ( data . getNewSymbolName ( ) , conditionalValues ) ;
2019-09-17 21:00:59 +02:00
}
2019-09-28 19:28:12 +02:00
// TODO: Pointer value..
executeExpression ( tok - > astOperand1 ( ) , data ) ;
executeExpression ( tok - > astOperand2 ( ) , data ) ;
2019-09-17 21:00:59 +02:00
return ExprEngine : : ValuePtr ( ) ;
}
2019-09-28 15:40:00 +02:00
static ExprEngine : : ValuePtr executeCast ( const Token * tok , Data & data )
{
const Token * expr = tok - > astOperand2 ( ) ? tok - > astOperand2 ( ) : tok - > astOperand1 ( ) ;
auto val = executeExpression ( expr , data ) ;
if ( expr - > valueType ( ) & & expr - > valueType ( ) - > type = = : : ValueType : : Type : : VOID & & expr - > valueType ( ) - > pointer > 0 ) {
2020-01-09 21:25:23 +01:00
if ( ! tok - > valueType ( ) | | expr - > valueType ( ) - > pointer < tok - > valueType ( ) - > pointer )
return std : : make_shared < ExprEngine : : UninitValue > ( ) ;
2019-09-28 15:40:00 +02:00
: : ValueType vt ( * tok - > valueType ( ) ) ;
vt . pointer = 0 ;
auto range = getValueRangeFromValueType ( data . getNewSymbolName ( ) , & vt , * data . settings ) ;
if ( tok - > valueType ( ) - > pointer = = 0 )
return range ;
2019-12-29 16:26:11 +01:00
bool uninitPointer = false , nullPointer = false ;
if ( val & & val - > type = = ExprEngine : : ValueType : : ArrayValue ) {
nullPointer = std : : static_pointer_cast < ExprEngine : : ArrayValue > ( val ) - > nullPointer ;
uninitPointer = std : : static_pointer_cast < ExprEngine : : ArrayValue > ( val ) - > uninitPointer ;
2019-09-28 15:40:00 +02:00
}
2019-12-29 16:26:11 +01:00
auto bufferSize = std : : make_shared < ExprEngine : : IntRange > ( data . getNewSymbolName ( ) , 1 , ~ 0UL ) ;
return std : : make_shared < ExprEngine : : ArrayValue > ( data . getNewSymbolName ( ) , bufferSize , range , true , nullPointer , uninitPointer ) ;
2019-09-28 15:40:00 +02:00
}
2019-12-29 19:17:36 +01:00
if ( val ) {
2019-09-28 15:40:00 +02:00
// TODO: Cast this..
2019-12-29 19:17:36 +01:00
call ( data . callbacks , tok , val , & data ) ;
2019-09-28 15:40:00 +02:00
return val ;
2019-12-29 19:17:36 +01:00
}
2019-09-28 15:40:00 +02:00
2019-12-29 19:17:36 +01:00
val = getValueRangeFromValueType ( data . getNewSymbolName ( ) , tok - > valueType ( ) , * data . settings ) ;
call ( data . callbacks , tok , val , & data ) ;
return val ;
2019-09-28 15:40:00 +02:00
}
2019-09-17 21:00:59 +02:00
static ExprEngine : : ValuePtr executeDot ( const Token * tok , Data & data )
{
2019-10-27 16:23:37 +01:00
if ( ! tok - > astOperand1 ( ) | | ! tok - > astOperand1 ( ) - > varId ( ) ) {
auto v = getValueRangeFromValueType ( data . getNewSymbolName ( ) , tok - > valueType ( ) , * data . settings ) ;
call ( data . callbacks , tok , v , & data ) ;
return v ;
}
2019-09-19 20:18:39 +02:00
std : : shared_ptr < ExprEngine : : StructValue > structValue = std : : dynamic_pointer_cast < ExprEngine : : StructValue > ( data . getValue ( tok - > astOperand1 ( ) - > varId ( ) , nullptr , nullptr ) ) ;
2019-09-29 08:26:09 +02:00
if ( ! structValue ) {
if ( tok - > originalName ( ) = = " -> " ) {
2019-12-29 16:26:11 +01:00
std : : shared_ptr < ExprEngine : : ArrayValue > pointerValue = std : : dynamic_pointer_cast < ExprEngine : : ArrayValue > ( data . getValue ( tok - > astOperand1 ( ) - > varId ( ) , nullptr , nullptr ) ) ;
2019-12-29 18:42:35 +01:00
if ( pointerValue & & pointerValue - > pointer & & ! pointerValue - > data . empty ( ) ) {
2019-10-02 21:47:00 +02:00
call ( data . callbacks , tok - > astOperand1 ( ) , pointerValue , & data ) ;
2019-12-29 18:42:35 +01:00
auto indexValue = std : : make_shared < ExprEngine : : IntRange > ( " 0 " , 0 , 0 ) ;
ExprEngine : : ValuePtr ret ;
for ( auto val : pointerValue - > read ( indexValue ) ) {
structValue = std : : dynamic_pointer_cast < ExprEngine : : StructValue > ( val . second ) ;
if ( structValue ) {
auto memberValue = structValue - > getValueOfMember ( tok - > astOperand2 ( ) - > str ( ) ) ;
call ( data . callbacks , tok , memberValue , & data ) ;
if ( ! ret )
ret = memberValue ;
}
}
return ret ;
2019-09-29 08:26:09 +02:00
} else {
2019-10-02 21:47:00 +02:00
call ( data . callbacks , tok - > astOperand1 ( ) , data . getValue ( tok - > astOperand1 ( ) - > varId ( ) , nullptr , nullptr ) , & data ) ;
2019-09-29 08:26:09 +02:00
}
}
2019-10-27 16:23:37 +01:00
if ( ! structValue ) {
auto v = getValueRangeFromValueType ( data . getNewSymbolName ( ) , tok - > valueType ( ) , * data . settings ) ;
2020-05-02 22:22:31 +02:00
if ( ! v )
v = std : : make_shared < ExprEngine : : BailoutValue > ( ) ;
2019-10-27 16:23:37 +01:00
call ( data . callbacks , tok , v , & data ) ;
return v ;
}
2019-09-29 08:26:09 +02:00
}
2019-10-02 21:47:00 +02:00
call ( data . callbacks , tok - > astOperand1 ( ) , structValue , & data ) ;
2020-01-12 10:28:48 +01:00
ExprEngine : : ValuePtr memberValue = structValue - > getValueOfMember ( tok - > astOperand2 ( ) - > str ( ) ) ;
call ( data . callbacks , tok , memberValue , & data ) ;
return memberValue ;
2019-09-17 21:00:59 +02:00
}
static ExprEngine : : ValuePtr executeBinaryOp ( const Token * tok , Data & data )
{
ExprEngine : : ValuePtr v1 = executeExpression ( tok - > astOperand1 ( ) , data ) ;
2019-12-24 15:51:45 +01:00
ExprEngine : : ValuePtr v2 ;
if ( tok - > str ( ) = = " && " | | tok - > str ( ) = = " || " ) {
Data data2 ( data ) ;
data2 . addConstraint ( v1 , tok - > str ( ) = = " && " ) ;
v2 = executeExpression ( tok - > astOperand2 ( ) , data2 ) ;
} else {
v2 = executeExpression ( tok - > astOperand2 ( ) , data ) ;
}
2019-09-17 21:00:59 +02:00
if ( v1 & & v2 ) {
2019-09-25 18:33:21 +02:00
auto result = simplifyValue ( std : : make_shared < ExprEngine : : BinOpResult > ( tok - > str ( ) , v1 , v2 ) ) ;
2019-10-02 21:47:00 +02:00
call ( data . callbacks , tok , result , & data ) ;
2019-09-17 21:00:59 +02:00
return result ;
}
2020-05-10 22:50:23 +02:00
if ( tok - > str ( ) = = " && " & & ( v1 | | v2 ) ) {
auto result = v1 ? v1 : v2 ;
call ( data . callbacks , tok , result , & data ) ;
return result ;
}
2019-09-17 21:00:59 +02:00
return ExprEngine : : ValuePtr ( ) ;
}
static ExprEngine : : ValuePtr executeAddressOf ( const Token * tok , Data & data )
{
auto addr = std : : make_shared < ExprEngine : : AddressOfValue > ( data . getNewSymbolName ( ) , tok - > astOperand1 ( ) - > varId ( ) ) ;
2019-10-02 21:47:00 +02:00
call ( data . callbacks , tok , addr , & data ) ;
2019-09-17 21:00:59 +02:00
return addr ;
}
static ExprEngine : : ValuePtr executeDeref ( const Token * tok , Data & data )
{
ExprEngine : : ValuePtr pval = executeExpression ( tok - > astOperand1 ( ) , data ) ;
2019-10-27 16:47:56 +01:00
if ( ! pval ) {
auto v = getValueRangeFromValueType ( data . getNewSymbolName ( ) , tok - > valueType ( ) , * data . settings ) ;
if ( tok - > astOperand1 ( ) - > varId ( ) ) {
2019-12-29 16:26:11 +01:00
pval = std : : make_shared < ExprEngine : : ArrayValue > ( data . getNewSymbolName ( ) , ExprEngine : : ValuePtr ( ) , v , true , false , false ) ;
2019-10-27 18:22:47 +01:00
data . assignValue ( tok - > astOperand1 ( ) , tok - > astOperand1 ( ) - > varId ( ) , pval ) ;
2019-09-17 21:00:59 +02:00
}
2019-10-27 16:47:56 +01:00
call ( data . callbacks , tok , v , & data ) ;
return v ;
}
auto addressOf = std : : dynamic_pointer_cast < ExprEngine : : AddressOfValue > ( pval ) ;
if ( addressOf ) {
auto val = data . getValue ( addressOf - > varId , tok - > valueType ( ) , tok ) ;
call ( data . callbacks , tok , val , & data ) ;
return val ;
}
2019-12-29 16:26:11 +01:00
auto pointer = std : : dynamic_pointer_cast < ExprEngine : : ArrayValue > ( pval ) ;
2019-10-27 16:47:56 +01:00
if ( pointer ) {
2019-12-29 16:26:11 +01:00
auto indexValue = std : : make_shared < ExprEngine : : IntRange > ( " 0 " , 0 , 0 ) ;
auto conditionalValues = pointer - > read ( indexValue ) ;
for ( auto value : conditionalValues )
call ( data . callbacks , tok , value . second , & data ) ;
if ( conditionalValues . size ( ) = = 1 & & ! conditionalValues [ 0 ] . first )
return conditionalValues [ 0 ] . second ;
return std : : make_shared < ExprEngine : : ConditionalValue > ( data . getNewSymbolName ( ) , conditionalValues ) ;
2019-09-17 21:00:59 +02:00
}
return ExprEngine : : ValuePtr ( ) ;
}
static ExprEngine : : ValuePtr executeVariable ( const Token * tok , Data & data )
{
2019-09-19 20:18:39 +02:00
auto val = data . getValue ( tok - > varId ( ) , tok - > valueType ( ) , tok ) ;
2019-10-02 21:47:00 +02:00
call ( data . callbacks , tok , val , & data ) ;
2019-09-17 21:00:59 +02:00
return val ;
}
2019-09-23 18:10:06 +02:00
static ExprEngine : : ValuePtr executeKnownMacro ( const Token * tok , Data & data )
{
auto val = std : : make_shared < ExprEngine : : IntRange > ( data . getNewSymbolName ( ) , tok - > getKnownIntValue ( ) , tok - > getKnownIntValue ( ) ) ;
2019-10-02 21:47:00 +02:00
call ( data . callbacks , tok , val , & data ) ;
2019-09-23 18:10:06 +02:00
return val ;
}
2019-10-25 21:46:02 +02:00
static ExprEngine : : ValuePtr executeNumber ( const Token * tok , Data & data )
2019-09-17 21:00:59 +02:00
{
2019-09-22 21:14:20 +02:00
if ( tok - > valueType ( ) - > isFloat ( ) ) {
long double value = MathLib : : toDoubleNumber ( tok - > str ( ) ) ;
2019-10-25 21:46:02 +02:00
auto v = std : : make_shared < ExprEngine : : FloatRange > ( tok - > str ( ) , value , value ) ;
call ( data . callbacks , tok , v , & data ) ;
return v ;
2019-09-22 21:14:20 +02:00
}
2019-09-17 21:00:59 +02:00
int128_t value = MathLib : : toLongNumber ( tok - > str ( ) ) ;
2019-10-25 21:46:02 +02:00
auto v = std : : make_shared < ExprEngine : : IntRange > ( tok - > str ( ) , value , value ) ;
call ( data . callbacks , tok , v , & data ) ;
return v ;
2019-09-17 21:00:59 +02:00
}
2019-09-21 11:36:34 +02:00
static ExprEngine : : ValuePtr executeStringLiteral ( const Token * tok , Data & data )
{
std : : string s = tok - > str ( ) ;
return std : : make_shared < ExprEngine : : StringLiteralValue > ( data . getNewSymbolName ( ) , s . substr ( 1 , s . size ( ) - 2 ) ) ;
}
2019-09-28 16:16:36 +02:00
static ExprEngine : : ValuePtr executeExpression1 ( const Token * tok , Data & data )
2019-09-17 21:00:59 +02:00
{
if ( tok - > str ( ) = = " return " )
return executeReturn ( tok , data ) ;
2019-09-21 21:15:51 +02:00
if ( tok - > isAssignmentOp ( ) )
// TODO: Handle more operators
2019-09-17 21:00:59 +02:00
return executeAssign ( tok , data ) ;
if ( tok - > astOperand1 ( ) & & tok - > astOperand2 ( ) & & tok - > str ( ) = = " [ " )
return executeArrayIndex ( tok , data ) ;
2019-09-28 15:40:00 +02:00
if ( tok - > str ( ) = = " ( " ) {
if ( ! tok - > isCast ( ) )
return executeFunctionCall ( tok , data ) ;
return executeCast ( tok , data ) ;
}
2019-09-17 21:00:59 +02:00
if ( tok - > str ( ) = = " . " )
return executeDot ( tok , data ) ;
2019-10-23 18:23:25 +02:00
if ( tok - > str ( ) = = " :: " & & tok - > hasKnownIntValue ( ) ) { // TODO handle :: better
auto v = tok - > getKnownIntValue ( ) ;
return std : : make_shared < ExprEngine : : IntRange > ( std : : to_string ( v ) , v , v ) ;
}
2019-09-17 21:00:59 +02:00
if ( tok - > astOperand1 ( ) & & tok - > astOperand2 ( ) )
return executeBinaryOp ( tok , data ) ;
if ( tok - > isUnaryOp ( " & " ) & & Token : : Match ( tok - > astOperand1 ( ) , " %var% " ) )
return executeAddressOf ( tok , data ) ;
if ( tok - > isUnaryOp ( " * " ) )
return executeDeref ( tok , data ) ;
if ( tok - > varId ( ) )
return executeVariable ( tok , data ) ;
2019-09-23 18:10:06 +02:00
if ( tok - > isName ( ) & & tok - > hasKnownIntValue ( ) )
return executeKnownMacro ( tok , data ) ;
2019-09-24 20:10:51 +02:00
if ( tok - > isNumber ( ) | | tok - > tokType ( ) = = Token : : Type : : eChar )
2019-10-25 21:46:02 +02:00
return executeNumber ( tok , data ) ;
2019-09-17 21:00:59 +02:00
2019-09-21 11:36:34 +02:00
if ( tok - > tokType ( ) = = Token : : Type : : eString )
return executeStringLiteral ( tok , data ) ;
2019-09-17 21:00:59 +02:00
return ExprEngine : : ValuePtr ( ) ;
}
2019-09-28 16:16:36 +02:00
static ExprEngine : : ValuePtr executeExpression ( const Token * tok , Data & data )
{
return translateUninitValueToRange ( executeExpression1 ( tok , data ) , tok - > valueType ( ) , data ) ;
}
2019-09-26 19:39:12 +02:00
static ExprEngine : : ValuePtr createVariableValue ( const Variable & var , Data & data ) ;
2019-09-17 21:00:59 +02:00
static void execute ( const Token * start , const Token * end , Data & data )
{
for ( const Token * tok = start ; tok ! = end ; tok = tok - > next ( ) ) {
2019-09-21 21:15:51 +02:00
if ( Token : : Match ( tok , " [;{}] " ) )
2019-09-19 20:18:39 +02:00
data . trackProgramState ( tok ) ;
2019-09-29 17:28:12 +02:00
2019-10-06 17:43:30 +02:00
if ( Token : : simpleMatch ( tok , " while ( 0 ) ; " ) ) {
tok = tok - > tokAt ( 4 ) ;
continue ;
}
2019-10-06 19:58:51 +02:00
if ( tok - > str ( ) = = " break " ) {
const Scope * scope = tok - > scope ( ) ;
while ( scope - > type = = Scope : : eIf | | scope - > type = = Scope : : eElse )
scope = scope - > nestedIn ;
tok = scope - > bodyEnd ;
2020-04-25 10:13:18 +02:00
if ( ! precedes ( tok , end ) )
return ;
2019-10-06 19:58:51 +02:00
}
2019-10-06 17:43:30 +02:00
2019-10-06 19:58:51 +02:00
if ( Token : : simpleMatch ( tok , " try " ) )
// TODO this is a bailout
2019-10-08 20:13:25 +02:00
throw VerifyException ( tok , " Unhandled: " + tok - > str ( ) ) ;
2019-10-06 19:58:51 +02:00
2019-09-29 17:28:12 +02:00
// Variable declaration..
2019-09-26 19:39:12 +02:00
if ( tok - > variable ( ) & & tok - > variable ( ) - > nameToken ( ) = = tok ) {
2019-09-27 21:03:47 +02:00
if ( Token : : Match ( tok , " %varid% ; %varid% = " , tok - > varId ( ) ) ) {
2019-09-29 08:26:09 +02:00
// if variable is not used in assignment rhs then we do not need to create a "confusing" variable value..
bool foundInRhs = false ;
visitAstNodes ( tok - > tokAt ( 3 ) - > astOperand2 ( ) , [ & ] ( const Token * rhs ) {
if ( rhs - > varId ( ) = = tok - > varId ( ) ) {
foundInRhs = true ;
return ChildrenToVisit : : done ;
}
return ChildrenToVisit : : op1_and_op2 ;
} ) ;
if ( ! foundInRhs ) {
tok = tok - > tokAt ( 2 ) ;
continue ;
}
2019-12-31 14:57:42 +01:00
} else if ( tok - > variable ( ) - > isArray ( ) ) {
2019-09-29 15:00:54 +02:00
data . assignValue ( tok , tok - > varId ( ) , std : : make_shared < ExprEngine : : ArrayValue > ( & data , tok - > variable ( ) ) ) ;
2019-09-26 19:39:12 +02:00
if ( Token : : Match ( tok , " %name% [ " ) )
tok = tok - > linkAt ( 1 ) ;
} else if ( Token : : Match ( tok , " %var% ; " ) )
2019-09-26 20:48:33 +02:00
data . assignValue ( tok , tok - > varId ( ) , createVariableValue ( * tok - > variable ( ) , data ) ) ;
2019-10-06 19:58:51 +02:00
} else if ( ! tok - > astParent ( ) & & ( tok - > astOperand1 ( ) | | tok - > astOperand2 ( ) ) ) {
2019-09-17 21:00:59 +02:00
executeExpression ( tok , data ) ;
2019-10-06 19:58:51 +02:00
if ( Token : : Match ( tok , " throw|return " ) )
return ;
}
2019-09-17 21:00:59 +02:00
2019-09-26 19:39:12 +02:00
else if ( Token : : simpleMatch ( tok , " if ( " ) ) {
2019-10-06 19:58:51 +02:00
const Token * cond = tok - > next ( ) - > astOperand2 ( ) ; // TODO: C++17 condition
2019-10-02 21:47:00 +02:00
const ExprEngine : : ValuePtr condValue = executeExpression ( cond , data ) ;
2020-04-30 12:18:49 +02:00
Data thenData ( data ) ;
2019-10-02 21:47:00 +02:00
Data elseData ( data ) ;
2020-04-30 12:18:49 +02:00
thenData . addConstraint ( condValue , true ) ;
2019-10-03 08:48:05 +02:00
elseData . addConstraint ( condValue , false ) ;
2019-10-02 21:47:00 +02:00
2019-09-17 21:00:59 +02:00
const Token * thenStart = tok - > linkAt ( 1 ) - > next ( ) ;
const Token * thenEnd = thenStart - > link ( ) ;
2020-04-24 18:51:54 +02:00
if ( Token : : Match ( thenStart , " { return|throw|break|continue " ) )
2020-04-30 12:18:49 +02:00
execute ( thenStart - > next ( ) , thenEnd , thenData ) ;
2020-04-24 18:51:54 +02:00
else
2020-04-30 12:18:49 +02:00
execute ( thenStart - > next ( ) , end , thenData ) ;
2020-04-24 18:51:54 +02:00
2019-09-17 21:00:59 +02:00
if ( Token : : simpleMatch ( thenEnd , " } else { " ) ) {
const Token * elseStart = thenEnd - > tokAt ( 2 ) ;
2019-10-02 21:47:00 +02:00
execute ( elseStart - > next ( ) , end , elseData ) ;
2019-10-06 18:26:40 +02:00
} else {
execute ( thenEnd , end , elseData ) ;
2019-09-17 21:00:59 +02:00
}
return ;
}
2019-10-06 19:58:51 +02:00
else if ( Token : : simpleMatch ( tok , " switch ( " ) ) {
auto condValue = executeExpression ( tok - > next ( ) - > astOperand2 ( ) , data ) ; // TODO: C++17 condition
const Token * bodyStart = tok - > linkAt ( 1 ) - > next ( ) ;
const Token * bodyEnd = bodyStart - > link ( ) ;
const Token * defaultStart = nullptr ;
Data defaultData ( data ) ;
for ( const Token * tok2 = bodyStart - > next ( ) ; tok2 ! = bodyEnd ; tok2 = tok2 - > next ( ) ) {
if ( tok2 - > str ( ) = = " { " )
tok2 = tok2 - > link ( ) ;
2020-05-01 15:15:24 +02:00
else if ( Token : : Match ( tok2 , " case %char%|%num% : " ) ) {
const MathLib : : bigint caseValue1 = tok2 - > next ( ) - > getKnownIntValue ( ) ;
auto caseValue = std : : make_shared < ExprEngine : : IntRange > ( MathLib : : toString ( caseValue1 ) , caseValue1 , caseValue1 ) ;
2019-10-06 19:58:51 +02:00
Data caseData ( data ) ;
caseData . addConstraint ( condValue , caseValue , true ) ;
defaultData . addConstraint ( condValue , caseValue , false ) ;
execute ( tok2 - > tokAt ( 2 ) , end , caseData ) ;
} else if ( Token : : simpleMatch ( tok2 , " default : " ) )
defaultStart = tok2 ;
}
execute ( defaultStart ? defaultStart : bodyEnd , end , defaultData ) ;
return ;
}
2019-10-07 17:44:26 +02:00
if ( Token : : Match ( tok , " for|while ( " ) & & Token : : simpleMatch ( tok - > linkAt ( 1 ) , " ) { " ) ) {
const Token * bodyStart = tok - > linkAt ( 1 ) - > next ( ) ;
const Token * bodyEnd = bodyStart - > link ( ) ;
// TODO this is very rough code
std : : set < int > changedVariables ;
for ( const Token * tok2 = tok ; tok2 ! = bodyEnd ; tok2 = tok2 - > next ( ) ) {
2019-10-10 11:12:36 +02:00
if ( Token : : Match ( tok2 , " %assign% " ) ) {
2019-10-14 19:41:32 +02:00
if ( Token : : Match ( tok2 - > astOperand1 ( ) , " . %name% = " ) & & tok2 - > astOperand1 ( ) - > astOperand1 ( ) & & tok2 - > astOperand1 ( ) - > astOperand1 ( ) - > valueType ( ) ) {
const Token * structToken = tok2 - > astOperand1 ( ) - > astOperand1 ( ) ;
2020-02-16 16:02:22 +01:00
if ( ! structToken - > valueType ( ) | | ! structToken - > varId ( ) )
2019-10-14 19:41:32 +02:00
throw VerifyException ( tok2 , " Unhandled assignment in loop " ) ;
const Scope * structScope = structToken - > valueType ( ) - > typeScope ;
if ( ! structScope )
throw VerifyException ( tok2 , " Unhandled assignment in loop " ) ;
const std : : string & memberName = tok2 - > previous ( ) - > str ( ) ;
ExprEngine : : ValuePtr memberValue ;
for ( const Variable & member : structScope - > varlist ) {
if ( memberName = = member . name ( ) & & member . valueType ( ) ) {
memberValue = createVariableValue ( member , data ) ;
break ;
}
}
if ( ! memberValue )
throw VerifyException ( tok2 , " Unhandled assignment in loop " ) ;
2019-10-14 22:03:55 +02:00
ExprEngine : : ValuePtr structVal1 = data . getValue ( structToken - > varId ( ) , structToken - > valueType ( ) , structToken ) ;
if ( ! structVal1 )
structVal1 = createVariableValue ( * structToken - > variable ( ) , data ) ;
auto structVal = std : : dynamic_pointer_cast < ExprEngine : : StructValue > ( structVal1 ) ;
if ( ! structVal )
throw VerifyException ( tok2 , " Unhandled assignment in loop " ) ;
2019-10-14 19:41:32 +02:00
data . assignStructMember ( tok2 , & * structVal , memberName , memberValue ) ;
continue ;
}
2020-05-01 18:10:18 +02:00
if ( tok2 - > astOperand1 ( ) & & tok2 - > astOperand1 ( ) - > isUnaryOp ( " * " ) & & tok2 - > astOperand1 ( ) - > astOperand1 ( ) - > varId ( ) ) {
2020-04-30 21:05:34 +02:00
const Token * varToken = tok2 - > astOperand1 ( ) - > astOperand1 ( ) ;
ExprEngine : : ValuePtr val = data . getValue ( varToken - > varId ( ) , varToken - > valueType ( ) , varToken ) ;
2020-05-01 18:10:18 +02:00
if ( val & & val - > type = = ExprEngine : : ValueType : : ArrayValue ) {
2020-04-30 21:05:34 +02:00
// Try to assign "any" value
auto arrayValue = std : : dynamic_pointer_cast < ExprEngine : : ArrayValue > ( val ) ;
//ExprEngine::ValuePtr anyValue = getValueRangeFromValueType(data.getNewSymbolName(), tok2->astOperand1()->valueType(), *data.settings);
arrayValue - > assign ( std : : make_shared < ExprEngine : : IntRange > ( " 0 " , 0 , 0 ) , std : : make_shared < ExprEngine : : BailoutValue > ( ) ) ;
continue ;
}
}
2019-10-10 11:12:36 +02:00
if ( ! Token : : Match ( tok2 - > astOperand1 ( ) , " %var% " ) )
throw VerifyException ( tok2 , " Unhandled assignment in loop " ) ;
2019-12-27 20:27:21 +01:00
if ( ! tok2 - > astOperand1 ( ) - > variable ( ) )
throw VerifyException ( tok2 , " Unhandled assignment in loop " ) ;
2019-10-07 17:44:26 +02:00
// give variable "any" value
2019-10-10 11:12:36 +02:00
int varid = tok2 - > astOperand1 ( ) - > varId ( ) ;
2019-10-07 17:44:26 +02:00
if ( changedVariables . find ( varid ) ! = changedVariables . end ( ) )
continue ;
changedVariables . insert ( varid ) ;
2019-12-31 22:32:16 +01:00
data . assignValue ( tok2 , varid , getValueRangeFromValueType ( data . getNewSymbolName ( ) , tok2 - > astOperand1 ( ) - > valueType ( ) , * data . settings ) ) ;
2019-10-07 17:44:26 +02:00
} else if ( Token : : Match ( tok2 , " ++|-- " ) & & tok2 - > astOperand1 ( ) & & tok2 - > astOperand1 ( ) - > variable ( ) ) {
// give variable "any" value
const Token * vartok = tok2 - > astOperand1 ( ) ;
int varid = vartok - > varId ( ) ;
if ( changedVariables . find ( varid ) ! = changedVariables . end ( ) )
continue ;
changedVariables . insert ( varid ) ;
2019-12-31 20:31:31 +01:00
data . assignValue ( tok2 , varid , getValueRangeFromValueType ( data . getNewSymbolName ( ) , vartok - > valueType ( ) , * data . settings ) ) ;
2019-10-07 17:44:26 +02:00
}
}
}
2019-09-17 21:00:59 +02:00
if ( Token : : simpleMatch ( tok , " } else { " ) )
tok = tok - > linkAt ( 2 ) ;
}
}
2020-04-28 17:16:13 +02:00
void ExprEngine : : executeAllFunctions ( ErrorLogger * errorLogger , const Tokenizer * tokenizer , const Settings * settings , const std : : vector < ExprEngine : : Callback > & callbacks , std : : ostream & report )
2019-09-17 21:00:59 +02:00
{
const SymbolDatabase * symbolDatabase = tokenizer - > getSymbolDatabase ( ) ;
for ( const Scope * functionScope : symbolDatabase - > functionScopes ) {
2019-09-27 14:36:33 +02:00
try {
2020-04-28 17:16:13 +02:00
executeFunction ( functionScope , errorLogger , tokenizer , settings , callbacks , report ) ;
2019-10-08 20:13:25 +02:00
} catch ( const VerifyException & e ) {
// FIXME.. there should not be exceptions
std : : string functionName = functionScope - > function - > name ( ) ;
std : : cout < < " Verify: Aborted analysis of function ' " < < functionName < < " ': " < < e . tok - > linenr ( ) < < " : " < < e . what < < std : : endl ;
2019-09-28 10:59:28 +02:00
} catch ( const std : : exception & e ) {
2019-09-27 14:36:33 +02:00
// FIXME.. there should not be exceptions
2019-09-28 10:59:28 +02:00
std : : string functionName = functionScope - > function - > name ( ) ;
2019-09-28 11:03:20 +02:00
std : : cout < < " Verify: Aborted analysis of function ' " < < functionName < < " ': " < < e . what ( ) < < std : : endl ;
2019-09-27 14:36:33 +02:00
}
2019-09-17 21:00:59 +02:00
}
}
2019-09-26 19:39:12 +02:00
static ExprEngine : : ValuePtr createStructVal ( const Scope * structScope , bool uninitData , Data & data )
2019-09-17 21:00:59 +02:00
{
2019-09-20 21:27:51 +02:00
if ( ! structScope )
return ExprEngine : : ValuePtr ( ) ;
2019-09-17 21:00:59 +02:00
std : : shared_ptr < ExprEngine : : StructValue > structValue = std : : make_shared < ExprEngine : : StructValue > ( data . getNewSymbolName ( ) ) ;
2019-09-26 19:39:12 +02:00
auto uninitValue = std : : make_shared < ExprEngine : : UninitValue > ( ) ;
2019-09-17 21:00:59 +02:00
for ( const Variable & member : structScope - > varlist ) {
2019-09-26 19:39:12 +02:00
if ( uninitData ) {
if ( member . isPointer ( ) ) {
structValue - > member [ member . name ( ) ] = uninitValue ;
continue ;
}
if ( member . valueType ( ) & & member . valueType ( ) - > type > = : : ValueType : : Type : : CHAR ) {
structValue - > member [ member . name ( ) ] = uninitValue ;
continue ;
}
}
2019-10-05 18:29:41 +02:00
if ( member . valueType ( ) & & member . valueType ( ) - > isIntegral ( ) ) {
ExprEngine : : ValuePtr memberValue = createVariableValue ( member , data ) ;
if ( memberValue )
structValue - > member [ member . name ( ) ] = memberValue ;
}
2019-09-17 21:00:59 +02:00
}
return structValue ;
}
static ExprEngine : : ValuePtr createVariableValue ( const Variable & var , Data & data )
{
2019-09-22 10:56:49 +02:00
if ( ! var . nameToken ( ) )
return ExprEngine : : ValuePtr ( ) ;
const ValueType * valueType = var . valueType ( ) ;
if ( ! valueType | | valueType - > type = = ValueType : : Type : : UNKNOWN_TYPE )
valueType = var . nameToken ( ) - > valueType ( ) ;
2019-09-29 08:26:09 +02:00
if ( ! valueType | | valueType - > type = = ValueType : : Type : : UNKNOWN_TYPE ) {
// variable with unknown type
if ( var . isLocal ( ) & & var . isPointer ( ) & & ! var . isArray ( ) )
return std : : make_shared < ExprEngine : : UninitValue > ( ) ;
2019-09-17 21:00:59 +02:00
return ExprEngine : : ValuePtr ( ) ;
2019-09-29 08:26:09 +02:00
}
2019-09-22 10:56:49 +02:00
if ( valueType - > pointer > 0 ) {
2019-09-29 08:26:09 +02:00
if ( var . isLocal ( ) )
return std : : make_shared < ExprEngine : : UninitValue > ( ) ;
2019-12-29 18:42:35 +01:00
auto bufferSize = std : : make_shared < ExprEngine : : IntRange > ( data . getNewSymbolName ( ) , 1 , ~ 0UL ) ;
ExprEngine : : ValuePtr pointerValue ;
if ( valueType - > type = = ValueType : : Type : : RECORD )
pointerValue = createStructVal ( valueType - > typeScope , var . isLocal ( ) & & ! var . isStatic ( ) , data ) ;
else {
ValueType vt ( * valueType ) ;
vt . pointer = 0 ;
2019-12-31 17:51:58 +01:00
if ( vt . constness & 1 )
pointerValue = getValueRangeFromValueType ( data . getNewSymbolName ( ) , & vt , * data . settings ) ;
else
pointerValue = std : : make_shared < ExprEngine : : UninitValue > ( ) ;
2019-12-29 18:42:35 +01:00
}
2019-12-31 17:51:58 +01:00
return std : : make_shared < ExprEngine : : ArrayValue > ( data . getNewSymbolName ( ) , bufferSize , pointerValue , true , true , var . isLocal ( ) & & ! var . isStatic ( ) ) ;
2019-09-22 10:56:49 +02:00
}
2019-09-27 18:58:23 +02:00
if ( var . isArray ( ) )
2019-09-29 15:00:54 +02:00
return std : : make_shared < ExprEngine : : ArrayValue > ( & data , & var ) ;
2019-12-30 19:47:18 +01:00
if ( valueType - > isIntegral ( ) | | valueType - > isFloat ( ) ) {
2019-12-31 14:57:42 +01:00
ExprEngine : : ValuePtr value ;
if ( var . isLocal ( ) & & ! var . isStatic ( ) )
value = std : : make_shared < ExprEngine : : UninitValue > ( ) ;
else
value = getValueRangeFromValueType ( data . getNewSymbolName ( ) , valueType , * data . settings ) ;
2019-12-24 21:14:14 +01:00
data . addConstraints ( value , var . nameToken ( ) ) ;
return value ;
}
2019-09-22 10:56:49 +02:00
if ( valueType - > type = = ValueType : : Type : : RECORD )
2019-09-26 19:39:12 +02:00
return createStructVal ( valueType - > typeScope , var . isLocal ( ) & & ! var . isStatic ( ) , data ) ;
2019-09-22 10:56:49 +02:00
if ( valueType - > smartPointerType ) {
2019-09-26 19:39:12 +02:00
auto structValue = createStructVal ( valueType - > smartPointerType - > classScope , var . isLocal ( ) & & ! var . isStatic ( ) , data ) ;
2019-12-29 16:26:11 +01:00
auto size = std : : make_shared < ExprEngine : : IntRange > ( data . getNewSymbolName ( ) , 1 , ~ 0UL ) ;
return std : : make_shared < ExprEngine : : ArrayValue > ( data . getNewSymbolName ( ) , size , structValue , true , true , false ) ;
2019-09-22 10:56:49 +02:00
}
2019-10-23 22:04:48 +02:00
if ( valueType - > container ) {
ExprEngine : : ValuePtr value ;
if ( valueType - > container - > stdStringLike )
value = std : : make_shared < ExprEngine : : IntRange > ( data . getNewSymbolName ( ) , - 128 , 127 ) ;
else if ( valueType - > containerTypeToken ) {
ValueType vt = ValueType : : parseDecl ( valueType - > containerTypeToken , data . settings ) ;
value = getValueRangeFromValueType ( data . getNewSymbolName ( ) , & vt , * data . settings ) ;
} else
return ExprEngine : : ValuePtr ( ) ;
2019-12-29 16:26:11 +01:00
auto bufferSize = std : : make_shared < ExprEngine : : IntRange > ( data . getNewSymbolName ( ) , 0 , ~ 0U ) ;
return std : : make_shared < ExprEngine : : ArrayValue > ( data . getNewSymbolName ( ) , bufferSize , value , false , false , false ) ;
2019-09-27 13:12:16 +02:00
}
2019-09-17 21:00:59 +02:00
return ExprEngine : : ValuePtr ( ) ;
}
2020-04-28 17:16:13 +02:00
void ExprEngine : : executeFunction ( const Scope * functionScope , ErrorLogger * errorLogger , const Tokenizer * tokenizer , const Settings * settings , const std : : vector < ExprEngine : : Callback > & callbacks , std : : ostream & report )
2019-09-17 21:00:59 +02:00
{
if ( ! functionScope - > bodyStart )
return ;
const Function * function = functionScope - > function ;
if ( ! function )
return ;
2019-09-21 19:34:06 +02:00
if ( functionScope - > bodyStart - > fileIndex ( ) > 0 )
// TODO.. what about functions in headers?
return ;
2019-09-17 21:00:59 +02:00
2020-04-27 09:08:50 +02:00
const std : : string currentFunction = function - > fullName ( ) ;
2019-09-17 21:00:59 +02:00
int symbolValueIndex = 0 ;
2019-09-19 20:18:39 +02:00
TrackExecution trackExecution ;
2020-04-28 17:16:13 +02:00
Data data ( & symbolValueIndex , errorLogger , tokenizer , settings , currentFunction , callbacks , & trackExecution ) ;
2019-09-17 21:00:59 +02:00
2019-09-26 20:48:33 +02:00
for ( const Variable & arg : function - > argumentList )
data . assignValue ( functionScope - > bodyStart , arg . declarationId ( ) , createVariableValue ( arg , data ) ) ;
2019-09-17 21:00:59 +02:00
2020-04-27 09:08:50 +02:00
data . contractConstraints ( function , executeExpression1 ) ;
2019-12-22 21:00:53 +01:00
try {
execute ( functionScope - > bodyStart , functionScope - > bodyEnd , data ) ;
} catch ( VerifyException & e ) {
trackExecution . setAbortLine ( e . tok - > linenr ( ) ) ;
auto bailoutValue = std : : make_shared < BailoutValue > ( ) ;
2020-01-12 11:53:49 +01:00
for ( const Token * tok = e . tok ; tok ! = functionScope - > bodyEnd ; tok = tok - > next ( ) ) {
if ( Token : : Match ( tok , " return|throw|while|if|for ( " ) ) {
tok = tok - > next ( ) ;
continue ;
}
2019-12-22 21:00:53 +01:00
call ( callbacks , tok , bailoutValue , & data ) ;
2020-01-12 11:53:49 +01:00
}
2019-12-22 21:00:53 +01:00
}
2019-09-19 20:18:39 +02:00
2020-01-14 21:17:07 +01:00
const bool bugHuntingReport = ! settings - > bugHuntingReport . empty ( ) ;
if ( settings - > debugBugHunting & & ( settings - > verbose | | callbacks . empty ( ) | | ! trackExecution . isAllOk ( ) ) ) {
if ( bugHuntingReport )
2019-12-27 19:05:10 +01:00
report < < " [debug] " < < std : : endl ;
trackExecution . print ( report ) ;
2019-12-28 22:09:16 +01:00
if ( ! callbacks . empty ( ) ) {
2020-01-14 21:17:07 +01:00
if ( bugHuntingReport )
2019-12-28 22:09:16 +01:00
report < < " [details] " < < std : : endl ;
trackExecution . report ( report , functionScope ) ;
}
2019-09-19 20:18:39 +02:00
}
2019-12-22 21:00:53 +01:00
2020-01-18 07:25:39 +01:00
// Write a report
2020-01-14 21:17:07 +01:00
if ( bugHuntingReport ) {
2020-04-28 22:09:01 +02:00
for ( const std : : string & f : trackExecution . getMissingContracts ( ) )
report < < " [missing contract] " < < f < < std : : endl ;
2019-12-27 19:05:10 +01:00
}
2019-09-17 21:00:59 +02:00
}
2020-01-01 08:33:27 +01: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 ;
}
2019-09-17 21:00:59 +02:00
void ExprEngine : : runChecks ( ErrorLogger * errorLogger , const Tokenizer * tokenizer , const Settings * settings )
{
2019-10-02 21:47:00 +02:00
std : : function < void ( const Token * , const ExprEngine : : Value & , ExprEngine : : DataBase * ) > divByZero = [ = ] ( const Token * tok , const ExprEngine : : Value & value , ExprEngine : : DataBase * dataBase ) {
2019-10-27 15:35:04 +01:00
if ( ! tok - > astParent ( ) | | ! std : : strchr ( " /% " , tok - > astParent ( ) - > str ( ) [ 0 ] ) )
2019-09-17 21:00:59 +02:00
return ;
2019-12-22 21:24:39 +01:00
if ( tok - > hasKnownIntValue ( ) & & tok - > getKnownIntValue ( ) ! = 0 )
return ;
2020-05-08 17:42:56 +02:00
if ( tok - > isImpossibleIntValue ( 0 ) )
return ;
2020-04-29 11:00:24 +02:00
if ( value . isUninit ( ) )
return ;
2020-01-01 08:33:27 +01:00
float f = getKnownFloatValue ( tok , 0.0f ) ;
if ( f > 0.0f | | f < 0.0f )
return ;
2020-01-19 09:10:50 +01:00
if ( value . type = = ExprEngine : : ValueType : : BailoutValue ) {
if ( Token : : simpleMatch ( tok - > previous ( ) , " sizeof ( " ) )
return ;
}
2019-10-08 20:13:25 +02:00
if ( tok - > astParent ( ) - > astOperand2 ( ) = = tok & & value . isEqual ( dataBase , 0 ) ) {
2019-12-22 21:00:53 +01:00
dataBase - > addError ( tok - > linenr ( ) ) ;
2020-01-19 09:16:02 +01:00
std : : list < const Token * > callstack { settings - > clang ? tok : tok - > astParent ( ) } ;
2020-01-18 07:25:39 +01:00
const char * const id = ( tok - > valueType ( ) & & tok - > valueType ( ) - > isFloat ( ) ) ? " bughuntingDivByZeroFloat " : " bughuntingDivByZero " ;
2020-04-29 10:58:01 +02:00
const bool bailout = ( value . type = = ExprEngine : : ValueType : : BailoutValue ) ;
2020-05-03 17:20:38 +02:00
ErrorLogger : : ErrorMessage errmsg ( callstack , & tokenizer - > list , Severity : : SeverityType : : error , id , " There is division, cannot determine that there can't be a division by zero. " , CWE ( 369 ) , false ) ;
2020-04-29 10:58:01 +02:00
if ( ! bailout )
2020-04-27 19:43:38 +02:00
errmsg . function = dataBase - > currentFunction ;
2020-05-03 17:20:38 +02:00
else
errmsg . incomplete = bailout ;
2019-09-17 21:00:59 +02:00
errorLogger - > reportErr ( errmsg ) ;
}
} ;
2020-01-15 21:06:00 +01:00
# ifdef BUG_HUNTING_INTEGEROVERFLOW
2019-10-02 21:47:00 +02:00
std : : function < void ( const Token * , const ExprEngine : : Value & , ExprEngine : : DataBase * ) > integerOverflow = [ & ] ( const Token * tok , const ExprEngine : : Value & value , ExprEngine : : DataBase * dataBase ) {
2019-09-27 14:20:17 +02:00
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 ( ) , * settings ) ;
2019-10-08 20:13:25 +02:00
if ( bits = = 0 | | bits > = 60 )
2019-09-28 06:31:47 +02:00
return ;
2019-10-08 20:13:25 +02:00
std : : string errorMessage ;
2019-09-27 14:20:17 +02:00
if ( tok - > valueType ( ) - > sign = = : : ValueType : : Sign : : SIGNED ) {
2019-10-08 20:13:25 +02:00
MathLib : : bigint v = 1LL < < ( bits - 1 ) ;
2019-10-09 11:24:57 +02:00
if ( b - > isGreaterThan ( dataBase , v - 1 ) )
2019-10-14 11:54:43 +02:00
errorMessage = " greater than " + std : : to_string ( v - 1 ) ;
2019-10-08 20:13:25 +02:00
if ( b - > isLessThan ( dataBase , - v ) ) {
if ( ! errorMessage . empty ( ) )
errorMessage + = " or " ;
2019-10-14 11:54:43 +02:00
errorMessage + = " less than " + std : : to_string ( - v ) ;
2019-10-08 20:13:25 +02:00
}
2019-09-27 14:20:17 +02:00
} else {
2019-10-08 20:13:25 +02:00
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 " ;
}
2019-09-27 14:20:17 +02:00
}
2019-10-08 20:13:25 +02:00
if ( errorMessage . empty ( ) )
return ;
errorMessage = " There is integer arithmetic, cannot determine that there can't be overflow (if result is " + errorMessage + " ). " ;
2019-09-27 14:20:17 +02:00
if ( tok - > valueType ( ) - > sign = = : : ValueType : : Sign : : UNSIGNED )
2019-10-08 20:13:25 +02:00
errorMessage + = " Note that unsigned integer overflow is defined and will wrap around. " ;
2019-09-27 14:20:17 +02:00
std : : list < const Token * > callstack { tok } ;
2020-01-18 07:25:39 +01:00
ErrorLogger : : ErrorMessage errmsg ( callstack , & tokenizer - > list , Severity : : SeverityType : : error , " bughuntingIntegerOverflow " , errorMessage , false ) ;
2019-09-27 14:20:17 +02:00
errorLogger - > reportErr ( errmsg ) ;
} ;
2019-10-02 17:59:04 +02:00
# endif
2019-09-27 14:20:17 +02:00
2019-12-30 18:55:16 +01:00
std : : function < void ( const Token * , const ExprEngine : : Value & , ExprEngine : : DataBase * ) > uninit = [ = ] ( const Token * tok , const ExprEngine : : Value & value , ExprEngine : : DataBase * dataBase ) {
if ( ! tok - > astParent ( ) )
return ;
if ( ! value . isUninit ( ) )
return ;
2020-01-12 13:35:09 +01:00
// lhs in assignment
if ( tok - > astParent ( ) - > str ( ) = = " = " & & tok = = tok - > astParent ( ) - > astOperand1 ( ) )
return ;
2020-01-12 11:53:49 +01:00
// Avoid FP when there is bailout..
if ( value . type = = ExprEngine : : ValueType : : BailoutValue ) {
2020-01-12 13:35:09 +01:00
if ( tok - > hasKnownValue ( ) )
return ;
if ( tok - > function ( ) )
return ;
if ( Token : : Match ( tok , " <<|>>|, " ) )
// Only warn about the operands
return ;
// lhs for scope operator
if ( Token : : Match ( tok , " %name% :: " ) )
return ;
if ( tok - > astParent ( ) - > str ( ) = = " :: " & & tok = = tok - > astParent ( ) - > astOperand1 ( ) )
return ;
2020-01-12 11:53:49 +01:00
if ( tok - > str ( ) = = " ( " )
// cast: result is not uninitialized if expression is initialized
// function: does not return a uninitialized value
return ;
2020-01-12 13:35:09 +01: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 ( ) ) ;
2020-01-12 11:53:49 +01:00
for ( const Token * t : tokens ) {
if ( t & & t - > valueType ( ) & & t - > valueType ( ) - > pointer = = 0 & & t - > valueType ( ) - > container )
return ;
}
const Variable * var = tok - > variable ( ) ;
if ( var & & ! var - > isPointer ( ) ) {
if ( ! var - > isLocal ( ) | | var - > isStatic ( ) )
return ;
}
2020-01-12 13:35:39 +01:00
if ( var & & ( Token : : Match ( var - > nameToken ( ) , " %name% = " ) | | Token : : Match ( var - > nameToken ( ) , " %varid% ; %varid% = " , var - > declarationId ( ) ) ) )
return ;
2020-01-12 13:35:09 +01:00
if ( var & & var - > nameToken ( ) = = tok )
return ;
2020-01-12 11:53:49 +01:00
}
2019-12-31 16:50:09 +01:00
// Avoid FP for array declaration
const Token * parent = tok - > astParent ( ) ;
while ( parent & & parent - > str ( ) = = " [ " )
parent = parent - > astParent ( ) ;
if ( ! parent )
return ;
2019-12-30 18:55:16 +01:00
dataBase - > addError ( tok - > linenr ( ) ) ;
std : : list < const Token * > callstack { tok } ;
2020-01-18 07:25:39 +01:00
ErrorLogger : : ErrorMessage errmsg ( callstack , & tokenizer - > list , Severity : : SeverityType : : error , " bughuntingUninit " , " Cannot determine that ' " + tok - > expressionString ( ) + " ' is initialized " , CWE_USE_OF_UNINITIALIZED_VARIABLE , false ) ;
2019-12-30 18:55:16 +01:00
errorLogger - > reportErr ( errmsg ) ;
} ;
2019-12-23 22:10:43 +01:00
std : : function < void ( const Token * , const ExprEngine : : Value & , ExprEngine : : DataBase * ) > checkFunctionCall = [ = ] ( const Token * tok , const ExprEngine : : Value & value , ExprEngine : : DataBase * dataBase ) {
if ( ! Token : : Match ( tok - > astParent ( ) , " [(,] " ) )
return ;
const Token * parent = tok - > astParent ( ) ;
2019-12-26 18:32:59 +01:00
while ( Token : : simpleMatch ( parent , " , " ) )
2019-12-23 22:10:43 +01:00
parent = parent - > astParent ( ) ;
2019-12-26 18:32:59 +01:00
if ( ! parent | | parent - > str ( ) ! = " ( " )
return ;
int num = 0 ;
for ( const Token * argTok : getArguments ( parent - > astOperand1 ( ) ) ) {
- - num ;
if ( argTok = = tok ) {
num = - num ;
break ;
}
2019-12-23 22:10:43 +01:00
}
2019-12-26 18:32:59 +01:00
if ( num < = 0 )
2019-12-23 22:10:43 +01:00
return ;
2019-12-26 18:32:59 +01:00
if ( parent - > astOperand1 ( ) - > function ( ) ) {
2019-12-25 09:23:07 +01:00
const Variable * arg = parent - > astOperand1 ( ) - > function ( ) - > getArgumentVar ( num - 1 ) ;
2020-01-15 07:15:47 +01:00
if ( arg & & arg - > nameToken ( ) ) {
2019-12-25 09:23:07 +01:00
std : : string bad ;
MathLib : : bigint low ;
if ( arg - > nameToken ( ) - > getCppcheckAttribute ( TokenImpl : : CppcheckAttributes : : Type : : LOW , & low ) ) {
2019-12-31 05:59:06 +01:00
if ( ! ( tok - > hasKnownIntValue ( ) & & tok - > getKnownIntValue ( ) > = low ) & & value . isLessThan ( dataBase , low ) )
bad = " __cppcheck_low__( " + std : : to_string ( low ) + " ) " ;
2019-12-25 09:23:07 +01:00
}
MathLib : : bigint high ;
if ( arg - > nameToken ( ) - > getCppcheckAttribute ( TokenImpl : : CppcheckAttributes : : Type : : HIGH , & high ) ) {
2019-12-31 05:59:06 +01:00
if ( ! ( tok - > hasKnownIntValue ( ) & & tok - > getKnownIntValue ( ) < = high ) & & value . isGreaterThan ( dataBase , high ) )
bad = " __cppcheck_high__( " + std : : to_string ( high ) + " ) " ;
2019-12-25 09:23:07 +01:00
}
if ( ! bad . empty ( ) ) {
dataBase - > addError ( tok - > linenr ( ) ) ;
std : : list < const Token * > callstack { tok } ;
ErrorLogger : : ErrorMessage errmsg ( callstack ,
& tokenizer - > list ,
Severity : : SeverityType : : error ,
2020-01-18 07:25:39 +01:00
" bughuntingInvalidArgValue " ,
2019-12-25 09:23:07 +01:00
" There is function call, cannot determine that " + std : : to_string ( num ) + getOrdinalText ( num ) + " argument value meets the attribute " + bad , CWE ( 0 ) , false ) ;
errorLogger - > reportErr ( errmsg ) ;
2019-12-26 18:32:59 +01:00
return ;
2019-12-25 09:23:07 +01:00
}
}
}
2019-12-23 22:10:43 +01:00
// Check invalid function argument values..
for ( const Library : : InvalidArgValue & invalidArgValue : Library : : getInvalidArgValues ( settings - > library . validarg ( parent - > astOperand1 ( ) , num ) ) ) {
bool err = false ;
std : : string bad ;
switch ( invalidArgValue . type ) {
case Library : : InvalidArgValue : : eq :
2019-12-31 05:59:06 +01:00
if ( ! tok - > hasKnownIntValue ( ) | | tok - > getKnownIntValue ( ) = = MathLib : : toLongNumber ( invalidArgValue . op1 ) )
err = value . isEqual ( dataBase , MathLib : : toLongNumber ( invalidArgValue . op1 ) ) ;
2019-12-23 22:10:43 +01:00
bad = " equals " + invalidArgValue . op1 ;
break ;
case Library : : InvalidArgValue : : le :
2019-12-31 05:59:06 +01:00
if ( ! tok - > hasKnownIntValue ( ) | | tok - > getKnownIntValue ( ) < = MathLib : : toLongNumber ( invalidArgValue . op1 ) )
err = value . isLessThan ( dataBase , MathLib : : toLongNumber ( invalidArgValue . op1 ) + 1 ) ;
2019-12-23 22:10:43 +01:00
bad = " less equal " + invalidArgValue . op1 ;
break ;
case Library : : InvalidArgValue : : lt :
2019-12-31 05:59:06 +01:00
if ( ! tok - > hasKnownIntValue ( ) | | tok - > getKnownIntValue ( ) < MathLib : : toLongNumber ( invalidArgValue . op1 ) )
err = value . isLessThan ( dataBase , MathLib : : toLongNumber ( invalidArgValue . op1 ) ) ;
2019-12-23 22:10:43 +01:00
bad = " less than " + invalidArgValue . op1 ;
break ;
case Library : : InvalidArgValue : : ge :
2019-12-31 05:59:06 +01:00
if ( ! tok - > hasKnownIntValue ( ) | | tok - > getKnownIntValue ( ) > = MathLib : : toLongNumber ( invalidArgValue . op1 ) )
err = value . isGreaterThan ( dataBase , MathLib : : toLongNumber ( invalidArgValue . op1 ) - 1 ) ;
2019-12-23 22:10:43 +01:00
bad = " greater equal " + invalidArgValue . op1 ;
break ;
case Library : : InvalidArgValue : : gt :
2019-12-31 05:59:06 +01:00
if ( ! tok - > hasKnownIntValue ( ) | | tok - > getKnownIntValue ( ) > MathLib : : toLongNumber ( invalidArgValue . op1 ) )
err = value . isGreaterThan ( dataBase , MathLib : : toLongNumber ( invalidArgValue . op1 ) ) ;
2019-12-23 22:10:43 +01:00
bad = " greater than " + invalidArgValue . op1 ;
break ;
case Library : : InvalidArgValue : : range :
// TODO
err = value . isEqual ( dataBase , MathLib : : toLongNumber ( invalidArgValue . op1 ) ) ;
err | = value . isEqual ( dataBase , MathLib : : toLongNumber ( invalidArgValue . op2 ) ) ;
bad = " range " + invalidArgValue . op1 + " - " + invalidArgValue . op2 ;
break ;
2020-04-21 17:27:51 +02:00
}
2019-12-23 22:10:43 +01:00
if ( err ) {
dataBase - > addError ( tok - > linenr ( ) ) ;
std : : list < const Token * > callstack { tok } ;
2020-01-18 07:25:39 +01:00
ErrorLogger : : ErrorMessage errmsg ( callstack , & tokenizer - > list , 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 ) ;
2019-12-23 22:10:43 +01:00
errorLogger - > reportErr ( errmsg ) ;
break ;
}
}
2020-01-01 14:35:39 +01:00
// Uninitialized function argument..
2020-01-02 06:16:36 +01:00
if ( settings - > library . isuninitargbad ( parent - > astOperand1 ( ) , num ) & & settings - > library . isnullargbad ( parent - > astOperand1 ( ) , num ) & & value . type = = ExprEngine : : ValueType : : ArrayValue ) {
2020-01-01 14:35:39 +01:00
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 ) ) {
if ( v . second - > isUninit ( ) ) {
dataBase - > addError ( tok - > linenr ( ) ) ;
std : : list < const Token * > callstack { tok } ;
2020-01-18 07:25:39 +01:00
ErrorLogger : : ErrorMessage errmsg ( callstack , & tokenizer - > list , 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 ) ;
2020-01-01 14:35:39 +01:00
errorLogger - > reportErr ( errmsg ) ;
break ;
}
}
}
2019-12-23 22:10:43 +01:00
} ;
2020-01-21 20:19:51 +01:00
std : : function < void ( const Token * , const ExprEngine : : Value & , ExprEngine : : DataBase * ) > checkAssignment = [ = ] ( const Token * tok , const ExprEngine : : Value & value , ExprEngine : : DataBase * dataBase ) {
if ( ! Token : : simpleMatch ( tok - > astParent ( ) , " = " ) )
return ;
const Token * lhs = tok - > astParent ( ) - > astOperand1 ( ) ;
2020-01-21 20:29:13 +01:00
while ( Token : : simpleMatch ( lhs , " . " ) )
lhs = lhs - > astOperand2 ( ) ;
2020-01-21 20:19:51 +01:00
if ( ! lhs | | ! lhs - > variable ( ) | | ! lhs - > variable ( ) - > nameToken ( ) )
return ;
const Token * vartok = lhs - > variable ( ) - > nameToken ( ) ;
MathLib : : bigint low ;
if ( vartok - > getCppcheckAttribute ( TokenImpl : : CppcheckAttributes : : Type : : LOW , & low ) ) {
if ( value . isLessThan ( dataBase , low ) ) {
dataBase - > addError ( tok - > linenr ( ) ) ;
std : : list < const Token * > callstack { tok } ;
ErrorLogger : : ErrorMessage errmsg ( callstack , & tokenizer - > list , Severity : : SeverityType : : error , " bughuntingAssign " , " There is assignment, cannot determine that value is greater or equal with " + std : : to_string ( low ) , CWE_INCORRECT_CALCULATION , false ) ;
errorLogger - > reportErr ( errmsg ) ;
}
}
MathLib : : bigint high ;
if ( vartok - > getCppcheckAttribute ( TokenImpl : : CppcheckAttributes : : Type : : HIGH , & high ) ) {
if ( value . isGreaterThan ( dataBase , high ) ) {
dataBase - > addError ( tok - > linenr ( ) ) ;
std : : list < const Token * > callstack { tok } ;
ErrorLogger : : ErrorMessage errmsg ( callstack , & tokenizer - > list , Severity : : SeverityType : : error , " bughuntingAssign " , " There is assignment, cannot determine that value is lower or equal with " + std : : to_string ( high ) , CWE_INCORRECT_CALCULATION , false ) ;
errorLogger - > reportErr ( errmsg ) ;
}
}
} ;
2019-09-17 21:00:59 +02:00
std : : vector < ExprEngine : : Callback > callbacks ;
callbacks . push_back ( divByZero ) ;
2019-12-23 22:10:43 +01:00
callbacks . push_back ( checkFunctionCall ) ;
2020-01-21 20:19:51 +01:00
callbacks . push_back ( checkAssignment ) ;
2020-01-15 21:06:00 +01:00
# ifdef BUG_HUNTING_INTEGEROVERFLOW
2019-09-27 14:20:17 +02:00
callbacks . push_back ( integerOverflow ) ;
2019-09-27 16:13:23 +02:00
# endif
2019-12-30 18:55:16 +01:00
callbacks . push_back ( uninit ) ;
2019-12-27 19:05:10 +01:00
std : : ostringstream report ;
2020-04-28 17:16:13 +02:00
ExprEngine : : executeAllFunctions ( errorLogger , tokenizer , settings , callbacks , report ) ;
2020-01-14 21:17:07 +01:00
if ( settings - > bugHuntingReport . empty ( ) )
2020-01-01 19:57:38 +01:00
std : : cout < < report . str ( ) ;
else if ( errorLogger )
2020-01-18 07:25:39 +01:00
errorLogger - > bughuntingReport ( report . str ( ) ) ;
2019-09-17 21:00:59 +02:00
}