about summary refs log tree commit diff homepage
diff options
context:
space:
mode:
-rw-r--r--lib/Core/Differentiator.cpp54
-rw-r--r--lib/Core/Differentiator.h10
-rw-r--r--lib/Core/Executor.cpp2
-rw-r--r--runtime/POSIX/fd_init.c2
-rwxr-xr-xtools/klee-psychic/klee-psychic171
5 files changed, 81 insertions, 158 deletions
diff --git a/lib/Core/Differentiator.cpp b/lib/Core/Differentiator.cpp
index 95c08bda..553df337 100644
--- a/lib/Core/Differentiator.cpp
+++ b/lib/Core/Differentiator.cpp
@@ -109,35 +109,41 @@ Differentiator::Differentiator(std::unique_ptr<TimingSolver>* s,
 
 Differentiator::Bytes
 run(const std::uint64_t rev, const char* prog,
-    const Differentiator::TestArgs& argv)
+    const Differentiator::TestInps& inputs)
 {
-  int fildes[2];
+  int ind[2], outd[2];
   {
-    const auto err = pipe(fildes);
+    auto err = pipe(ind);
+    assert(!err);
+    err = pipe(outd);
     assert(!err);
   }
   posix_spawn_file_actions_t action;
   posix_spawn_file_actions_init(&action);
-  posix_spawn_file_actions_addclose(&action, fildes[0]);
-  posix_spawn_file_actions_adddup2(&action, fildes[1], 1);
+  posix_spawn_file_actions_adddup2(&action, ind[0], 0);
+  posix_spawn_file_actions_addclose(&action, ind[1]);
+  posix_spawn_file_actions_addclose(&action, outd[0]);
+  posix_spawn_file_actions_adddup2(&action, outd[1], 1);
   {
     pid_t pid;
-    std::vector<const char*> argp {prog};
-    for (const auto& v : argv)
-      argp.push_back((const char *) v.data());
-    argp.push_back(NULL);
+    std::vector<const char*> argv {prog};
+    for (const auto& v : inputs.first)
+      argv.push_back((const char *) v.data());
+    argv.push_back(NULL);
     std::ostringstream env;
     env << "KLEE_META=" << rev;
     char *const envp[] = {const_cast<char* const>(env.str().c_str()), NULL};
     const auto err = posix_spawn(&pid, prog, &action, NULL,
-                                 const_cast<char* const *>(argp.data()), envp);
+                                 const_cast<char* const *>(argv.data()), envp);
     assert(!err);
   }
-  close(fildes[1]);
+  write(ind[0], inputs.second.data(), inputs.second.size());
+  close(ind[0]);
+  close(outd[1]);
 
   char buffer[128]; // output buffer for concrete execution
   Differentiator::Bytes result;
-  for (unsigned char n; n = read(fildes[0], buffer, sizeof(buffer));) {
+  for (unsigned char n; n = read(outd[0], buffer, sizeof(buffer));) {
     assert(n >= 0);
     for (unsigned char i = 0; i < n; ++i)
       result.push_back(buffer[i]);
@@ -156,14 +162,14 @@ logBytes(const Differentiator::Bytes& buffer)
 }
 
 void
-logArgs(const Differentiator::TestArgs& args)
+logInputs(const Differentiator::TestInps& inputs)
 {
   llvm::errs() << "Args:";
-  for (const auto& s : args) {
+  for (const auto& s : inputs.first) {
     llvm::errs() << ' ';
     logBytes(s);
   }
-  llvm::errs() << '\n';
+  llvm::errs() << "\nStdin: " << inputs.second.data() << '\n';
 }
 
 void
@@ -188,7 +194,7 @@ Differentiator::extract(ExecutionState* a, ExecutionState* b,
                         const std::vector<const Array*>& objects,
                         const std::vector<Bytes>& values)
 {
-  TestArgs argv;
+  TestInps inputs;
   TestOuts outputs;
   {
     std::map<std::uint8_t, Bytes> args;
@@ -200,18 +206,20 @@ Differentiator::extract(ExecutionState* a, ExecutionState* b,
       } else if (isSymOut(name.substr(0, name.size() - 2))) {
         const auto rev = ((name[name.size() - 1] == 'a') ? a : b)->patchNo;
         outputs[rev].first[name.substr(4, name.size() - 6)] = values[i];
+      } else if (name == "stdin") {
+          inputs.second = values[i];
       }
     }
     uint8_t last = 0;
     for (const auto& [rev, arg] : args) {
       assert(rev == last);
-      argv.push_back(arg);
+      inputs.first.push_back(arg);
       last++;
     }
   }
   for (const auto& rev : this->revisions)
-    outputs[rev].second = run(rev, this->prog, argv);
-  this->tests[argv] = outputs;
+    outputs[rev].second = run(rev, this->prog, inputs);
+  this->tests[inputs] = outputs;
 
   std::map<std::string, Clusters> revOut;
   for (const auto& [rev, out] : outputs) {
@@ -228,7 +236,7 @@ Differentiator::extract(ExecutionState* a, ExecutionState* b,
         for (std::uint64_t i : p.second)
           for (std::uint64_t j : q.second)
             if (i < j)
-              this->done.emplace(std::make_pair(i, j), &this->tests[argv]);
+              this->done.emplace(std::make_pair(i, j), &this->tests[inputs]);
       }
 }
 
@@ -314,7 +322,7 @@ Differentiator::search(ExecutionState* latest)
 void
 Differentiator::log()
 {
-  std::vector<std::pair<const TestArgs, const Clusters>> clusterings;
+  std::vector<std::pair<const TestInps, const Clusters>> clusterings;
   for (auto& t : this->tests) {
     Clusters clusters;
     for (const auto& rev : this->revisions) {
@@ -327,8 +335,8 @@ Differentiator::log()
     if (clusters.size() > 1)
       clusterings.push_back({std::move(t.first), std::move(clusters)});
   }
-  for (const auto& [args, clusters] : clusterings) {
-    logArgs(args);
+  for (const auto& [inputs, clusters] : clusterings) {
+    logInputs(inputs);
     logClusters(clusters);
     llvm::errs() << '\n';
   }
diff --git a/lib/Core/Differentiator.h b/lib/Core/Differentiator.h
index 11225ac7..98c15e92 100644
--- a/lib/Core/Differentiator.h
+++ b/lib/Core/Differentiator.h
@@ -35,8 +35,8 @@ private:
 struct Differentiator {
   /// Buffer of bytes
   typedef std::vector<unsigned char> Bytes;
-  /// CLI arguments
-  typedef std::vector<Bytes> TestArgs;
+  /// argv stdin
+  typedef std::pair<std::vector<Bytes>, Bytes> TestInps;
   /// :rev (:var val) stdout
   typedef std::map<std::uint64_t,
                    std::pair<std::map<std::string, Bytes>,
@@ -59,6 +59,8 @@ struct Differentiator {
 
   /// Program revision numbers
   std::set<std::uint64_t> revisions;
+  /// Differentiated pairs
+  std::map<std::pair<std::uint64_t, std::uint64_t>, TestOuts*> done;
 private:
   /// Program path for concrete execution.
   const char* prog;
@@ -67,7 +69,7 @@ private:
            std::set<ExecutionState*,
                     ExecutionStatePathCondCompare>> exits;
   /// Differential tests
-  std::map<TestArgs, TestOuts> tests;
+  std::map<TestInps, TestOuts> tests;
   /// SMT solver "borrowed" from Executor
   std::unique_ptr<TimingSolver>* solver;
   /// SMT solving timeout
@@ -76,8 +78,6 @@ private:
   ArrayCache& arrayCache;
   /// Symbolic output renamers
   ExprCmbnVisitor visitorA, visitorB;
-  /// Differentiated pairs
-  std::map<std::pair<std::uint64_t, std::uint64_t>, TestOuts*> done;
 };
 
 } // namespace klee
diff --git a/lib/Core/Executor.cpp b/lib/Core/Executor.cpp
index 465100f4..34b3d7b8 100644
--- a/lib/Core/Executor.cpp
+++ b/lib/Core/Executor.cpp
@@ -1104,7 +1104,7 @@ Executor::StatePair Executor::fork(ExecutionState &current, ref<Expr> condition,
 
   // Fix branch in only-replay-seed mode, if we don't have both true
   // and false seeds.
-  if (isSeeding && 
+  if (isSeeding && !Differentiator::extractPatchNumber(condition).first &&
       (current.forkDisabled || OnlyReplaySeeds) && 
       res == Solver::Unknown) {
     bool trueSeed=false, falseSeed=false;
diff --git a/runtime/POSIX/fd_init.c b/runtime/POSIX/fd_init.c
index b93148ec..90c04a73 100644
--- a/runtime/POSIX/fd_init.c
+++ b/runtime/POSIX/fd_init.c
@@ -153,7 +153,7 @@ void klee_init_fds(unsigned n_files, unsigned file_length,
     __exe_fs.sym_stdout = malloc(sizeof(*__exe_fs.sym_stdout));
     if (!__exe_fs.sym_stdout)
       klee_report_error(__FILE__, __LINE__, "out of memory in klee_init_env", "user.err");
-    __create_new_dfile(__exe_fs.sym_stdout, stdout_length, "stdout", &s);
+    __create_new_dfile(__exe_fs.sym_stdout, stdout_length, "out!stdout!0", &s);
     __exe_env.fds[1].dfile = __exe_fs.sym_stdout;
     __exe_fs.stdout_writes = 0;
   }
diff --git a/tools/klee-psychic/klee-psychic b/tools/klee-psychic/klee-psychic
index 6f1c1bcf..271c7496 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=o{#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[@]}