<commit_before>#!/usr/bin/env python

import copy
import functools
import itertools
#import weakref

import logging
l = logging.getLogger("simuvex.s_state")

def arch_overrideable(f):
    @functools.wraps(f)
    def wrapped_f(self, *args, **kwargs):
        if hasattr(self.arch, f.__name__):
            arch_f = getattr(self.arch, f.__name__)
            return arch_f(self, *args, **kwargs)
        else:
            return f(self, *args, **kwargs)
    return wrapped_f

from .plugins import default_plugins

# This is a counter for the state-merging symbolic variables
merge_counter = itertools.count()

class SimState(object): # pylint: disable=R0904
    '''The SimState represents the state of a program, including its memory, registers, and so forth.'''

    def __init__(self, solver_engine, temps=None, arch="AMD64", plugins=None, memory_backer=None, mode=None, options=None):
        # the architecture is used for function simulations (autorets) and the bitness
        self.arch = Architectures[arch]() if isinstance(arch, str) else arch
        self.abiv = None

        # the solving engine
        self._engine = solver_engine

        # VEX temps are temporary variables local to an IRSB
        self.temps = temps if temps is not None else { }

        # the options
        if options is None:
            if mode is None:
                l.warning("SimState defaulting to static mode.")
                mode = "static"
            options = set(o.default_options[mode])
        self.options = options
        self.mode = mode

        # plugins
        self.plugins = { }
        if plugins is not None:
            for n,p in plugins.iteritems():
                self.register_plugin(n, p)

        if not self.has_plugin('memory'):
            if o.ABSTRACT_MEMORY in self.options:
                # We use SimAbstractMemory in static mode
                self['memory'] = SimAbstractMemory(memory_backer, memory_id="mem")
            else:
                self['memory'] = SimSymbolicMemory(memory_backer, memory_id="mem")
        if not self.has_plugin('registers'):
            if o.ABSTRACT_MEMORY in self.options:
                self['registers'] = SimSymbolicMemory(memory_id="reg", uninitialized_read_callback=SimAbstractMemory.default_read)
            else:
                self['registers'] = SimSymbolicMemory(memory_id="reg")

        # the native environment for native execution
        self.native_env = None

    # accessors for memory and registers and such
    @property
    def memory(self):
        return self['memory']

    @property
    def registers(self):
        return self['registers']

    @property
    def se(self):
        return self['solver_engine']

    @property
    def inspect(self):
        return self['inspector']

    def _inspect(self, *args, **kwargs):
        if self.has_plugin('inspector'):
            self.inspect.action(*args, **kwargs)

    #
    # Plugins
    #

    def has_plugin(self, name):
        return name in self.plugins

    def get_plugin(self, name):
        if name not in self.plugins:
            p = default_plugins[name]()
            self.register_plugin(name, p)
            return p
        return self.plugins[name]

    # ok, ok
    def __getitem__(self, name): return self.get_plugin(name)
    def __setitem__(self, name, plugin): return self.register_plugin(name, plugin)

    def register_plugin(self, name, plugin):
        #l.debug("Adding plugin %s of type %s", name, plugin.__class__.__name__)
        plugin.set_state(self)
        self.plugins[name] = plugin

    def release_plugin(self, name):
        if name in self.plugins:
            del self.plugins[name]

    #
    # Constraint pass-throughs
    #

    def simplify(self, *args): return self.se.simplify(*args)

    def add_constraints(self, *args):
        if len(args) > 0 and type(args[0]) in (list, tuple):
            raise Exception("Tuple or list passed to add_constraints!")

        if o.TRACK_CONSTRAINTS in self.options and len(args) > 0:
            if o.SIMPLIFY_CONSTRAINTS in self.options:
                constraints = [ self.simplify(a) for a in args ]
            else:
                constraints = args

            self._inspect('constraints', BP_BEFORE, added_constraints=constraints)
            self.se.add(*constraints)
            self._inspect('constraints', BP_AFTER)

    def BV(self, name, size, explicit_name=None):
        size = self.arch.bits if size is None else size

        self._inspect('symbolic_variable', BP_BEFORE, symbolic_name=name, symbolic_size=size)
        v = self.se.BitVec(name, size, explicit_name=explicit_name)
        self._inspect('symbolic_variable', BP_AFTER, symbolic_expr=v)
        return v

    def BVV(self, value, size=None):
        if type(value) is str:
            v = 0
            for c in value:
                v = v << 8
                v += ord(c)
            size = len(value)*8
            value = v
        size = self.arch.bits if size is None else size
        return self.se.BitVecVal(value, size)

    def satisfiable(self):
        return self.se.satisfiable()

    def downsize(self):
        if 'solver_engine' in self.plugins:
            self.se.downsize()

    #
    # Memory helpers
    #

    # Helper function for loading from symbolic memory and tracking constraints
    def _do_load(self, simmem, addr, length, strategy=None, limit=None, condition=None, fallback=None):
        # do the load and track the constraints
        m,e = simmem.load(addr, length, strategy=strategy, limit=limit, condition=condition, fallback=fallback)
        self.add_constraints(*e)
        return m

    # Helper function for storing to symbolic memory and tracking constraints
    def _do_store(self, simmem, addr, content, symbolic_length=None, strategy=None, limit=None):
        # do the store and track the constraints
        e = simmem.store(addr, content, symbolic_length=symbolic_length, strategy=strategy, limit=limit)
        self.add_constraints(*e)
        return e

    #
    # State branching operations
    #

    # Returns a dict that is a copy of all the state's plugins
    def copy_plugins(self):
        return { n: p.copy() for n,p in self.plugins.iteritems() }

    def copy(self):
        '''
        Returns a copy of the state.
        '''

        c_temps = copy.copy(self.temps)
        c_arch = self.arch
        c_plugins = self.copy_plugins()
        state = SimState(self._engine, temps=c_temps, arch=c_arch, plugins=c_plugins, options=self.options, mode=self.mode)
        state.abiv = self.abiv
        return state

    # Merges this state with the other states. Returns the merged state and the merge flag.
    def merge(self, *others):<commit_msg>added add_options and remove_options for more granularity, changed default state to symbolic<commit_after>#!/usr/bin/env python

import functools
import itertools
#import weakref

import logging
l = logging.getLogger("simuvex.s_state")

def arch_overrideable(f):
    @functools.wraps(f)
    def wrapped_f(self, *args, **kwargs):
        if hasattr(self.arch, f.__name__):
            arch_f = getattr(self.arch, f.__name__)
            return arch_f(self, *args, **kwargs)
        else:
            return f(self, *args, **kwargs)
    return wrapped_f

from .plugins import default_plugins

# This is a counter for the state-merging symbolic variables
merge_counter = itertools.count()

class SimState(object): # pylint: disable=R0904
    '''The SimState represents the state of a program, including its memory, registers, and so forth.'''

    def __init__(self, temps=None, arch="AMD64", plugins=None, memory_backer=None, mode=None, options=None, add_options=None, remove_options=None):
        # the architecture is used for function simulations (autorets) and the bitness
        self.arch = Architectures[arch]() if isinstance(arch, str) else arch
        self.abiv = None

        # VEX temps are temporary variables local to an IRSB
        self.temps = temps if temps is not None else { }

        # the options
        if options is None:
            if mode is None:
                l.warning("SimState defaulting to symbolic mode.")
                mode = "symbolic"
            options = set(o.default_options[mode])
        if add_options is not None:
            options |= add_options
        if remove_options is not None:
            options -= remove_options
        self.options = options
        self.mode = mode

        # plugins
        self.plugins = { }
        if plugins is not None:
            for n,p in plugins.iteritems():
                self.register_plugin(n, p)

        if not self.has_plugin('memory'):
            if o.ABSTRACT_MEMORY in self.options:
                # We use SimAbstractMemory in static mode
                self['memory'] = SimAbstractMemory(memory_backer, memory_id="mem")
            else:
                self['memory'] = SimSymbolicMemory(memory_backer, memory_id="mem")
        if not self.has_plugin('registers'):
            if o.ABSTRACT_MEMORY in self.options:
                self['registers'] = SimSymbolicMemory(memory_id="reg", uninitialized_read_callback=SimAbstractMemory.default_read)
            else:
                self['registers'] = SimSymbolicMemory(memory_id="reg")

        # the native environment for native execution
        self.native_env = None

    # accessors for memory and registers and such
    @property
    def memory(self):
        return self['memory']

    @property
    def registers(self):
        return self['registers']

    @property
    def se(self):
        return self['solver_engine']

    @property
    def inspect(self):
        return self['inspector']

    def _inspect(self, *args, **kwargs):
        if self.has_plugin('inspector'):
            self.inspect.action(*args, **kwargs)

    #
    # Plugins
    #

    def has_plugin(self, name):
        return name in self.plugins

    def get_plugin(self, name):
        if name not in self.plugins:
            p = default_plugins[name]()
            self.register_plugin(name, p)
            return p
        return self.plugins[name]

    # ok, ok
    def __getitem__(self, name): return self.get_plugin(name)
    def __setitem__(self, name, plugin): return self.register_plugin(name, plugin)

    def register_plugin(self, name, plugin):
        #l.debug("Adding plugin %s of type %s", name, plugin.__class__.__name__)
        plugin.set_state(self)
        self.plugins[name] = plugin

    def release_plugin(self, name):
        if name in self.plugins:
            del self.plugins[name]

    #
    # Constraint pass-throughs
    #

    def simplify(self, *args): return self.se.simplify(*args)

    def add_constraints(self, *args):
        if len(args) > 0 and type(args[0]) in (list, tuple):
            raise Exception("Tuple or list passed to add_constraints!")

        if o.TRACK_CONSTRAINTS in self.options and len(args) > 0:
            if o.SIMPLIFY_CONSTRAINTS in self.options:
                constraints = [ self.simplify(a) for a in args ]
            else:
                constraints = args

            self._inspect('constraints', BP_BEFORE, added_constraints=constraints)
            self.se.add(*constraints)
            self._inspect('constraints', BP_AFTER)

    def BV(self, name, size, explicit_name=None):
        size = self.arch.bits if size is None else size

        self._inspect('symbolic_variable', BP_BEFORE, symbolic_name=name, symbolic_size=size)
        v = self.se.BitVec(name, size, explicit_name=explicit_name)
        self._inspect('symbolic_variable', BP_AFTER, symbolic_expr=v)
        return v

    def BVV(self, value, size=None):
        if type(value) is str:
            v = 0
            for c in value:
                v = v << 8
                v += ord(c)
            size = len(value)*8
            value = v
        size = self.arch.bits if size is None else size
        return self.se.BitVecVal(value, size)

    def satisfiable(self):
        return self.se.satisfiable()

    def downsize(self):
        if 'solver_engine' in self.plugins:
            self.se.downsize()

    #
    # Memory helpers
    #

    # Helper function for loading from symbolic memory and tracking constraints
    def _do_load(self, simmem, addr, length, strategy=None, limit=None, condition=None, fallback=None):
        # do the load and track the constraints
        m,e = simmem.load(addr, length, strategy=strategy, limit=limit, condition=condition, fallback=fallback)
        self.add_constraints(*e)
        return m

    # Helper function for storing to symbolic memory and tracking constraints
    def _do_store(self, simmem, addr, content, symbolic_length=None, strategy=None, limit=None):
        # do the store and track the constraints
        e = simmem.store(addr, content, symbolic_length=symbolic_length, strategy=strategy, limit=limit)
        self.add_constraints(*e)
        return e

    #
    # State branching operations
    #

    # Returns a dict that is a copy of all the state's plugins
    def copy_plugins(self):
        return { n: p.copy() for n,p in self.plugins.iteritems() }

    def copy(self):
        '''
        Returns a copy of the state.
        '''

        c_temps = dict(self.temps)
        c_arch = self.arch
        c_plugins = self.copy_plugins()
        state = SimState(temps=c_temps, arch=c_arch, plugins=c_plugins, options=self.options, mode=self.mode)
        state.abiv = self.abiv
        return state

    # Merges this state with the other states. Returns the merged state and the merge flag.
    def merge(self, *others):
{'fraction_non_alphanumeric': 0.09555310547592796, 'fraction_numerical': 0.0017640573318632856, 'mean_word_length': 2.4203117144293613, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7417309', 'n_tokens_mistral': 4038, 'n_tokens_neox': 3890, 'n_words': 1133}