def refine_for_under_approx(patch_formula, path_condition, patch_space, p_specification): parameter_constraint = smt2.generate_constraint_for_patch_space(patch_space) patch_space_constraint = patch_formula if parameter_constraint: patch_space_constraint = And(patch_formula, parameter_constraint) path_feasibility = And(path_condition, And(patch_space_constraint, p_specification)) path_constraint = And(path_condition, patch_formula) if values.VALID_INPUT_SPACE: input_space_constraint = Not(smt2.generate_constraint_for_input_space(values.VALID_INPUT_SPACE)) path_feasibility = And(path_condition, And(patch_space_constraint, input_space_constraint)) path_constraint = And(And(path_condition, input_space_constraint), patch_formula) # invalid input range is used to check for violations refined_patch_space = patch_space is_under_approx = False if is_sat(path_feasibility): is_under_approx = True if values.DEFAULT_REFINE_METHOD in ["under-approx", "overfit"]: emitter.debug("refining for universal quantification") if parameter_constraint: refined_patch_space = refine_parameter_space(path_constraint, patch_space, p_specification) is_under_approx = False else: refined_patch_space = None return refined_patch_space, is_under_approx
def check_patch_feasibility(assertion, var_relationship, patch_constraint, path_condition, index): # TODO path_constraint = And(path_condition, patch_constraint) patch_score = 0 is_under_approx = None is_over_approx = None result = True if assertion: if is_sat(path_constraint): if is_loc_in_trace(values.CONF_LOC_BUG): patch_score = 2 is_under_approx = not is_unsat(And(path_constraint, Not(assertion))) if values.DEFAULT_REFINE_METHOD in ["under-approx", "overfit"]: if is_under_approx: emitter.debug("removing due to universal quantification") result = False negated_path_condition = values.NEGATED_PPC_FORMULA path_constraint = And(negated_path_condition, patch_constraint) is_over_approx = not is_unsat(And(path_constraint, assertion)) if values.DEFAULT_REFINE_METHOD in ["over-approx", "overfit"]: if is_over_approx: emitter.debug("removing due to existential quantification") result = False else: patch_score = 1 # else: # specification = And(path_condition, Not(patch_constraint)) # existential_quantification = is_unsat(And(specification, assertion)) # result = existential_quantification return result, index, patch_score, is_under_approx, is_over_approx
def initialize(): emitter.title("Initializing Program") test_input_list = values.LIST_TEST_INPUT second_var_list = list() output_dir_path = definitions.DIRECTORY_OUTPUT emitter.sub_title("Running Test-Suite") test_case_id = 0 count_seeds = len(values.LIST_SEED_INPUT) count_inputs = len(test_input_list) for argument_list in test_input_list[:count_inputs - count_seeds]: print_argument_list = app.configuration.extract_input_arg_list( argument_list) generalized_arg_list = [] seed_file = None test_case_id = test_case_id + 1 for arg in print_argument_list: if arg in (list(values.LIST_SEED_FILES.values()) + list(values.LIST_TEST_FILES.values())): generalized_arg_list.append("$POC") seed_file = arg else: generalized_arg_list.append(arg) emitter.sub_sub_title("Test Case #" + str(test_case_id)) emitter.highlight("\tUsing Arguments: " + str(generalized_arg_list)) emitter.highlight("\tUsing Input File: " + str(seed_file)) emitter.debug("input list in test case:" + argument_list) argument_list = app.configuration.extract_input_arg_list(argument_list) klee_out_dir = output_dir_path + "/klee-out-test-" + str(test_case_id - 1) if values.LIST_TEST_BINARY: program_path = values.LIST_TEST_BINARY[test_case_id - 1] values.CONF_PATH_PROGRAM = program_path else: program_path = values.CONF_PATH_PROGRAM emitter.highlight("\tUsing Binary: " + str(program_path)) extractor.extract_byte_code(program_path) if not os.path.isfile(program_path + ".bc"): app.utilities.error_exit("Unable to generate bytecode for " + program_path) exit_code = run_concrete_execution(program_path + ".bc", argument_list, True, klee_out_dir) assert exit_code == 0 # set location of bug/crash values.IS_CRASH = False latest_crash_loc = reader.collect_crash_point(values.FILE_MESSAGE_LOG) # if oracle.is_loc_in_trace(values.CONF_LOC_PATCH): # values.USEFUL_SEED_ID_LIST.append(test_case_id) if latest_crash_loc: values.IS_CRASH = True emitter.success("\t\t\t[info] identified a crash location: " + str(latest_crash_loc)) if latest_crash_loc not in values.CONF_LOC_LIST_CRASH: values.CONF_LOC_LIST_CRASH.append(latest_crash_loc)
def run_concrete_execution(program, argument_list, print_output=False, output_dir=None): """ This function will execute the program in concrete mode using the concrete inputs program: the absolute path of the bitcode of the program argument_list : a list containing each argument in the order that should be fed to the program second_var_list: a list of tuples where a tuple is (var identifier, var size, var value) """ logger.info("running concolic execution") emitter.normal("\texecuting klee in concrete mode") global File_Log_Path current_dir = os.getcwd() directory_path = "/".join(str(program).split("/")[:-1]) emitter.debug("changing directory:" + directory_path) os.chdir(directory_path) binary_name = str(program).split("/")[-1] input_argument = "" runtime_lib_path = definitions.DIRECTORY_LIB + "/libtrident_runtime.bca" for argument in argument_list: if "$POC" in argument: argument = values.FILE_POC_GEN # if "_" in argument: # file_index = "_".join(str(argument).split("_")[1:]) # argument = values.LIST_TEST_FILES[file_index] # else: # argument = values.CONF_PATH_POC # if values.FILE_POC_GEN: # argument = values.FILE_POC_GEN input_argument += " " + str(argument) if output_dir: klee_command = "klee --output-dir=" + str(output_dir) + " " else: klee_command = "klee " klee_command += "--posix-runtime " \ "--libc=uclibc " \ "--search=dfs " \ "--write-smt2s " \ "--external-calls=all " \ "--max-forks {0} ".format(values.DEFAULT_MAX_FORK) \ + values.CONF_KLEE_FLAGS + " " \ + "--link-llvm-lib={0} ".format(runtime_lib_path) \ + "{0} ".format(binary_name) \ + input_argument if not print_output: klee_command += " > " + File_Log_Path + " 2>&1 " return_code = utilities.execute_command(klee_command) emitter.debug("changing directory:" + current_dir) os.chdir(current_dir) return return_code
def recover_patch_list(result_list, patch_list, path_condition, assertion): recover_list = [] emitter.error( "\t[error] something went wrong with patch validation, attempting to recover" ) emitter.debug("result list size: " + str(len(result_list))) emitter.debug("patch list size: " + str(len(patch_list))) emitter.warning( "\t[warning] attempting to re-run parallel refinement: missing " + str(len(patch_list) - len(result_list))) diff_list_a = get_diff_list(result_list, patch_list) result_list_a = parallel.refine_patch_space(diff_list_a, path_condition, assertion) recover_list = update_index(result_list_a, diff_list_a, patch_list) if len(diff_list_a) != len(result_list_a): emitter.error( "\t[error] something went wrong with patch validation, attempting to recover" ) emitter.debug("result list size: " + str(len(result_list))) emitter.debug("patch list size: " + str(len(patch_list))) emitter.warning( "\t[warning] attempting to re-run sequential refinement: missing " + str(len(diff_list_a) - len(result_list_a))) diff_list_b = get_diff_list(result_list_a, diff_list_a) result_list_b = parallel.refine_patch_space(diff_list_b, path_condition, assertion, True) recover_list = recover_list + update_index(result_list_b, diff_list_b, patch_list) return recover_list
def generate_flipped_path(ppc): """ This function will check if a selected path is feasible ppc : partial path conditoin at chosen control loc chosen_control_loc: branch location selected for flip returns satisfiability of the negated path """ parser = SmtLibParser() new_path = None try: script = parser.get_script(cStringIO(ppc)) formula = script.get_last_formula() prefix = formula.arg(0) constraint = formula.arg(1) new_path = And(prefix, Not(constraint)) assert str(new_path.serialize()) != str(formula.serialize()) except Exception as ex: emitter.debug("Pysmt parser error, skipping path flip") finally: return new_path
def initialize(): emitter.title("Initializing Program") program_path = values.CONF_PATH_PROGRAM extractor.extract_byte_code(program_path) test_input_list = values.LIST_TEST_INPUT second_var_list = list() directory_path = "/".join(str(program_path).split("/")[:-1]) klee_out_dir = directory_path + "/klee-last" emitter.sub_title("Running Test-Suite") test_case_id = 0 for argument_list in test_input_list: print_argument_list = app.configuration.extract_input_arg_list(argument_list) generalized_arg_list = [] seed_file = None test_case_id = test_case_id + 1 for arg in print_argument_list: if arg in (values.LIST_SEED_FILES + list(values.LIST_TEST_FILES.values())): generalized_arg_list.append("$POC") seed_file = arg else: generalized_arg_list.append(arg) emitter.sub_sub_title("Test Case #" + str(test_case_id)) emitter.highlight("\tUsing Arguments: " + str(generalized_arg_list)) emitter.highlight("\tUsing Input: " + str(seed_file)) emitter.debug("input list in test case:" + argument_list) argument_list = app.configuration.extract_input_arg_list(argument_list) exit_code = run_concrete_execution(program_path + ".bc", argument_list, True) assert exit_code == 0 # set location of bug/crash values.IS_CRASH = False latest_crash_loc = reader.collect_crash_point(values.FILE_MESSAGE_LOG) if latest_crash_loc: values.IS_CRASH = True emitter.success("\t\t\t[info] identified a crash location: " + str(latest_crash_loc)) if latest_crash_loc not in values.CONF_LOC_LIST_CRASH: values.CONF_LOC_LIST_CRASH.append(latest_crash_loc)
def run_concolic_execution(program, argument_list, second_var_list, print_output=False, klee_out_dir=None): """ This function will execute the program in concolic mode using the generated ktest file program: the absolute path of the bitcode of the program argument_list : a list containing each argument in the order that should be fed to the program second_var_list: a list of tuples where a tuple is (var identifier, var size, var value) """ logger.info("running concolic execution") global File_Log_Path current_dir = os.getcwd() directory_path = "/".join(str(program).split("/")[:-1]) emitter.debug("changing directory:" + directory_path) project_path = values.CONF_DIR_SRC os.chdir(directory_path) binary_name = str(program).split("/")[-1] input_argument = "" # argument_list = str(argument_str).split(" ") for argument in argument_list: index = list(argument_list).index(argument) if "$POC" in argument: file_path = values.FILE_POC_GEN # if "_" in argument: # file_index = "_".join(str(argument).split("_")[1:]) # file_path = values.LIST_TEST_FILES[file_index] # else: # file_path = values.CONF_PATH_POC # if values.FILE_POC_GEN: # file_path = values.FILE_POC_GEN # elif values.FILE_POC_SEED: # file_path = values.FILE_POC_SEED concrete_file = open(file_path, 'rb') bit_size = os.fstat(concrete_file.fileno()).st_size input_argument += " A --sym-files 1 " + str(bit_size) + " " elif str(index) in values.CONF_MASK_ARG: input_argument += " " + argument else: input_argument += " --sym-arg " + str(len(str(argument))) ktest_path, return_code = generator.generate_ktest(argument_list, second_var_list) ktest_log_file = "/tmp/ktest.log" ktest_command = "ktest-tool " + ktest_path + " > " + ktest_log_file utilities.execute_command(ktest_command) bit_length_list = reader.read_bit_length(ktest_log_file) if values.LIST_BIT_LENGTH: for var in bit_length_list: if var in values.LIST_BIT_LENGTH: if values.LIST_BIT_LENGTH[var] < bit_length_list[var]: values.LIST_BIT_LENGTH[var] = bit_length_list[var] else: values.LIST_BIT_LENGTH[var] = bit_length_list[var] else: values.LIST_BIT_LENGTH = bit_length_list emitter.normal("\texecuting klee in concolic mode") # hit_location_flag = " " runtime_lib_path = definitions.DIRECTORY_LIB + "/libtrident_runtime.bca" # if values.CONF_DISTANCE_METRIC == "control-loc": hit_location_flag = "--hit-locations " + values.CONF_LOC_BUG + "," + values.CONF_LOC_PATCH if values.CONF_LOC_LIST_CRASH: crash_locations = ', '.join( ['{}'.format(loc) for loc in values.CONF_LOC_LIST_CRASH]) hit_location_flag += "," + crash_locations + " " else: hit_location_flag += " " ppc_log_flag = "" if values.DEFAULT_DISTANCE_METRIC != values.OPTIONS_DIST_METRIC[2]: ppc_log_flag = "--log-ppc " klee_command = "timeout " + str(values.DEFAULT_TIMEOUT_KLEE_CONCOLIC) + " " if klee_out_dir: klee_command += "klee --output-dir=" + str(klee_out_dir) + " " values.KLEE_LAST_DIR = klee_out_dir else: klee_command += "klee " klee_command += "--posix-runtime " \ "--libc=uclibc " \ "--write-smt2s " \ "-allow-seed-extension " \ "-named-seed-matching " \ "--log-trace " \ + "--external-calls=all " \ + "--link-llvm-lib={0} " .format(runtime_lib_path) \ + "--max-time={0} ".format(values.DEFAULT_TIMEOUT_KLEE_CONCOLIC) \ + "{0}".format(ppc_log_flag) \ + "{0}".format(hit_location_flag) \ + "--max-forks {0} ".format(values.DEFAULT_MAX_FORK) \ + values.CONF_KLEE_FLAGS + " " \ + "--seed-out={0} ".format(ktest_path) \ + "{0} ".format(binary_name) \ + input_argument if not print_output: klee_command += " > " + File_Log_Path + " 2>&1 " return_code = utilities.execute_command(klee_command) emitter.debug("changing directory:" + current_dir) os.chdir(current_dir) # collect artifacts ppc_log_path = klee_out_dir + "/ppc.log" trace_log_path = klee_out_dir + "/trace.log" if values.DEFAULT_DISTANCE_METRIC != values.OPTIONS_DIST_METRIC[2]: ppc_list, path_formula = reader.collect_symbolic_path( ppc_log_path, project_path) values.LIST_PPC = values.LIST_PPC = ppc_list values.LAST_PPC_FORMULA = path_formula values.PREFIX_PPC_STR = reader.collect_symbolic_path_prefix( ppc_log_path, project_path) else: values.LAST_PPC_FORMULA = extractor.extract_largest_path_condition( klee_out_dir) if values.LAST_PPC_FORMULA: ppc_list = generator.generate_ppc_from_formula( values.LAST_PPC_FORMULA) values.LIST_PPC = values.LIST_PPC + ppc_list # else: # values.LIST_PPC = [] values.PREFIX_PPC_FORMULA = generator.generate_formula( values.PREFIX_PPC_STR) values.LIST_TRACE = reader.collect_trace(trace_log_path, project_path) if oracle.is_loc_in_trace(values.CONF_LOC_BUG) and oracle.is_loc_in_trace( values.CONF_LOC_PATCH): if values.DEFAULT_DISTANCE_METRIC != values.OPTIONS_DIST_METRIC[2]: values.NEGATED_PPC_FORMULA = generator.generate_path_for_negation() else: if values.LAST_PPC_FORMULA: values.NEGATED_PPC_FORMULA = generator.generate_negated_path( values.LAST_PPC_FORMULA) else: values.NEGATED_PPC_FORMULA = None return return_code
def generate_model(formula): """ This function will invoke PySMT APIs to solve the provided formula and return the byte list of the model Arguments: formula: smtlib formatted formula """ emitter.debug("extracting z3 model") model = get_model(formula) if model is None: return None path_script = "/tmp/z3_script_model" write_smtlib(formula, path_script) with open(path_script, "r") as script_file: script_lines = script_file.readlines() script = "".join(script_lines) var_list = set(re.findall("\(declare-fun (.+?) \(\)", script)) sym_var_list = dict() for var_name in var_list: # sym_var_list[var_name] = dict() if "const_" in var_name and not "const_arr" in var_name: sym_def = Symbol(var_name, BV32) if sym_def not in model: continue x = model[sym_def] byte_list = dict() default_value = x.bv_signed_value() byte_list[0] = default_value else: sym_def = Symbol(var_name, ArrayType(BV32, BV8)) if sym_def not in model: continue x = model[sym_def].simplify() byte_list = dict() value_array_map = x.array_value_assigned_values_map() default_value = int(str(x.array_value_default()).split("_")[0]) if not value_array_map: byte_list[0] = default_value else: for idx, val in value_array_map.items(): index = int(str(idx).split("_")[0]) value = int(str(val).split("_")[0]) byte_list[index] = value max_index = max(list(byte_list.keys())) if var_name in values.LIST_BIT_LENGTH: array_size = values.LIST_BIT_LENGTH[var_name] - 1 if var_name in ["A-data"]: array_size = max_index else: array_size = max_index + 1 # TODO: this could be wrong calculation if max_index == 0: array_size = 2 if var_name not in ["A-data"]: for i in range(0, array_size): if i not in byte_list: byte_list[i] = default_value if var_name not in ["A-data", "A-data-stat"]: for i in range(array_size - 1, -1, -1): if byte_list[i] == 0: byte_list.pop(i) else: break sym_var_list[var_name] = byte_list emitter.data("model var list", sym_var_list) return sym_var_list