From 75e5fea6ebf0471ed97f6ca4611277b8d3d0cef5 Mon Sep 17 00:00:00 2001
From: Per Lindgren <per.lindgren@ltu.se>
Date: Sun, 25 Nov 2018 13:59:09 +0100
Subject: [PATCH] cargo-klee

---
 .gitignore             |   2 +-
 cargo-klee/Cargo.lock  |  14 +++++
 cargo-klee/Cargo.toml  |   7 +++
 cargo-klee/src/main.rs | 125 +++++++++++++++++++++++++++++++++++++++++
 4 files changed, 147 insertions(+), 1 deletion(-)
 create mode 100644 cargo-klee/Cargo.lock
 create mode 100644 cargo-klee/Cargo.toml
 create mode 100644 cargo-klee/src/main.rs

diff --git a/.gitignore b/.gitignore
index 53eaa21..84a323e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,2 @@
-/target
+target
 **/*.rs.bk
diff --git a/cargo-klee/Cargo.lock b/cargo-klee/Cargo.lock
new file mode 100644
index 0000000..f7f05af
--- /dev/null
+++ b/cargo-klee/Cargo.lock
@@ -0,0 +1,14 @@
+[[package]]
+name = "cargo-klee"
+version = "0.1.0"
+dependencies = [
+ "libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "libc"
+version = "0.2.33"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[metadata]
+"checksum libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)" = "5ba3df4dcb460b9dfbd070d41c94c19209620c191b0340b929ce748a2bcd42d2"
diff --git a/cargo-klee/Cargo.toml b/cargo-klee/Cargo.toml
new file mode 100644
index 0000000..1a6a266
--- /dev/null
+++ b/cargo-klee/Cargo.toml
@@ -0,0 +1,7 @@
+[package]
+authors = ["Jorge Aparicio <jorge@japaric.io>"]
+name = "cargo-klee"
+version = "0.1.0"
+
+[dependencies]
+libc = "0.2.33"
diff --git a/cargo-klee/src/main.rs b/cargo-klee/src/main.rs
new file mode 100644
index 0000000..025dfc2
--- /dev/null
+++ b/cargo-klee/src/main.rs
@@ -0,0 +1,125 @@
+extern crate libc;
+
+use std::io::{self, Read, Write};
+use std::process::{Command, Stdio};
+use std::{env, str};
+
+fn main() {
+    // first argument is the path to this binary; the second argument is always "klee" -- both can
+    // be ignored
+    let args = env::args().skip(2).collect::<Vec<_>>();
+
+    // turn `cargo klee --example foo` into `xargo rustc --example foo -- (..)`
+    let mut c = Command::new("xargo");
+    c.arg("rustc")
+        .args(&args)
+        // verbose output for debugging purposes
+        .arg("-v")
+        .arg("--color=always")
+        .arg("--")
+        // ignore linking
+        .args(&["-C", "linker=true"])
+        // force LTO
+        .args(&["-C", "lto"])
+        // also output the LLVM-IR (.ll file) for debugging purposes
+        .arg("--emit=llvm-bc,llvm-ir")
+        // force panic=abort in all crates
+        .env("RUSTFLAGS", "-C panic=abort")
+        // collect stderr
+        .stderr(Stdio::piped());
+
+    // print full command for debugging purposes
+    eprintln!("{:?}", c);
+
+    // spawn process and drive it completion while replicating its stderr in ours
+    let mut p = c.spawn().unwrap();
+    let mut pstderr = p.stderr.take().unwrap();
+    let mut buf = [0; 1024];
+    let mut output = vec![];
+
+    let stderr = io::stderr();
+    let mut stderr = stderr.lock();
+    loop {
+        if let Ok(n) = pstderr.read(&mut buf) {
+            stderr.write(&buf[..n]).unwrap();
+            output.extend_from_slice(&buf[..n]);
+        }
+
+        if let Some(status) = p.try_wait().unwrap() {
+            assert!(status.success());
+            break;
+        }
+    }
+
+    // next we are going to try to run the .bc file using klee inside a docker container
+    // FIXME the way we find out which .bc file we have to use is pretty hacky; using Cargo as a
+    // library would be cleaner, I suppose
+
+    // find out the argument passed to `--example`, if used at all
+    let mut example = None;
+    let mut iargs = args.iter();
+    while let Some(arg) = iargs.next() {
+        if arg == "--example" {
+            example = iargs.next();
+            break;
+        }
+    }
+
+    // get the suffix of the example
+    let (example, suffix) = if let Some(example) = example {
+        let mut suffix = None;
+        let output = str::from_utf8(&output).unwrap();
+        for line in output.lines() {
+            if line.contains(&format!("examples/{}.rs", example)) {
+                for part in line.split(' ') {
+                    if part.starts_with("extra-filename=") {
+                        suffix = part.split('=').nth(1);
+                    }
+                }
+            }
+        }
+
+        (example, suffix.unwrap())
+    } else {
+        // nothing to do if the user didn't use `--example`
+        return;
+    };
+
+    let profile = if args.iter().any(|a| a == "--release") {
+        "release"
+    } else {
+        "debug"
+    };
+
+    let mut docker = Command::new("docker");
+    docker
+        .arg("run")
+        .arg("--rm")
+        .args(&["--user", &format!("{}:{}", uid(), gid())])
+        .args(&[
+            "-v",
+            &format!("{}:/mnt", env::current_dir().unwrap().display()),
+        ])
+        .args(&["-w", "/mnt"])
+        .arg("-it")
+        .arg("afoht/llvm-klee-4")
+        .args(&[
+            "/usr/bin/klee",
+            &format!("target/{}/examples/{}{}.bc", profile, example, suffix),
+        ]);
+
+    // print full command for debugging purposes
+    eprintln!("{:?}", docker);
+
+    assert!(docker.status().unwrap().success());
+}
+
+// NOTE from cross
+fn gid() -> u32 {
+    unsafe { libc::getgid() }
+}
+
+// NOTE from cross
+fn uid() -> u32 {
+    unsafe { libc::getuid() }
+}
-- 
GitLab