diff --git a/VTIL-Architecture/symex/context.cpp b/VTIL-Architecture/symex/context.cpp index 2967ffd..8a68798 100644 --- a/VTIL-Architecture/symex/context.cpp +++ b/VTIL-Architecture/symex/context.cpp @@ -30,9 +30,9 @@ namespace vtil::symbolic { - // Checks if the symbolic context contains any writes to the given region described by the register desc. + // Returns the absolute mask of known/unknown bits of the given register. // - bool context::contains( const register_desc& desc ) const + uint64_t context::known_mask( const register_desc& desc ) const { // If identifier is not in the store, return false. // @@ -42,27 +42,35 @@ namespace vtil::symbolic // Enumerate each bit set within (size+offset, 0]: // - bool found = false; + uint64_t known_mask = 0; math::bit_enum( it->second.bitmap & math::fill( desc.bit_count + desc.bit_offset ), [ & ] ( bitcnt_t i ) { - // If value extends into the region, declare found. + // If value extends into the region, declare found, set known mask. // const expression::reference& value = it->second.linear_store[ i ]; if ( ( value.size() + i ) > desc.bit_offset ) - found = true; + known_mask |= math::fill( value.size(), i ); } ); - return found; + return known_mask & desc.get_mask(); + } + uint64_t context::unknown_mask( const register_desc& desc ) const + { + return desc.get_mask() & ~known_mask( desc ); } // Reads the value of the given region described by the register desc. + // - Will output the mask of bits contained in the state into contains. // - expression::reference context::read( const register_desc& desc, const il_const_iterator& reference_iterator ) const + expression::reference context::read( const register_desc& desc, const il_const_iterator& reference_iterator, uint64_t* contains ) const { + uint64_t tmp; + if ( !contains ) contains = &tmp; + // If identifier is not in the store, return default. // auto it = value_map.find( desc ); if ( it == value_map.end() ) - return CTX( reference_iterator )[ desc ]; + return *contains = 0, CTX( reference_iterator )[ desc ]; // Allocate storage for result and create masks. // @@ -99,7 +107,8 @@ namespace vtil::symbolic // If no bits set in known mask, return default. // - if ( !known_mask ) + *contains = known_mask & read_mask; + if ( !*contains ) return CTX( reference_iterator )[ desc ]; // If all bits set in known mask, return as is. diff --git a/VTIL-Architecture/symex/context.hpp b/VTIL-Architecture/symex/context.hpp index 2433f52..b2c3d99 100644 --- a/VTIL-Architecture/symex/context.hpp +++ b/VTIL-Architecture/symex/context.hpp @@ -65,13 +65,15 @@ namespace vtil::symbolic size_t size() const { return value_map.size(); } void reset() { value_map.clear(); } - // Checks if the symbolic context contains any writes to the given region described by the register desc. + // Returns the absolute mask of known/unknown bits of the given register. // - bool contains( const register_desc& desc ) const; + uint64_t known_mask( const register_desc& desc ) const; + uint64_t unknown_mask( const register_desc& desc ) const; // Reads the value of the given region described by the register desc. + // - Will output the mask of bits contained in the state into contains. // - expression::reference read( const register_desc& desc, const il_const_iterator& reference_iterator = symbolic::free_form_iterator ) const; + expression::reference read( const register_desc& desc, const il_const_iterator& reference_iterator = symbolic::free_form_iterator, uint64_t* contains = nullptr ) const; // Writes the given value to the region described by the register desc. // diff --git a/VTIL-Architecture/symex/memory.cpp b/VTIL-Architecture/symex/memory.cpp index bf569f5..9080320 100644 --- a/VTIL-Architecture/symex/memory.cpp +++ b/VTIL-Architecture/symex/memory.cpp @@ -30,15 +30,22 @@ namespace vtil::symbolic { - // Checks if the symbolic memory contains any writes to the given memory region. + // Returns the mask of known/unknown bits of the given region, if alias failure occurs returns nullopt. // - trilean memory::contains( const pointer& ptr, bitcnt_t size ) const + std::optional memory::known_mask( const pointer& ptr, bitcnt_t size ) const { - uint64_t mask_value = math::fill( size ); + if ( auto value = unknown_mask( ptr, size ) ) + return math::fill( size ) & ~*value; + else + return std::nullopt; + } + std::optional memory::unknown_mask( const pointer& ptr, bitcnt_t size ) const + { + uint64_t mask_pending = math::fill( size ); // For each entry, iterating backwards: // - for ( auto it = value_map.rbegin(); it != value_map.rend(); it++ ) + for ( auto it = value_map.rbegin(); it != value_map.rend() && mask_pending; it++ ) { // If pointer cannot overlap lookup, skip. // @@ -49,25 +56,25 @@ namespace vtil::symbolic // std::optional byte_distance = it->first - ptr; if ( !byte_distance ) - return trilean::unknown; + return std::nullopt; - // Calculate relative mask, return true if overlapping. + // Calculate relative mask, clear pending mask. // - bitcnt_t bit_distance = math::narrow_cast< bitcnt_t >( *byte_distance * 8 ); + bitcnt_t bit_distance = math::narrow_cast( *byte_distance * 8 ); uint64_t relative_mask = math::fill( it->second.size(), bit_distance ); - if ( relative_mask & mask_value ) - return true; + mask_pending &= ~relative_mask; } - - // None found, return false. - // - return false; + return mask_pending; } // Reads N bits from the given pointer, returns null reference if alias failure occurs. + // - Will output the mask of bits contained in the state into contains if it does not fail. // - expression::reference memory::read( const pointer& ptr, bitcnt_t size, const il_const_iterator& reference_iterator ) const + expression::reference memory::read( const pointer& ptr, bitcnt_t size, const il_const_iterator& reference_iterator, uint64_t* contains ) const { + uint64_t tmp; + if ( !contains ) contains = &tmp; + uint64_t mask_pending = math::fill( size ); stack_vector, 8> merge_list; @@ -111,7 +118,8 @@ namespace vtil::symbolic // If no overlapping keys found, return default. // - if ( merge_list.empty() ) + *contains = math::fill( size ) & ~mask_pending; + if ( !*contains ) return MEMORY( reference_iterator )( ptr, size ); // Declare common bit selector. diff --git a/VTIL-Architecture/symex/memory.hpp b/VTIL-Architecture/symex/memory.hpp index 2e815e8..a17ee9d 100644 --- a/VTIL-Architecture/symex/memory.hpp +++ b/VTIL-Architecture/symex/memory.hpp @@ -67,13 +67,15 @@ namespace vtil::symbolic size_t size() const { return value_map.size(); } void reset() { value_map.clear(); } - // Checks if the symbolic memory contains any writes to the given memory region. - // - trilean contains( const pointer& ptr, bitcnt_t size ) const; + // Returns the mask of known/unknown bits of the given region, if alias failure occurs returns nullopt. + // + std::optional known_mask( const pointer& ptr, bitcnt_t size ) const; + std::optional unknown_mask( const pointer& ptr, bitcnt_t size ) const; // Reads N bits from the given pointer, returns null reference if alias failure occurs. + // - Will output the mask of bits contained in the state into contains if it does not fail. // - expression::reference read( const pointer& ptr, bitcnt_t size, const il_const_iterator& reference_iterator = symbolic::free_form_iterator ) const; + expression::reference read( const pointer& ptr, bitcnt_t size, const il_const_iterator& reference_iterator = symbolic::free_form_iterator, uint64_t* contains = nullptr ) const; // Writes the given value to the pointer, returns null reference if alias failure occurs. // diff --git a/VTIL-Architecture/vm/symbolic.hpp b/VTIL-Architecture/vm/symbolic.hpp index e5e145f..b8f083d 100644 --- a/VTIL-Architecture/vm/symbolic.hpp +++ b/VTIL-Architecture/vm/symbolic.hpp @@ -66,7 +66,7 @@ namespace vtil // symbolic::expression::reference read_memory( const symbolic::expression::reference& pointer, size_t byte_count ) override { - return memory_state.read( pointer, math::narrow_cast< bitcnt_t >( byte_count * 8 ), reference_iterator ); + return memory_state.read( pointer, math::narrow_cast( byte_count * 8 ), reference_iterator ); } // Writes the given expression to the memory.