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