diff options
| -rwxr-xr-x | tools/klee-psychic/klee-psychic | 171 | 
1 files changed, 43 insertions, 128 deletions
| diff --git a/tools/klee-psychic/klee-psychic b/tools/klee-psychic/klee-psychic index 6f1c1bcf..df066a41 100755 --- a/tools/klee-psychic/klee-psychic +++ b/tools/klee-psychic/klee-psychic @@ -1,128 +1,43 @@ -#!/usr/bin/env python3 -import sys -import os -import subprocess -import tempfile -import select -import shutil -from itertools import dropwhile, takewhile -from pathlib import Path - -HELP="""OVERVIEW: ZESTI like wrapper of KLEE/Psychic - -USAGE: klee-psychic [klee-options] <input bytecode> <concrete program> - <concrete program arguments> - -WARNING this script is not equivalent to ZESTI in ICSE 2012. It just provides a similar interface to KLEE. Namely it first explores the path of <concrete program arguments> and then continues symbolic execution from that point. Most importantly it does not implement the ZESTI searcher. -""" - - -KLEE="klee" -KTEST_GEN="ktest-gen" - -def find_klee_bin_dir(): - global KLEE - global KTEST_GEN - bin_dir = os.path.dirname(os.path.realpath(__file__)) - KLEE = bin_dir + "/klee" - KTEST_GEN = bin_dir + "/ktest-gen" - if not os.path.isfile(KLEE): - print("WARNING can't find klee at " + KLEE) - KLEE= shutil.which("klee") - print("Using klee in PATH", KLEE) - if not os.path.isfile(KTEST_GEN): - print("WARNING can't find ktest-gen at " + KTEST_GEN) - KTEST_GEN= shutil.which("ktest-gen") - print("Using ktest-gen in PATH", KTEST_GEN) - if KTEST_GEN is None or KLEE is None: - print("Failed to find KLEE at this script location or in PATH. Quitting ...") - sys.exit(1) - print("Using", KLEE) - - -def is_option(arg): - """Check if argument is optional.""" - return arg.startswith('-') - - -def split_args(): - """Return KLEE options, bitcode, executable and its arguments.""" - klee_args = tuple(takewhile(is_option, sys.argv[1:])) - rest = dropwhile(is_option, sys.argv[1:]) - return klee_args, next(rest), next(rest), tuple(rest) - - -def maybe_file_size(name): - try: - return os.path.getsize(name) - except: - return None - -def get_stdin_file(tmpdir): - stdin = "" - stdin_size = 0 - if sys.stdin in select.select([sys.stdin], [], [], 0)[0]: - stdin += sys.stdin.readline() - if stdin == "": - return None, stdin_size - stdin_file_name = tmpdir.name + "/stdin.file" - with open(stdin_file_name, 'w') as f: - stdin_size = f.write(stdin) - return stdin_file_name, stdin_size - - - -def prog_args_to_posix(prog_args): - posix_args = [] - sym_file = 'A' - sym_file_sizes = [] - gen_out_args = [] - for parg in prog_args: - file_size = maybe_file_size(parg) - if file_size is None: - posix_args += ['--sym-arg', str(len(parg))] - gen_out_args += [parg] - else: - sym_file_sizes += [file_size] - posix_args += [sym_file] - sym_file = chr(ord(sym_file) + 1) - gen_out_args += ['--sym-file', parg] - - if ord(sym_file) - ord('A') > 0: - posix_args += ['--sym-files', str(ord(sym_file) - ord('A')), str(max(sym_file_sizes))] - return posix_args, gen_out_args - -def create_ktest_file(gen_out_args, tmpdir): - out_file=tmpdir + "/test.ktest" - subprocess.run([KTEST_GEN, "--bout-file", out_file] + gen_out_args, check=True) - return out_file - - -def main(): - try: - klee_args, bc, prog, prog_args = split_args() - except StopIteration: - print(HELP) - return - find_klee_bin_dir() - tmpdir = tempfile.TemporaryDirectory() - stdin_file, stdin_size = get_stdin_file(tmpdir) - posix_args, gen_out_args = prog_args_to_posix(prog_args) - if stdin_file is not None: - gen_out_args += ["--sym-stdin", stdin_file] - posix_args += ["--sym-stdin", str(stdin_size)] - ktest_file = create_ktest_file(gen_out_args,tmpdir.name) - Path('test.ktest').write_bytes(Path(ktest_file).read_bytes()) - proc = subprocess.Popen([KLEE, *klee_args, f'-seed-file={ktest_file}', - bc, prog, *posix_args], - stdout=sys.stdout, stderr=sys.stderr) - while proc.returncode is None: - try: - proc.wait() - except KeyboardInterrupt: - pass # This is expected when stopping KLEE, so we wait for KLEE to finish - sys.exit(proc.returncode) - - -if __name__ == "__main__": - main() +#!/bin/sh +set -eux + +ktest_file=$(mktemp -t ktest.XXXXXXXXXX) +trap 'rm "$ktest_file"' EXIT +klee_args="--allow-seed-extension --only-replay-seeds --seed-file=$ktest_file" +while test "$1" != "${1#-}" # hyphen-prefixed +do + klee_args+=" $1" + shift +done +program_bc="$1" +shift +program_bin="$1" +shift + +stdin=$(mktemp -t in.XXXXXXXXXX) +trap 'rm "$stdin"' EXIT +cat > $stdin +stdout=$(mktemp -t out.XXXXXXXXXX) +trap 'rm "$stdout"' EXIT +"$program_bin" "$@" < $stdin > $stdout + +ktest_args="--sym-stdin $stdin" +sym_args= +for arg in $@ +do + if test -r $arg + then + ktest_args+=" --sym-file $arg" + # TODO: --sym-files + else + ktest_args+=" $arg" + sym_args+=" --sym-arg=${#arg}" + fi +done + +ktest_gen=$KLEE_PREFIX/bin/ktest-gen +test -x "$ktest_gen" +"$ktest_gen" "${ktest_args[@]}" --bout-file "$ktest_file" +klee_bin=$KLEE_PREFIX/bin/klee +test -x "$klee_bin" +"$klee_bin" ${klee_args[@]} "$program_bc" "$program_bin" ${sym_args[@]} | 
