def _interp_total_size(interp):
        """
        Compute total load size of interpreter.

        :param ELFFile interp: interpreter ELF .so
        :return: total load size of interpreter, not aligned
        :rtype: int
        """
        load_segs = [x for x in interp.iter_segments() if x.header.p_type == 'PT_LOAD']
        last = load_segs[-1]
        return last.header.p_vaddr + last.header.p_memsz

def _interp_total_size ( interp ) : load_segs = [ x for x in interp . iter_segments ( ) if x . header . p_type == 'PT_LOAD' ] last = load_segs [ - 1 ] return last . header . p_vaddr + last . header . p_memsz

Compute total load size of interpreter .


def sys_openat(self, dirfd, buf, flags, mode):
        """
        A version of openat that includes a symbolic path and symbolic directory file descriptor

        :param dirfd: directory file descriptor
        :param buf: address of zero-terminated pathname
        :param flags: file access bits
        :param mode: file permission mode
        """

        if issymbolic(dirfd):
            logger.debug("Ask to read from a symbolic directory file descriptor!!")
            # Constrain to a valid fd and one past the end of fds
            self.constraints.add(dirfd >= 0)
            self.constraints.add(dirfd <= len(self.files))
            raise ConcretizeArgument(self, 0)

        if issymbolic(buf):
            logger.debug("Ask to read to a symbolic buffer")
            raise ConcretizeArgument(self, 1)

        return super().sys_openat(dirfd, buf, flags, mode)


def sys_allocate(self, cpu, length, isX, addr):
        """ allocate - allocate virtual memory

           The  allocate  system call creates a new allocation in the virtual address
           space of the calling process.  The length argument specifies the length of
           the allocation in bytes which will be rounded up to the hardware page size.

           The kernel chooses the address at which to create the allocation; the
           address of the new allocation is returned in *addr as the result of the call.

           All newly allocated memory is readable and writeable. In addition, the
           is_X argument is a boolean that allows newly allocated memory to be marked
           as executable (non-zero) or non-executable (zero).

           The allocate function is invoked through system call number 5.

           :param cpu: current CPU
           :param length: the length of the allocation in bytes
           :param isX: boolean that allows newly allocated memory to be marked as executable
           :param addr: the address of the new allocation is returned in *addr

           :return: On success, allocate returns zero and a pointer to the allocated area
                               is returned in *addr.  Otherwise, an error code is returned
                               and *addr is undefined.
                   EINVAL   length is zero.
                   EINVAL   length is too large.
                   EFAULT   addr points to an invalid address.
                   ENOMEM   No memory is available or the process' maximum number of allocations
                            would have been exceeded.
        """
        # TODO: check 4 bytes from addr
        if addr not in cpu.memory:
            logger.info("ALLOCATE: addr points to invalid address. Returning EFAULT")
            return Decree.CGC_EFAULT

        perms = ['rw ', 'rwx'][bool(isX)]
        try:
            result = cpu.memory.mmap(None, length, perms)
        except Exception as e:
            logger.info("ALLOCATE exception %s. Returning ENOMEM %r", str(e), length)
            return Decree.CGC_ENOMEM
        cpu.write_int(addr, result, 32)
        logger.info("ALLOCATE(%d, %s, 0x%08x) -> 0x%08x" % (length, perms, addr, result))
        self.syscall_trace.append(("_allocate", -1, length))
        return 0


def sys_random(self, cpu, buf, count, rnd_bytes):
        """ random - fill a buffer with random data

           The  random  system call populates the buffer referenced by buf with up to
           count bytes of random data. If count is zero, random returns 0 and optionally
           sets *rx_bytes to zero. If count is greater than SSIZE_MAX, the result is unspecified.

           :param cpu: current CPU
           :param buf: a memory buffer
           :param count: max number of bytes to receive
           :param rnd_bytes: if valid, points to the actual number of random bytes

           :return:  0        On success
                     EINVAL   count is invalid.
                     EFAULT   buf or rnd_bytes points to an invalid address.
        """

        ret = 0
        if count != 0:
            if count > Decree.CGC_SSIZE_MAX or count < 0:
                ret = Decree.CGC_EINVAL
            else:
                # TODO check count bytes from buf
                if buf not in cpu.memory or (buf + count) not in cpu.memory:
                    logger.info("RANDOM: buf points to invalid address. Returning EFAULT")
                    return Decree.CGC_EFAULT

                with open("/dev/urandom", "rb") as f:
                    data = f.read(count)

                self.syscall_trace.append(("_random", -1, data))
                cpu.write_bytes(buf, data)

        # TODO check 4 bytes from rx_bytes
        if rnd_bytes:
            if rnd_bytes not in cpu.memory:
                logger.info("RANDOM: Not valid rnd_bytes. Returning EFAULT")
                return Decree.CGC_EFAULT
            cpu.write_int(rnd_bytes, len(data), 32)

        logger.info("RANDOM(0x%08x, %d, 0x%08x) -> <%s>)" % (buf, count, rnd_bytes, repr(data[:10])))
        return ret


def sys_receive(self, cpu, fd, buf, count, rx_bytes):
        """ receive - receive bytes from a file descriptor

            The receive system call reads up to count bytes from file descriptor fd to the
            buffer pointed to by buf. If count is zero, receive returns 0 and optionally
            sets *rx_bytes to zero.

            :param cpu: current CPU.
            :param fd: a valid file descriptor
            :param buf: a memory buffer
            :param count: max number of bytes to receive
            :param rx_bytes: if valid, points to the actual number of bytes received
            :return: 0            Success
                     EBADF        fd is not a valid file descriptor or is not open
                     EFAULT       buf or rx_bytes points to an invalid address.
        """
        data = ''
        if count != 0:
            if not self._is_open(fd):
                logger.info("RECEIVE: Not valid file descriptor on receive. Returning EBADF")
                return Decree.CGC_EBADF

            # TODO check count bytes from buf
            if buf not in cpu.memory:  # or not  buf+count in cpu.memory:
                logger.info("RECEIVE: buf points to invalid address. Returning EFAULT")
                return Decree.CGC_EFAULT

            #import random
            #count = random.randint(1,count)
            if fd > 2 and self.files[fd].is_empty():
                cpu.PC -= cpu.instruction.size
                self.wait([fd], [], None)
                raise RestartSyscall()

            # get some potential delay
            # if random.randint(5) == 0 and count > 1:
            #    count = count/2

            # Read the data and put it in memory
            data = self.files[fd].receive(count)
            self.syscall_trace.append(("_receive", fd, data))
            cpu.write_bytes(buf, data)

            self.signal_receive(fd)

        # TODO check 4 bytes from rx_bytes
        if rx_bytes:
            if rx_bytes not in cpu.memory:
                logger.info("RECEIVE: Not valid file descriptor on receive. Returning EFAULT")
                return Decree.CGC_EFAULT
            cpu.write_int(rx_bytes, len(data), 32)

        logger.info("RECEIVE(%d, 0x%08x, %d, 0x%08x) -> <%s> (size:%d)" % (fd, buf, count, rx_bytes, repr(data)[:min(count, 10)], len(data)))
        return 0


def sys_deallocate(self, cpu, addr, size):
        """ deallocate - remove allocations
        The  deallocate  system call deletes the allocations for the specified
        address range, and causes further references to the addresses within the
        range to generate invalid memory accesses. The region is also
        automatically deallocated when the process is terminated.

        The address addr must be a multiple of the page size.  The length parameter
        specifies the size of the region to be deallocated in bytes.  All pages
        containing a part of the indicated range are deallocated, and subsequent
        references will terminate the process.  It is not an error if the indicated
        range does not contain any allocated pages.

        The deallocate function is invoked through system call number 6.

        :param cpu: current CPU
        :param addr: the starting address to unmap.
        :param size: the size of the portion to unmap.
        :return 0        On success
                EINVAL   addr is not page aligned.
                EINVAL   length is zero.
                EINVAL   any  part  of  the  region  being  deallocated  is outside the valid
                         address range of the process.

        :param cpu: current CPU.
        :return: C{0} on success.
        """
        logger.info("DEALLOCATE(0x%08x, %d)" % (addr, size))

        if addr & 0xfff != 0:
            logger.info("DEALLOCATE: addr is not page aligned")
            return Decree.CGC_EINVAL
        if size == 0:
            logger.info("DEALLOCATE:length is zero")
            return Decree.CGC_EINVAL
        # unlikely AND WRONG!!!
        # if addr > Decree.CGC_SSIZE_MAX or addr+size > Decree.CGC_SSIZE_MAX:
        #    logger.info("DEALLOCATE: part of the region being deallocated is outside the valid address range of the process")
        #    return Decree.CGC_EINVAL

        cpu.memory.munmap(addr, size)
        self.syscall_trace.append(("_deallocate", -1, size))
        return 0

def
sys_deallocate
(
self
,
cpu
,
addr
,
size
)
:
logger
.
info
(
"DEALLOCATE(0x%08x, %d)"
%
(
addr
,
size
)
)
if
addr
&
0xfff
!=
0
:
logger
.
info
(
"DEALLOCATE: addr is not page aligned"
)
return
Decree
.
CGC_EINVAL
if
size
==
0
:
logger
.
info
(
"DEALLOCATE:length is zero"
)
return
Decree
.
CGC_EINVAL
cpu
.
memory
.
munmap
(
addr
,
size
)
self
.
syscall_trace
.
append
(
(
"_deallocate"
,
1
,
size
)
)
return
0

deallocate - remove allocations The deallocate system call deletes the allocations for the specified address range and causes further references to the addresses within the range to generate invalid memory accesses . The region is also automatically deallocated when the process is terminated .


def sched(self):
        """ Yield CPU.
            This will choose another process from the RUNNNIG list and change
            current running process. May give the same cpu if only one running
            process.
        """
        if len(self.procs) > 1:
            logger.info("SCHED:")
            logger.info("\tProcess: %r", self.procs)
            logger.info("\tRunning: %r", self.running)
            logger.info("\tRWait: %r", self.rwait)
            logger.info("\tTWait: %r", self.twait)
            logger.info("\tTimers: %r", self.timers)
            logger.info("\tCurrent clock: %d", self.clocks)
            logger.info("\tCurrent cpu: %d", self._current)

        if len(self.running) == 0:
            logger.info("None running checking if there is some process waiting for a timeout")
            if all([x is None for x in self.timers]):
                raise Deadlock()
            self.clocks = min([x for x in self.timers if x is not None]) + 1
            self.check_timers()
            assert len(self.running) != 0, "DEADLOCK!"
            self._current = self.running[0]
            return
        next_index = (self.running.index(self._current) + 1) % len(self.running)
        next = self.running[next_index]
        if len(self.procs) > 1:
            logger.info("\tTransfer control from process %d to %d", self._current, next)
        self._current = next

Yield CPU . This will choose another process from the RUNNNIG list and change current running process . May give the same cpu if only one running process .


def wait(self, readfds, writefds, timeout):
        """ Wait for filedescriptors or timeout.
            Adds the current process to the corresponding waiting list and
            yields the cpu to another running process.
        """
        logger.info("WAIT:")
        logger.info("\tProcess %d is going to wait for [ %r %r %r ]", self._current, readfds, writefds, timeout)
        logger.info("\tProcess: %r", self.procs)
        logger.info("\tRunning: %r", self.running)
        logger.info("\tRWait: %r", self.rwait)
        logger.info("\tTWait: %r", self.twait)
        logger.info("\tTimers: %r", self.timers)

        for fd in readfds:
            self.rwait[fd].add(self._current)
        for fd in writefds:
            self.twait[fd].add(self._current)
        if timeout is not None:
            self.timers[self._current] = self.clocks + timeout
        else:
            self.timers[self._current] = None
        procid = self._current
        # self.sched()
        next_index = (self.running.index(procid) + 1) % len(self.running)
        self._current = self.running[next_index]
        logger.info("\tTransfer control from process %d to %d", procid, self._current)
        logger.info("\tREMOVING %r from %r. Current: %r", procid, self.running, self._current)
        self.running.remove(procid)
        if self._current not in self.running:
            logger.info("\tCurrent not running. Checking for timers...")
            self._current = None
            if all([x is None for x in self.timers]):
                raise Deadlock()
            self.check_timers()

Wait for filedescriptors or timeout . Adds the current process to the corresponding waiting list and yields the cpu to another running process .


def sys_receive(self, cpu, fd, buf, count, rx_bytes):
        """
        Symbolic version of Decree.sys_receive
        """
        if issymbolic(fd):
            logger.info("Ask to read from a symbolic file descriptor!!")
            cpu.PC = cpu.PC - cpu.instruction.size
            raise SymbolicSyscallArgument(cpu, 0)

        if issymbolic(buf):
            logger.info("Ask to read to a symbolic buffer")
            cpu.PC = cpu.PC - cpu.instruction.size
            raise SymbolicSyscallArgument(cpu, 1)

        if issymbolic(count):
            logger.info("Ask to read a symbolic number of bytes ")
            cpu.PC = cpu.PC - cpu.instruction.size
            raise SymbolicSyscallArgument(cpu, 2)

        if issymbolic(rx_bytes):
            logger.info("Ask to return size to a symbolic address ")
            cpu.PC = cpu.PC - cpu.instruction.size
            raise SymbolicSyscallArgument(cpu, 3)

        return super().sys_receive(cpu, fd, buf, count, rx_bytes)

Symbolic version of Decree . sys_receive


def sys_transmit(self, cpu, fd, buf, count, tx_bytes):
        """
        Symbolic version of Decree.sys_transmit
        """
        if issymbolic(fd):
            logger.info("Ask to write to a symbolic file descriptor!!")
            cpu.PC = cpu.PC - cpu.instruction.size
            raise SymbolicSyscallArgument(cpu, 0)

        if issymbolic(buf):
            logger.info("Ask to write to a symbolic buffer")
            cpu.PC = cpu.PC - cpu.instruction.size
            raise SymbolicSyscallArgument(cpu, 1)

        if issymbolic(count):
            logger.info("Ask to write a symbolic number of bytes ")
            cpu.PC = cpu.PC - cpu.instruction.size
            raise SymbolicSyscallArgument(cpu, 2)

        if issymbolic(tx_bytes):
            logger.info("Ask to return size to a symbolic address ")
            cpu.PC = cpu.PC - cpu.instruction.size
            raise SymbolicSyscallArgument(cpu, 3)

        return super().sys_transmit(cpu, fd, buf, count, tx_bytes)

Symbolic version of Decree . sys_transmit


def sync(f):
    """ Synchronization decorator. """

    def new_function(self, *args, **kw):
        self._lock.acquire()
        try:
            return f(self, *args, **kw)
        finally:
            self._lock.release()
    return new_function

def sync ( f ) : def new_function ( self , * args , ** kw ) : self . _lock . acquire ( ) try : return f ( self , * args , ** kw ) finally : self . _lock . release ( ) return new_function


def save_value(self, key, value):
        """
        Save an arbitrary, serializable `value` under `key`.

        :param str key: A string identifier under which to store the value.
        :param value: A serializable value
        :return:
        """
        with self.save_stream(key) as s:
            s.write(value)

def save_value ( self , key , value ) : with self . save_stream ( key ) as s : s . write ( value )

Save an arbitrary serializable value under key .


def load_value(self, key, binary=False):
        """
        Load an arbitrary value identified by `key`.

        :param str key: The key that identifies the value
        :return: The loaded value
        """
        with self.load_stream(key, binary=binary) as s:
            return s.read()

def load_value ( self , key , binary = False ) : with self . load_stream ( key , binary = binary ) as s : return s . read ( )

Load an arbitrary value identified by key .


def save_stream(self, key, binary=False):
        """
        Return a managed file-like object into which the calling code can write
        arbitrary data.

        :param key:
        :return: A managed stream-like object
        """
        s = io.BytesIO() if binary else io.StringIO()
        yield s
        self.save_value(key, s.getvalue())

def save_stream ( self , key , binary = False ) : s = io . BytesIO ( ) if binary else io . StringIO ( ) yield s self . save_value ( key , s . getvalue ( ) )

Return a managed file - like object into which the calling code can write arbitrary data .


def load_stream(self, key, binary=False):
        """
        Return a managed file-like object from which the calling code can read
        previously-serialized data.

        :param key:
        :return: A managed stream-like object
        """
        value = self.load_value(key, binary=binary)
        yield io.BytesIO(value) if binary else io.StringIO(value)

def load_stream ( self , key , binary = False ) : value = self . load_value ( key , binary = binary ) yield io . BytesIO ( value ) if binary else io . StringIO ( value )

Return a managed file - like object from which the calling code can read previously - serialized data .


def save_state(self, state, key):
        """
        Save a state to storage.

        :param manticore.core.StateBase state:
        :param str key:
        :return:
        """
        with self.save_stream(key, binary=True) as f:
            self._serializer.serialize(state, f)

def save_state ( self , state , key ) : with self . save_stream ( key , binary = True ) as f : self . _serializer . serialize ( state , f )


def load_state(self, key, delete=True):
        """
        Load a state from storage.

        :param key: key that identifies state
        :rtype: manticore.core.StateBase
        """
        with self.load_stream(key, binary=True) as f:
            state = self._serializer.deserialize(f)
            if delete:
                self.rm(key)
            return state

def load_state ( self , key , delete = True ) : with self . load_stream ( key , binary = True ) as f : state = self . _serializer . deserialize ( f ) if delete : self . rm ( key ) return state


def save_stream(self, key, binary=False):
        """
        Yield a file object representing `key`

        :param str key: The file to save to
        :param bool binary: Whether we should treat it as binary
        :return:
        """
        mode = 'wb' if binary else 'w'
        with open(os.path.join(self.uri, key), mode) as f:
            yield f

def save_stream ( self , key , binary = False ) : mode = 'wb' if binary else 'w' with open ( os . path . join ( self . uri , key ) , mode ) as f : yield f


def rm(self, key):
        """
        Remove file identified by `key`.

        :param str key: The file to delete
        """
        path = os.path.join(self.uri, key)
        os.remove(path)

def rm ( self , key ) : path = os . path . join ( self . uri , key ) os . remove ( path )


def ls(self, glob_str):
        """
        Return just the filenames that match `glob_str` inside the store directory.

        :param str glob_str: A glob string, i.e. 'state_*'
        :return: list of matched keys
        """
        path = os.path.join(self.uri, glob_str)
        return [os.path.split(s)[1] for s in glob.glob(path)]

def ls ( self , glob_str ) : path = os . path . join ( self . uri , glob_str ) return [ os . path . split ( s ) [ 1 ] for s in glob . glob ( path ) ]

Return just the filenames that match glob_str inside the store directory .


def _get_id(self):
        """
        Get a unique state id.

        :rtype: int
        """
        id_ = self._last_id.value
        self._last_id.value += 1
        return id_

def _get_id ( self ) : id_ = self . _last_id . value self . _last_id . value += 1 return id_

Get a unique state id.

        :rtype: int


def load_state(self, state_id, delete=True):
        """
        Load a state from storage identified by `state_id`.

        :param state_id: The state reference of what to load
        :return: The deserialized state
        :rtype: State
        """
        return self._store.load_state(f'{self._prefix}{state_id:08x}{self._suffix}', delete=delete)

Load a state from storage identified by state_id .


def save_state(self, state, state_id=None):
        """
        Save a state to storage, return identifier.

        :param state: The state to save
        :param int state_id: If not None force the state id potentially overwriting old states
        :return: New state id
        :rtype: int
        """
        assert isinstance(state, StateBase)
        if state_id is None:
            state_id = self._get_id()
        else:
            self.rm_state(state_id)

        self._store.save_state(state, f'{self._prefix}{state_id:08x}{self._suffix}')
        return state_id

Save a state to storage return identifier .


def _named_stream(self, name, binary=False):
        """
        Create an indexed output stream i.e. 'test_00000001.name'

        :param name: Identifier for the stream
        :return: A context-managed stream-like object
        """
        with self._store.save_stream(self._named_key(name), binary=binary) as s:
            yield s

def _named_stream ( self , name , binary = False ) : with self . _store . save_stream ( self . _named_key ( name ) , binary = binary ) as s : yield s

Create an indexed output stream i . e . test_00000001 . name


def cmp_regs(cpu, should_print=False):
    """
    Compare registers from a remote gdb session to current mcore.

    :param manticore.core.cpu Cpu: Current cpu
    :param bool should_print: Whether to print values to stdout
    :return: Whether or not any differences were detected
    :rtype: bool
    """
    differing = False
    gdb_regs = gdb.getCanonicalRegisters()
    for name in sorted(gdb_regs):
        vg = gdb_regs[name]
        if name.endswith('psr'):
            name = 'apsr'
        v = cpu.read_register(name.upper())
        if should_print:
            logger.debug(f'{name} gdb:{vg:x} mcore:{v:x}')
        if vg != v:
            if should_print:
                logger.warning('^^ unequal')
            differing = True
    if differing:
        logger.debug(qemu.correspond(None))
    return differing

def
cmp_regs
(
cpu
,
should_print
=
False
)
:
differing
=
False
gdb_regs
=
gdb
.
getCanonicalRegisters
(
)
for
name
in
sorted
(
gdb_regs
)
:
vg
=
gdb_regs
[
name
]
if
name
.
endswith
(
'psr'
)
:
name
=
'apsr'
v
=
cpu
.
read_register
(
name
.
upper
(
)
)
if
should_print
:
logger
.
debug
(
f'{name} gdb:{vg:x} mcore:{v:x}'
)
if
vg
!=
v
:
if
should_print
:
logger
.
warning
(
'^^ unequal'
)
differing
=
True
if
differing
:
logger
.
debug
(
qemu
.
correspond
(
)
)
return
differing

Compare registers from a remote gdb session to current mcore .


def sync_svc(state):
    """
    Mirror some service calls in manticore. Happens after qemu executed a SVC
    instruction, but before manticore did.
    """
    syscall = state.cpu.R7 # Grab idx from manticore since qemu could have exited
    name = linux_syscalls.armv7[syscall]

    logger.debug(f"Syncing syscall: {name}")

    try:
        # Make sure mmap returns the same address
        if 'mmap' in name:
            returned = gdb.getR('R0')
            logger.debug(f"Syncing mmap ({returned:x})")
            state.cpu.write_register('R0', returned)
        if 'exit' in name:
            return
    except ValueError:
        for reg in state.cpu.canonical_registers:
            print(f'{reg}: {state.cpu.read_register(reg):x}')
        raise

Mirror some service calls in manticore . Happens after qemu executed a SVC instruction but before manticore did .


def visit(self, node, use_fixed_point=False):
        """
        The entry point of the visitor.
        The exploration algorithm is a DFS post-order traversal
        The implementation used two stacks instead of a recursion
        The final result is store in self.result

        :param node: Node to explore
        :type node: Expression
        :param use_fixed_point: if True, it runs _methods until a fixed point is found
        :type use_fixed_point: Bool
        """
        cache = self._cache
        visited = set()
        stack = []
        stack.append(node)
        while stack:
            node = stack.pop()
            if node in cache:
                self.push(cache[node])
            elif isinstance(node, Operation):
                if node in visited:
                    operands = [self.pop() for _ in range(len(node.operands))]
                    value = self._method(node, *operands)

                    visited.remove(node)
                    self.push(value)
                    cache[node] = value
                else:
                    visited.add(node)
                    stack.append(node)
                    stack.extend(node.operands)
            else:
                self.push(self._method(node))

        if use_fixed_point:
            old_value = None
            new_value = self.pop()
            while old_value is not new_value:
                self.visit(new_value)
                old_value = new_value
                new_value = self.pop()
            self.push(new_value)

def visit ( self , node , use_fixed_point = False ) : cache = self . _cache visited = set ( ) stack = [ ] stack . append ( node ) while stack : node = stack . pop ( ) if node in cache : self . push ( cache [ node ] ) elif isinstance ( node , Operation ) : if node in visited : operands = [ self . pop ( ) for _ in range ( len ( node . operands ) ) ] value = self . _method ( node , * operands ) visited . remove ( node ) self . push ( value ) cache [ node ] = value else : visited . add ( node ) stack . append ( node ) stack . extend ( node . operands ) else : self . push ( self . _method ( node ) ) if use_fixed_point : old_value = None new_value = self . pop ( ) while old_value is not new_value : self . visit ( new_value ) old_value = new_value new_value = self . pop ( ) self . push ( new_value )

The entry point of the visitor . The exploration algorithm is a DFS post - order traversal The implementation used two stacks instead of a recursion The final result is store in self . result


def _method(self, expression, *args):
        """
        Overload Visitor._method because we want to stop to iterate over the
        visit_ functions as soon as a valid visit_ function is found
        """
        assert expression.__class__.__mro__[-1] is object
        for cls in expression.__class__.__mro__:
            sort = cls.__name__
            methodname = 'visit_%s' % sort
            method = getattr(self, methodname, None)
            if method is not None:
                method(expression, *args)
                return
        return

def _method ( self , expression , * args ) : assert expression . __class__ . __mro__ [ - 1 ] is object for cls in expression . __class__ . __mro__ : sort = cls . __name__ methodname = 'visit_%s' % sort method = getattr ( self , methodname , None ) if method is not None : method ( expression , * args ) return return

Overload Visitor . _method because we want to stop to iterate over the visit_ functions as soon as a valid visit_ function is found


def visit_BitVecAdd(self, expression, *operands):
        """ a + 0  ==> a
            0 + a  ==> a
        """
        left = expression.operands[0]
        right = expression.operands[1]
        if isinstance(right, BitVecConstant):
            if right.value == 0:
                return left
        if isinstance(left, BitVecConstant):
            if left.value == 0:
                return right

def visit_BitVecAdd ( self , expression , * operands ) : left = expression . operands [ 0 ] right = expression . operands [ 1 ] if isinstance ( right , BitVecConstant ) : if right . value == 0 : return left if isinstance ( left , BitVecConstant ) : if left . value == 0 : return right


def visit_BitVecOr(self, expression, *operands):
        """ a | 0 => a
            0 | a => a
            0xffffffff & a => 0xffffffff
            a & 0xffffffff => 0xffffffff

        """
        left = expression.operands[0]
        right = expression.operands[1]
        if isinstance(right, BitVecConstant):
            if right.value == 0:
                return left
            elif right.value == left.mask:
                return right
            elif isinstance(left, BitVecOr):
                left_left = left.operands[0]
                left_right = left.operands[1]
                if isinstance(right, Constant):
                    return BitVecOr(left_left, (left_right | right), taint=expression.taint)
        elif isinstance(left, BitVecConstant):
            return BitVecOr(right, left, taint=expression.taint)

def visit_BitVecOr ( self , expression , * operands ) : left = expression . operands [ 0 ] right = expression . operands [ 1 ] if isinstance ( right , BitVecConstant ) : if right . value == 0 : return left elif right . value == left . mask : return right elif isinstance ( left , BitVecOr ) : left_left = left . operands [ 0 ] left_right = left . operands [ 1 ] if isinstance ( right , Constant ) : return BitVecOr ( left_left , ( left_right | right ) , taint = expression . taint ) elif isinstance ( left , BitVecConstant ) : return BitVecOr ( right , left , taint = expression . taint )

a | 0 = > a 0 | a = > a 0xffffffff & a = > 0xffffffff a & 0xffffffff = > 0xffffffff


def _type_size(ty):
        """ Calculate `static` type size """
        if ty[0] in ('int', 'uint', 'bytesM', 'function'):
            return 32
        elif ty[0] in ('tuple'):
            result = 0
            for ty_i in ty[1]:
                result += ABI._type_size(ty_i)
            return result
        elif ty[0] in ('array'):
            rep = ty[1]
            result = 32  # offset link
            return result
        elif ty[0] in ('bytes', 'string'):
            result = 32  # offset link
            return result
        raise ValueError

def _type_size ( ty ) : if ty [ 0 ] in ( 'int' , 'uint' , 'bytesM' , 'function' ) : return 32 elif ty [ 0 ] in ( 'tuple' ) : result = 0 for ty_i in ty [ 1 ] : result += ABI . _type_size ( ty_i ) return result elif ty [ 0 ] in ( 'array' ) : rep = ty [ 1 ] result = 32 return result elif ty [ 0 ] in ( 'bytes' , 'string' ) : result = 32 return result raise ValueError


def function_call(type_spec, *args):
        """
        Build transaction data from function signature and arguments
        """
        m = re.match(r"(?P<name>[a-zA-Z_][a-zA-Z_0-9]*)(?P<type>\(.*\))", type_spec)
        if not m:
            raise EthereumError("Function signature expected")

        ABI._check_and_warn_num_args(type_spec, *args)

        result = ABI.function_selector(type_spec)  # Funcid
        result += ABI.serialize(m.group('type'), *args)
        return result


def function_selector(method_name_and_signature):
        """
        Makes a function hash id from a method signature
        """
        s = sha3.keccak_256()
        s.update(method_name_and_signature.encode())
        return bytes(s.digest()[:4])

def function_selector ( method_name_and_signature ) : s = sha3 . keccak_256 ( ) s . update ( method_name_and_signature . encode ( ) ) return bytes ( s . digest ( ) [ : 4 ] )


def _serialize_uint(value, size=32, padding=0):
        """
        Translates a python integral or a BitVec into a 32 byte string, MSB first
        """
        if size <= 0 or size > 32:
            raise ValueError

        from .account import EVMAccount  # because of circular import
        if not isinstance(value, (int, BitVec, EVMAccount)):
            raise ValueError
        if issymbolic(value):
            # FIXME This temporary array variable should be obtained from a specific constraint store
            bytes = ArrayVariable(index_bits=256, index_max=32, value_bits=8, name='temp{}'.format(uuid.uuid1()))
            if value.size <= size * 8:
                value = Operators.ZEXTEND(value, size * 8)
            else:
                # automatically truncate, e.g. if they passed a BitVec(256) for an `address` argument (160 bits)
                value = Operators.EXTRACT(value, 0, size * 8)
            bytes = ArrayProxy(bytes.write_BE(padding, value, size))
        else:
            value = int(value)
            bytes = bytearray()
            for _ in range(padding):
                bytes.append(0)
            for position in reversed(range(size)):
                bytes.append(Operators.EXTRACT(value, position * 8, 8))
        assert len(bytes) == size + padding
        return bytes

def _serialize_uint ( value , size = 32 , padding = 0 ) : if size <= 0 or size > 32 : raise ValueError from . account import EVMAccount if not isinstance ( value , ( int , BitVec , EVMAccount ) ) : raise ValueError if issymbolic ( value ) : bytes = ArrayVariable ( index_bits = 256 , index_max = 32 , value_bits = 8 , name = 'temp{}' . format ( uuid . uuid1 ( ) ) ) if value . size <= size * 8 : value = Operators . ZEXTEND ( value , size * 8 ) else : value = Operators . EXTRACT ( value , 0 , size * 8 ) bytes = ArrayProxy ( bytes . write_BE ( padding , value , size ) ) else : value = int ( value ) bytes = bytearray ( ) for _ in range ( padding ) : bytes . append ( 0 ) for position in reversed ( range ( size ) ) : bytes . append ( Operators . EXTRACT ( value , position * 8 , 8 ) ) assert len ( bytes ) == size + padding return bytes

Translates a python integral or a BitVec into a 32 byte string MSB first


def _serialize_int(value, size=32, padding=0):
        """
        Translates a signed python integral or a BitVec into a 32 byte string, MSB first
        """
        if size <= 0 or size > 32:
            raise ValueError
        if not isinstance(value, (int, BitVec)):
            raise ValueError
        if issymbolic(value):
            buf = ArrayVariable(index_bits=256, index_max=32, value_bits=8, name='temp{}'.format(uuid.uuid1()))
            value = Operators.SEXTEND(value, value.size, size * 8)
            buf = ArrayProxy(buf.write_BE(padding, value, size))
        else:
            value = int(value)
            buf = bytearray()
            for _ in range(padding):
                buf.append(0)

            for position in reversed(range(size)):
                buf.append(Operators.EXTRACT(value, position * 8, 8))
        return buf

def _serialize_int ( value , size = 32 , padding = 0 ) : if size <= 0 or size > 32 : raise ValueError if not isinstance ( value , ( int , BitVec ) ) : raise ValueError if issymbolic ( value ) : buf = ArrayVariable ( index_bits = 256 , index_max = 32 , value_bits = 8 , name = 'temp{}' . format ( uuid . uuid1 ( ) ) ) value = Operators . SEXTEND ( value , value . size , size * 8 ) buf = ArrayProxy ( buf . write_BE ( padding , value , size ) ) else : value = int ( value ) buf = bytearray ( ) for _ in range ( padding ) : buf . append ( 0 ) for position in reversed ( range ( size ) ) : buf . append ( Operators . EXTRACT ( value , position * 8 , 8 ) ) return buf

Translates a signed python integral or a BitVec into a 32 byte string MSB first


def _deserialize_uint(data, nbytes=32, padding=0, offset=0):
        """
        Read a `nbytes` bytes long big endian unsigned integer from `data` starting at `offset`

        :param data: sliceable buffer; symbolic buffer of Eth ABI encoded data
        :param nbytes: number of bytes to read starting from least significant byte
        :rtype: int or Expression
        """
        assert isinstance(data, (bytearray, Array))
        value = ABI._readBE(data, nbytes, padding=True, offset=offset)
        value = Operators.ZEXTEND(value, (nbytes + padding) * 8)
        return value

def _deserialize_uint ( data , nbytes = 32 , padding = 0 , offset = 0 ) : assert isinstance ( data , ( bytearray , Array ) ) value = ABI . _readBE ( data , nbytes , padding = True , offset = offset ) value = Operators . ZEXTEND ( value , ( nbytes + padding ) * 8 ) return value

Read a nbytes bytes long big endian unsigned integer from data starting at offset


def _deserialize_int(data, nbytes=32, padding=0):
        """
        Read a `nbytes` bytes long big endian signed integer from `data` starting at `offset`

        :param data: sliceable buffer; symbolic buffer of Eth ABI encoded data
        :param nbytes: number of bytes to read starting from least significant byte
        :rtype: int or Expression
        """
        assert isinstance(data, (bytearray, Array))
        value = ABI._readBE(data, nbytes, padding=True)
        value = Operators.SEXTEND(value, nbytes * 8, (nbytes + padding) * 8)
        if not issymbolic(value):
            # sign bit on
            if value & (1 << (nbytes * 8 - 1)):
                value = -(((~value) + 1) & ((1 << (nbytes * 8)) - 1))
        return value

def _deserialize_int ( data , nbytes = 32 , padding = 0 ) : assert isinstance ( data , ( bytearray , Array ) ) value = ABI . _readBE ( data , nbytes , padding = True ) value = Operators . SEXTEND ( value , nbytes * 8 , ( nbytes + padding ) * 8 ) if not issymbolic ( value ) : if value & ( 1 << ( nbytes * 8 - 1 ) ) : value = - ( ( ( ~ value ) + 1 ) & ( ( 1 << ( nbytes * 8 ) ) - 1 ) ) return value

Read a nbytes bytes long big endian signed integer from data starting at offset


def concretized_args(**policies):
    """
    Make sure an EVM instruction has all of its arguments concretized according to
    provided policies.

    Example decoration:

        @concretized_args(size='ONE', address='')
        def LOG(self, address, size, *topics):
            ...

    The above will make sure that the |size| parameter to LOG is Concretized when symbolic
    according to the 'ONE' policy and concretize |address| with the default policy.

    :param policies: A kwargs list of argument names and their respective policies.
                         Provide None or '' as policy to use default.
    :return: A function decorator
    """
    def concretizer(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            spec = inspect.getfullargspec(func)
            for arg, policy in policies.items():
                assert arg in spec.args, "Concretizer argument not found in wrapped function."
                # index is 0-indexed, but ConcretizeArgument is 1-indexed. However, this is correct
                # since implementation method is always a bound method (self is param 0)
                index = spec.args.index(arg)
                if not issymbolic(args[index]):
                    continue
                if not policy:
                    policy = 'SAMPLED'

                if policy == "ACCOUNTS":
                    value = args[index]
                    world = args[0].world
                    #special handler for EVM only policy
                    cond = world._constraint_to_accounts(value, ty='both', include_zero=True)
                    world.constraints.add(cond)
                    policy = 'ALL'
                raise ConcretizeArgument(index, policy=policy)
            return func(*args, **kwargs)
        wrapper.__signature__ = inspect.signature(func)
        return wrapper
    return concretizer

Make sure an EVM instruction has all of its arguments concretized according to provided policies .


def _get_memfee(self, address, size=1):
        """
        This calculates the amount of extra gas needed for accessing to
        previously unused memory.

        :param address: base memory offset
        :param size: size of the memory access
        """
        if not issymbolic(size) and size == 0:
            return 0

        address = self.safe_add(address, size)
        allocated = self.allocated
        GMEMORY = 3
        GQUADRATICMEMDENOM = 512  # 1 gas per 512 quadwords
        old_size = Operators.ZEXTEND(Operators.UDIV(self.safe_add(allocated, 31), 32), 512)
        new_size = Operators.ZEXTEND(Operators.UDIV(self.safe_add(address, 31), 32), 512)

        old_totalfee = self.safe_mul(old_size, GMEMORY) + Operators.UDIV(self.safe_mul(old_size, old_size), GQUADRATICMEMDENOM)
        new_totalfee = self.safe_mul(new_size, GMEMORY) + Operators.UDIV(self.safe_mul(new_size, new_size), GQUADRATICMEMDENOM)
        memfee = new_totalfee - old_totalfee
        flag = Operators.UGT(new_totalfee, old_totalfee)
        return Operators.ITEBV(512, size == 0, 0, Operators.ITEBV(512, flag, memfee, 0))

def _get_memfee ( self , address , size = 1 ) : if not issymbolic ( size ) and size == 0 : return 0 address = self . safe_add ( address , size ) allocated = self . allocated GMEMORY = 3 GQUADRATICMEMDENOM = 512 old_size = Operators . ZEXTEND ( Operators . UDIV ( self . safe_add ( allocated , 31 ) , 32 ) , 512 ) new_size = Operators . ZEXTEND ( Operators . UDIV ( self . safe_add ( address , 31 ) , 32 ) , 512 ) old_totalfee = self . safe_mul ( old_size , GMEMORY ) + Operators . UDIV ( self . safe_mul ( old_size , old_size ) , GQUADRATICMEMDENOM ) new_totalfee = self . safe_mul ( new_size , GMEMORY ) + Operators . UDIV ( self . safe_mul ( new_size , new_size ) , GQUADRATICMEMDENOM ) memfee = new_totalfee - old_totalfee flag = Operators . UGT ( new_totalfee , old_totalfee ) return Operators . ITEBV ( 512 , size == 0 , 0 , Operators . ITEBV ( 512 , flag , memfee , 0 ) )

This calculates the amount of extra gas needed for accessing to previously unused memory .


def read_code(self, address, size=1):
        """
        Read size byte from bytecode.
        If less than size bytes are available result will be pad with \x00
        """
        assert address < len(self.bytecode)
        value = self.bytecode[address:address + size]
        if len(value) < size:
            value += '\x00' * (size - len(value))  # pad with null (spec)
        return value

def read_code ( self , address , size = 1 ) : assert address < len ( self . bytecode ) value = self . bytecode [ address : address + size ] if len ( value ) < size : value += '\x00' * ( size - len ( value ) ) return value

Read size byte from bytecode . If less than size bytes are available result will be pad with \ x00


def instruction(self):
        """
        Current instruction pointed by self.pc
        """
        # FIXME check if pc points to invalid instruction
        # if self.pc >= len(self.bytecode):
        #    return InvalidOpcode('Code out of range')
        # if self.pc in self.invalid:
        #    raise InvalidOpcode('Opcode inside a PUSH immediate')
        try:
            _decoding_cache = getattr(self, '_decoding_cache')
        except Exception:
            _decoding_cache = self._decoding_cache = {}

        pc = self.pc
        if isinstance(pc, Constant):
            pc = pc.value

        if pc in _decoding_cache:
            return _decoding_cache[pc]

        def getcode():
            bytecode = self.bytecode
            for pc_i in range(pc, len(bytecode)):
                yield simplify(bytecode[pc_i]).value
            while True:
                yield 0
        instruction = EVMAsm.disassemble_one(getcode(), pc=pc, fork=DEFAULT_FORK)
        _decoding_cache[pc] = instruction
        return instruction

def instruction ( self ) : try : _decoding_cache = getattr ( self , '_decoding_cache' ) except Exception : _decoding_cache = self . _decoding_cache = { } pc = self . pc if isinstance ( pc , Constant ) : pc = pc . value if pc in _decoding_cache : return _decoding_cache [ pc ] def getcode ( ) : bytecode = self . bytecode for pc_i in range ( pc , len ( bytecode ) ) : yield simplify ( bytecode [ pc_i ] ) . value while True : yield 0 instruction = EVMAsm . disassemble_one ( getcode ( ) , pc = pc , fork = DEFAULT_FORK ) _decoding_cache [ pc ] = instruction return instruction

Current instruction pointed by self . pc


def _push(self, value):
        """
        Push into the stack

              ITEM0
              ITEM1
              ITEM2
        sp->  {empty}
        """
        assert isinstance(value, int) or isinstance(value, BitVec) and value.size == 256
        if len(self.stack) >= 1024:
            raise StackOverflow()

        if isinstance(value, int):
            value = value & TT256M1

        value = simplify(value)
        if isinstance(value, Constant) and not value.taint:
            value = value.value
        self.stack.append(value)

def _push ( self , value ) : assert isinstance ( value , int ) or isinstance ( value , BitVec ) and value . size == 256 if len ( self . stack ) >= 1024 : raise StackOverflow ( ) if isinstance ( value , int ) : value = value & TT256M1 value = simplify ( value ) if isinstance ( value , Constant ) and not value . taint : value = value . value self . stack . append ( value )


def _top(self, n=0):
        """Read a value from the top of the stack without removing it"""
        if len(self.stack) - n < 0:
            raise StackUnderflow()
        return self.stack[n - 1]

def _top ( self , n = 0 ) : if len ( self . stack ) - n < 0 : raise StackUnderflow ( ) return self . stack [ n - 1 ]


def _rollback(self):
        """Revert the stack, gas, pc and memory allocation so it looks like before executing the instruction"""
        last_pc, last_gas, last_instruction, last_arguments, fee, allocated = self._checkpoint_data
        self._push_arguments(last_arguments)
        self._gas = last_gas
        self._pc = last_pc
        self._allocated = allocated
        self._checkpoint_data = None

def _rollback ( self ) : last_pc , last_gas , last_instruction , last_arguments , fee , allocated = self . _checkpoint_data self . _push_arguments ( last_arguments ) self . _gas = last_gas self . _pc = last_pc self . _allocated = allocated self . _checkpoint_data = None

Revert the stack gas pc and memory allocation so it looks like before executing the instruction


def _store(self, offset, value, size=1):
        """Stores value in memory as a big endian"""
        self.memory.write_BE(offset, value, size)
        for i in range(size):
            self._publish('did_evm_write_memory', offset + i, Operators.EXTRACT(value, (size - i - 1) * 8, 8))

def _store ( self , offset , value , size = 1 ) : self . memory . write_BE ( offset , value , size ) for i in range ( size ) : self . _publish ( 'did_evm_write_memory' , offset + i , Operators . EXTRACT ( value , ( size - i - 1 ) * 8 , 8 ) )


def DIV(self, a, b):
        """Integer division operation"""
        try:
            result = Operators.UDIV(a, b)
        except ZeroDivisionError:
            result = 0
        return Operators.ITEBV(256, b == 0, 0, result)

def DIV ( self , a , b ) : try : result = Operators . UDIV ( a , b ) except ZeroDivisionError : result = 0 return Operators . ITEBV ( 256 , b == 0 , 0 , result )


def MOD(self, a, b):
        """Modulo remainder operation"""
        try:
            result = Operators.ITEBV(256, b == 0, 0, a % b)
        except ZeroDivisionError:
            result = 0
        return result

def MOD ( self , a , b ) : try : result = Operators . ITEBV ( 256 , b == 0 , 0 , a % b ) except ZeroDivisionError : result = 0 return result


def SMOD(self, a, b):
        """Signed modulo remainder operation"""
        s0, s1 = to_signed(a), to_signed(b)
        sign = Operators.ITEBV(256, s0 < 0, -1, 1)
        try:
            result = (Operators.ABS(s0) % Operators.ABS(s1)) * sign
        except ZeroDivisionError:
            result = 0

        return Operators.ITEBV(256, s1 == 0, 0, result)

def SMOD ( self , a , b ) : s0 , s1 = to_signed ( a ) , to_signed ( b ) sign = Operators . ITEBV ( 256 , s0 < 0 , - 1 , 1 ) try : result = ( Operators . ABS ( s0 ) % Operators . ABS ( s1 ) ) * sign except ZeroDivisionError : result = 0 return Operators . ITEBV ( 256 , s1 == 0 , 0 , result )


def ADDMOD(self, a, b, c):
        """Modulo addition operation"""
        try:
            result = Operators.ITEBV(256, c == 0, 0, (a + b) % c)
        except ZeroDivisionError:
            result = 0
        return result

def ADDMOD ( self , a , b , c ) : try : result = Operators . ITEBV ( 256 , c == 0 , 0 , ( a + b ) % c ) except ZeroDivisionError : result = 0 return result


def EXP_gas(self, base, exponent):
        """Calculate extra gas fee"""
        EXP_SUPPLEMENTAL_GAS = 10   # cost of EXP exponent per byte

        def nbytes(e):
            result = 0
            for i in range(32):
                result = Operators.ITEBV(512, Operators.EXTRACT(e, i * 8, 8) != 0, i + 1, result)
            return result
        return EXP_SUPPLEMENTAL_GAS * nbytes(exponent)

def EXP_gas ( self , base , exponent ) : EXP_SUPPLEMENTAL_GAS = 10 def nbytes ( e ) : result = 0 for i in range ( 32 ) : result = Operators . ITEBV ( 512 , Operators . EXTRACT ( e , i * 8 , 8 ) != 0 , i + 1 , result ) return result return EXP_SUPPLEMENTAL_GAS * nbytes ( exponent )


def SIGNEXTEND(self, size, value):
        """Extend length of two's complement signed integer"""
        # FIXME maybe use Operators.SEXTEND
        testbit = Operators.ITEBV(256, size <= 31, size * 8 + 7, 257)
        result1 = (value | (TT256 - (1 << testbit)))
        result2 = (value & ((1 << testbit) - 1))
        result = Operators.ITEBV(256, (value & (1 << testbit)) != 0, result1, result2)
        return Operators.ITEBV(256, size <= 31, result, value)

def SIGNEXTEND ( self , size , value ) : testbit = Operators . ITEBV ( 256 , size <= 31 , size * 8 + 7 , 257 ) result1 = ( value | ( TT256 - ( 1 << testbit ) ) ) result2 = ( value & ( ( 1 << testbit ) - 1 ) ) result = Operators . ITEBV ( 256 , ( value & ( 1 << testbit ) ) != 0 , result1 , result2 ) return Operators . ITEBV ( 256 , size <= 31 , result , value )

Extend length of two s complement signed integer


def LT(self, a, b):
        """Less-than comparison"""
        return Operators.ITEBV(256, Operators.ULT(a, b), 1, 0)

def LT ( self , a , b ) : return Operators . ITEBV ( 256 , Operators . ULT ( a , b ) , 1 , 0 )


def GT(self, a, b):
        """Greater-than comparison"""
        return Operators.ITEBV(256, Operators.UGT(a, b), 1, 0)

def GT ( self , a , b ) : return Operators . ITEBV ( 256 , Operators . UGT ( a , b ) , 1 , 0 )


def SGT(self, a, b):
        """Signed greater-than comparison"""
        # http://gavwood.com/paper.pdf
        s0, s1 = to_signed(a), to_signed(b)
        return Operators.ITEBV(256, s0 > s1, 1, 0)

def SGT ( self , a , b ) : s0 , s1 = to_signed ( a ) , to_signed ( b ) return Operators . ITEBV ( 256 , s0 > s1 , 1 , 0 )


def BYTE(self, offset, value):
        """Retrieve single byte from word"""
        offset = Operators.ITEBV(256, offset < 32, (31 - offset) * 8, 256)
        return Operators.ZEXTEND(Operators.EXTRACT(value, offset, 8), 256)

def BYTE ( self , offset , value ) : offset = Operators . ITEBV ( 256 , offset < 32 , ( 31 - offset ) * 8 , 256 ) return Operators . ZEXTEND ( Operators . EXTRACT ( value , offset , 8 ) , 256 )


def SHA3(self, start, size):
        """Compute Keccak-256 hash"""
        # read memory from start to end
        # http://gavwood.com/paper.pdf
        data = self.try_simplify_to_constant(self.read_buffer(start, size))

        if issymbolic(data):
            known_sha3 = {}
            # Broadcast the signal
            self._publish('on_symbolic_sha3', data, known_sha3)  # This updates the local copy of sha3 with the pairs we need to explore

            value = 0  # never used
            known_hashes_cond = False
            for key, hsh in known_sha3.items():
                assert not issymbolic(key), "Saved sha3 data,hash pairs should be concrete"
                cond = key == data
                known_hashes_cond = Operators.OR(cond, known_hashes_cond)
                value = Operators.ITEBV(256, cond, hsh, value)
            return value

        value = sha3.keccak_256(data).hexdigest()
        value = int(value, 16)
        self._publish('on_concrete_sha3', data, value)
        logger.info("Found a concrete SHA3 example %r -> %x", data, value)
        return value


def CALLDATALOAD(self, offset):
        """Get input data of current environment"""

        if issymbolic(offset):
            if solver.can_be_true(self._constraints, offset == self._used_calldata_size):
                self.constraints.add(offset == self._used_calldata_size)
            raise ConcretizeArgument(1, policy='SAMPLED')

        self._use_calldata(offset, 32)

        data_length = len(self.data)

        bytes = []
        for i in range(32):
            try:
                c = Operators.ITEBV(8, offset + i < data_length, self.data[offset + i], 0)
            except IndexError:
                # offset + i is concrete and outside data
                c = 0

            bytes.append(c)
        return Operators.CONCAT(256, *bytes)

def CALLDATALOAD ( self , offset ) : if issymbolic ( offset ) : if solver . can_be_true ( self . _constraints , offset == self . _used_calldata_size ) : self . constraints . add ( offset == self . _used_calldata_size ) raise ConcretizeArgument ( 1 , policy = 'SAMPLED' ) self . _use_calldata ( offset , 32 ) data_length = len ( self . data ) bytes = [ ] for i in range ( 32 ) : try : c = Operators . ITEBV ( 8 , offset + i < data_length , self . data [ offset + i ] , 0 ) except IndexError : c = 0 bytes . append ( c ) return Operators . CONCAT ( 256 , * bytes )


def CALLDATACOPY(self, mem_offset, data_offset, size):
        """Copy input data in current environment to memory"""

        if issymbolic(size):
            if solver.can_be_true(self._constraints, size <= len(self.data) + 32):
                self.constraints.add(size <= len(self.data) + 32)
            raise ConcretizeArgument(3, policy='SAMPLED')

        if issymbolic(data_offset):
            if solver.can_be_true(self._constraints, data_offset == self._used_calldata_size):
                self.constraints.add(data_offset == self._used_calldata_size)
            raise ConcretizeArgument(2, policy='SAMPLED')

        #account for calldata usage
        self._use_calldata(data_offset, size)
        self._allocate(mem_offset, size)
        for i in range(size):
            try:
                c = Operators.ITEBV(8, data_offset + i < len(self.data), Operators.ORD(self.data[data_offset + i]), 0)
            except IndexError:
                # data_offset + i is concrete and outside data
                c = 0
            self._store(mem_offset + i, c)

def CALLDATACOPY ( self , mem_offset , data_offset , size ) : if issymbolic ( size ) : if solver . can_be_true ( self . _constraints , size <= len ( self . data ) + 32 ) : self . constraints . add ( size <= len ( self . data ) + 32 ) raise ConcretizeArgument ( 3 , policy = 'SAMPLED' ) if issymbolic ( data_offset ) : if solver . can_be_true ( self . _constraints , data_offset == self . _used_calldata_size ) : self . constraints . add ( data_offset == self . _used_calldata_size ) raise ConcretizeArgument ( 2 , policy = 'SAMPLED' ) self . _use_calldata ( data_offset , size ) self . _allocate ( mem_offset , size ) for i in range ( size ) : try : c = Operators . ITEBV ( 8 , data_offset + i < len ( self . data ) , Operators . ORD ( self . data [ data_offset + i ] ) , 0 ) except IndexError : c = 0 self . _store ( mem_offset + i , c )


def CODECOPY(self, mem_offset, code_offset, size):
        """Copy code running in current environment to memory"""

        self._allocate(mem_offset, size)
        GCOPY = 3             # cost to copy one 32 byte word
        copyfee = self.safe_mul(GCOPY, Operators.UDIV(self.safe_add(size, 31), 32))
        self._consume(copyfee)

        if issymbolic(size):
            max_size = solver.max(self.constraints, size)
        else:
            max_size = size

        for i in range(max_size):
            if issymbolic(i < size):
                default = Operators.ITEBV(8, i < size, 0, self._load(mem_offset + i, 1))  # Fixme. unnecessary memory read
            else:
                if i < size:
                    default = 0
                else:
                    default = self._load(mem_offset + i, 1)

            if issymbolic(code_offset):
                value = Operators.ITEBV(8, code_offset + i >= len(self.bytecode), default, self.bytecode[code_offset + i])
            else:
                if code_offset + i >= len(self.bytecode):
                    value = default
                else:
                    value = self.bytecode[code_offset + i]
            self._store(mem_offset + i, value)
        self._publish('did_evm_read_code', code_offset, size)

def CODECOPY ( self , mem_offset , code_offset , size ) : self . _allocate ( mem_offset , size ) GCOPY = 3 copyfee = self . safe_mul ( GCOPY , Operators . UDIV ( self . safe_add ( size , 31 ) , 32 ) ) self . _consume ( copyfee ) if issymbolic ( size ) : max_size = solver . max ( self . constraints , size ) else : max_size = size for i in range ( max_size ) : if issymbolic ( i < size ) : default = Operators . ITEBV ( 8 , i < size , 0 , self . _load ( mem_offset + i , 1 ) ) else : if i < size : default = 0 else : default = self . _load ( mem_offset + i , 1 ) if issymbolic ( code_offset ) : value = Operators . ITEBV ( 8 , code_offset + i >= len ( self . bytecode ) , default , self . bytecode [ code_offset + i ] ) else : if code_offset + i >= len ( self . bytecode ) : value = default else : value = self . bytecode [ code_offset + i ] self . _store ( mem_offset + i , value ) self . _publish ( 'did_evm_read_code' , code_offset , size )


def EXTCODECOPY(self, account, address, offset, size):
        """Copy an account's code to memory"""
        extbytecode = self.world.get_code(account)
        self._allocate(address + size)

        for i in range(size):
            if offset + i < len(extbytecode):
                self._store(address + i, extbytecode[offset + i])
            else:
                self._store(address + i, 0)

def EXTCODECOPY ( self , account , address , offset , size ) : extbytecode = self . world . get_code ( account ) self . _allocate ( address + size ) for i in range ( size ) : if offset + i < len ( extbytecode ) : self . _store ( address + i , extbytecode [ offset + i ] ) else : self . _store ( address + i , 0 )


def MLOAD(self, address):
        """Load word from memory"""
        self._allocate(address, 32)
        value = self._load(address, 32)
        return value

def MLOAD ( self , address ) : self . _allocate ( address , 32 ) value = self . _load ( address , 32 ) return value


def MSTORE(self, address, value):
        """Save word to memory"""
        if istainted(self.pc):
            for taint in get_taints(self.pc):
                value = taint_with(value, taint)
        self._allocate(address, 32)
        self._store(address, value, 32)

def MSTORE ( self , address , value ) : if istainted ( self . pc ) : for taint in get_taints ( self . pc ) : value = taint_with ( value , taint ) self . _allocate ( address , 32 ) self . _store ( address , value , 32 )


def MSTORE8(self, address, value):
        """Save byte to memory"""
        if istainted(self.pc):
            for taint in get_taints(self.pc):
                value = taint_with(value, taint)
        self._allocate(address, 1)
        self._store(address, Operators.EXTRACT(value, 0, 8), 1)

def MSTORE8 ( self , address , value ) : if istainted ( self . pc ) : for taint in get_taints ( self . pc ) : value = taint_with ( value , taint ) self . _allocate ( address , 1 ) self . _store ( address , Operators . EXTRACT ( value , 0 , 8 ) , 1 )


def SLOAD(self, offset):
        """Load word from storage"""
        storage_address = self.address
        self._publish('will_evm_read_storage', storage_address, offset)
        value = self.world.get_storage_data(storage_address, offset)
        self._publish('did_evm_read_storage', storage_address, offset, value)
        return value

def SLOAD ( self , offset ) : storage_address = self . address self . _publish ( 'will_evm_read_storage' , storage_address , offset ) value = self . world . get_storage_data ( storage_address , offset ) self . _publish ( 'did_evm_read_storage' , storage_address , offset , value ) return value


def SSTORE(self, offset, value):
        """Save word to storage"""
        storage_address = self.address
        self._publish('will_evm_write_storage', storage_address, offset, value)
        #refund = Operators.ITEBV(256,
        #                         previous_value != 0,
        #                         Operators.ITEBV(256, value != 0, 0, GSTORAGEREFUND),
        #                         0)

        if istainted(self.pc):
            for taint in get_taints(self.pc):
                value = taint_with(value, taint)
        self.world.set_storage_data(storage_address, offset, value)
        self._publish('did_evm_write_storage', storage_address, offset, value)

def SSTORE ( self , offset , value ) : storage_address = self . address self . _publish ( 'will_evm_write_storage' , storage_address , offset , value ) if istainted ( self . pc ) : for taint in get_taints ( self . pc ) : value = taint_with ( value , taint ) self . world . set_storage_data ( storage_address , offset , value ) self . _publish ( 'did_evm_write_storage' , storage_address , offset , value )


def JUMPI(self, dest, cond):
        """Conditionally alter the program counter"""
        self.pc = Operators.ITEBV(256, cond != 0, dest, self.pc + self.instruction.size)
        #This set ups a check for JMPDEST in the next instruction if cond != 0
        self._set_check_jmpdest(cond != 0)

def JUMPI ( self , dest , cond ) : self . pc = Operators . ITEBV ( 256 , cond != 0 , dest , self . pc + self . instruction . size ) self . _set_check_jmpdest ( cond != 0 )


def SWAP(self, *operands):
        """Exchange 1st and 2nd stack items"""
        a = operands[0]
        b = operands[-1]
        return (b,) + operands[1:-1] + (a,)

def SWAP ( self , * operands ) : a = operands [ 0 ] b = operands [ - 1 ] return ( b , ) + operands [ 1 : - 1 ] + ( a , )


def CALLCODE(self, gas, _ignored_, value, in_offset, in_size, out_offset, out_size):
        """Message-call into this account with alternative account's code"""
        self.world.start_transaction('CALLCODE',
                                     address=self.address,
                                     data=self.read_buffer(in_offset, in_size),
                                     caller=self.address,
                                     value=value,
                                     gas=gas)
        raise StartTx()

def CALLCODE ( self , gas , _ignored_ , value , in_offset , in_size , out_offset , out_size ) : self . world . start_transaction ( 'CALLCODE' , address = self . address , data = self . read_buffer ( in_offset , in_size ) , caller = self . address , value = value , gas = gas ) raise StartTx ( )

Message - call into this account with alternative account s code


def RETURN(self, offset, size):
        """Halt execution returning output data"""
        data = self.read_buffer(offset, size)
        raise EndTx('RETURN', data)

def RETURN ( self , offset , size ) : data = self . read_buffer ( offset , size ) raise EndTx ( 'RETURN' , data )


def SELFDESTRUCT(self, recipient):
        """Halt execution and register account for later deletion"""
        #This may create a user account
        recipient = Operators.EXTRACT(recipient, 0, 160)
        address = self.address
        #FIXME for on the known addresses
        if issymbolic(recipient):
            logger.info("Symbolic recipient on self destruct")
            recipient = solver.get_value(self.constraints, recipient)

        if recipient not in self.world:
            self.world.create_account(address=recipient)

        self.world.send_funds(address, recipient, self.world.get_balance(address))
        self.world.delete_account(address)

        raise EndTx('SELFDESTRUCT')

def
SELFDESTRUCT
(
self
,
recipient
)
:
recipient
=
Operators
.
EXTRACT
(
recipient
,
0
,
160
)
address
=
self
.
address
if
issymbolic
(
recipient
)
:
logger
.
info
(
"Symbolic recipient on self destruct"
)
recipient
=
solver
.
get_value
(
self
.
constraints
,
recipient
)
if
recipient
not
in
self
.
world
:
self
.
world
.
create_account
(
address
=
recipient
)
self
.
world
.
send_funds
(
address
,
recipient
,
self
.
world
.
get_balance
(
address
)
)
self
.
world
.
delete_account
(
address
)
raise
EndTx
(
'SELFDESTRUCT'
)


def human_transactions(self):
        """Completed human transaction"""
        txs = []
        for tx in self.transactions:
            if tx.depth == 0:
                txs.append(tx)
        return tuple(txs)

def human_transactions ( self ) : txs = [ ] for tx in self . transactions : if tx . depth == 0 : txs . append ( tx ) return tuple ( txs )


def current_human_transaction(self):
        """Current ongoing human transaction"""
        try:
            tx, _, _, _, _ = self._callstack[0]
            if tx.result is not None:
                #That tx finished. No current tx.
                return None
            assert tx.depth == 0
            return tx
        except IndexError:
            return None

def current_human_transaction ( self ) : try : tx , _ , _ , _ , _ = self . _callstack [ 0 ] if tx . result is not None : return None assert tx . depth == 0 return tx except IndexError : return None


def get_storage_data(self, storage_address, offset):
        """
        Read a value from a storage slot on the specified account

        :param storage_address: an account address
        :param offset: the storage slot to use.
        :type offset: int or BitVec
        :return: the value
        :rtype: int or BitVec
        """
        value = self._world_state[storage_address]['storage'].get(offset, 0)
        return simplify(value)

def get_storage_data ( self , storage_address , offset ) : value = self . _world_state [ storage_address ] [ 'storage' ] . get ( offset , 0 ) return simplify ( value )


def set_storage_data(self, storage_address, offset, value):
        """
        Writes a value to a storage slot in specified account

        :param storage_address: an account address
        :param offset: the storage slot to use.
        :type offset: int or BitVec
        :param value: the value to write
        :type value: int or BitVec
        """
        self._world_state[storage_address]['storage'][offset] = value

def set_storage_data ( self , storage_address , offset , value ) : self . _world_state [ storage_address ] [ 'storage' ] [ offset ] = value


def get_storage_items(self, address):
        """
        Gets all items in an account storage

        :param address: account address
        :return: all items in account storage. items are tuple of (index, value). value can be symbolic
        :rtype: list[(storage_index, storage_value)]
        """
        storage = self._world_state[address]['storage']
        items = []
        array = storage.array
        while not isinstance(array, ArrayVariable):
            items.append((array.index, array.value))
            array = array.array
        return items

def get_storage_items ( self , address ) : storage = self . _world_state [ address ] [ 'storage' ] items = [ ] array = storage . array while not isinstance ( array , ArrayVariable ) : items . append ( ( array . index , array . value ) ) array = array . array return items


def has_storage(self, address):
        """
        True if something has been written to the storage.
        Note that if a slot has been erased from the storage this function may
        lose any meaning.
        """
        storage = self._world_state[address]['storage']
        array = storage.array
        while not isinstance(array, ArrayVariable):
            if isinstance(array, ArrayStore):
                return True
            array = array.array
        return False

def has_storage ( self , address ) : storage = self . _world_state [ address ] [ 'storage' ] array = storage . array while not isinstance ( array , ArrayVariable ) : if isinstance ( array , ArrayStore ) : return True array = array . array return False

True if something has been written to the storage . Note that if a slot has been erased from the storage this function may lose any meaning .


def new_address(self, sender=None, nonce=None):
        """Create a fresh 160bit address"""
        if sender is not None and nonce is None:
            nonce = self.get_nonce(sender)

        new_address = self.calculate_new_address(sender, nonce)
        if sender is None and new_address in self:
            return self.new_address(sender, nonce)
        return new_address

def new_address ( self , sender = None , nonce = None ) : if sender is not None and nonce is None : nonce = self . get_nonce ( sender ) new_address = self . calculate_new_address ( sender , nonce ) if sender is None and new_address in self : return self . new_address ( sender , nonce ) return new_address


def create_contract(self, price=0, address=None, caller=None, balance=0, init=None, gas=None):
        """
        Create a contract account. Sends a transaction to initialize the contract

        :param address: the address of the new account, if known. If omitted, a new address will be generated as closely to the Yellow Paper as possible.
        :param balance: the initial balance of the account in Wei
        :param init: the initialization code of the contract

        The way that the Solidity compiler expects the constructor arguments to
        be passed is by appending the arguments to the byte code produced by the
        Solidity compiler. The arguments are formatted as defined in the Ethereum
        ABI2. The arguments are then copied from the init byte array to the EVM
        memory through the CODECOPY opcode with appropriate values on the stack.
        This is done when the byte code in the init byte array is actually run
        on the network.
        """
        expected_address = self.create_account(self.new_address(sender=caller))
        if address is None:
            address = expected_address
        elif caller is not None and address != expected_address:
            raise EthereumError(f"Error: contract created from address {hex(caller)} with nonce {self.get_nonce(caller)} was expected to be at address {hex(expected_address)}, but create_contract was called with address={hex(address)}")
        self.start_transaction('CREATE', address, price, init, caller, balance, gas=gas)
        self._process_pending_transaction()
        return address

Create a contract account . Sends a transaction to initialize the contract


def _swap_mode(self):
        """Toggle between ARM and Thumb mode"""
        assert self.mode in (cs.CS_MODE_ARM, cs.CS_MODE_THUMB)
        if self.mode == cs.CS_MODE_ARM:
            self.mode = cs.CS_MODE_THUMB
        else:
            self.mode = cs.CS_MODE_ARM

def _swap_mode ( self ) : assert self . mode in ( cs . CS_MODE_ARM , cs . CS_MODE_THUMB ) if self . mode == cs . CS_MODE_ARM : self . mode = cs . CS_MODE_THUMB else : self . mode = cs . CS_MODE_ARM


def MRC(cpu, coprocessor, opcode1, dest, coprocessor_reg_n, coprocessor_reg_m, opcode2):
        """
        MRC moves to ARM register from coprocessor.

        :param Armv7Operand coprocessor: The name of the coprocessor; immediate
        :param Armv7Operand opcode1: coprocessor specific opcode; 3-bit immediate
        :param Armv7Operand dest: the destination operand: register
        :param Armv7Operand coprocessor_reg_n: the coprocessor register; immediate
        :param Armv7Operand coprocessor_reg_m: the coprocessor register; immediate
        :param Armv7Operand opcode2: coprocessor specific opcode; 3-bit immediate
        """
        assert coprocessor.type == 'coprocessor'
        assert opcode1.type == 'immediate'
        assert opcode2.type == 'immediate'
        assert dest.type == 'register'
        imm_coprocessor = coprocessor.read()
        imm_opcode1 = opcode1.read()
        imm_opcode2 = opcode2.read()
        coprocessor_n_name = coprocessor_reg_n.read()
        coprocessor_m_name = coprocessor_reg_m.read()

        if 15 == imm_coprocessor:  # MMU
            if 0 == imm_opcode1:
                if 13 == coprocessor_n_name:
                    if 3 == imm_opcode2:
                        dest.write(cpu.regfile.read('P15_C13'))
                        return
        raise NotImplementedError("MRC: unimplemented combination of coprocessor, opcode, and coprocessor register")

MRC moves to ARM register from coprocessor .


def LDRD(cpu, dest1, dest2, src, offset=None):
        """Loads double width data from memory."""
        assert dest1.type == 'register'
        assert dest2.type == 'register'
        assert src.type == 'memory'
        mem1 = cpu.read_int(src.address(), 32)
        mem2 = cpu.read_int(src.address() + 4, 32)
        writeback = cpu._compute_writeback(src, offset)
        dest1.write(mem1)
        dest2.write(mem2)
        cpu._cs_hack_ldr_str_writeback(src, offset, writeback)

def LDRD ( cpu , dest1 , dest2 , src , offset = None ) : assert dest1 . type == 'register' assert dest2 . type == 'register' assert src . type == 'memory' mem1 = cpu . read_int ( src . address ( ) , 32 ) mem2 = cpu . read_int ( src . address ( ) + 4 , 32 ) writeback = cpu . _compute_writeback ( src , offset ) dest1 . write ( mem1 ) dest2 . write ( mem2 ) cpu . _cs_hack_ldr_str_writeback ( src , offset , writeback )


def STRD(cpu, src1, src2, dest, offset=None):
        """Writes the contents of two registers to memory."""
        assert src1.type == 'register'
        assert src2.type == 'register'
        assert dest.type == 'memory'
        val1 = src1.read()
        val2 = src2.read()
        writeback = cpu._compute_writeback(dest, offset)
        cpu.write_int(dest.address(), val1, 32)
        cpu.write_int(dest.address() + 4, val2, 32)
        cpu._cs_hack_ldr_str_writeback(dest, offset, writeback)

def STRD ( cpu , src1 , src2 , dest , offset = None ) : assert src1 . type == 'register' assert src2 . type == 'register' assert dest . type == 'memory' val1 = src1 . read ( ) val2 = src2 . read ( ) writeback = cpu . _compute_writeback ( dest , offset ) cpu . write_int ( dest . address ( ) , val1 , 32 ) cpu . write_int ( dest . address ( ) + 4 , val2 , 32 ) cpu . _cs_hack_ldr_str_writeback ( dest , offset , writeback )

Writes the contents of two registers to memory .


def ADR(cpu, dest, src):
        """
        Address to Register adds an immediate value to the PC value, and writes the result to the destination register.

        :param ARMv7Operand dest: Specifies the destination register.
        :param ARMv7Operand src:
            Specifies the label of an instruction or literal data item whose address is to be loaded into
            <Rd>. The assembler calculates the required value of the offset from the Align(PC,4)
            value of the ADR instruction to this label.
        """
        aligned_pc = (cpu.instruction.address + 4) & 0xfffffffc
        dest.write(aligned_pc + src.read())

def ADR ( cpu , dest , src ) : aligned_pc = ( cpu . instruction . address + 4 ) & 0xfffffffc dest . write ( aligned_pc + src . read ( ) )

Address to Register adds an immediate value to the PC value and writes the result to the destination register .


def ADDW(cpu, dest, src, add):
        """
        This instruction adds an immediate value to a register value, and writes the result to the destination register.
        It doesn't update the condition flags.

        :param ARMv7Operand dest: Specifies the destination register. If omitted, this register is the same as src.
        :param ARMv7Operand src:
            Specifies the register that contains the first operand. If the SP is specified for dest, see ADD (SP plus
            immediate). If the PC is specified for dest, see ADR.
        :param ARMv7Operand add:
            Specifies the immediate value to be added to the value obtained from src. The range of allowed values is
            0-4095.
        """
        aligned_pc = (cpu.instruction.address + 4) & 0xfffffffc
        if src.type == 'register' and src.reg in ('PC', 'R15'):
            src = aligned_pc
        else:
            src = src.read()
        dest.write(src + add.read())

def ADDW ( cpu , dest , src , add ) : aligned_pc = ( cpu . instruction . address + 4 ) & 0xfffffffc if src . type == 'register' and src . reg in ( 'PC' , 'R15' ) : src = aligned_pc else : src = src . read ( ) dest . write ( src + add . read ( ) )

This instruction adds an immediate value to a register value and writes the result to the destination register . It doesn t update the condition flags .


def CBZ(cpu, op, dest):
        """
        Compare and Branch on Zero compares the value in a register with zero, and conditionally branches forward
        a constant value. It does not affect the condition flags.

        :param ARMv7Operand op: Specifies the register that contains the first operand.
        :param ARMv7Operand dest:
            Specifies the label of the instruction that is to be branched to. The assembler calculates the
            required value of the offset from the PC value of the CBZ instruction to this label, then
            selects an encoding that will set imm32 to that offset. Allowed offsets are even numbers in
            the range 0 to 126.
        """
        cpu.PC = Operators.ITEBV(cpu.address_bit_size,
                                 op.read(), cpu.PC, dest.read())

def CBZ ( cpu , op , dest ) : cpu . PC = Operators . ITEBV ( cpu . address_bit_size , op . read ( ) , cpu . PC , dest . read ( ) )

Compare and Branch on Zero compares the value in a register with zero and conditionally branches forward a constant value . It does not affect the condition flags .


def TBH(cpu, dest):
        """
        Table Branch Halfword causes a PC-relative forward branch using a table of single halfword offsets. A base
        register provides a pointer to the table, and a second register supplies an index into the table. The branch
        length is twice the value of the halfword returned from the table.

        :param ARMv7Operand dest: see below; register
        """
        # Capstone merges the two registers values into one operand, so we need to extract them back

        # Specifies the base register. This contains the address of the table of branch lengths. This
        # register is allowed to be the PC. If it is, the table immediately follows this instruction.
        base_addr = dest.get_mem_base_addr()
        if dest.mem.base in ('PC', 'R15'):
            base_addr = cpu.PC

        # Specifies the index register. This contains an integer pointing to a halfword within the table.
        # The offset within the table is twice the value of the index.
        offset = cpu.read_int(base_addr + dest.get_mem_offset(), 16)
        offset = Operators.ZEXTEND(offset, cpu.address_bit_size)

        cpu.PC += (offset << 1)

def TBH ( cpu , dest ) : base_addr = dest . get_mem_base_addr ( ) if dest . mem . base in ( 'PC' , 'R15' ) : base_addr = cpu . PC offset = cpu . read_int ( base_addr + dest . get_mem_offset ( ) , 16 ) offset = Operators . ZEXTEND ( offset , cpu . address_bit_size ) cpu . PC += ( offset << 1 )

Table Branch Halfword causes a PC - relative forward branch using a table of single halfword offsets . A base register provides a pointer to the table and a second register supplies an index into the table . The branch length is twice the value of the halfword returned from the table .


def _dict_diff(d1, d2):
    """
    Produce a dict that includes all the keys in d2 that represent different values in d1, as well as values that
    aren't in d1.

    :param dict d1: First dict
    :param dict d2: Dict to compare with
    :rtype: dict
    """
    d = {}
    for key in set(d1).intersection(set(d2)):
        if d2[key] != d1[key]:
            d[key] = d2[key]
    for key in set(d2).difference(set(d1)):
        d[key] = d2[key]
    return d

def _dict_diff ( d1 , d2 ) : d = { } for key in set ( d1 ) . intersection ( set ( d2 ) ) : if d2 [ key ] != d1 [ key ] : d [ key ] = d2 [ key ] for key in set ( d2 ) . difference ( set ( d1 ) ) : d [ key ] = d2 [ key ] return d

Produce a dict that includes all the keys in d2 that represent different values in d1 as well as values that aren t in d1 .


def disassemble_instruction(self, code, pc):
        """Get next instruction using the Capstone disassembler

        :param str code: binary blob to be disassembled
        :param long pc: program counter
        """
        return next(self.disasm.disasm(code, pc))

def disassemble_instruction ( self , code , pc ) : return next ( self . disasm . disasm ( code , pc ) )


def add(self, constraint, check=False):
        """
        Add a constraint to the set

        :param constraint: The constraint to add to the set.
        :param check: Currently unused.
        :return:
        """
        if isinstance(constraint, bool):
            constraint = BoolConstant(constraint)
        assert isinstance(constraint, Bool)
        constraint = simplify(constraint)
        # If self._child is not None this constraint set has been forked and a
        # a derived constraintset may be using this. So we can't add any more
        # constraints to this one. After the child constraintSet is deleted
        # we regain the ability to add constraints.
        if self._child is not None:
            raise Exception('ConstraintSet is frozen')

        if isinstance(constraint, BoolConstant):
            if not constraint.value:
                logger.info("Adding an impossible constant constraint")
                self._constraints = [constraint]
            else:
                return

        self._constraints.append(constraint)

        if check:
            from ...core.smtlib import solver
            if not solver.check(self):
                raise ValueError("Added an impossible constraint")


def _declare(self, var):
        """ Declare the variable `var` """
        if var.name in self._declarations:
            raise ValueError('Variable already declared')
        self._declarations[var.name] = var
        return var

def
_declare
(
self
,
var
)
:
if
var
.
name
in
self
.
_declarations
:
raise
ValueError
(
'Variable already declared'
)
self
.
_declarations
[
var
.
name
]
=
var
return
var


def declarations(self):
        """ Returns the variable expressions of this constraint set """
        declarations = GetDeclarations()
        for a in self.constraints:
            try:
                declarations.visit(a)
            except RuntimeError:
                # TODO: (defunct) move recursion management out of PickleSerializer
                if sys.getrecursionlimit() >= PickleSerializer.MAX_RECURSION:
                    raise Exception(f'declarations recursion limit surpassed {PickleSerializer.MAX_RECURSION}, aborting')
                new_limit = sys.getrecursionlimit() + PickleSerializer.DEFAULT_RECURSION
                if new_limit <= PickleSerializer.DEFAULT_RECURSION:
                    sys.setrecursionlimit(new_limit)
                    return self.declarations
        return declarations.result


def is_declared(self, expression_var):
        """ True if expression_var is declared in this constraint set """
        if not isinstance(expression_var, Variable):
            raise ValueError(f'Expression must be a Variable (not a {type(expression_var)})')
        return any(expression_var is x for x in self.get_declared_variables())


def migrate(self, expression, name_migration_map=None):
        """ Migrate an expression created for a different constraint set to self.
            Returns an expression that can be used with this constraintSet

            All the foreign variables used in the expression are replaced by
            variables of this constraint set. If the variable was replaced before
            the replacement is taken from the provided migration map.

            The migration mapping is updated with new replacements.

            :param expression: the potentially foreign expression
            :param name_migration_map: mapping of already migrated variables. maps from string name of foreign variable to its currently existing migrated string name. this is updated during this migration.
            :return: a migrated expression where all the variables are local. name_migration_map is updated

        """
        if name_migration_map is None:
            name_migration_map = {}

        #  name_migration_map -> object_migration_map
        #  Based on the name mapping in name_migration_map build an object to
        #  object mapping to be used in the replacing of variables
        #  inv: object_migration_map's keys should ALWAYS be external/foreign
        #  expressions, and its values should ALWAYS be internal/local expressions
        object_migration_map = {}

        #List of foreign vars used in expression
        foreign_vars = itertools.filterfalse(self.is_declared, get_variables(expression))
        for foreign_var in foreign_vars:
            # If a variable with the same name was previously migrated
            if foreign_var.name in name_migration_map:
                migrated_name = name_migration_map[foreign_var.name]
                native_var = self.get_variable(migrated_name)
                assert native_var is not None, "name_migration_map contains a variable that does not exist in this ConstraintSet"
                object_migration_map[foreign_var] = native_var
            else:
                # foreign_var was not found in the local declared variables nor
                # any variable with the same name was previously migrated
                # let's make a new unique internal name for it
                migrated_name = foreign_var.name
                if migrated_name in self._declarations:
                    migrated_name = self._make_unique_name(f'{foreign_var.name}_migrated')
                # Create and declare a new variable of given type
                if isinstance(foreign_var, Bool):
                    new_var = self.new_bool(name=migrated_name)
                elif isinstance(foreign_var, BitVec):
                    new_var = self.new_bitvec(foreign_var.size, name=migrated_name)
                elif isinstance(foreign_var, Array):
                    # Note that we are discarding the ArrayProxy encapsulation
                    new_var = self.new_array(index_max=foreign_var.index_max, index_bits=foreign_var.index_bits, value_bits=foreign_var.value_bits, name=migrated_name).array
                else:
                    raise NotImplemented(f"Unknown expression type {type(var)} encountered during expression migration")
                # Update the var to var mapping
                object_migration_map[foreign_var] = new_var
                # Update the name to name mapping
                name_migration_map[foreign_var.name] = new_var.name

        #  Actually replace each appearance of migrated variables by the new ones
        migrated_expression = replace(expression, object_migration_map)
        return migrated_expression

Migrate an expression created for a different constraint set to self . Returns an expression that can be used with this constraintSet


def inverse_transform(self, X_in):
        """
        Perform the inverse transformation to encoded data. Will attempt best case reconstruction, which means
        it will return nan for handle_missing and handle_unknown settings that break the bijection. We issue
        warnings when some of those cases occur.

        Parameters
        ----------
        X_in : array-like, shape = [n_samples, n_features]

        Returns
        -------
        p: array, the same size of X_in

        """
        X = X_in.copy(deep=True)

        # first check the type
        X = util.convert_input(X)

        if self._dim is None:
            raise ValueError(
                'Must train encoder before it can be used to inverse_transform data')

        # then make sure that it is the right size
        if X.shape[1] != self._dim:
            if self.drop_invariant:
                raise ValueError("Unexpected input dimension %d, the attribute drop_invariant should "
                                 "set as False when transform data" % (X.shape[1],))
            else:
                raise ValueError('Unexpected input dimension %d, expected %d' % (X.shape[1], self._dim,))

        if not self.cols:
            return X if self.return_df else X.values

        if self.handle_unknown == 'value':
            for col in self.cols:
                if any(X[col] == -1):
                    warnings.warn("inverse_transform is not supported because transform impute "
                                  "the unknown category -1 when encode %s" % (col,))

        if self.handle_unknown == 'return_nan' and self.handle_missing == 'return_nan':
            for col in self.cols:
                if X[col].isnull().any():
                    warnings.warn("inverse_transform is not supported because transform impute "
                                  "the unknown category nan when encode %s" % (col,))

        for switch in self.mapping:
            column_mapping = switch.get('mapping')
            inverse = pd.Series(data=column_mapping.index, index=column_mapping.get_values())
            X[switch.get('col')] = X[switch.get('col')].map(inverse).astype(switch.get('data_type'))

        return X if self.return_df else X.values

Perform the inverse transformation to encoded data . Will attempt best case reconstruction which means it will return nan for handle_missing and handle_unknown settings that break the bijection . We issue warnings when some of those cases occur .


def ordinal_encoding(X_in, mapping=None, cols=None, handle_unknown='value', handle_missing='value'):
        """
        Ordinal encoding uses a single column of integers to represent the classes. An optional mapping dict can be passed
        in, in this case we use the knowledge that there is some true order to the classes themselves. Otherwise, the classes
        are assumed to have no true order and integers are selected at random.
        """

        return_nan_series = pd.Series(data=[np.nan], index=[-2])

        X = X_in.copy(deep=True)

        if cols is None:
            cols = X.columns.values

        if mapping is not None:
            mapping_out = mapping
            for switch in mapping:
                column = switch.get('col')
                X[column] = X[column].map(switch['mapping'])

                try:
                    X[column] = X[column].astype(int)
                except ValueError as e:
                    X[column] = X[column].astype(float)

                if handle_unknown == 'value':
                    X[column].fillna(-1, inplace=True)
                elif handle_unknown == 'error':
                    missing = X[column].isnull()
                    if any(missing):
                        raise ValueError('Unexpected categories found in column %s' % column)

                if handle_missing == 'return_nan':
                    X[column] = X[column].map(return_nan_series).where(X[column] == -2, X[column])

        else:
            mapping_out = []
            for col in cols:

                nan_identity = np.nan

                if util.is_category(X[col].dtype):
                    categories = X[col].cat.categories
                else:
                    categories = X[col].unique()

                index = pd.Series(categories).fillna(nan_identity).unique()

                data = pd.Series(index=index, data=range(1, len(index) + 1))

                if handle_missing == 'value' and ~data.index.isnull().any():
                    data.loc[nan_identity] = -2
                elif handle_missing == 'return_nan':
                    data.loc[nan_identity] = -2

                mapping_out.append({'col': col, 'mapping': data, 'data_type': X[col].dtype}, )

        return X, mapping_out

Ordinal encoding uses a single column of integers to represent the classes . An optional mapping dict can be passed in in this case we use the knowledge that there is some true order to the classes themselves . Otherwise the classes are assumed to have no true order and integers are selected at random .


def reverse_dummies(self, X, mapping):
        """
        Convert dummy variable into numerical variables

        Parameters
        ----------
        X : DataFrame
        mapping: list-like
              Contains mappings of column to be transformed to it's new columns and value represented

        Returns
        -------
        numerical: DataFrame

        """
        out_cols = X.columns.values.tolist()
        mapped_columns = []
        for switch in mapping:
            col = switch.get('col')
            mod = switch.get('mapping')
            insert_at = out_cols.index(mod.columns[0])

            X.insert(insert_at, col, 0)
            positive_indexes = mod.index[mod.index > 0]
            for i in range(positive_indexes.shape[0]):
                existing_col = mod.columns[i]
                val = positive_indexes[i]
                X.loc[X[existing_col] == 1, col] = val
                mapped_columns.append(existing_col)
            X.drop(mod.columns, axis=1, inplace=True)
            out_cols = X.columns.values.tolist()

        return X

def reverse_dummies ( self , X , mapping ) : out_cols = X . columns . values . tolist ( ) mapped_columns = [ ] for switch in mapping : col = switch . get ( 'col' ) mod = switch . get ( 'mapping' ) insert_at = out_cols . index ( mod . columns [ 0 ] ) X . insert ( insert_at , col , 0 ) positive_indexes = mod . index [ mod . index > 0 ] for i in range ( positive_indexes . shape [ 0 ] ) : existing_col = mod . columns [ i ] val = positive_indexes [ i ] X . loc [ X [ existing_col ] == 1 , col ] = val mapped_columns . append ( existing_col ) X . drop ( mod . columns , axis = 1 , inplace = True ) out_cols = X . columns . values . tolist ( ) return X


def get_cars_data():
    """
    Load the cars dataset, split it into X and y, and then call the label encoder to get an integer y column.

    :return:
    """

    df = pd.read_csv('source_data/cars/car.data.txt')
    X = df.reindex(columns=[x for x in df.columns.values if x != 'class'])
    y = df.reindex(columns=['class'])
    y = preprocessing.LabelEncoder().fit_transform(y.values.reshape(-1, ))

    mapping = [
        {'col': 'buying', 'mapping': [('vhigh', 0), ('high', 1), ('med', 2), ('low', 3)]},
        {'col': 'maint', 'mapping': [('vhigh', 0), ('high', 1), ('med', 2), ('low', 3)]},
        {'col': 'doors', 'mapping': [('2', 0), ('3', 1), ('4', 2), ('5more', 3)]},
        {'col': 'persons', 'mapping': [('2', 0), ('4', 1), ('more', 2)]},
        {'col': 'lug_boot', 'mapping': [('small', 0), ('med', 1), ('big', 2)]},
        {'col': 'safety', 'mapping': [('high', 0), ('med', 1), ('low', 2)]},
    ]

    return X, y, mapping

def get_cars_data ( ) : df = pd . read_csv ( 'source_data/cars/car.data.txt' ) X = df . reindex ( columns = [ x for x in df . columns . values if x != 'class' ] ) y = df . reindex ( columns = [ 'class' ] ) y = preprocessing . LabelEncoder ( ) . fit_transform ( y . values . reshape ( - 1 , ) ) mapping = [ { 'col' : 'buying' , 'mapping' : [ ( 'vhigh' , 0 ) , ( 'high' , 1 ) , ( 'med' , 2 ) , ( 'low' , 3 ) ] } , { 'col' : 'maint' , 'mapping' : [ ( 'vhigh' , 0 ) , ( 'high' , 1 ) , ( 'med' , 2 ) , ( 'low' , 3 ) ] } , { 'col' : 'doors' , 'mapping' : [ ( '2' , 0 ) , ( '3' , 1 ) , ( '4' , 2 ) , ( '5more' , 3 ) ] } , { 'col' : 'persons' , 'mapping' : [ ( '2' , 0 ) , ( '4' , 1 ) , ( 'more' , 2 ) ] } , { 'col' : 'lug_boot' , 'mapping' : [ ( 'small' , 0 ) , ( 'med' , 1 ) , ( 'big' , 2 ) ] } , { 'col' : 'safety' , 'mapping' : [ ( 'high' , 0 ) , ( 'med' , 1 ) , ( 'low' , 2 ) ] } , ] return X , y , mapping

Load the cars dataset split it into X and y and then call the label encoder to get an integer y column .


def get_splice_data():
    """
    Load the mushroom dataset, split it into X and y, and then call the label encoder to get an integer y column.

    :return:
    """

    df = pd.read_csv('source_data/splice/splice.csv')
    X = df.reindex(columns=[x for x in df.columns.values if x != 'class'])
    X['dna'] = X['dna'].map(lambda x: list(str(x).strip()))
    for idx in range(60):
        X['dna_%d' % (idx, )] = X['dna'].map(lambda x: x[idx])
    del X['dna']

    y = df.reindex(columns=['class'])
    y = preprocessing.LabelEncoder().fit_transform(y.values.reshape(-1, ))

    # this data is truly categorical, with no known concept of ordering
    mapping = None

    return X, y, mapping

def get_splice_data ( ) : df = pd . read_csv ( 'source_data/splice/splice.csv' ) X = df . reindex ( columns = [ x for x in df . columns . values if x != 'class' ] ) X [ 'dna' ] = X [ 'dna' ] . map ( lambda x : list ( str ( x ) . strip ( ) ) ) for idx in range ( 60 ) : X [ 'dna_%d' % ( idx , ) ] = X [ 'dna' ] . map ( lambda x : x [ idx ] ) del X [ 'dna' ] y = df . reindex ( columns = [ 'class' ] ) y = preprocessing . LabelEncoder ( ) . fit_transform ( y . values . reshape ( - 1 , ) ) mapping = None return X , y , mapping

Load the mushroom dataset split it into X and y and then call the label encoder to get an integer y column .