From cb395a2b66181bcb85f806ef72a9d18ea683ba2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Can=20B=C3=B6l=C3=BCk?= Date: Sun, 12 Jul 2020 08:00:52 +0200 Subject: [PATCH] Symbolic VM now uses references, implemented special unique ptr for dirs --- VTIL-Architecture/symex/memory.hpp | 8 +-- VTIL-Architecture/trace/tracer.cpp | 12 ++--- VTIL-Architecture/vm/interface.cpp | 13 ++--- VTIL-Architecture/vm/interface.hpp | 8 +-- VTIL-Architecture/vm/lambda.hpp | 8 +-- VTIL-Architecture/vm/symbolic.cpp | 18 +++---- VTIL-Architecture/vm/symbolic.hpp | 10 ++-- .../optimizer/symbolic_rewrite_pass.cpp | 14 ++--- VTIL-Compiler/validation/pass_validation.cpp | 46 ++++++++-------- VTIL-SymEx/directives/directive.cpp | 27 +++++++++- VTIL-SymEx/directives/directive.hpp | 37 ++++++++++++- VTIL-SymEx/directives/fast_matcher.hpp | 11 ++-- VTIL-SymEx/directives/transformer.cpp | 8 +-- VTIL-SymEx/directives/transformer.hpp | 4 +- VTIL-SymEx/expressions/expression.cpp | 42 ++++++++++++++- VTIL-SymEx/simplifier/boolean_directives.cpp | 8 +-- VTIL-SymEx/simplifier/boolean_directives.hpp | 6 +-- VTIL-SymEx/simplifier/directives.hpp | 8 +-- VTIL-SymEx/simplifier/simplifier.cpp | 54 +++++++++---------- 19 files changed, 221 insertions(+), 121 deletions(-) diff --git a/VTIL-Architecture/symex/memory.hpp b/VTIL-Architecture/symex/memory.hpp index c003364..7b14f4c 100644 --- a/VTIL-Architecture/symex/memory.hpp +++ b/VTIL-Architecture/symex/memory.hpp @@ -55,7 +55,7 @@ namespace vtil::symbolic // Declaration of symbolic memory type using sinkhole. // - using memory = sinkhole; + using memory = sinkhole; // Creates a symbolic memory of the given type. // @@ -64,11 +64,11 @@ namespace vtil::symbolic switch ( type ) { case memory_type::free: - return { [ ] ( auto& ptr, bitcnt_t size ) -> expression { return make_memory_ex( ptr, size ); } }; + return { [ ] ( auto& ptr, bitcnt_t size ) -> expression::reference { return make_memory_ex( ptr, size ); } }; case memory_type::relaxed: - return { [ ] ( auto& ptr, bitcnt_t size ) -> expression { return make_undefined_ex( size ); } }; + return { [ ] ( auto& ptr, bitcnt_t size ) -> expression::reference { return make_undefined_ex( size ); } }; default: - return { [ ] ( auto& ptr, bitcnt_t size ) -> expression { unreachable(); return expression{}; } }; + return { [ ] ( auto& ptr, bitcnt_t size ) -> expression::reference { unreachable(); return nullptr; } }; } } }; \ No newline at end of file diff --git a/VTIL-Architecture/trace/tracer.cpp b/VTIL-Architecture/trace/tracer.cpp index 3e9875f..e00188f 100644 --- a/VTIL-Architecture/trace/tracer.cpp +++ b/VTIL-Architecture/trace/tracer.cpp @@ -498,33 +498,33 @@ namespace vtil // Create a lambda virtual machine and allocate a temporary result. // lambda_vm lvm; - symbolic::expression result = {}; + symbolic::expression::reference result = {}; lvm.hooks.read_register = [ & ] ( const register_desc& desc ) { return trace( { it, desc } ); }; - lvm.hooks.read_memory = [ & ] ( const symbolic::expression& pointer, size_t byte_count ) + lvm.hooks.read_memory = [ & ] ( const symbolic::expression::reference& pointer, size_t byte_count ) { auto exp = trace( symbolic::variable{ it, { pointer, math::narrow_cast( byte_count * 8 ) } } ); return exp.is_valid() ? exp.resize( result_bcnt ) : exp; }; - lvm.hooks.write_register = [ & ] ( const register_desc& desc, symbolic::expression value ) + lvm.hooks.write_register = [ & ] ( const register_desc& desc, symbolic::expression::reference value ) { if ( desc == lookup.reg() ) result = std::move( value ); }; - lvm.hooks.write_memory = [ & ] ( const symbolic::expression& pointer, symbolic::expression value ) + lvm.hooks.write_memory = [ & ] ( const symbolic::expression::reference& pointer, symbolic::expression::reference value ) { - if ( pointer.equals( lookup.mem().decay() ) ) + if ( pointer->equals( lookup.mem().decay() ) ) result = std::move( value ); }; // Step one instruction, if result was successfuly captured, return. // if ( lvm.execute( *it ), result ) - return result; + return *result; // If we could not describe the behaviour, increment iterator and return. // diff --git a/VTIL-Architecture/vm/interface.cpp b/VTIL-Architecture/vm/interface.cpp index e8d66ff..f77e94f 100644 --- a/VTIL-Architecture/vm/interface.cpp +++ b/VTIL-Architecture/vm/interface.cpp @@ -36,7 +36,7 @@ namespace vtil { // Declare a helper to convert operands of current instruction into expressions. // - auto cvt_operand = [ & ] ( int i ) -> symbolic::expression + auto cvt_operand = [ & ] ( int i ) -> symbolic::expression::reference { const operand& op = ins.operands[ i ]; @@ -46,7 +46,7 @@ namespace vtil { // Trace the source register. // - symbolic::expression result = read_register( op.reg() ); + symbolic::expression::reference result = read_register( op.reg() ); // If stack pointer, add the current virtual offset. // @@ -76,7 +76,7 @@ namespace vtil // write_register( ins.operands[ 0 ].reg(), - cvt_operand( 1 ).resize( ins.operands[ 0 ].bit_count(), cast_signed ) + cvt_operand( 1 )->resize( ins.operands[ 0 ].bit_count(), cast_signed ) ); return true; } @@ -108,13 +108,14 @@ namespace vtil // Read the source operand and byte-align. // auto src = cvt_operand( 2 ); - src.resize( ( src.size() + 7 ) & ~7 ); + bitcnt_t bsize = ( src->size() + 7 ) & ~7; + if ( src->size() != bsize ) ( +src )->resize( bsize ); // Query base pointer without using the wrapper to skip SP adjustment and // add offset. Write the source to the pointer. // auto [base, offset] = ins.memory_location(); - write_memory( read_register( base ) + offset, src ); + write_memory( read_register( base ) + offset, *src ); return true; } // If any symbolic operator: @@ -161,7 +162,7 @@ namespace vtil else if ( ( ins.operands[ 0 ].size() + ins.operands[ 1 ].size() ) <= 8 ) { auto op1_low = cvt_operand( 0 ); - auto op1 = op1_low | ( op1_high.resize( op1_high.size() + op1_low.size() ) << op1_low.size() ); + auto op1 = op1_low | ( op1_high->resize( op1_high->size() + op1_low->size() ) << op1_low->size() ); result = { op1, op_id, cvt_operand( 2 ) }; } // If operation is 65 bits or bigger: diff --git a/VTIL-Architecture/vm/interface.hpp b/VTIL-Architecture/vm/interface.hpp index 7318c07..71b997c 100644 --- a/VTIL-Architecture/vm/interface.hpp +++ b/VTIL-Architecture/vm/interface.hpp @@ -41,19 +41,19 @@ namespace vtil // Reads from the register. // - virtual symbolic::expression read_register( const register_desc& desc ) { unreachable(); return {}; } + virtual symbolic::expression::reference read_register( const register_desc& desc ) { unreachable(); return {}; } // Reads the given number of bytes from the memory. // - virtual symbolic::expression read_memory( const symbolic::expression& pointer, size_t byte_count ) { unreachable(); return {}; } + virtual symbolic::expression::reference read_memory( const symbolic::expression::reference& pointer, size_t byte_count ) { unreachable(); return {}; } // Writes to the register. // - virtual void write_register( const register_desc& desc, symbolic::expression value ) { unreachable(); } + virtual void write_register( const register_desc& desc,symbolic::expression::reference value ) { unreachable(); } // Writes the given expression to the memory. // - virtual void write_memory( const symbolic::expression& pointer, symbolic::expression value ) { unreachable(); } + virtual void write_memory( const symbolic::expression::reference& pointer, symbolic::expression::reference value ) { unreachable(); } // Runs the given instruction, returns whether it was successful. // diff --git a/VTIL-Architecture/vm/lambda.hpp b/VTIL-Architecture/vm/lambda.hpp index d80f0c8..20265db 100644 --- a/VTIL-Architecture/vm/lambda.hpp +++ b/VTIL-Architecture/vm/lambda.hpp @@ -70,25 +70,25 @@ namespace vtil ? hooks.size_register( desc ) : vm_base::size_register( desc ); } - symbolic::expression read_register( const register_desc& desc ) override + symbolic::expression::reference read_register( const register_desc& desc ) override { return hooks.read_register ? hooks.read_register( desc ) : vm_base::read_register( desc ); } - symbolic::expression read_memory( const symbolic::expression& pointer, size_t byte_count ) override + symbolic::expression::reference read_memory( const symbolic::expression::reference& pointer, size_t byte_count ) override { return hooks.read_memory ? hooks.read_memory( pointer, byte_count ) : vm_base::read_memory( pointer, byte_count ); } - void write_register( const register_desc& desc, symbolic::expression value ) override + void write_register( const register_desc& desc, symbolic::expression::reference value ) override { return hooks.write_register ? hooks.write_register( desc, std::move( value ) ) : vm_base::write_register( desc, std::move( value ) ); } - void write_memory( const symbolic::expression& pointer, symbolic::expression value ) override + void write_memory( const symbolic::expression::reference& pointer, symbolic::expression::reference value ) override { return hooks.write_memory ? hooks.write_memory( pointer, std::move( value ) ) diff --git a/VTIL-Architecture/vm/symbolic.cpp b/VTIL-Architecture/vm/symbolic.cpp index 88edaec..d409cf6 100644 --- a/VTIL-Architecture/vm/symbolic.cpp +++ b/VTIL-Architecture/vm/symbolic.cpp @@ -31,13 +31,13 @@ namespace vtil { // Reads from the register. // - symbolic::expression symbolic_vm::read_register( const register_desc& desc ) + symbolic::expression::reference symbolic_vm::read_register( const register_desc& desc ) { bitcnt_t size = size_register( desc ); register_desc full = { desc.flags, desc.local_id, size, 0, desc.architecture }; auto it = register_state.find( full ); - symbolic::expression exp = it == register_state.end() + auto exp = it == register_state.end() ? symbolic::variable{ full }.to_expression( false ) : it->second; @@ -49,7 +49,7 @@ namespace vtil // Writes to the register. // - void symbolic_vm::write_register( const register_desc& desc, symbolic::expression value ) + void symbolic_vm::write_register( const register_desc& desc, symbolic::expression::reference value ) { bitcnt_t size = size_register( desc ); register_desc full = { desc.flags, desc.local_id, size, 0, desc.architecture }; @@ -57,22 +57,22 @@ namespace vtil if ( desc.bit_count == size && desc.bit_offset == 0 ) { register_state.erase( desc ); - register_state.emplace( desc, std::move( value ) ); + register_state.emplace( desc, value ); } else { auto& exp = register_state[ full ]; if ( !exp ) exp = symbolic::make_register_ex( full ); - exp = ( exp & ~desc.get_mask() ) | ( value.resize( desc.bit_count ).resize( size ) << desc.bit_offset ); + exp = ( std::move( exp ) & ~desc.get_mask() ) | ( value.resize( desc.bit_count ).resize( size ) << desc.bit_offset ); } } // Reads the given number of bytes from the memory. // - symbolic::expression symbolic_vm::read_memory( const symbolic::expression& pointer, size_t byte_count ) + symbolic::expression::reference symbolic_vm::read_memory( const symbolic::expression::reference& pointer, size_t byte_count ) { bitcnt_t bcnt = math::narrow_cast( byte_count * 8 ); - symbolic::expression exp = memory_state.read_v( + symbolic::expression::reference exp = memory_state.read_v( pointer, bcnt ); @@ -81,11 +81,11 @@ namespace vtil // Writes the given expression to the memory. // - void symbolic_vm::write_memory( const symbolic::expression& pointer, symbolic::expression value ) + void symbolic_vm::write_memory( const symbolic::expression::reference& pointer, symbolic::expression::reference value ) { memory_state.write( pointer, - value.resize( ( value.size() + 7 ) & ~7 ) + value.resize( ( value->size() + 7 ) & ~7 ) ); } diff --git a/VTIL-Architecture/vm/symbolic.hpp b/VTIL-Architecture/vm/symbolic.hpp index b18dd50..a957009 100644 --- a/VTIL-Architecture/vm/symbolic.hpp +++ b/VTIL-Architecture/vm/symbolic.hpp @@ -42,7 +42,7 @@ namespace vtil // State of the virtual machine. // symbolic::memory memory_state; - std::map register_state; + std::map register_state; // Construct from memory type, defaults to free. // @@ -52,19 +52,19 @@ namespace vtil // Reads from the register. // - Value will be unpacked. // - symbolic::expression read_register( const register_desc& desc ) override; + symbolic::expression::reference read_register( const register_desc& desc ) override; // Writes to the register. // - void write_register( const register_desc& desc, symbolic::expression value ) override; + void write_register( const register_desc& desc, symbolic::expression::reference value ) override; // Reads the given number of bytes from the memory. // - symbolic::expression read_memory( const symbolic::expression& pointer, size_t byte_count ) override; + symbolic::expression::reference read_memory( const symbolic::expression::reference& pointer, size_t byte_count ) override; // Writes the given expression to the memory. // - void write_memory( const symbolic::expression& pointer, symbolic::expression value ) override; + void write_memory( const symbolic::expression::reference& pointer, symbolic::expression::reference value ) override; // Override execute to enforce lazyness. // diff --git a/VTIL-Compiler/optimizer/symbolic_rewrite_pass.cpp b/VTIL-Compiler/optimizer/symbolic_rewrite_pass.cpp index aa46e89..2af7d58 100644 --- a/VTIL-Compiler/optimizer/symbolic_rewrite_pass.cpp +++ b/VTIL-Compiler/optimizer/symbolic_rewrite_pass.cpp @@ -131,7 +131,7 @@ namespace vtil::optimizer // auto k = pair.first; auto v = pair.second.simplify(); symbolic::expression v0 = symbolic::make_register_ex( k ); - if ( v0.equals( v ) ) + if ( v->equals( v0 ) ) continue; // If register value is not used after this instruction, skip from emitted state. @@ -191,11 +191,11 @@ namespace vtil::optimizer // Pack registers and the expression. // - v = symbolic::variable::pack_all( v.simplify( true ) ); + symbolic::expression final_value = symbolic::variable::pack_all( ( +v )->simplify( true ) ); // Buffer a mov instruction. // - instruction_buffer.push_back( { &ins::mov, { k, translator << v } } ); + instruction_buffer.push_back( { &ins::mov, { k, translator << final_value } } ); } // For each memory state: @@ -208,7 +208,7 @@ namespace vtil::optimizer // If value is unchanged, skip. // - if ( v.equals( v0 ) ) + if ( v->equals( v0 ) ) continue; // Try minimizing expression size. @@ -230,7 +230,7 @@ namespace vtil::optimizer // Pack registers and the expression. // - v = symbolic::variable::pack_all( v.simplify( true ) ); + v = symbolic::variable::pack_all( v->simplify( true ) ); // If pointer can be rewritten as $sp + C: // @@ -242,7 +242,7 @@ namespace vtil::optimizer instruction_buffer.push_back( { &ins::str, - { REG_SP, make_imm( *displacement ), translator << v } + { REG_SP, make_imm( *displacement ), translator << *v } } ); } else @@ -283,7 +283,7 @@ namespace vtil::optimizer instruction_buffer.push_back( { &ins::str, - { base, make_imm( offset ), translator << v } + { base, make_imm( offset ), translator << *v } } ); } } diff --git a/VTIL-Compiler/validation/pass_validation.cpp b/VTIL-Compiler/validation/pass_validation.cpp index ea6c52d..b2c7c89 100644 --- a/VTIL-Compiler/validation/pass_validation.cpp +++ b/VTIL-Compiler/validation/pass_validation.cpp @@ -123,12 +123,12 @@ namespace vtil::optimizer::validation // Validate target. // - symbolic::expression target_call = ins.operands[ 0 ].is_immediate() - ? ins.operands[ 0 ].imm().u64 + auto target_call = ins.operands[ 0 ].is_immediate() + ? symbolic::expression::reference{ ins.operands[ 0 ].imm().u64 } : vm.read_register( ins.operands[ 0 ].reg() ); - if ( target_call.value.get() != call.address ) + if ( target_call->value.get() != call.address ) { - logger::warning( "Unexpected callee, expected 0x%llx, got [%s].", call.address, target_call ); + logger::warning( "Unexpected callee, expected 0x%llx, got [%s].", call.address, *target_call ); success = false; return false; } @@ -139,7 +139,7 @@ namespace vtil::optimizer::validation auto it = call_conv.param_registers.begin(); for ( auto [value, id] : zip( call.parameters, iindices() ) ) { - symbolic::expression exp; + symbolic::expression::reference exp; // If we did not reach the end of registers yet: // @@ -163,7 +163,7 @@ namespace vtil::optimizer::validation // Fail if value does not match. // - if ( exp.value.get() != value ) + if ( exp->value.get() != value ) { logger::warning( "Parameter %d does not match, expected 0x%llx, got [%s].", id, value, exp ); success = false; @@ -196,12 +196,12 @@ namespace vtil::optimizer::validation // Validate return address. // - symbolic::expression sreturn_address = ins.operands[ 0 ].is_immediate() - ? ins.operands[ 0 ].imm().u64 + auto sreturn_address = ins.operands[ 0 ].is_immediate() + ? symbolic::expression::reference{ ins.operands[ 0 ].imm().u64 } : vm.read_register( ins.operands[ 0 ].reg() ); - if ( sreturn_address.value.get() != return_address ) + if ( sreturn_address->value.get() != return_address ) { - logger::warning( "Unexpected return address, expected 0x%llx, got [%s].", return_address, sreturn_address ); + logger::warning( "Unexpected return address, expected 0x%llx, got [%s].", return_address, *sreturn_address ); success = false; return false; } @@ -210,8 +210,8 @@ namespace vtil::optimizer::validation // for ( auto& [reg, value] : exit.register_state ) { - symbolic::expression exp = vm.read_register( reg ); - if ( exp.value.get() != value ) + auto exp = vm.read_register( reg ); + if ( exp->value.get() != value ) { logger::warning( "Return state %s does not match, expected 0x%llx, got [%s].", reg, value, exp ); success = false; @@ -227,42 +227,40 @@ namespace vtil::optimizer::validation return vm.symbolic_vm::execute( ins ); }; - vm.hooks.read_memory = [ & ] ( const symbolic::expression& pointer, size_t sz ) + vm.hooks.read_memory = [ & ] ( const symbolic::expression::reference& pointer, size_t sz ) { - symbolic::expression ptr = pointer.simplify(); - // If action log has a matching read memory on top of the stack: // if ( action_it != action_end && std::get_if( &*action_it ) ) { auto& mem = std::get( *action_it ); - if ( ptr.value.get() == mem.address ) + if ( pointer->value.get() == mem.address ) { // Write fake value to the state and pop the stack. // symbolic::expression value = { mem.fake_value, mem.size }; - vm.symbolic_vm::write_memory( ptr, value ); + vm.symbolic_vm::write_memory( pointer, value ); ++action_it; } } - return vm.symbolic_vm::read_memory( ptr, sz ); + return vm.symbolic_vm::read_memory( pointer, sz ); }; - vm.hooks.write_memory = [ & ] ( const symbolic::expression& pointer, symbolic::expression exp ) + vm.hooks.write_memory = [ & ] ( const symbolic::expression::reference& pointer, symbolic::expression::reference exp ) { // If action log has a matching write memory on top of the stack: // if ( action_it != action_end && std::get_if( &*action_it ) ) { auto& mem = std::get( *action_it ); - if ( pointer.value.get() == mem.address ) + if ( pointer->value.get() == mem.address ) { // Pop the stack and validate the value. // - if ( exp.value.get() != mem.value ) + if ( exp->value.get() != mem.value ) { - logger::warning( "Unexpected memory write into 0x%llx, expected 0x%llx, got [%s].", mem.address, mem.value, exp ); + logger::warning( "Unexpected memory write into 0x%llx, expected 0x%llx, got [%s].", mem.address, mem.value, *exp ); success = false; } ++action_it; @@ -315,7 +313,7 @@ namespace vtil::optimizer::validation // operand dst = {}; if ( lim->base == &ins::js ) - dst = *vm.read_register( lim->operands[ 0 ].reg() ).get() ? lim->operands[ 1 ] : lim->operands[ 2 ]; + dst = *vm.read_register( lim->operands[ 0 ].reg() )->get() ? lim->operands[ 1 ] : lim->operands[ 2 ]; else if ( lim->base == &ins::jmp ) dst = lim->operands[ 0 ]; @@ -326,7 +324,7 @@ namespace vtil::optimizer::validation eit = rtn->explored_blocks.find( dst.imm().u64 ); // Otherwise read VM context. // - else if ( auto jmp_dst = vm.read_register( dst.reg() ).get() ) + else if ( auto jmp_dst = vm.read_register( dst.reg() )->get() ) eit = rtn->explored_blocks.find( *jmp_dst ); // If no valid destination, fail. diff --git a/VTIL-SymEx/directives/directive.cpp b/VTIL-SymEx/directives/directive.cpp index 31494d7..36f8b92 100644 --- a/VTIL-SymEx/directives/directive.cpp +++ b/VTIL-SymEx/directives/directive.cpp @@ -31,11 +31,13 @@ namespace vtil::symbolic::directive { // Constructor for directive representing the result of an unary operator. // - instance::instance( math::operator_id op, const instance& e1 ) : rhs( e1 ), op( op ) {} + instance::instance( math::operator_id op, const instance& e1 ) + : rhs( e1 ), op( op ) {} // Constructor for directive representing the result of a binary operator. // - instance::instance( const instance& e1, math::operator_id op, const instance& e2 ) : lhs( e1 ), rhs( e2 ), op( op ) {} + instance::instance( const instance& e1, math::operator_id op, const instance& e2 ) + : lhs( e1 ), rhs( e2 ), op( op ) {} // Enumerates each unique variable. // @@ -94,4 +96,25 @@ namespace vtil::symbolic::directive else if ( !o.lhs || !lhs->equals( *o.lhs ) ) return false; return true; } + + // Simple copyable unique pointer implementation. + // + instance::reference::reference( const instance& o ) : ptr( new instance( o ) ) {} + instance::reference::reference( instance&& o ) : ptr( new instance( std::move( o ) ) ) {} + instance::reference::reference( const reference& o ) : ptr( o ? new instance( *o ) : nullptr ) {} + instance::reference::reference( reference&& o ) : ptr( std::exchange( o.ptr, nullptr ) ) {} + instance::reference::~reference() + { + if ( ptr ) delete ptr; + } + instance::reference& instance::reference::operator=( instance::reference&& o ) + { + ptr = std::exchange( o.ptr, nullptr ); + return *this; + } + instance::reference& instance::reference::operator=( const instance::reference& o ) + { + ptr = o ? new instance( *o ) : nullptr; + return *this; + } }; \ No newline at end of file diff --git a/VTIL-SymEx/directives/directive.hpp b/VTIL-SymEx/directives/directive.hpp index eb2caf7..7aa940b 100644 --- a/VTIL-SymEx/directives/directive.hpp +++ b/VTIL-SymEx/directives/directive.hpp @@ -147,7 +147,42 @@ namespace vtil::symbolic::directive // struct instance : math::operable { - using reference = shared_reference; + // Simple copyable unique pointer implementation. + // + struct reference + { + instance* ptr = nullptr; + + // Construct by implicit null or instance value. + // + reference() {} + reference( const instance& i ); + reference( instance&& i ); + + // Copy / Move from another reference. + // + reference( const reference& o ); + reference( reference&& o ); + reference& operator=( reference&& o ); + reference& operator=( const reference& o ); + + // Destructor deletes the value. + // + ~reference(); + + // Null check. + // + explicit operator bool() const { return ptr; } + + // Pointer interface. + // + operator instance*() { return ptr; } + operator const instance*() const { return ptr; } + instance& operator*() { return *ptr; } + const instance& operator*() const { return *ptr; } + instance* operator->() { return ptr; } + const instance* operator->() const { return ptr; } + }; // If symbolic variable, the identifier of the variable // and type of expressions it can match. diff --git a/VTIL-SymEx/directives/fast_matcher.hpp b/VTIL-SymEx/directives/fast_matcher.hpp index 12380b4..0b49e39 100644 --- a/VTIL-SymEx/directives/fast_matcher.hpp +++ b/VTIL-SymEx/directives/fast_matcher.hpp @@ -40,7 +40,7 @@ namespace vtil::symbolic::directive // Adds the mapping of a variable to an expression. // - bool add( const instance::reference& dir, const expression::reference& exp ) + bool add( const instance* dir, const expression::reference& exp ) { // If it's the first time this variable is being used: // @@ -74,7 +74,7 @@ namespace vtil::symbolic::directive // Translates a variable to the matching expression. // - expression::reference translate( const instance::reference& dir ) const + expression::reference translate( const instance* dir ) const { // Assert the looked up type is variable. // @@ -83,7 +83,8 @@ namespace vtil::symbolic::directive // Translate using the lookup table. // return lookup_table[ dir->lookup_index ]; - } + } + expression::reference translate( const instance& dir ) const { return translate( &dir ); } }; // Tries to match the the given expression with the directive and fills the @@ -91,7 +92,7 @@ namespace vtil::symbolic::directive // template, int> = 0> static size_t fast_match( T* results, - const instance::reference& dir, + const instance* dir, const expression::reference& exp, size_t index = 0 ) { @@ -196,4 +197,6 @@ namespace vtil::symbolic::directive // return ( results->size() + 1 ) - size_0; } + template, int> = 0> + static size_t fast_match( T* results, const instance& dir, const expression::reference& exp, size_t index = 0 ) { return fast_match( results, &dir, exp, index ); } }; \ No newline at end of file diff --git a/VTIL-SymEx/directives/transformer.cpp b/VTIL-SymEx/directives/transformer.cpp index f4cd7fb..e811dc3 100644 --- a/VTIL-SymEx/directives/transformer.cpp +++ b/VTIL-SymEx/directives/transformer.cpp @@ -38,7 +38,7 @@ namespace vtil::symbolic // or a null reference if it would fail. // expression::reference translate( const symbol_table_t& sym, - const instance::reference& dir, + const instance* dir, bitcnt_t bit_cnt, bool speculative_condition, int64_t max_depth ) @@ -260,7 +260,7 @@ namespace vtil::symbolic // and returns the first instance that matches query. // expression::reference transform( const expression::reference& exp, - const instance::reference& from, const instance::reference& to, + const instance* from, const instance* to, const expression_filter_t& filter, int64_t max_depth ) { @@ -283,7 +283,7 @@ namespace vtil::symbolic // Log the translation. // log( "Translating [%s] => [%s]:\n", *from, *to ); - from->enum_variables( [ & ] ( const instance& ins ) + from->enum_variables( [ & ] ( const instance* ins ) { log( " %s: %s\n", ins.id, *match.translate( ins ) ); } ); @@ -343,7 +343,7 @@ namespace vtil::symbolic // Log the translation. // log( "Translating [%s] => [%s]:\n", *from, *to ); - from->enum_variables( [ & ] ( const instance& ins ) + from->enum_variables( [ & ] ( const instance* ins ) { log( " %s: %s\n", ins.id, *match.translate( ins ) ); } ); diff --git a/VTIL-SymEx/directives/transformer.hpp b/VTIL-SymEx/directives/transformer.hpp index 903ef53..5788034 100644 --- a/VTIL-SymEx/directives/transformer.hpp +++ b/VTIL-SymEx/directives/transformer.hpp @@ -40,7 +40,7 @@ namespace vtil::symbolic // or a null reference if it would fail. // expression::reference translate( const directive::symbol_table_t& sym, - const directive::instance::reference& dir, + const directive::instance* dir, bitcnt_t bit_cnt, bool speculative_condition, int64_t max_depth ); @@ -49,7 +49,7 @@ namespace vtil::symbolic // and returns the first instance that matches query. // expression::reference transform( const expression::reference& exp, - const directive::instance::reference& from, const directive::instance::reference& to, + const directive::instance* from, const directive::instance* to, const expression_filter_t& filter, int64_t max_depth ); }; \ No newline at end of file diff --git a/VTIL-SymEx/expressions/expression.cpp b/VTIL-SymEx/expressions/expression.cpp index c56b735..28a6cac 100644 --- a/VTIL-SymEx/expressions/expression.cpp +++ b/VTIL-SymEx/expressions/expression.cpp @@ -691,7 +691,7 @@ namespace vtil::symbolic // this way and additionally we avoid copying where an operand is being simplified // as that can be replaced by a simple swap of shared references. // - auto ref = make_local_reference( this ); + reference ref = make_local_reference( this ); simplify_expression( ref, prettify ); // Only thing that we should be careful about is the case expression->simplify(), @@ -869,4 +869,44 @@ namespace vtil::symbolic if ( is_variable() ) return uid.to_string(); return "null"; } + + // Implement some helpers to conditionally copy. + // + expression_reference& expression_reference::resize( bitcnt_t new_size, bool signed_cast, bool no_explicit ) + { + if ( new_size != get()->size() ) + own()->resize( new_size, signed_cast, no_explicit ); + return *this; + } + expression_reference expression_reference::resize( bitcnt_t new_size, bool signed_cast, bool no_explicit ) const + { + return expression_reference{ *this }.resize( new_size, signed_cast, no_explicit ); + } + expression_reference& expression_reference::simplify( bool prettify ) + { + if ( prettify || !get()->simplify_hint ) + own()->simplify( prettify ); + return *this; + } + expression_reference expression_reference::simplify( bool prettify ) const + { + return expression_reference{ *this }.simplify( prettify ); + } + expression_reference& expression_reference::make_lazy() + { + if ( !get()->is_lazy ) + own()->is_lazy = true; + return *this; + } + expression_reference expression_reference::make_lazy() const + { + return expression_reference{ *this }.make_lazy(); + } + + // Implemented for sinkhole-use. + // + bitcnt_t expression_reference::size() const + { + return get()->size(); + } }; diff --git a/VTIL-SymEx/simplifier/boolean_directives.cpp b/VTIL-SymEx/simplifier/boolean_directives.cpp index ebe75c6..dd0d068 100644 --- a/VTIL-SymEx/simplifier/boolean_directives.cpp +++ b/VTIL-SymEx/simplifier/boolean_directives.cpp @@ -29,7 +29,7 @@ namespace vtil::symbolic::directive { - std::vector> boolean_simplifiers + std::vector> boolean_simplifiers { // Manually added: // @@ -57,12 +57,12 @@ namespace vtil::symbolic::directive { A!=-A, 1 }, }; - const std::vector>& build_boolean_simplifiers() + const std::vector>& build_boolean_simplifiers() { if ( !boolean_simplifiers.empty() ) return boolean_simplifiers; #ifndef __INTELLISENSE__ -#define ADD_DIRECTIVE( ... ) ([ ] () { boolean_simplifiers.push_back( { __VA_ARGS__ } ); })() +#define ADD_DIRECTIVE( ... ) ([ ] () { boolean_simplifiers.emplace_back( __VA_ARGS__ ); })() // Boolean Simplifiers // ADD_DIRECTIVE( ((A>B)&(A>C)), __iff((B>C), (A>B)) ); @@ -2617,7 +2617,7 @@ namespace vtil::symbolic::directive constexpr auto overflow = [ ] ( auto a, auto b ) { return ((a<0)==(b<0))&((a<0)!=((a+b)<0)); }; constexpr auto underflow = [ ] ( auto a, auto b ) { return ((a<0)!=(b<0))&((a<0)!=((a-b)<0)); }; - const std::vector> boolean_joiners = + const std::vector> boolean_joiners = { // Manually added: // diff --git a/VTIL-SymEx/simplifier/boolean_directives.hpp b/VTIL-SymEx/simplifier/boolean_directives.hpp index be1abfa..a1f193b 100644 --- a/VTIL-SymEx/simplifier/boolean_directives.hpp +++ b/VTIL-SymEx/simplifier/boolean_directives.hpp @@ -32,10 +32,10 @@ namespace vtil::symbolic::directive { - extern std::vector> boolean_simplifiers; - extern const std::vector> boolean_joiners; + extern std::vector> boolean_simplifiers; + extern const std::vector> boolean_joiners; - const std::vector>& build_boolean_simplifiers(); + const std::vector>& build_boolean_simplifiers(); /* Auto generated using: diff --git a/VTIL-SymEx/simplifier/directives.hpp b/VTIL-SymEx/simplifier/directives.hpp index 699c7bf..217e30e 100644 --- a/VTIL-SymEx/simplifier/directives.hpp +++ b/VTIL-SymEx/simplifier/directives.hpp @@ -34,7 +34,7 @@ namespace vtil::symbolic::directive // List of universal simplifiers, they have to reduce complexity or keep it equal // at the very least to not cause an infinity loop. // - static const std::pair universal_simplifiers[] = + static const std::pair universal_simplifiers[] = { // TODO: Arithmetic operators, */% etc. // @@ -235,7 +235,7 @@ namespace vtil::symbolic::directive // Describes the way operands of two operators join each other. // - Has no obligation to produce simple output, should be checked. // - static const std::pair join_descriptors[] = + static const std::pair join_descriptors[] = { // TODO: Arithmetic operators, */% etc. // TODO: Should we add ADD and SUB to bitwise despite the partial evaluator? @@ -391,7 +391,7 @@ namespace vtil::symbolic::directive // Grouping of simple representations into more complex directives. // - static const std::pair pack_descriptors[] = + static const std::pair pack_descriptors[] = { { __ucast(A>>B, 0x1), __bt(A, B) }, { (A>>B)&1, __ucast(__bt(A,B),__bcnt(A)) }, @@ -413,7 +413,7 @@ namespace vtil::symbolic::directive // Conversion from more complex directives into simple representations. // - static const std::pair unpack_descriptors[] = + static const std::pair unpack_descriptors[] = { { __bt(A,B), __ucast((A&(1<>B,1) }, { __min(A,B), __if(A<=B,A)|__if(A>B,B) }, diff --git a/VTIL-SymEx/simplifier/simplifier.cpp b/VTIL-SymEx/simplifier/simplifier.cpp index fb54362..769fbf7 100644 --- a/VTIL-SymEx/simplifier/simplifier.cpp +++ b/VTIL-SymEx/simplifier/simplifier.cpp @@ -44,8 +44,8 @@ namespace vtil::symbolic // Implement lookup-table based dynamic tables. // - using static_directive_table_entry = std::pair; - using dynamic_directive_table_entry = std::pair; + using static_directive_table_entry = std::pair; + using dynamic_directive_table_entry = std::pair; using dynamic_directive_table = std::vector; using organized_directive_table = std::array; @@ -56,8 +56,8 @@ namespace vtil::symbolic organized_directive_table table; for ( auto [table, op] : zip( table, iindices() ) ) for( auto& directive : container ) - if ( directive.first->op == ( math::operator_id ) op ) - table.emplace_back( directive.first.get(), directive.second.get() ); + if ( directive.first.op == ( math::operator_id ) op ) + table.emplace_back( &directive.first, &directive.second ); return table; }; @@ -122,7 +122,7 @@ namespace vtil::symbolic { // If we can transform the expression by the directive set: // - if ( auto exp_new = transform( exp, *dir_src, *dir_dst, {}, -1 ) ) + if ( auto exp_new = transform( exp, dir_src, dir_dst, {}, -1 ) ) { #if VTIL_SYMEX_SIMPLIFY_VERBOSE log( "[Pack] %s => %s\n", *dir_src, *dir_dst ); @@ -141,7 +141,7 @@ namespace vtil::symbolic // Checks if the expression can be interpreted as a vector-boolean expression. // - static std::pair match_boolean_expression( const expression::reference& exp ) + static std::pair match_boolean_expression( const expression::reference& exp ) { switch ( exp->op ) { @@ -149,7 +149,7 @@ namespace vtil::symbolic // case math::operator_id::invalid: { - if ( exp->is_variable() ) return { true, &*exp }; + if ( exp->is_variable() ) return { true, exp }; else return { true, nullptr }; } @@ -170,11 +170,11 @@ namespace vtil::symbolic auto [m2, p2] = match_boolean_expression( exp->rhs ); if ( !m2 ) return { false, nullptr }; - if ( !p2 ) return { true, p1 }; - if ( !p1 ) return { true, p2 }; + if ( !p2 ) return { true, std::move( p1 ) }; + if ( !p1 ) return { true, std::move( p2 ) }; if ( p1->uid == p2->uid ) - return { true, p1 }; + return { true, std::move( p1 ) }; else return { false, nullptr }; } @@ -208,18 +208,18 @@ namespace vtil::symbolic // Apply each mask if not no-op. // - expression exp_new = uid_base->clone(); + expression::reference& exp_new = uid_base; if ( and_mask != ~0ull ) exp_new = exp_new & expression{ and_mask, exp->size() }; if ( xor_mask ) exp_new = exp_new ^ expression{ xor_mask, exp->size() }; if ( or_mask ) exp_new = exp_new | expression{ or_mask, exp->size() }; // If complexity was higher or equal, fail. // - if ( exp_new.complexity >= exp->complexity ) return false; + if ( exp_new->complexity >= exp->complexity ) return false; // Apply and return. // - *+exp = exp_new; + exp = std::move( exp_new ); return true; } @@ -304,7 +304,7 @@ namespace vtil::symbolic // Invoke resize with failure on explicit cast: // - ( +exp_new )->resize( new_size, exp->op == math::operator_id::cast, true ); + exp_new.resize( new_size, exp->op == math::operator_id::cast, true ); // If implicit resize failed: // @@ -330,7 +330,7 @@ namespace vtil::symbolic } } - ( +exp )->simplify_hint = true; + exp->simplify_hint = true; cache_entry = *exp; return success_flag; } @@ -342,7 +342,7 @@ namespace vtil::symbolic // Recurse, and indicate success. // simplify_expression( exp, pretty, max_depth - 1 ); - ( +exp )->simplify_hint = true; + exp->simplify_hint = true; cache_entry = *exp; success_flag = true; return true; @@ -374,7 +374,7 @@ namespace vtil::symbolic // Recurse, and indicate success. // simplify_expression( exp, pretty, max_depth - 1 ); - ( +exp )->simplify_hint = true; + exp->simplify_hint = true; cache_entry = *exp; success_flag = true; return true; @@ -462,7 +462,7 @@ namespace vtil::symbolic { // If we can transform the expression by the directive set: // - if ( auto exp_new = transform( exp, *dir_src, *dir_dst, {}, max_depth ) ) + if ( auto exp_new = transform( exp, dir_src, dir_dst, {}, max_depth ) ) { #if VTIL_SYMEX_SIMPLIFY_VERBOSE log( "[Simplify] %s => %s\n", *dir_src, *dir_dst ); @@ -471,7 +471,7 @@ namespace vtil::symbolic // Recurse, set the hint and return the simplified instance. // simplify_expression( exp_new, pretty, max_depth ); - ( +exp_new )->simplify_hint = true; + exp_new->simplify_hint = true; cache_entry = exp_new; success_flag = !exp->is_identical( *exp_new ); exp = exp_new; @@ -489,7 +489,7 @@ namespace vtil::symbolic { // If we can transform the expression by the directive set: // - if ( auto exp_new = transform( exp, *dir_src, *dir_dst, {}, max_depth ) ) + if ( auto exp_new = transform( exp, dir_src, dir_dst, {}, max_depth ) ) { #if VTIL_SYMEX_SIMPLIFY_VERBOSE log( "[Simplify] %s => %s\n", *dir_src, *dir_dst ); @@ -498,7 +498,7 @@ namespace vtil::symbolic // Recurse, set the hint and return the simplified instance. // simplify_expression( exp_new, pretty, max_depth ); - ( +exp_new )->simplify_hint = true; + exp_new->simplify_hint = true; cache_entry = exp_new; success_flag = !exp->is_identical( *exp_new ); exp = exp_new; @@ -513,7 +513,7 @@ namespace vtil::symbolic { // If we can transform the expression by the directive set: // - if ( auto exp_new = transform( exp, *dir_src, *dir_dst, filter, max_depth ) ) + if ( auto exp_new = transform( exp, dir_src, dir_dst, filter, max_depth ) ) { #if VTIL_SYMEX_SIMPLIFY_VERBOSE log( "[Join] %s => %s\n", *dir_src, *dir_dst ); @@ -523,7 +523,7 @@ namespace vtil::symbolic // Recurse, set the hint and return the simplified instance. // simplify_expression( exp_new, pretty, max_depth - 1 ); - ( +exp_new )->simplify_hint = true; + exp_new->simplify_hint = true; cache_entry = exp_new; success_flag = !exp->is_identical( *exp_new ); exp = exp_new; @@ -541,7 +541,7 @@ namespace vtil::symbolic { // If we can transform the expression by the directive set: // - if ( auto exp_new = transform( exp, *dir_src, *dir_dst, filter, max_depth ) ) + if ( auto exp_new = transform( exp, dir_src, dir_dst, filter, max_depth ) ) { #if VTIL_SYMEX_SIMPLIFY_VERBOSE log( "[Join] %s => %s\n", *dir_src, *dir_dst ); @@ -551,7 +551,7 @@ namespace vtil::symbolic // Recurse, set the hint and return the simplified instance. // simplify_expression( exp_new, pretty, max_depth - 1 ); - ( +exp_new )->simplify_hint = true; + exp_new->simplify_hint = true; cache_entry = exp_new; success_flag = !exp->is_identical( *exp_new ); exp = exp_new; @@ -570,7 +570,7 @@ namespace vtil::symbolic { // If we can transform the expression by the directive set: // - if ( auto exp_new = transform( exp, *dir_src, *dir_dst, + if ( auto exp_new = transform( exp, dir_src, dir_dst, [ & ] ( auto& exp_new ) { simplify_expression( exp_new, true, max_depth - 1 ); return exp_new->complexity < exp->complexity; }, max_depth ) ) { #if VTIL_SYMEX_SIMPLIFY_VERBOSE @@ -580,7 +580,7 @@ namespace vtil::symbolic // Set the hint and return the simplified instance. // - ( +exp_new )->simplify_hint = true; + exp_new->simplify_hint = true; cache_entry = exp_new; success_flag = !exp->is_identical( *exp_new ); exp = exp_new;