Instruction:
Write a Python 3 function that can
write a memory object to a page.

Response:
def _apply_object_to_page(self, page_base, mo, page=None, overwrite=True):
        """
        Writes a memory object to a `page`

        :param page_base:   The base address of the page.
        :param mo:          The memory object.
        :param page:        (optional) the page to use.
        :param overwrite:   (optional) If False, only write to currently-empty memory.
        """
        page_num = page_base // self._page_size
        try:
            page = self._get_page(page_num,
                                  write=True,
                                  create=not self.allow_segv) if page is None else page
        except KeyError:
            if self.allow_segv:
                raise SimSegfaultError(mo.base, 'write-miss')
            else:
                raise
        if self.allow_segv and not page.concrete_permissions & Page.PROT_WRITE:
            raise SimSegfaultError(mo.base, 'non-writable')

        page.store_mo(self.state, mo, overwrite=overwrite)
        return True


Instruction:
Can you implement a function in Python 3 that
stores the memory object in the memory store.

Response:
def store_memory_object(self, mo, overwrite=True):
        """
        This function optimizes a large store by storing a single reference to the :class:`SimMemoryObject` instead of
        one for each byte.

        :param memory_object: the memory object to store
        """

        for p in self._containing_pages_mo(mo):
            self._apply_object_to_page(p, mo, overwrite=overwrite)

        self._update_range_mappings(mo.base, mo.object, mo.length)


Instruction:
Given the following Python 3 function, write the documentation
def replace_memory_object(self, old, new_content):

        if old.object.size() != new_content.size():
            raise SimMemoryError("memory objects can only be replaced by the same length content")

        new = SimMemoryObject(new_content, old.base, byte_width=self.byte_width)
        for p in self._containing_pages_mo(old):
            self._get_page(p//self._page_size, write=True).replace_mo(self.state, old, new)

        if isinstance(new.object, claripy.ast.BV):
            for b in range(old.base, old.base+old.length):
                self._update_mappings(b, new.object)
        return new

Response: Replaces the memory object old with a new memory object containing new_content.


Instruction:
Can you implement a function in Python 3 that
replaces all instances of expression old with expression new.

Response:
def replace_all(self, old, new):
        """
        Replaces all instances of expression `old` with expression `new`.

        :param old: A claripy expression. Must contain at least one named variable (to make it possible to use the
                    name index for speedup).
        :param new: The new variable to replace it with.
        """

        if options.REVERSE_MEMORY_NAME_MAP not in self.state.options:
            raise SimMemoryError("replace_all is not doable without a reverse name mapping. Please add "
                                 "sim_options.REVERSE_MEMORY_NAME_MAP to the state options")

        if not isinstance(old, claripy.ast.BV) or not isinstance(new, claripy.ast.BV):
            raise SimMemoryError("old and new arguments to replace_all() must be claripy.BV objects")

        if len(old.variables) == 0:
            raise SimMemoryError("old argument to replace_all() must have at least one named variable")

        # Compute an intersection between sets of memory objects for each unique variable name. The eventual memory
        # object set contains all memory objects that we should update.
        memory_objects = None
        for v in old.variables:
            if memory_objects is None:
                memory_objects = self.memory_objects_for_name(v)
            elif len(memory_objects) == 0:
                # It's a set and it's already empty
                # there is no way for it to go back...
                break
            else:
                memory_objects &= self.memory_objects_for_name(v)

        replaced_objects_cache = { }
        for mo in memory_objects:
            replaced_object = None

            if mo.object in replaced_objects_cache:
                if mo.object is not replaced_objects_cache[mo.object]:
                    replaced_object = replaced_objects_cache[mo.object]

            else:
                replaced_object = mo.object.replace(old, new)
                replaced_objects_cache[mo.object] = replaced_object
                if mo.object is replaced_object:
                    # The replace does not really occur
                    replaced_object = None

            if replaced_object is not None:
                self.replace_memory_object(mo, replaced_object)


Instruction:
Create a Python 3 function for
returning a generator of addresses that contain expressions that contain a variable named n.

Response:
def addrs_for_name(self, n):
        """
        Returns addresses that contain expressions that contain a variable named `n`.
        """
        if n not in self._name_mapping:
            return

        self._mark_updated_mapping(self._name_mapping, n)

        to_discard = set()
        for e in self._name_mapping[n]:
            try:
                if n in self[e].object.variables: yield e
                else: to_discard.add(e)
            except KeyError:
                to_discard.add(e)
        self._name_mapping[n] -= to_discard


Instruction:
Create a Python 3 function to
return a list of addresses that contain expressions that contain a variable with the hash h.

Response:
def addrs_for_hash(self, h):
        """
        Returns addresses that contain expressions that contain a variable with the hash of `h`.
        """
        if h not in self._hash_mapping:
            return

        self._mark_updated_mapping(self._hash_mapping, h)

        to_discard = set()
        for e in self._hash_mapping[h]:
            try:
                if h == hash(self[e].object): yield e
                else: to_discard.add(e)
            except KeyError:
                to_discard.add(e)
        self._hash_mapping[h] -= to_discard


Instruction:
Here you have a function in Python 3, explain what it does
def get_fd(self, fd):
        try:
            fd = self.state.solver.eval_one(fd)
        except SimSolverError:
            ideal = self._pick_fd()
            self.state.solver.add(fd == ideal)
            if not self.state.solver.satisfiable():
                raise SimPosixError("Tried to do operation on symbolic but partially constrained file descriptor")
            fd = ideal
            new_filename = b'/tmp/angr_implicit_%d' % self.autotmp_counter
            l.warning("Tried to look up a symbolic fd - constrained to %d and opened %s", ideal, new_filename)
            self.autotmp_counter += 1
            if self.open(new_filename, Flags.O_RDWR, preferred_fd=fd) != fd:
                raise SimPosixError("Something went wrong trying to open implicit temp")

        return self.fd.get(fd)

Response: Returns the SimFileDescriptor associated with the given number.


Instruction:
Explain what the following Python 3 code does
def close(self, fd):
        try:
            fd = self.state.solver.eval_one(fd)
        except SimSolverError:
            l.error("Trying to close a symbolic file descriptor")
            return False

        if fd not in self.fd:
            l.info("Trying to close an unopened file descriptor")
            return False

        self.state.history.add_event('fs_close', fd=fd, close_idx=len(self.closed_fds))
        self.closed_fds.append((fd, self.fd[fd]))

        del self.fd[fd]
        return True

Response: Closes the given file descriptor. Returns True if the operation succeeded.


Instruction:
How would you implement a function in Python 3 that
gets the current sigmask.

Response:
def sigmask(self, sigsetsize=None):
        """
        Gets the current sigmask. If it's blank, a new one is created (of sigsetsize).

        :param sigsetsize: the size (in *bytes* of the sigmask set)
        :return: the sigmask
        """
        if self._sigmask is None:
            if sigsetsize is not None:
                sc = self.state.solver.eval(sigsetsize)
                self.state.add_constraints(sc == sigsetsize)
                self._sigmask = self.state.solver.BVS('initial_sigmask', sc*self.state.arch.byte_width, key=('initial_sigmask',), eternal=True)
            else:
                self._sigmask = self.state.solver.BVS('initial_sigmask', self.sigmask_bits, key=('initial_sigmask',), eternal=True)
        return self._sigmask


Instruction:
Can you generate the documentation for the following Python 3 function
def sigprocmask(self, how, new_mask, sigsetsize, valid_ptr=True):
        oldmask = self.sigmask(sigsetsize)
        self._sigmask = self.state.solver.If(valid_ptr,
            self.state.solver.If(how == self.SIG_BLOCK,
                oldmask | new_mask,
                self.state.solver.If(how == self.SIG_UNBLOCK,
                    oldmask & (~new_mask),
                    self.state.solver.If(how == self.SIG_SETMASK,
                        new_mask,
                        oldmask
                     )
                )
            ),
            oldmask
        )

Response: Updates the signal mask.


Instruction:
Can you create a Python 3 function that
dumps a file by path.

Response:
def dump_file_by_path(self, path, **kwargs):
        """
        Returns the concrete content for a file by path.

        :param path: file path as string
        :param kwargs: passed to state.solver.eval
        :return: file contents as string
        """
        file = self.state.fs.get(path)
        if file is None:
            return None
        return file.concretize(**kwargs)


Instruction:
Can you implement a function in Python 3 that
dumps the concrete content for a file descriptor.

Response:
def dumps(self, fd, **kwargs):
        """
        Returns the concrete content for a file descriptor.

        BACKWARD COMPATIBILITY: if you ask for file descriptors 0 1 or 2, it will return the data from stdin, stdout,
        or stderr as a flat string.

        :param fd:  A file descriptor.
        :return:    The concrete content.
        :rtype:     str
        """
        if 0 <= fd <= 2:
            data = [self.stdin, self.stdout, self.stderr][fd].concretize(**kwargs)
            if type(data) is list:
                data = b''.join(data)
            return data
        return self.get_fd(fd).concretize(**kwargs)


Instruction:
Implement a Python 3 function for
handling the concrete execution of the process This method takes care of: 1- Set the breakpoints on the addresses provided by the user 2- Concretize the symbolic variables and perform the write inside the concrete process 3- Continue the program execution. :param state: The state with which to execute :param extra_stop_points: list of a addresses where to stop the concrete execution and return to the simulated one :param concretize: list of tuples (address, symbolic variable) that are going to be written in the concrete process memory :param timeout: how long we should wait the concrete target to reach the breakpoint :return: None

Response:
def to_engine(self, state, extra_stop_points, concretize, timeout):
        """
        Handle the concrete execution of the process
        This method takes care of:
        1- Set the breakpoints on the addresses provided by the user
        2- Concretize the symbolic variables and perform the write inside the concrete process
        3- Continue the program execution.

        :param state:               The state with which to execute
        :param extra_stop_points:   list of a addresses where to stop the concrete execution and return to the
                                    simulated one
        :param concretize:          list of tuples (address, symbolic variable) that are going to be written
                                    in the concrete process memory
        :param timeout:             how long we should wait the concrete target to reach the breakpoint
        :return: None
        """

        state.timeout = False
        state.errored = False
        extra_stop_points = [] if extra_stop_points is None else extra_stop_points

        l.debug("Entering in SimEngineConcrete: simulated address %#x concrete address %#x stop points %s",
                state.addr, self.target.read_register("pc"), map(hex, extra_stop_points))

        if concretize:
            l.debug("SimEngineConcrete is concretizing variables before resuming the concrete process")

            for sym_var in concretize:
                sym_var_address = state.solver.eval(sym_var[0])
                sym_var_value = state.solver.eval(sym_var[1], cast_to=bytes)
                l.debug("Concretize memory at address %#x with value %s", sym_var_address, str(sym_var_value))
                self.target.write_memory(sym_var_address, sym_var_value, raw=True)

        # Set breakpoint on remote target
        for stop_point in extra_stop_points:
            l.debug("Setting breakpoints at %#x", stop_point)
            self.target.set_breakpoint(stop_point, temporary=True)

        if timeout > 0:
            l.debug("Found timeout as option, setting it up!")

            def timeout_handler():
                self.target.stop()    # stop the concrete target now!
                state.timeout = True  # this will end up in the timeout stash

            execution_timer = threading.Timer(timeout, timeout_handler)
            execution_timer.start()  # start the timer!

        # resuming of the concrete process, if the target won't reach the
        # breakpoint specified by the user the timeout will abort angr execution.
        l.debug("SimEngineConcrete is resuming the concrete process")
        self.target.run()
        l.debug("SimEngineConcrete has successfully resumed the process")

        if state.timeout:
            l.critical("Timeout has been reached during resuming of concrete process")
            l.critical("This can be a bad thing ( the ConcreteTarget didn't hit your breakpoint ) or"
                       "just it will take a while.")

        # reset the alarm
        if timeout > 0:
            execution_timer.cancel()

        # removing all breakpoints set by the concrete target
        for stop_point in extra_stop_points:
            self.target.remove_breakpoint(stop_point)

        # handling the case in which the program stops at a point different than the breakpoints set
        # by the user.
        current_pc = self.target.read_register("pc")
        if current_pc not in extra_stop_points and not self.target.timeout:
            l.critical("Stopped at unexpected location inside the concrete process: %#x", current_pc)
            raise AngrError


Instruction:
Explain what the following Python 3 code does
def check_concrete_target_methods(concrete_target):
        entry_point = concrete_target.read_register("pc")
        if not type(entry_point) is int:
            l.error("read_register result type is %s, should be <type 'int'>", (type(entry_point)))
            return False

        mem_read = concrete_target.read_memory(entry_point, 0x4)

        if not type(mem_read) is bytes:
            l.error("read_memory result type is %s, should be <type 'bytes'>", (type(mem_read)))
            return False

        try:
            concrete_target.read_register("not_existent_reg")
            l.error("read_register should raise a SimConcreteRegisterError when accessing non existent registers")
            return False

        except SimConcreteRegisterError:
            l.debug("read_register raise a SimConcreteRegisterError, ok!")

        try:
            concrete_target.read_memory(0x0, 0x4)
            l.error("read_memory should raise a SimConcreteMemoryError when accessing non mapped memory")
            return False

        except SimConcreteMemoryError:
            l.debug("read_register raise a SimConcreteMemoryError, ok!")

        return True

Response: Check if the concrete target methods return the correct type of data


Instruction:
Can you generate the documentation for the following Python 3 function
def process(self, state,
            irsb=None,
            skip_stmts=0,
            last_stmt=99999999,
            whitelist=None,
            inline=False,
            force_addr=None,
            insn_bytes=None,
            size=None,
            num_inst=None,
            traceflags=0,
            thumb=False,
            extra_stop_points=None,
            opt_level=None,
            **kwargs):
        """
        :param state:       The state with which to execute
        :param irsb:        The PyVEX IRSB object to use for execution. If not provided one will be lifted.
        :param skip_stmts:  The number of statements to skip in processing
        :param last_stmt:   Do not execute any statements after this statement
        :param whitelist:   Only execute statements in this set
        :param inline:      This is an inline execution. Do not bother copying the state.
        :param force_addr:  Force execution to pretend that we're working at this concrete address

        :param thumb:       Whether the block should be lifted in ARM's THUMB mode.
        :param extra_stop_points:
                            An extra set of points at which to break basic blocks
        :param opt_level:   The VEX optimization level to use.
        :param insn_bytes:  A string of bytes to use for the block instead of the project.
        :param size:        The maximum size of the block, in bytes.
        :param num_inst:    The maximum number of instructions.
        :param traceflags:  traceflags to be passed to VEX. (default: 0)
        :returns:           A SimSuccessors object categorizing the block's successors
        """
        if 'insn_text' in kwargs:

            if insn_bytes is not None:
                raise SimEngineError("You cannot provide both 'insn_bytes' and 'insn_text'!")

            insn_bytes = \
                self.project.arch.asm(kwargs['insn_text'], addr=kwargs.get('addr', 0),
                                      thumb=thumb, as_bytes=True)

            if insn_bytes is None:
                raise AngrAssemblyError("Assembling failed. Please make sure keystone is installed, and the assembly"
                                        " string is correct.")

        return super(SimEngineVEX, self).process(state, irsb,
                skip_stmts=skip_stmts,
                last_stmt=last_stmt,
                whitelist=whitelist,
                inline=inline,
                force_addr=force_addr,
                insn_bytes=insn_bytes,
                size=size,
                num_inst=num_inst,
                traceflags=traceflags,
                thumb=thumb,
                extra_stop_points=extra_stop_points,
                opt_level=opt_level)

Response: Executes the given state and returns a list of SimSuccessors objects.


Instruction:
Given the following Python 3 function, write the documentation
def _handle_statement(self, state, successors, stmt):
        if type(stmt) == pyvex.IRStmt.IMark:
            # TODO how much of this could be moved into the imark handler
            ins_addr = stmt.addr + stmt.delta
            state.scratch.ins_addr = ins_addr

            # Raise an exception if we're suddenly in self-modifying code
            for subaddr in range(stmt.len):
                if subaddr + stmt.addr in state.scratch.dirty_addrs:
                    raise SimReliftException(state)
            state._inspect('instruction', BP_AFTER)

            l.debug("IMark: %#x", stmt.addr)
            state.scratch.num_insns += 1
            state._inspect('instruction', BP_BEFORE, instruction=ins_addr)

        # process it!
        try:
            stmt_handler = self.stmt_handlers[stmt.tag_int]
        except IndexError:
            l.error("Unsupported statement type %s", (type(stmt)))
            if o.BYPASS_UNSUPPORTED_IRSTMT not in state.options:
                raise UnsupportedIRStmtError("Unsupported statement type %s" % (type(stmt)))
            state.history.add_event('resilience', resilience_type='irstmt', stmt=type(stmt).__name__, message='unsupported IRStmt')
            return None
        else:
            exit_data = stmt_handler(self, state, stmt)

        # for the exits, put *not* taking the exit on the list of constraints so
        # that we can continue on. Otherwise, add the constraints
        if exit_data is not None:
            l.debug("%s adding conditional exit", self)

            target, guard, jumpkind = exit_data

            # Produce our successor state!
            # Let SimSuccessors.add_successor handle the nitty gritty details

            cont_state = None
            exit_state = None

            if o.COPY_STATES not in state.options:
                # very special logic to try to minimize copies
                # first, check if this branch is impossible
                if guard.is_false():
                    cont_state = state
                elif o.LAZY_SOLVES not in state.options and not state.solver.satisfiable(extra_constraints=(guard,)):
                    cont_state = state

                # then, check if it's impossible to continue from this branch
                elif guard.is_true():
                    exit_state = state
                elif o.LAZY_SOLVES not in state.options and not state.solver.satisfiable(extra_constraints=(claripy.Not(guard),)):
                    exit_state = state
                else:
                    exit_state = state.copy()
                    cont_state = state
            else:
                exit_state = state.copy()
                cont_state = state

            if exit_state is not None:
                successors.add_successor(exit_state, target, guard, jumpkind,
                                         exit_stmt_idx=state.scratch.stmt_idx, exit_ins_addr=state.scratch.ins_addr)

            if cont_state is None:
                return False

            # Do our bookkeeping on the continuing state
            cont_condition = claripy.Not(guard)
            cont_state.add_constraints(cont_condition)
            cont_state.scratch.guard = claripy.And(cont_state.scratch.guard, cont_condition)

        return True

Response: This function handles the single statement.


Instruction:
Can you write a function in Python 3 where it
lifts an IRSB. There are many possible valid sets of parameters. You at the very least must pass some source of data, some source of an architecture, and some source of an address. Sources of data in order of priority: insn_bytes, clemory, state Sources of an address, in order of priority: addr, state Sources of an architecture, in order of priority: arch, clemory, state :param state: A state to use as a data source. :param clemory: A cle.memory.Clemory object to use as a data source. :param addr: The address at which to start the block. :param thumb: Whether the block should be lifted in ARM's THUMB mode. :param opt_level: The VEX optimization level to use. The final IR optimization level is determined by (ordered by priority): - Argument opt_level - opt_level is set to 1 if OPTIMIZE_IR exists in state options - self._default_opt_level :param insn_bytes: A string of bytes to use as a data source. :param size: The maximum size of the block, in bytes. :param num_inst: The maximum number of instructions. :param traceflags: traceflags to be passed to VEX. (default: 0) :param strict_block_end: Whether to force blocks to end at all conditional branches (default: false)

Response:
def lift(self,
             state=None,
             clemory=None,
             insn_bytes=None,
             arch=None,
             addr=None,
             size=None,
             num_inst=None,
             traceflags=0,
             thumb=False,
             extra_stop_points=None,
             opt_level=None,
             strict_block_end=None,
             skip_stmts=False,
             collect_data_refs=False):

        """
        Lift an IRSB.

        There are many possible valid sets of parameters. You at the very least must pass some
        source of data, some source of an architecture, and some source of an address.

        Sources of data in order of priority: insn_bytes, clemory, state

        Sources of an address, in order of priority: addr, state

        Sources of an architecture, in order of priority: arch, clemory, state

        :param state:           A state to use as a data source.
        :param clemory:         A cle.memory.Clemory object to use as a data source.
        :param addr:            The address at which to start the block.
        :param thumb:           Whether the block should be lifted in ARM's THUMB mode.
        :param opt_level:       The VEX optimization level to use. The final IR optimization level is determined by
                                (ordered by priority):
                                - Argument opt_level
                                - opt_level is set to 1 if OPTIMIZE_IR exists in state options
                                - self._default_opt_level
        :param insn_bytes:      A string of bytes to use as a data source.
        :param size:            The maximum size of the block, in bytes.
        :param num_inst:        The maximum number of instructions.
        :param traceflags:      traceflags to be passed to VEX. (default: 0)
        :param strict_block_end:   Whether to force blocks to end at all conditional branches (default: false)
        """

        # phase 0: sanity check
        if not state and not clemory and not insn_bytes:
            raise ValueError("Must provide state or clemory or insn_bytes!")
        if not state and not clemory and not arch:
            raise ValueError("Must provide state or clemory or arch!")
        if addr is None and not state:
            raise ValueError("Must provide state or addr!")
        if arch is None:
            arch = clemory._arch if clemory else state.arch
        if arch.name.startswith("MIPS") and self._single_step:
            l.error("Cannot specify single-stepping on MIPS.")
            self._single_step = False

        # phase 1: parameter defaults
        if addr is None:
            addr = state.solver.eval(state._ip)
        if size is not None:
            size = min(size, VEX_IRSB_MAX_SIZE)
        if size is None:
            size = VEX_IRSB_MAX_SIZE
        if num_inst is not None:
            num_inst = min(num_inst, VEX_IRSB_MAX_INST)
        if num_inst is None and self._single_step:
            num_inst = 1
        if opt_level is None:
            if state and o.OPTIMIZE_IR in state.options:
                opt_level = 1
            else:
                opt_level = self._default_opt_level
        if strict_block_end is None:
            strict_block_end = self.default_strict_block_end
        if self._support_selfmodifying_code:
            if opt_level > 0:
                if once('vex-engine-smc-opt-warning'):
                    l.warning("Self-modifying code is not always correctly optimized by PyVEX. "
                              "To guarantee correctness, VEX optimizations have been disabled.")
                opt_level = 0
                if state and o.OPTIMIZE_IR in state.options:
                    state.options.remove(o.OPTIMIZE_IR)
        if skip_stmts is not True:
            skip_stmts = False

        use_cache = self._use_cache
        if skip_stmts or collect_data_refs:
            # Do not cache the blocks if skip_stmts or collect_data_refs are enabled
            use_cache = False

        # phase 2: thumb normalization
        thumb = int(thumb)
        if isinstance(arch, ArchARM):
            if addr % 2 == 1:
                thumb = 1
            if thumb:
                addr &= ~1
        elif thumb:
            l.error("thumb=True passed on non-arm architecture!")
            thumb = 0

        # phase 3: check cache
        cache_key = None
        if use_cache:
            cache_key = (addr, insn_bytes, size, num_inst, thumb, opt_level, strict_block_end)
            if cache_key in self._block_cache:
                self._block_cache_hits += 1
                irsb = self._block_cache[cache_key]
                stop_point = self._first_stoppoint(irsb, extra_stop_points)
                if stop_point is None:
                    return irsb
                else:
                    size = stop_point - addr
                    # check the cache again
                    cache_key = (addr, insn_bytes, size, num_inst, thumb, opt_level, strict_block_end)
                    if cache_key in self._block_cache:
                        self._block_cache_hits += 1
                        return self._block_cache[cache_key]
                    else:
                        self._block_cache_misses += 1
            else:
                # a special case: `size` is used as the maximum allowed size
                tmp_cache_key = (addr, insn_bytes, VEX_IRSB_MAX_SIZE, num_inst, thumb, opt_level, strict_block_end)
                try:
                    irsb = self._block_cache[tmp_cache_key]
                    if irsb.size <= size:
                        self._block_cache_hits += 1
                        return self._block_cache[tmp_cache_key]
                except KeyError:
                    self._block_cache_misses += 1

        # phase 4: get bytes
        if insn_bytes is not None:
            buff, size = insn_bytes, len(insn_bytes)
        else:
            buff, size = self._load_bytes(addr, size, state, clemory)

        if not buff or size == 0:
            raise SimEngineError("No bytes in memory for block starting at %#x." % addr)

        # phase 5: call into pyvex
        # l.debug("Creating pyvex.IRSB of arch %s at %#x", arch.name, addr)
        try:
            for subphase in range(2):

                irsb = pyvex.lift(buff, addr + thumb, arch,
                                  max_bytes=size,
                                  max_inst=num_inst,
                                  bytes_offset=thumb,
                                  traceflags=traceflags,
                                  opt_level=opt_level,
                                  strict_block_end=strict_block_end,
                                  skip_stmts=skip_stmts,
                                  collect_data_refs=collect_data_refs,
                                  )

                if subphase == 0 and irsb.statements is not None:
                    # check for possible stop points
                    stop_point = self._first_stoppoint(irsb, extra_stop_points)
                    if stop_point is not None:
                        size = stop_point - addr
                        continue

                if use_cache:
                    self._block_cache[cache_key] = irsb
                return irsb

        # phase x: error handling
        except pyvex.PyVEXError as e:
            l.debug("VEX translation error at %#x", addr)
            if isinstance(buff, bytes):
                l.debug('Using bytes: %r', buff)
            else:
                l.debug("Using bytes: %r", pyvex.ffi.buffer(buff, size))
            raise SimTranslationError("Unable to translate bytecode") from e


Instruction:
Can you implement a function in Python 3 that
enumerates the imarks in the block and return the first stop point.

Response:
def _first_stoppoint(self, irsb, extra_stop_points=None):
        """
        Enumerate the imarks in the block. If any of them (after the first one) are at a stop point, returns the address
        of the stop point. None is returned otherwise.
        """
        if self._stop_points is None and extra_stop_points is None and self.project is None:
            return None

        first_imark = True
        for stmt in irsb.statements:
            if type(stmt) is pyvex.stmt.IMark:  # pylint: disable=unidiomatic-typecheck
                addr = stmt.addr + stmt.delta
                if not first_imark:
                    if self.is_stop_point(addr, extra_stop_points):
                        # could this part be moved by pyvex?
                        return addr
                    if stmt.delta != 0 and self.is_stop_point(stmt.addr, extra_stop_points):
                        return addr

                first_imark = False
        return None


Instruction:
Can you generate the documentation for the following Python 3 function
def _genenare_callmap_sif(self, filepath):
        with open(filepath, "wb") as f:
            for src, dst in self.callgraph.edges():
                f.write("%#x\tDirectEdge\t%#x\n" % (src, dst))

Response: Generate a sif file from the call map.


Instruction:
Can you implement a function in Python 3 that
returns the function who has the least address that is greater than or equal to addr.

Response:
def ceiling_func(self, addr):
        """
        Return the function who has the least address that is greater than or equal to `addr`.

        :param int addr: The address to query.
        :return:         A Function instance, or None if there is no other function after `addr`.
        :rtype:          Function or None
        """

        try:
            next_addr = self._function_map.ceiling_addr(addr)
            return self._function_map.get(next_addr)

        except KeyError:
            return None


Instruction:
How would you implement a function in Python 3 that
returns the function who has the greatest address that is less than or equal to addr.

Response:
def floor_func(self, addr):
        """
        Return the function who has the greatest address that is less than or equal to `addr`.

        :param int addr: The address to query.
        :return:         A Function instance, or None if there is no other function before `addr`.
        :rtype:          Function or None
        """

        try:
            prev_addr = self._function_map.floor_addr(addr)
            return self._function_map[prev_addr]

        except KeyError:
            return None


Instruction:
Can you generate a brief explanation for the following Python 3 code
def function(self, addr=None, name=None, create=False, syscall=False, plt=None):
        if addr is not None:
            try:
                f = self._function_map.get(addr)
                if plt is None or f.is_plt == plt:
                    return f
            except KeyError:
                if create:
                    # the function is not found
                    f = self._function_map[addr]
                    if name is not None:
                        f.name = name
                    if syscall:
                        f.is_syscall=True
                    return f
        elif name is not None:
            for func in self._function_map.values():
                if func.name == name:
                    if plt is None or func.is_plt == plt:
                        return func

        return None

Response: Returns a Function instance from the function manager.


Instruction:
Can you create a Python 3 function that
returns a list of nodes that are control dependent on the given node in the control dependence graph

Response:
def get_dependants(self, run):
        """
        Return a list of nodes that are control dependent on the given node in the control dependence graph
        """
        if run in self._graph.nodes():
            return list(self._graph.successors(run))
        else:
            return []


Instruction:
Here you have a function in Python 3, explain what it does
def get_guardians(self, run):
        if run in self._graph.nodes():
            return list(self._graph.predecessors(run))
        else:
            return []

Response: Returns a list of nodes on whom the specific node is control dependent in the control dependence graph


Instruction:
Can you generate the documentation for the following Python 3 function
def _construct(self):

        self._acyclic_cfg = self._cfg.copy()
        # TODO: Cycle-removing is not needed - confirm it later
        # The CFG we use should be acyclic!
        #self._acyclic_cfg.remove_cycles()

        # Pre-process the acyclic CFG
        self._pre_process_cfg()

        # Construct post-dominator tree
        self._pd_construct()

        self._graph = networkx.DiGraph()

        # Construct the reversed dominance frontier mapping
        rdf = compute_dominance_frontier(self._normalized_cfg, self._post_dom)

        for y in self._cfg.graph.nodes():
            if y not in rdf:
                continue
            for x in rdf[y]:
                self._graph.add_edge(x, y)

Response: Construct a control dependence graph for the current CFG and the tree that is acyclic.


Instruction:
How would you explain what the following Python 3 function does
def _pre_process_cfg(self):
        for _, dst, data in self._acyclic_cfg.graph.edges(data=True):
            if 'jumpkind' in data and data['jumpkind'] == 'Ijk_FakeRet':
                all_edges_to_dst = self._acyclic_cfg.graph.in_edges([ dst ], data=True)
                if not any((s, d) for s, d, da in all_edges_to_dst if da['jumpkind'] != 'Ijk_FakeRet' ):
                    # All in edges are FakeRets
                    # Change them to a normal edge
                    for _, _, data_ in all_edges_to_dst:
                        data_['jumpkind'] = 'Ijk_Boring'

Response: Pre - process the acyclic CFG.


Instruction:
Given the following Python 3 function, write the documentation
def _post_process(self):
        # TODO: Verify its correctness
        loop_back_edges = self._cfg.get_loop_back_edges()
        for b1, b2 in loop_back_edges:
            self._graph.add_edge(b1, b2)

Response: Add edges to the graph.


Instruction:
Create a Python 3 function for
processing the post dominator of the BBL.

Response:
def _pd_post_process(self, cfg):
        """
        Take care of those loop headers/tails where we manually broke their
        connection to the next BBL
        """
        loop_back_edges = self._cfg.get_loop_back_edges()

        for b1, b2 in loop_back_edges:
            # The edge between b1 and b2 is manually broken
            # The post dominator of b1 should be b2 (or not?)

            successors = list(self._pd_graph_successors(cfg, b1))

            if len(successors) == 0:
                if b2 in self._post_dom:
                    self._post_dom.add_edge(b1, b2)
                else:
                    _l.debug("%s is not in post dominator dict.", b2)


Instruction:
Create a Python 3 function to
create a phi variable for variables at the current block.

Response:
def make_phi_node(self, block_addr, *variables):
        """
        Create a phi variable for variables at block `block_addr`.

        :param int block_addr:  The address of the current block.
        :param variables:       Variables that the phi variable represents.
        :return:                The created phi variable.
        """

        existing_phis = set()
        non_phis = set()
        for var in variables:
            if self.is_phi_variable(var):
                existing_phis.add(var)
            else:
                non_phis.add(var)
        if len(existing_phis) == 1:
            existing_phi = next(iter(existing_phis))
            if non_phis.issubset(self.get_phi_subvariables(existing_phi)):
                return existing_phi
            else:
                # Update phi variables
                self._phi_variables[existing_phi] |= non_phis
                return existing_phi

        repre = next(iter(variables))
        repre_type = type(repre)
        if repre_type is SimRegisterVariable:
            ident_sort = 'register'
            a = SimRegisterVariable(repre.reg, repre.size, ident=self.next_variable_ident(ident_sort))
        elif repre_type is SimMemoryVariable:
            ident_sort = 'memory'
            a = SimMemoryVariable(repre.addr, repre.size, ident=self.next_variable_ident(ident_sort))
        elif repre_type is SimStackVariable:
            ident_sort = 'stack'
            a = SimStackVariable(repre.offset, repre.size, ident=self.next_variable_ident(ident_sort))
        else:
            raise TypeError('make_phi_node(): Unsupported variable type "%s".' % type(repre))

        # Keep a record of all phi variables
        self._phi_variables[a] = set(variables)
        self._phi_variables_by_block[block_addr].add(a)

        return a


Instruction:
Can you generate the documentation for the following Python 3 function
def get_variables(self, sort=None, collapse_same_ident=False):

        variables = [ ]

        if collapse_same_ident:
            raise NotImplementedError()

        for var in self._variables:
            if sort == 'stack' and not isinstance(var, SimStackVariable):
                continue
            if sort == 'reg' and not isinstance(var, SimRegisterVariable):
                continue
            variables.append(var)

        return variables

Response: Get a list of variables.


Instruction:
Explain what the following Python 3 code does
def get_phi_subvariables(self, var):

        if not self.is_phi_variable(var):
            return set()
        return self._phi_variables[var]

Response: Returns a set of sub - variables that phi variable var represents.


Instruction:
Implement a Python 3 function for
getting a dict of phi variables and their corresponding variables.

Response:
def get_phi_variables(self, block_addr):
        """
        Get a dict of phi variables and their corresponding variables.

        :param int block_addr:  Address of the block.
        :return:                A dict of phi variables of an empty dict if there are no phi variables at the block.
        :rtype:                 dict
        """

        if block_addr not in self._phi_variables_by_block:
            return dict()
        variables = { }
        for phi in self._phi_variables_by_block[block_addr]:
            variables[phi] = self._phi_variables[phi]
        return variables


Instruction:
Can you tell what is the following Python 3 function doing
def input_variables(self, exclude_specials=True):

        def has_write_access(accesses):
            return any(acc for acc in accesses if acc.access_type == 'write')

        def has_read_access(accesses):
            return any(acc for acc in accesses if acc.access_type == 'read')

        input_variables = [ ]

        for variable, accesses in self._variable_accesses.items():
            if not has_write_access(accesses) and has_read_access(accesses):
                if not exclude_specials or not variable.category:
                    input_variables.append(variable)

        return input_variables

Response: Get all variables that have never been written to.


Instruction:
Write a Python 3 script for
assigning default names to all variables.

Response:
def assign_variable_names(self):
        """
        Assign default names to all variables.

        :return: None
        """

        for var in self._variables:
            if isinstance(var, SimStackVariable):
                if var.name is not None:
                    continue
                if var.ident.startswith('iarg'):
                    var.name = 'arg_%x' % var.offset
                else:
                    var.name = 's_%x' % (-var.offset)
                    # var.name = var.ident
            elif isinstance(var, SimRegisterVariable):
                if var.name is not None:
                    continue
                var.name = var.ident


Instruction:
Can you tell what is the following Python 3 function doing
def get_variable_accesses(self, variable, same_name=False):

        if variable.region == 'global':
            return self.global_manager.get_variable_accesses(variable, same_name=same_name)

        elif variable.region in self.function_managers:
            return self.function_managers[variable.region].get_variable_accesses(variable, same_name=same_name)

        l.warning('get_variable_accesses(): Region %s is not found.', variable.region)
        return [ ]

Response: Get a list of all references to the given variable.


Instruction:
Given the following Python 3 function, write the documentation
def call_c(self, c_args):

        c_args = c_args.strip()
        if c_args[0] != "(":
            c_args = "(" + c_args
        if c_args[-1] != ")":
            c_args += ")"

        # Parse arguments
        content = "int main() { func%s; }" % c_args
        ast = pycparser.CParser().parse(content)

        if not ast.ext or not isinstance(ast.ext[0], pycparser.c_ast.FuncDef):
            raise AngrCallableError("Error in parsing the given C-style argument string.")

        if not ast.ext[0].body.block_items or not isinstance(ast.ext[0].body.block_items[0], pycparser.c_ast.FuncCall):
            raise AngrCallableError("Error in parsing the given C-style argument string: "
                                    "Cannot find the expected function call.")

        arg_exprs = ast.ext[0].body.block_items[0].args.exprs

        args = [ ]
        for expr in arg_exprs:
            if isinstance(expr, pycparser.c_ast.Constant):
                # string
                if expr.type == "string":
                    args.append(expr.value[1:-1])
                elif expr.type == "int":
                    args.append(int(expr.value))
                else:
                    raise AngrCallableError("Unsupported expression type %s." % expr.type)
            else:
                raise AngrCallableError("Unsupported expression type %s." % type(expr))

        return self.__call__(*args)

Response: This is a private method that is called by the function that is called by the function.


Instruction:
Implement a Python 3 function for
discarding the ancestry of this state.

Response:
def trim(self):
        """
        Discard the ancestry of this state.
        """
        new_hist = self.copy({})
        new_hist.parent = None
        self.state.register_plugin('history', new_hist)


Instruction:
How would you explain what the following Python 3 function does
def filter_actions(self, block_addr=None, block_stmt=None, insn_addr=None, read_from=None, write_to=None):
        if read_from is not None:
            if write_to is not None:
                raise ValueError("Can't handle read_from and write_to at the same time!")
            if read_from in ('reg', 'mem'):
                read_type = read_from
                read_offset = None
            elif isinstance(read_from, str):
                read_type = 'reg'
                read_offset = self.state.project.arch.registers[read_from][0]
            else:
                read_type = 'mem'
                read_offset = read_from
        if write_to is not None:
            if write_to in ('reg', 'mem'):
                write_type = write_to
                write_offset = None
            elif isinstance(write_to, str):
                write_type = 'reg'
                write_offset = self.state.project.arch.registers[write_to][0]
            else:
                write_type = 'mem'
                write_offset = write_to

        #def addr_of_stmt(bbl_addr, stmt_idx):
        #    if stmt_idx is None:
        #        return None
        #    stmts = self.state.project.factory.block(bbl_addr).vex.statements
        #    if stmt_idx >= len(stmts):
        #        return None
        #    for i in reversed(range(stmt_idx + 1)):
        #        if stmts[i].tag == 'Ist_IMark':
        #            return stmts[i].addr + stmts[i].delta
        #    return None

        def action_reads(action):
            if action.type != read_type:
                return False
            if action.action != 'read':
                return False
            if read_offset is None:
                return True
            addr = action.addr
            if isinstance(addr, SimActionObject):
                addr = addr.ast
            if isinstance(addr, claripy.ast.Base):
                if addr.symbolic:
                    return False
                addr = self.state.solver.eval(addr)
            if addr != read_offset:
                return False
            return True

        def action_writes(action):
            if action.type != write_type:
                return False
            if action.action != 'write':
                return False
            if write_offset is None:
                return True
            addr = action.addr
            if isinstance(addr, SimActionObject):
                addr = addr.ast
            if isinstance(addr, claripy.ast.Base):
                if addr.symbolic:
                    return False
                addr = self.state.solver.eval(addr)
            if addr != write_offset:
                return False
            return True

        return [x for x in reversed(self.actions) if
                    (block_addr is None or x.bbl_addr == block_addr) and
                    (block_stmt is None or x.stmt_idx == block_stmt) and
                    (read_from is None or action_reads(x)) and
                    (write_to is None or action_writes(x)) and
                    (insn_addr is None or (x.sim_procedure is None and x.ins_addr == insn_addr))
                    #(insn_addr is None or (x.sim_procedure is None and addr_of_stmt(x.bbl_addr, x.stmt_idx) == insn_addr))
            ]

Response: Filter self. actions based on some common parameters.


Instruction:
Write a Python 3 function for
finding the common ancestor between this path history node and other.

Response:
def closest_common_ancestor(self, other):
        """
        Find the common ancestor between this history node and 'other'.

        :param other:    the PathHistory to find a common ancestor with.
        :return:        the common ancestor SimStateHistory, or None if there isn't one
        """
        our_history_iter = reversed(HistoryIter(self))
        their_history_iter = reversed(HistoryIter(other))
        sofar = set()

        while True:
            our_done = False
            their_done = False

            try:
                our_next = next(our_history_iter)
                if our_next in sofar:
                    # we found it!
                    return our_next
                sofar.add(our_next)
            except StopIteration:
                # we ran out of items during iteration
                our_done = True

            try:
                their_next = next(their_history_iter)
                if their_next in sofar:
                    # we found it!
                    return their_next
                sofar.add(their_next)
            except StopIteration:
                # we ran out of items during iteration
                their_done = True

            # if we ran out of both lists, there's no common ancestor
            if our_done and their_done:
                return None


Instruction:
Write a Python 3 function for
returning the list of constraints that have been accumulated since other.

Response:
def constraints_since(self, other):
        """
        Returns the constraints that have been accumulated since `other`.

        :param other: a prior PathHistory object
        :returns: a list of constraints
        """

        constraints = [ ]
        cur = self
        while cur is not other and cur is not None:
            constraints.extend(cur.recent_constraints)
            cur = cur.parent
        return constraints


Instruction:
Implement a function in Python 3 to
count the number of occurrences of value v in the entire history.

Response:
def count(self, v):
        """
        Count occurrences of value v in the entire history. Note that the subclass must implement the __reversed__
        method, otherwise an exception will be thrown.
        :param object v: The value to look for
        :return: The number of occurrences
        :rtype: int
        """
        ctr = 0
        for item in reversed(self):
            if item == v:
                ctr += 1
        return ctr


Instruction:
Here you have a function in Python 3, explain what it does
def slice_graph(graph, node, frontier, include_frontier=False):

        subgraph = networkx.DiGraph()

        for frontier_node in frontier:
            for simple_path in networkx.all_simple_paths(graph, node, frontier_node):
                for src, dst in zip(simple_path, simple_path[1:]):
                    if include_frontier or (src not in frontier and dst not in frontier):
                        subgraph.add_edge(src, dst)
        if not list(subgraph.nodes):
            # HACK: FIXME: for infinite loop nodes, this would return an empty set, so we include the loop body itself
            # Make sure this makes sense (EDG thinks it does)
            if (node, node) in graph.edges:
                subgraph.add_edge(node, node)
        return subgraph

Response: Generate a slice of the graph from the given node to the given frontier.


Instruction:
Create a Python 3 function for
converting a condition into a lambda that checks if state s current basic block matches some condition.

Response:
def condition_to_lambda(condition, default=False):
    """
    Translates an integer, set, list or function into a lambda that checks if state's current basic block matches
    some condition.

    :param condition:   An integer, set, list or lambda to convert to a lambda.
    :param default:     The default return value of the lambda (in case condition is None). Default: false.

    :returns:           A tuple of two items: a lambda that takes a state and returns the set of addresses that it
                        matched from the condition, and a set that contains the normalized set of addresses to stop
                        at, or None if no addresses were provided statically.
    """
    if condition is None:
        condition_function = lambda state: default
        static_addrs = set()

    elif isinstance(condition, int):
        return condition_to_lambda((condition,))

    elif isinstance(condition, (tuple, set, list)):
        static_addrs = set(condition)
        def condition_function(state):
            if state.addr in static_addrs:
                # returning {state.addr} instead of True to properly handle find/avoid conflicts
                return {state.addr}

            if not isinstance(state.project.engines.default_engine, engines.SimEngineVEX):
                return False

            try:
                # If the address is not in the set (which could mean it is
                # not at the top of a block), check directly in the blocks
                # (Blocks are repeatedly created for every check, but with
                # the IRSB cache in angr lifter it should be OK.)
                return static_addrs.intersection(set(state.block().instruction_addrs))
            except (AngrError, SimError):
                return False

    elif hasattr(condition, '__call__'):
        condition_function = condition
        static_addrs = None
    else:
        raise AngrExplorationTechniqueError("ExplorationTechnique is unable to convert given type (%s) to a callable condition function." % condition.__class__)

    return condition_function, static_addrs


Instruction:
Implement a function in Python 3 to
determine a persistent ID for an object.

Response:
def _get_persistent_id(self, o):
        """
        Determines a persistent ID for an object.
        Does NOT do stores.
        """
        if type(o) in self.hash_dedup:
            oid = o.__class__.__name__ + "-" + str(hash(o))
            self._object_cache[oid] = o
            return oid

        if any(isinstance(o,c) for c in self.unsafe_key_baseclasses):
            return None

        try:
            return self._uuid_cache[o]
        except KeyError:
            pass
        except TypeError:
            return None

        #if type(o) in self.uuid_dedup:
        #    return self._get_id(o)
        if o.__class__.__module__.split('.')[0] in self.module_dedup or o.__class__ in self.uuid_dedup:
            oid = o.__class__.__name__.split(".")[-1] + '-' + str(uuid.uuid4())
            self._object_cache[oid] = o
            self._uuid_cache[o] = oid
            return oid

        return None


Instruction:
Can you implement a function in Python 3 that
checks if the id is already in the vault.

Response:
def is_stored(self, i):
        """
        Checks if the provided id is already in the vault.
        """
        if i in self.stored:
            return True

        try:
            with self._read_context(i):
                return True
        except (AngrVaultError, EOFError):
            return False


Instruction:
Make a summary of the following Python 3 code
def load(self, id): #pylint:disable=redefined-builtin
        l.debug("LOAD: %s", id)
        try:
            l.debug("... trying cached")
            return self._object_cache[id]
        except KeyError:
            l.debug("... cached failed")
            with self._read_context(id) as u:
                return VaultUnpickler(self, u).load()

Response: Load one object from the pickler with the provided ID.


Instruction:
Can you generate a brief explanation for the following Python 3 code
def store(self, o, id=None): #pylint:disable=redefined-builtin
        actual_id = id or self._get_persistent_id(o) or "TMP-"+str(uuid.uuid4())

        l.debug("STORE: %s %s", o, actual_id)

        # this handles recursive objects
        if actual_id in self.storing:
            return actual_id

        if self.is_stored(actual_id):
            l.debug("... already stored")
            return actual_id

        with self._write_context(actual_id) as output:
            self.storing.add(actual_id)
            VaultPickler(self, output, assigned_objects=(o,)).dump(o)
            self.stored.add(actual_id)

        return actual_id

Response: Stores an object and returns its ID.


Instruction:
How would you code a function in Python 3 to
return a serialized string representing the object

Response:
def dumps(self, o):
        """
        Returns a serialized string representing the object, post-deduplication.

        :param o: the object
        """
        f = io.BytesIO()
        VaultPickler(self, f).dump(o)
        f.seek(0)
        return f.read()


Instruction:
Can you tell what is the following Python 3 function doing
def loads(self, s):
        f = io.BytesIO(s)
        return VaultUnpickler(self, f).load()

Response: Deserializes a string representation of the object.


Instruction:
How would you code a function in Python 3 to
try to find the name of a register in the arch. bytes.

Response:
def get_reg_name(arch, reg_offset):
        """
        :param arch: the architecture
        :param reg_offset: Tries to find the name of a register given the offset in the registers.
        :return: The register name
        """
        # todo does this make sense
        if reg_offset is None:
            return None

        original_offset = reg_offset
        while reg_offset >= 0 and reg_offset >= original_offset - (arch.bytes):
            if reg_offset in arch.register_names:
                return arch.register_names[reg_offset]
            else:
                reg_offset -= 1
        return None


Instruction:
Write a Python 3 function for
converting an input state into a state with symbolic registers

Response:
def _make_regs_symbolic(input_state, reg_list, project):
        """
        converts an input state into a state with symbolic registers
        :return: the symbolic state
        """
        state = input_state.copy()
        # overwrite all registers
        for reg in reg_list:
            state.registers.store(reg, state.solver.BVS("sreg_" + reg + "-", project.arch.bits, explicit_name=True))
        # restore sp
        state.regs.sp = input_state.regs.sp
        # restore bp
        state.regs.bp = input_state.regs.bp
        return state


Instruction:
Make a summary of the following Python 3 code
def make_initial_state(project, stack_length):
        initial_state = project.factory.blank_state(
            add_options={options.AVOID_MULTIVALUED_READS, options.AVOID_MULTIVALUED_WRITES,
                         options.NO_SYMBOLIC_JUMP_RESOLUTION, options.CGC_NO_SYMBOLIC_RECEIVE_LENGTH,
                         options.NO_SYMBOLIC_SYSCALL_RESOLUTION, options.TRACK_ACTION_HISTORY},
            remove_options=options.resilience | options.simplification)
        initial_state.options.discard(options.CGC_ZERO_FILL_UNCONSTRAINED_MEMORY)
        initial_state.options.update({options.TRACK_REGISTER_ACTIONS, options.TRACK_MEMORY_ACTIONS,
                                      options.TRACK_JMP_ACTIONS, options.TRACK_CONSTRAINT_ACTIONS})
        symbolic_stack = initial_state.solver.BVS("symbolic_stack", project.arch.bits * stack_length)
        initial_state.memory.store(initial_state.regs.sp, symbolic_stack)
        if initial_state.arch.bp_offset != initial_state.arch.sp_offset:
            initial_state.regs.bp = initial_state.regs.sp + 20 * initial_state.arch.bytes
        initial_state.solver._solver.timeout = 500  # only solve for half a second at most
        return initial_state

Response: Creates an initial state with a symbolic stack and good options for rop


Instruction:
Can you tell what is the following Python 3 function doing
def make_symbolic_state(project, reg_list, stack_length=80):
        input_state = Identifier.make_initial_state(project, stack_length)
        symbolic_state = input_state.copy()
        # overwrite all registers
        for reg in reg_list:
            symbolic_state.registers.store(reg, symbolic_state.solver.BVS("sreg_" + reg + "-", project.arch.bits))
        # restore sp
        symbolic_state.regs.sp = input_state.regs.sp
        # restore bp
        symbolic_state.regs.bp = input_state.regs.bp
        return symbolic_state

Response: Converts an input state into a state with symbolic registers


Instruction:
Can you generate a brief explanation for the following Python 3 code
def set_state_options(self, left_add_options=None, left_remove_options=None, right_add_options=None, right_remove_options=None):
        s_right = self.project.factory.full_init_state(
            add_options=right_add_options, remove_options=right_remove_options,
            args=[],
        )
        s_left = self.project.factory.full_init_state(
            add_options=left_add_options, remove_options=left_remove_options,
            args=[],
        )

        return self.set_states(s_left, s_right)

Response: Sets the state options of the current state.


Instruction:
Make a summary of the following Python 3 code
def set_states(self, left_state, right_state):

        simgr = self.project.factory.simulation_manager(right_state)
        simgr.stash(to_stash='right')
        simgr.active.append(left_state)
        simgr.stash(to_stash='left')
        simgr.stash(to_stash='stashed_left')
        simgr.stash(to_stash='stashed_right')

        return self.set_simgr(simgr)

Response: Sets the states of the current tree to the given states.


Instruction:
Can you write a function in Python 3 where it
validates that a detected incongruency is not caused by translation backends having a different incongruency.

Response:
def _validate_incongruency(self):
        """
        Checks that a detected incongruency is not caused by translation backends having a different
        idea of what constitutes a basic block.
        """

        ot = self._throw

        try:
            self._throw = False
            l.debug("Validating incongruency.")

            if ("UNICORN" in self.simgr.right[0].options) ^ ("UNICORN" in self.simgr.left[0].options):
                if "UNICORN" in self.simgr.right[0].options:
                    unicorn_stash = 'right'
                    normal_stash = 'left'
                else:
                    unicorn_stash = 'left'
                    normal_stash = 'right'

                unicorn_path = self.simgr.stashes[unicorn_stash][0]
                normal_path = self.simgr.stashes[normal_stash][0]

                if unicorn_path.arch.name in ("X86", "AMD64"):
                    # unicorn "falls behind" on loop and rep instructions, since
                    # it sees them as ending a basic block. Here, we will
                    # step the unicorn until it's caught up
                    npg = self.project.factory.simulation_manager(unicorn_path)
                    npg.explore(find=lambda p: p.addr == normal_path.addr, n=200)
                    if len(npg.found) == 0:
                        l.debug("Validator failed to sync paths.")
                        return True

                    new_unicorn = npg.found[0]
                    delta = new_unicorn.history.block_count - normal_path.history.block_count
                    normal_path.history.recent_block_count += delta
                    new_normal = normal_path
                elif unicorn_path.arch.name == "MIPS32":
                    # unicorn gets ahead here, because VEX falls behind for unknown reasons
                    # for example, this block:
                    #
                    # 0x1016f20:      lui     $gp, 0x17
                    # 0x1016f24:      addiu   $gp, $gp, -0x35c0
                    # 0x1016f28:      addu    $gp, $gp, $t9
                    # 0x1016f2c:      addiu   $sp, $sp, -0x28
                    # 0x1016f30:      sw      $ra, 0x24($sp)
                    # 0x1016f34:      sw      $s0, 0x20($sp)
                    # 0x1016f38:      sw      $gp, 0x10($sp)
                    # 0x1016f3c:      lw      $v0, -0x6cf0($gp)
                    # 0x1016f40:      move    $at, $at
                    npg = self.project.factory.simulation_manager(normal_path)
                    npg.explore(find=lambda p: p.addr == unicorn_path.addr, n=200)
                    if len(npg.found) == 0:
                        l.debug("Validator failed to sync paths.")
                        return True

                    new_normal = npg.found[0]
                    delta = new_normal.history.block_count - unicorn_path.history.block_count
                    unicorn_path.history.recent_block_count += delta
                    new_unicorn = unicorn_path
                else:
                    l.debug("Dunno!")
                    return True

                if self.compare_paths(new_unicorn, new_normal):
                    l.debug("Divergence accounted for by unicorn.")
                    self.simgr.stashes[unicorn_stash][0] = new_unicorn
                    self.simgr.stashes[normal_stash][0] = new_normal
                    return False
                else:
                    l.warning("Divergence unaccounted for by unicorn.")
                    return True
            else:
                # no idea
                l.warning("Divergence unaccounted for.")
                return True
        finally:
            self._throw = ot


Instruction:
Write a Python 3 script for
checking that the paths in the specified path group stay the same over the next `depth` bytes. The path group should have a "left" and a "right" stash, each with a single path.

Response:
def run(self, depth=None):
        """
        Checks that the paths in the specified path group stay the same over the next
        `depth` bytes.

        The path group should have a "left" and a "right" stash, each with a single
        path.
        """
        #pg_history = [ ]
        if len(self.simgr.right) != 1 or len(self.simgr.left) != 1:
            self._report_incongruency("Single path in pg.left and pg.right required.")
            return False

        if "UNICORN" in self.simgr.one_right.options and depth is not None:
            self.simgr.one_right.unicorn.max_steps = depth

        if "UNICORN" in self.simgr.one_left.options and depth is not None:
            self.simgr.one_left.unicorn.max_steps = depth

        l.debug("Performing initial path comparison.")
        if not self.compare_paths(self.simgr.left[0], self.simgr.right[0]):
            self._report_incongruency("Initial path comparison check failed.")
            return False

        while len(self.simgr.left) > 0 and len(self.simgr.right) > 0:
            if depth is not None:
                self._update_progress(100. * float(self.simgr.one_left.history.block_count) / depth)

            if len(self.simgr.deadended) != 0:
                self._report_incongruency("Unexpected deadended paths before step.")
                return False
            if len(self.simgr.right) == 0 and len(self.simgr.left) == 0:
                l.debug("All done!")
                return True
            if len(self.simgr.right) != 1 or len(self.simgr.left) != 1:
                self._report_incongruency("Different numbers of paths in left and right stash..")
                return False

            # do a step
            l.debug(
                "Stepping right path with weighted length %d/%d",
                self.simgr.right[0].history.block_count,
                depth
            )
            self.prev_pg = self.simgr.copy() #pylint:disable=unused-variable
            self.simgr.step(stash='right')
            CongruencyCheck._sync_steps(self.simgr)

            if len(self.simgr.errored) != 0:
                self._report_incongruency("Unexpected errored paths.")
                return False

            try:
                if not self.compare_path_group(self.simgr) and self._validate_incongruency():
                    self._report_incongruency("Path group comparison failed.")
                    return False
            except AngrIncongruencyError:
                if self._validate_incongruency():
                    raise

            if depth is not None:
                self.simgr.drop(stash='left', filter_func=lambda p: p.history.block_count >= depth)
                self.simgr.drop(stash='right', filter_func=lambda p: p.history.block_count >= depth)

            self.simgr.right.sort(key=lambda p: p.addr)
            self.simgr.left.sort(key=lambda p: p.addr)
            self.simgr.stashed_right[:] = self.simgr.stashed_right[::-1]
            self.simgr.stashed_left[:] = self.simgr.stashed_left[::-1]
            self.simgr.move('stashed_right', 'right')
            self.simgr.move('stashed_left', 'left')

            if len(self.simgr.left) > 1:
                self.simgr.split(from_stash='left', limit=1, to_stash='stashed_left')
                self.simgr.split(from_stash='right', limit=1, to_stash='stashed_right')


Instruction:
Explain what the following Python 3 code does
def compare_states(self, sl, sr):
        joint_solver = claripy.Solver()

        # make sure the canonicalized constraints are the same
        n_map, n_counter, n_canon_constraint = claripy.And(*sr.solver.constraints).canonicalize() #pylint:disable=no-member
        u_map, u_counter, u_canon_constraint = claripy.And(*sl.solver.constraints).canonicalize() #pylint:disable=no-member
        n_canoner_constraint = sr.solver.simplify(n_canon_constraint)
        u_canoner_constraint = sl.solver.simplify(u_canon_constraint)
        joint_solver.add((n_canoner_constraint, u_canoner_constraint))
        if n_canoner_constraint is not u_canoner_constraint:
            self._report_incongruency("Different constraints!")
            return False

        # get the differences in registers and memory
        mem_diff = sr.memory.changed_bytes(sl.memory)
        reg_diff = sr.registers.changed_bytes(sl.registers)

        # this is only for unicorn
        if "UNICORN" in sl.options or "UNICORN" in sr.options:
            if sl.arch.name == "X86":
                reg_diff -= set(range(40, 52)) #ignore cc psuedoregisters
                reg_diff -= set(range(320, 324)) #some other VEX weirdness
                reg_diff -= set(range(340, 344)) #ip_at_syscall
            elif sl.arch.name == "AMD64":
                reg_diff -= set(range(144, 168)) #ignore cc psuedoregisters

        # make sure the differences in registers and memory are actually just renamed
        # versions of the same ASTs
        for diffs,(um,nm) in (
            (reg_diff, (sl.registers, sr.registers)),
            (mem_diff, (sl.memory, sr.memory)),
        ):
            for i in diffs:
                bn = nm.load(i, 1)
                bu = um.load(i, 1)

                bnc = bn.canonicalize(var_map=n_map, counter=n_counter)[-1]
                buc = bu.canonicalize(var_map=u_map, counter=u_counter)[-1]

                if bnc is not buc:
                    self._report_incongruency("Different memory or registers (index %d, values %r and %r)!", i, bn, bu)
                    return False

        # make sure the flags are the same
        if sl.arch.name in ("AMD64", "X86", "ARM", "ARMEL", "ARMHF", "AARCH64"):
            # pylint: disable=unused-variable
            n_bkp = sr.regs.cc_op, sr.regs.cc_dep1, sr.regs.cc_dep2, sr.regs.cc_ndep
            u_bkp = sl.regs.cc_op, sl.regs.cc_dep1, sl.regs.cc_dep2, sl.regs.cc_ndep
            if sl.arch.name in ('AMD64', 'X86'):
                n_flags = sr.regs.eflags.canonicalize(var_map=n_map, counter=n_counter)[-1]
                u_flags = sl.regs.eflags.canonicalize(var_map=u_map, counter=u_counter)[-1]
            else:
                n_flags = sr.regs.flags.canonicalize(var_map=n_map, counter=n_counter)[-1]
                u_flags = sl.regs.flags.canonicalize(var_map=u_map, counter=u_counter)[-1]
            if n_flags is not u_flags and sl.solver.simplify(n_flags) is not sr.solver.simplify(u_flags):
                self._report_incongruency("Different flags!")
                return False

        return True

Response: Compares two states for similarity.


Instruction:
Can you generate a brief explanation for the following Python 3 code
def check(self, state, when):
        ok = self.enabled and (when == self.when or self.when == BP_BOTH)
        if not ok:
            return ok
        l.debug("... after enabled and when: %s", ok)

        for a in [ _ for _ in self.kwargs if not _.endswith("_unique") ]:
            current_expr = getattr(state.inspect, a)
            needed = self.kwargs.get(a, None)

            l.debug("... checking condition %s", a)

            if current_expr is None and needed is None:
                l.debug("...... both None, True")
                c_ok = True
            elif current_expr is not None and needed is not None:
                if state.solver.solution(current_expr, needed):
                    l.debug("...... is_solution!")
                    c_ok = True
                else:
                    l.debug("...... not solution...")
                    c_ok = False

                if c_ok and self.kwargs.get(a+'_unique', True):
                    l.debug("...... checking uniqueness")
                    if not state.solver.unique(current_expr):
                        l.debug("...... not unique")
                        c_ok = False
            else:
                l.debug("...... one None, False")
                c_ok = False

            ok = ok and c_ok
            if not ok:
                return ok
            l.debug("... after condition %s: %s", a, ok)

        ok = ok and (self.condition is None or self.condition(state))
        l.debug("... after condition func: %s", ok)
        return ok

Response: Checks state to see if the breakpoint should fire.


Instruction:
Implement a function in Python 3 to
trigger the breakpoint. :param state: The state.

Response:
def fire(self, state):
        """
        Trigger the breakpoint.

        :param state:   The state.
        """
        if self.action is None or self.action == BP_IPDB:
            import ipdb; ipdb.set_trace() #pylint:disable=F0401
        elif self.action == BP_IPYTHON:
            import IPython
            shell = IPython.terminal.embed.InteractiveShellEmbed()
            shell.mainloop(display_banner="This is an ipython shell for you to happily debug your state!\n" + \
                           "The state can be accessed through the variable 'state'. You can\n" +\
                           "make modifications, then exit this shell to resume your analysis.")
        else:
            self.action(state)


Instruction:
Explain what the following Python 3 code does
def action(self, event_type, when, **kwargs):
        l.debug("Event %s (%s) firing...", event_type, when)
        for k,v in kwargs.items():
            if k not in inspect_attributes:
                raise ValueError("Invalid inspect attribute %s passed in. Should be one of: %s" % (k, inspect_attributes))
            #l.debug("... %s = %r", k, v)
            l.debug("... setting %s", k)
            setattr(self, k, v)

        for bp in self._breakpoints[event_type]:
            l.debug("... checking bp %r", bp)
            if bp.check(self.state, when):
                l.debug("... FIRE")
                bp.fire(self.state)

Response: Called from within SimuVEX when events happen.


Instruction:
Create a Python 3 function for
creating and adds a breakpoint which would trigger on event_type. Additional arguments are passed to the BP constructor.

Response:
def make_breakpoint(self, event_type, *args, **kwargs):
        """
        Creates and adds a breakpoint which would trigger on `event_type`. Additional arguments are passed to the
        :class:`BP` constructor.

        :return:    The created breakpoint, so that it can be removed later.
        """
        bp = BP(*args, **kwargs)
        self.add_breakpoint(event_type, bp)
        return bp


Instruction:
Can you tell what is the following Python 3 function doing
def add_breakpoint(self, event_type, bp):
        if event_type not in event_types:
            raise ValueError("Invalid event type %s passed in. Should be one of: %s" % (event_type,
                                                                                ", ".join(event_types))
                             )
        self._breakpoints[event_type].append(bp)

Response: Adds a breakpoint which would trigger on event_type.


Instruction:
Implement a function in Python 3 to
remove a breakpoint from the cache.

Response:
def remove_breakpoint(self, event_type, bp=None, filter_func=None):
        """
        Removes a breakpoint.

        :param bp:  The breakpoint to remove.
        :param filter_func: A filter function to specify whether each breakpoint should be removed or not.
        """

        if bp is None and filter_func is None:
            raise ValueError('remove_breakpoint(): You must specify either "bp" or "filter".')

        try:
            if bp is not None:
                self._breakpoints[event_type].remove(bp)
            else:
                self._breakpoints[event_type] = [ b for b in self._breakpoints[event_type] if not filter_func(b) ]
        except ValueError:
            # the breakpoint is not found
            l.error('remove_breakpoint(): Breakpoint %s (type %s) is not found.', bp, event_type)


Instruction:
How would you implement a function in Python 3 that
removes previously stored attributes from this plugin instance to save memory.

Response:
def downsize(self):
        """
        Remove previously stored attributes from this plugin instance to save memory.
        This method is supposed to be called by breakpoint implementors. A typical workflow looks like the following :

        >>> # Add `attr0` and `attr1` to `self.state.inspect`
        >>> self.state.inspect(xxxxxx, attr0=yyyy, attr1=zzzz)
        >>> # Get new attributes out of SimInspect in case they are modified by the user
        >>> new_attr0 = self.state._inspect.attr0
        >>> new_attr1 = self.state._inspect.attr1
        >>> # Remove them from SimInspect
        >>> self.state._inspect.downsize()
        """
        for k in inspect_attributes:
            if hasattr(self, k):
                setattr(self, k, None)


Instruction:
Can you write a function in Python 3 where it
gives a state return the corresponding syscall.

Response:
def syscall(self, state, allow_unsupported=True):
        """
        Given a state, return the procedure corresponding to the current syscall.
        This procedure will have .syscall_number, .display_name, and .addr set.

        :param state:               The state to get the syscall number from
        :param allow_unsupported:   Whether to return a "dummy" sycall instead of raising an unsupported exception
        """
        abi = self.syscall_abi(state)

        if state.os_name in SYSCALL_CC[state.arch.name]:
            cc = SYSCALL_CC[state.arch.name][state.os_name](state.arch)
        else:
            # Use the default syscall calling convention - it may bring problems
            _l.warning("No syscall calling convention available for %s/%s", state.arch.name, state.os_name)
            cc = SYSCALL_CC[state.arch.name]['default'](state.arch)

        sym_num = cc.syscall_num(state)
        try:
            num = state.solver.eval_one(sym_num)
        except SimSolverError:
            if allow_unsupported:
                num = self.unknown_syscall_number
            else:
                if not state.solver.satisfiable():
                    raise AngrUnsupportedSyscallError("The program state is not satisfiable")
                else:
                    raise AngrUnsupportedSyscallError("Got a symbolic syscall number")

        proc = self.syscall_from_number(num, allow_unsupported=allow_unsupported, abi=abi)
        proc.cc = cc
        return proc


Instruction:
Can you tell what is the following Python 3 function doing
def is_syscall_addr(self, addr):
        if self.kernel_base is None or addr < self.kernel_base:
            return False

        addr -= self.kernel_base

        if addr % self.syscall_addr_alignment != 0:
            return False

        addr //= self.syscall_addr_alignment
        return addr <= self.unknown_syscall_number

Response: Return whether or not the given address corresponds to a syscall implementation.


Instruction:
Write a Python 3 function for
getting a syscall SimProcedure from an address.

Response:
def syscall_from_addr(self, addr, allow_unsupported=True):
        """
        Get a syscall SimProcedure from an address.

        :param addr: The address to convert to a syscall SimProcedure
        :param allow_unsupported: Whether to return a dummy procedure for an unsupported syscall instead of raising an
                                  exception.
        :return: The SimProcedure for the syscall, or None if the address is not a syscall address.
        """
        if not self.is_syscall_addr(addr):
            return None

        number = (addr - self.kernel_base) // self.syscall_addr_alignment
        for abi in self.syscall_abis:
            baseno, minno, maxno = self.syscall_abis[abi]
            if baseno <= number <= baseno + maxno - minno:
                number += minno
                number -= baseno
                break
        else:
            abi = None
        return self.syscall_from_number(number, allow_unsupported=allow_unsupported, abi=abi)


Instruction:
How would you explain what the following Python 3 function does
def syscall_from_number(self, number, allow_unsupported=True, abi=None):
        abilist = self.syscall_abis if abi is None else [abi]

        if self.syscall_library is None:
            if not allow_unsupported:
                raise AngrUnsupportedSyscallError("%s does not have a library of syscalls implemented" % self.name)
            proc = P['stubs']['syscall']()
        elif not allow_unsupported and not self.syscall_library.has_implementation(number, self.arch, abilist):
            raise AngrUnsupportedSyscallError("No implementation for syscall %d" % number)
        else:
            proc = self.syscall_library.get(number, self.arch, abilist)

        if proc.abi is not None:
            baseno, minno, _ = self.syscall_abis[proc.abi]
            mapno = number - minno + baseno
        else:
            mapno = self.unknown_syscall_number

        proc.addr = mapno * self.syscall_addr_alignment + self.kernel_base
        return proc

Response: Get a SimProcedure from its number.


Instruction:
Can you generate a brief explanation for the following Python 3 code
def convert_prot(prot):
    # https://msdn.microsoft.com/en-us/library/windows/desktop/aa366786(v=vs.85).aspx
    if prot & 0x10:
        return 4
    if prot & 0x20:
        return 5
    if prot & 0x40:
        return 7
    if prot & 0x80:
        return 7
    if prot & 0x01:
        return 0
    if prot & 0x02:
        return 1
    if prot & 0x04:
        return 3
    if prot & 0x08:
        return 3
    raise angr.errors.SimValueError("Unknown windows memory protection constant: %#x" % prot)

Response: Convert a windows memory protection constant to an angr bitmask.


Instruction:
Explain what the following Python 3 code does
def copy(self):

        ld = LiveDefinitions()
        ld._memory_map = self._memory_map.copy()
        ld._register_map = self._register_map.copy()
        ld._defs = self._defs.copy()

        return ld

Response: Make a hard copy of self.


Instruction:
Implement a Python 3 function for
adding a new definition of variable at location.

Response:
def add_def(self, variable, location, size_threshold=32):
        """
        Add a new definition of variable.

        :param SimVariable variable: The variable being defined.
        :param CodeLocation location: Location of the varaible being defined.
        :param int size_threshold: The maximum bytes to consider for the variable.
        :return: True if the definition was new, False otherwise
        :rtype: bool
        """

        new_defs_added = False

        if isinstance(variable, SimRegisterVariable):
            if variable.reg is None:
                l.warning('add_def: Got a None for a SimRegisterVariable. Consider fixing.')
                return new_defs_added

            size = min(variable.size, size_threshold)
            offset = variable.reg
            while offset < variable.reg + size:
                if location not in self._register_map[offset]:
                    new_defs_added = True
                self._register_map[offset].add(location)
                offset += 1

            self._defs[variable].add(location)

        elif isinstance(variable, SimMemoryVariable):
            size = min(variable.size, size_threshold)
            offset = variable.addr
            while offset < variable.addr + size:
                if location not in self._memory_map[offset]:
                    new_defs_added = True
                self._memory_map[offset].add(location)
                offset += 1

            self._defs[variable].add(location)

        else:
            l.error('Unsupported variable type "%s".', type(variable))

        return new_defs_added


Instruction:
Write a Python 3 function that can
add a collection of new definitions of a variable.

Response:
def add_defs(self, variable, locations, size_threshold=32):
        """
        Add a collection of new definitions of a variable.

        :param SimVariable variable: The variable being defined.
        :param iterable locations: A collection of locations where the variable was defined.
        :param int size_threshold: The maximum bytes to consider for the variable.
        :return: True if any of the definition was new, False otherwise
        :rtype: bool
        """

        new_defs_added = False

        for loc in locations:
            new_defs_added |= self.add_def(variable, loc, size_threshold=size_threshold)

        return new_defs_added


Instruction:
Given the following Python 3 function, write the documentation
def kill_def(self, variable, location, size_threshold=32):

        if isinstance(variable, SimRegisterVariable):
            if variable.reg is None:
                l.warning('kill_def: Got a None for a SimRegisterVariable. Consider fixing.')
                return None

            size = min(variable.size, size_threshold)
            offset = variable.reg
            while offset < variable.reg + size:
                self._register_map[offset] = { location }
                offset += 1

            self._defs[variable] = { location }

        elif isinstance(variable, SimMemoryVariable):
            size = min(variable.size, size_threshold)
            offset = variable.addr
            while offset < variable.addr + size:
                self._memory_map[offset] = { location }
                offset += 1

            self._defs[variable] = { location }

        else:
            l.error('Unsupported variable type "%s".', type(variable))

Response: Add a new definition for variable and kill all previous definitions.


Instruction:
Can you implement a function in Python 3 that
returns a set of code locations where the variable is defined.

Response:
def lookup_defs(self, variable, size_threshold=32):
        """
        Find all definitions of the varaible

        :param SimVariable variable: The variable to lookup for.
        :param int size_threshold: The maximum bytes to consider for the variable. For example, if the variable is 100
                                   byte long, only the first `size_threshold` bytes are considered.
        :return: A set of code locations where the variable is defined.
        :rtype: set
        """

        live_def_locs = set()

        if isinstance(variable, SimRegisterVariable):

            if variable.reg is None:
                l.warning('lookup_defs: Got a None for a SimRegisterVariable. Consider fixing.')
                return live_def_locs

            size = min(variable.size, size_threshold)
            offset = variable.reg
            while offset < variable.reg + size:
                if offset in self._register_map:
                    live_def_locs |= self._register_map[offset]
                offset += 1

        elif isinstance(variable, SimMemoryVariable):
            size = min(variable.size, size_threshold)
            offset = variable.addr
            while offset < variable.addr + size:
                if offset in self._memory_map:
                    live_def_locs |= self._memory_map[offset]
                offset += 1

        else:
            # umm unsupported variable type
            l.error('Unsupported variable type "%s".', type(variable))

        return live_def_locs


Instruction:
How would you implement a function in Python 3 that
converts a ProgramVariable instance to a DDGViewItem object.

Response:
def _to_viewitem(self, prog_var):
        """
        Convert a ProgramVariable instance to a DDGViewItem object.

        :param ProgramVariable prog_var: The ProgramVariable object to convert.
        :return:                         The converted DDGViewItem object.
        :rtype:                          DDGViewItem
        """

        return DDGViewItem(self._ddg, prog_var, simplified=self._simplified)


Instruction:
Create a Python 3 function for
returning a list of ProgramVariable instances that are located at the current instruction address.

Response:
def definitions(self):
        """
        Get all definitions located at the current instruction address.

        :return: A list of ProgramVariable instances.
        :rtype:  list
        """

        defs = set()

        if self._simplified:
            graph = self._ddg.simplified_data_graph
        else:
            graph = self._ddg.data_graph

        for n in graph.nodes():  # type: ProgramVariable
            if n.location.ins_addr == self._insn_addr:
                defs.add(DDGViewItem(self._ddg, n, simplified=self._simplified))

        return list(defs)


Instruction:
Implement a function in Python 3 to
return a dependency graph for the function func.

Response:
def function_dependency_graph(self, func):
        """
        Get a dependency graph for the function `func`.

        :param func:    The Function object in CFG.function_manager.
        :returns:       A networkx.DiGraph instance.
        """

        if self._function_data_dependencies is None:
            self._build_function_dependency_graphs()

        if func in self._function_data_dependencies:
            return self._function_data_dependencies[func]

        # Not found
        return None


Instruction:
Write a Python 3 script to
get a subgraph of the data graph that starts from a given node pv.

Response:
def data_sub_graph(self, pv, simplified=True, killing_edges=False, excluding_types=None):
        """
        Get a subgraph from the data graph or the simplified data graph that starts from node pv.

        :param ProgramVariable pv: The starting point of the subgraph.
        :param bool simplified: When True, the simplified data graph is used, otherwise the data graph is used.
        :param bool killing_edges: Are killing edges included or not.
        :param iterable excluding_types: Excluding edges whose types are among those excluded types.
        :return: A subgraph.
        :rtype: networkx.MultiDiGraph
        """

        result = networkx.MultiDiGraph()
        result.add_node(pv)

        base_graph = self.simplified_data_graph if simplified else self.data_graph
        if pv not in base_graph:
            return result

        # traverse all edges and add them to the result graph if needed
        queue = [ pv ]
        traversed = set()
        while queue:
            elem = queue[0]
            queue = queue[1:]
            if elem in traversed:
                continue
            traversed.add(elem)

            out_edges = base_graph.out_edges(elem, data=True)

            if not killing_edges:
                # remove killing edges
                out_edges = [ (a, b, data) for a, b, data in out_edges if 'type' not in data or data['type'] != 'kill']

            if excluding_types:
                out_edges = [ (a, b, data) for a, b, data in out_edges if
                              'type' not in data or data['type'] not in excluding_types
                              ]

            for src, dst, data in out_edges:
                result.add_edge(src, dst, **data)

                if dst not in traversed:
                    queue.append(dst)

        return result