(self, job, successor, new_block_id, new_call_stack)
return new_jobs
Create a list of new VFG jobs for the successor state.

        :param VFGJob job:                 The VFGJob instance.
        :param SimState successor: The succeeding state.
        :param BlockID new_block_id:       Block ID for the new VFGJob
        :param new_call_stack:             The new callstack.
        :return:                           A list of newly created VFG jobs.
        :rtype:                            list
Create a list of new VFG jobs for the successor state.
Create a list of new VFG jobs for the successor state .
def _create_new_jobs(self, job, successor, new_block_id, new_call_stack):
        """
        Create a list of new VFG jobs for the successor state.

        :param VFGJob job:                 The VFGJob instance.
        :param SimState successor: The succeeding state.
        :param BlockID new_block_id:       Block ID for the new VFGJob
        :param new_call_stack:             The new callstack.
        :return:                           A list of newly created VFG jobs.
        :rtype:                            list
        """

        # TODO: basic block stack is probably useless

        jumpkind = successor.history.jumpkind
        stmt_idx = successor.scratch.stmt_idx
        ins_addr = successor.scratch.ins_addr
        # Make a copy of the state in case we use it later
        successor_state = successor.copy()
        successor_addr = successor_state.solver.eval(successor_state.ip)

        new_jobs = [ ]

        if jumpkind == "Ijk_FakeRet":
            assert job.is_call_jump

            # This is the default "fake" return successor generated at each call, if and only if the target function
            # returns.

            # if the call is skipped (for whatever reason, like we reached the interfunction tracing limit), we use
            # this FakeRet successor as the final state of the function. Otherwise we save the FakeRet state in case the
            # callee does not return normally, but don't process them right away.

            # Clear the useless values (like return addresses, parameters) on stack if needed
            if self._cfg is not None:
                current_function = self.kb.functions.function(job.call_target)
                if current_function is not None:
                    sp_difference = current_function.sp_delta
                else:
                    sp_difference = 0
                arch = successor_state.arch
                reg_sp_offset = arch.sp_offset
                reg_sp_expr = successor_state.registers.load(reg_sp_offset, size=arch.bytes,
                                                             endness=arch.register_endness) + sp_difference
                successor_state.registers.store(arch.sp_offset, reg_sp_expr)

                # Clear the return value with a TOP
                top_si = successor_state.solver.TSI(arch.bits)
                successor_state.registers.store(arch.ret_offset, top_si)

            if job.call_skipped:

                # TODO: Make sure the return values make sense
                #if self.project.arch.name == 'X86':
                #    successor_state.regs.eax = successor_state.solver.BVS('ret_val', 32, min=0, max=0xffffffff, stride=1)

                new_job = VFGJob(successor_addr,
                                 successor_state,
                                 self._context_sensitivity_level,
                                 block_id=new_block_id,
                                 jumpkind='Ijk_Ret',
                                 call_stack=new_call_stack,
                                 src_block_id=job.block_id,
                                 src_exit_stmt_idx=stmt_idx,
                                 src_ins_addr=ins_addr,
                                 )

                new_jobs.append(new_job)
                assert isinstance(self._task_stack[-2], FunctionAnalysis)
                self._task_stack[-2].jobs.append(new_job)
                job.dbg_exit_status[successor] = "Pending"

            else:
                self._pending_returns[new_block_id] = PendingJob(new_block_id, successor_state, new_call_stack,
                                                                 job.block_id, stmt_idx, ins_addr)
                job.dbg_exit_status[successor] = "Pending"

        else:
            if sim_options.ABSTRACT_MEMORY in successor.options:
                if self._is_call_jumpkind(successor.history.jumpkind):
                    # If this is a call, we create a new stack address mapping
                    reg_sp_si = self._create_stack_region(successor_state, successor_addr)

                    # Save the new sp register
                    new_reg_sp_expr = successor_state.solver.ValueSet(successor_state.arch.bits,
                                                                       'global',
                                                                       0,
                                                                       reg_sp_si
                                                                       )
                    successor_state.regs.sp = new_reg_sp_expr

                elif successor.history.jumpkind == "Ijk_Ret":
                    # Remove the existing stack address mapping
                    # FIXME: Now we are assuming the sp is restored to its original value
                    reg_sp_expr = successor_state.regs.sp

                    if isinstance(reg_sp_expr._model_vsa, claripy.vsa.StridedInterval):
                        reg_sp_si = reg_sp_expr._model_vsa
                        reg_sp_val = reg_sp_si.min
                    elif isinstance(reg_sp_expr._model_vsa, claripy.vsa.ValueSet):
                        reg_sp_si = next(iter(reg_sp_expr._model_vsa.items()))[1]
                        reg_sp_val = reg_sp_si.min
                        # TODO: Finish it!

            new_job = VFGJob(successor_addr,
                             successor_state,
                             self._context_sensitivity_level,
                             block_id=new_block_id,
                             jumpkind=successor_state.history.jumpkind,
                             call_stack=new_call_stack,
                             src_block_id=job.block_id,
                             src_exit_stmt_idx=stmt_idx,
                             src_ins_addr=ins_addr,
                             )

            if successor.history.jumpkind == 'Ijk_Ret':
                # it's returning to the return site

                # save the state as a final state of the function that we are returning from
                if self._record_function_final_states:
                    # key of the function that we are returning from
                    source_function_key = FunctionKey.new(job.func_addr,
                                                          job.call_stack_suffix
                                                          )
                    self._save_function_final_state(source_function_key, job.func_addr, successor_state)

                # TODO: add an assertion that requires the returning target being the same as the return address we
                # TODO: stored before

                current_task = self._top_task
                if current_task.call_analysis is not None:
                    current_task.call_analysis.add_final_job(new_job)

                    job.dbg_exit_status[successor] = "Appended to the call analysis task"
                else:
                    job.dbg_exit_status[successor] = "Discarded (no call analysis task)"

            else:
                if self._is_call_jumpkind(successor.history.jumpkind):
                    # create a function analysis task
                    # TODO: the return address
                    task = FunctionAnalysis(new_job.addr, None)
                    self._task_stack.append(task)
                    # link it to the call analysis
                    job.call_task.register_function_analysis(task)

                else:
                    task = self._top_task

                # register the job to the function task
                task.jobs.append(new_job)
                # insert the new job into the new job array
                new_jobs.append(new_job)

                job.dbg_exit_status[successor] = "Appended"

        if not job.is_call_jump or jumpkind != "Ijk_FakeRet":
            new_target = (new_block_id, jumpkind)
        else:
            new_target = (new_block_id, "Ijk_FakeRet")  # This is the fake return!
        self._exit_targets[job.call_stack_suffix + (job.addr,)].append(new_target)

        return new_jobs
def _create_new_jobs ( self , job , successor , new_block_id , new_call_stack ) : # TODO: basic block stack is probably useless jumpkind = successor . history . jumpkind stmt_idx = successor . scratch . stmt_idx ins_addr = successor . scratch . ins_addr # Make a copy of the state in case we use it later successor_state = successor . copy ( ) successor_addr = successor_state . solver . eval ( successor_state . ip ) new_jobs = [ ] if jumpkind == "Ijk_FakeRet" : assert job . is_call_jump # This is the default "fake" return successor generated at each call, if and only if the target function # returns. # if the call is skipped (for whatever reason, like we reached the interfunction tracing limit), we use # this FakeRet successor as the final state of the function. Otherwise we save the FakeRet state in case the # callee does not return normally, but don't process them right away. # Clear the useless values (like return addresses, parameters) on stack if needed if self . _cfg is not None : current_function = self . kb . functions . function ( job . call_target ) if current_function is not None : sp_difference = current_function . sp_delta else : sp_difference = 0 arch = successor_state . arch reg_sp_offset = arch . sp_offset reg_sp_expr = successor_state . registers . load ( reg_sp_offset , size = arch . bytes , endness = arch . register_endness ) + sp_difference successor_state . registers . store ( arch . sp_offset , reg_sp_expr ) # Clear the return value with a TOP top_si = successor_state . solver . TSI ( arch . bits ) successor_state . registers . store ( arch . ret_offset , top_si ) if job . call_skipped : # TODO: Make sure the return values make sense #if self.project.arch.name == 'X86': #    successor_state.regs.eax = successor_state.solver.BVS('ret_val', 32, min=0, max=0xffffffff, stride=1) new_job = VFGJob ( successor_addr , successor_state , self . _context_sensitivity_level , block_id = new_block_id , jumpkind = 'Ijk_Ret' , call_stack = new_call_stack , src_block_id = job . block_id , src_exit_stmt_idx = stmt_idx , src_ins_addr = ins_addr , ) new_jobs . append ( new_job ) assert isinstance ( self . _task_stack [ - 2 ] , FunctionAnalysis ) self . _task_stack [ - 2 ] . jobs . append ( new_job ) job . dbg_exit_status [ successor ] = "Pending" else : self . _pending_returns [ new_block_id ] = PendingJob ( new_block_id , successor_state , new_call_stack , job . block_id , stmt_idx , ins_addr ) job . dbg_exit_status [ successor ] = "Pending" else : if sim_options . ABSTRACT_MEMORY in successor . options : if self . _is_call_jumpkind ( successor . history . jumpkind ) : # If this is a call, we create a new stack address mapping reg_sp_si = self . _create_stack_region ( successor_state , successor_addr ) # Save the new sp register new_reg_sp_expr = successor_state . solver . ValueSet ( successor_state . arch . bits , 'global' , 0 , reg_sp_si ) successor_state . regs . sp = new_reg_sp_expr elif successor . history . jumpkind == "Ijk_Ret" : # Remove the existing stack address mapping # FIXME: Now we are assuming the sp is restored to its original value reg_sp_expr = successor_state . regs . sp if isinstance ( reg_sp_expr . _model_vsa , claripy . vsa . StridedInterval ) : reg_sp_si = reg_sp_expr . _model_vsa reg_sp_val = reg_sp_si . min elif isinstance ( reg_sp_expr . _model_vsa , claripy . vsa . ValueSet ) : reg_sp_si = next ( iter ( reg_sp_expr . _model_vsa . items ( ) ) ) [ 1 ] reg_sp_val = reg_sp_si . min # TODO: Finish it! new_job = VFGJob ( successor_addr , successor_state , self . _context_sensitivity_level , block_id = new_block_id , jumpkind = successor_state . history . jumpkind , call_stack = new_call_stack , src_block_id = job . block_id , src_exit_stmt_idx = stmt_idx , src_ins_addr = ins_addr , ) if successor . history . jumpkind == 'Ijk_Ret' : # it's returning to the return site # save the state as a final state of the function that we are returning from if self . _record_function_final_states : # key of the function that we are returning from source_function_key = FunctionKey . new ( job . func_addr , job . call_stack_suffix ) self . _save_function_final_state ( source_function_key , job . func_addr , successor_state ) # TODO: add an assertion that requires the returning target being the same as the return address we # TODO: stored before current_task = self . _top_task if current_task . call_analysis is not None : current_task . call_analysis . add_final_job ( new_job ) job . dbg_exit_status [ successor ] = "Appended to the call analysis task" else : job . dbg_exit_status [ successor ] = "Discarded (no call analysis task)" else : if self . _is_call_jumpkind ( successor . history . jumpkind ) : # create a function analysis task # TODO: the return address task = FunctionAnalysis ( new_job . addr , None ) self . _task_stack . append ( task ) # link it to the call analysis job . call_task . register_function_analysis ( task ) else : task = self . _top_task # register the job to the function task task . jobs . append ( new_job ) # insert the new job into the new job array new_jobs . append ( new_job ) job . dbg_exit_status [ successor ] = "Appended" if not job . is_call_jump or jumpkind != "Ijk_FakeRet" : new_target = ( new_block_id , jumpkind ) else : new_target = ( new_block_id , "Ijk_FakeRet" ) # This is the fake return! self . _exit_targets [ job . call_stack_suffix + ( job . addr , ) ] . append ( new_target ) return new_jobs