diff --git a/Cargo.toml b/Cargo.toml index c28b6f8f1b..36a0cae884 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,6 +15,7 @@ members = [ "libafl_sugar", "libafl_tests", "libafl_concolic/symcc_runtime", + "libafl_concolic/symcc_libafl", "libafl_concolic/test/dump_constraints", "libafl_concolic/test/runtime_test", ] diff --git a/Dockerfile b/Dockerfile index 08a43e21f2..50c4c4f1b4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -65,6 +65,9 @@ COPY scripts/dummy.rs libafl_concolic/test/runtime_test/src/lib.rs COPY libafl_concolic/symcc_runtime/Cargo.toml libafl_concolic/symcc_runtime/build.rs libafl_concolic/symcc_runtime/ COPY scripts/dummy.rs libafl_concolic/symcc_runtime/src/lib.rs +COPY libafl_concolic/symcc_libafl/Cargo.toml libafl_concolic/symcc_libafl/ +COPY scripts/dummy.rs libafl_concolic/symcc_libafl/src/lib.rs + RUN cargo build && cargo build --release COPY scripts scripts @@ -94,6 +97,7 @@ COPY libafl_frida/src libafl_frida/src RUN touch libafl_qemu/src/lib.rs COPY libafl_qemu/src libafl_qemu/src RUN touch libafl_frida/src/lib.rs +COPY libafl_concolic/symcc_libafl libafl_concolic/symcc_libafl RUN cargo build && cargo build --release # Copy fuzzers over diff --git a/fuzzers/libfuzzer_stb_image_concolic/fuzzer/Cargo.toml b/fuzzers/libfuzzer_stb_image_concolic/fuzzer/Cargo.toml index 7cd288eca0..d7e86848a7 100644 --- a/fuzzers/libfuzzer_stb_image_concolic/fuzzer/Cargo.toml +++ b/fuzzers/libfuzzer_stb_image_concolic/fuzzer/Cargo.toml @@ -24,3 +24,4 @@ cc = { version = "1.0", features = ["parallel"] } num_cpus = "1.0" cmake = "0.1" which = "4.1" +symcc_libafl = {path = "../../../libafl_concolic/symcc_libafl"} diff --git a/fuzzers/libfuzzer_stb_image_concolic/fuzzer/build.rs b/fuzzers/libfuzzer_stb_image_concolic/fuzzer/build.rs index 26a03d1206..69e1eb97f7 100644 --- a/fuzzers/libfuzzer_stb_image_concolic/fuzzer/build.rs +++ b/fuzzers/libfuzzer_stb_image_concolic/fuzzer/build.rs @@ -92,48 +92,11 @@ fn main() { println!("cargo:rerun-if-changed=harness_symcc.c"); } -const SYMCC_REPO_URL: &str = "https://github.com/AFLplusplus/symcc.git"; -const SYMCC_REPO_COMMIT: &str = "45cde0269ae22aef4cca2e1fb98c3b24f7bb2984"; - fn clone_and_build_symcc(out_path: &Path) -> PathBuf { let repo_dir = out_path.join("libafl_symcc_src"); if !repo_dir.exists() { - build_dep_check(&["git"]); - let mut cmd = Command::new("git"); - cmd.arg("clone").arg(SYMCC_REPO_URL).arg(&repo_dir); - let output = cmd.output().expect("failed to execute git clone"); - if output.status.success() { - let mut cmd = Command::new("git"); - cmd.arg("checkout") - .arg(SYMCC_REPO_COMMIT) - .current_dir(&repo_dir); - let output = cmd.output().expect("failed to execute git checkout"); - if output.status.success() { - } else { - println!("failed to checkout symcc git repository commit:"); - let mut stdout = stdout(); - stdout - .write_all(&output.stderr) - .expect("failed to write git error message to stdout"); - exit(1) - } - } else { - println!("failed to clone symcc git repository:"); - let mut stdout = stdout(); - stdout - .write_all(&output.stderr) - .expect("failed to write git error message to stdout"); - exit(1) - } + symcc_libafl::clone_symcc(&repo_dir); } - build_dep_check(&["cmake"]); - - use cmake::Config; - - Config::new(repo_dir) - .define("Z3_TRUST_SYSTEM_VERSION", "ON") - .no_build_target(true) - .build() - .join("build") + symcc_libafl::build_symcc(&repo_dir) } diff --git a/libafl_concolic/symcc_libafl/Cargo.toml b/libafl_concolic/symcc_libafl/Cargo.toml new file mode 100644 index 0000000000..6f91514f71 --- /dev/null +++ b/libafl_concolic/symcc_libafl/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "symcc_libafl" +version = "0.1.0" +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[features] +default = ["build", "clone"] +build = ["which", "cmake"] # common functionality to build symcc +clone = ["which"] # common functionality to check out the symcc repo using git + +[dependencies] +which = { version = "4.1", optional = true } +cmake = { version = "0.1", optional = true } \ No newline at end of file diff --git a/libafl_concolic/symcc_libafl/src/lib.rs b/libafl_concolic/symcc_libafl/src/lib.rs new file mode 100644 index 0000000000..b5cfb7e721 --- /dev/null +++ b/libafl_concolic/symcc_libafl/src/lib.rs @@ -0,0 +1,81 @@ +//! This is a 'meta-package' for libafl that exposes a consistent URL and commit hash for the +//! [`SymCC` fork](https://github.com/AFLplusplus/symcc). + +/// The URL of the `LibAFL` `SymCC` fork. +pub const SYMCC_REPO_URL: &str = "https://github.com/AFLplusplus/symcc.git"; +/// The commit of the `LibAFL` `SymCC` fork. +pub const SYMCC_REPO_COMMIT: &str = "45cde0269ae22aef4cca2e1fb98c3b24f7bb2984"; + +#[cfg(feature = "clone")] +mod clone { + use std::{ + io::{stdout, Write}, + path::Path, + process::Command, + }; + + use which::which; + + use crate::{SYMCC_REPO_COMMIT, SYMCC_REPO_URL}; + + /// Checks out the repository into the given directory with the given URL and commit hash. + /// Any errors will trigger a panic. + pub fn clone_symcc_at_version(path: &Path, url: &str, commit: &str) { + if which("git").is_err() { + panic!("ERROR: unable to find git. Git is required to download SymCC."); + } + let mut cmd = Command::new("git"); + cmd.arg("clone").arg(url).arg(&path); + let output = cmd.output().expect("failed to execute git clone"); + if output.status.success() { + let mut cmd = Command::new("git"); + cmd.arg("checkout").arg(commit).current_dir(&path); + let output = cmd.output().expect("failed to execute git checkout"); + if !output.status.success() { + println!("failed to checkout symcc git repository commit:"); + let mut stdout = stdout(); + stdout + .write_all(&output.stderr) + .expect("failed to write git error message to stdout"); + panic!(); + } + } else { + println!("failed to clone symcc git repository:"); + let mut stdout = stdout(); + stdout + .write_all(&output.stderr) + .expect("failed to write git error message to stdout"); + panic!(); + } + } + + /// Checks out the repository into the given directory. + /// Any errors will trigger a panic. + pub fn clone_symcc(path: &Path) { + clone_symcc_at_version(path, SYMCC_REPO_URL, SYMCC_REPO_COMMIT); + } +} + +#[cfg(feature = "clone")] +pub use clone::clone_symcc; + +#[cfg(feature = "build")] +mod build { + use std::path::{Path, PathBuf}; + + /// Builds `SymCC` at the given directory using [`cmake`](https://crates.io/crates/cmake). + /// Returns the build artifact directory. + #[must_use] + pub fn build_symcc(path: &Path) -> PathBuf { + use cmake::Config; + + Config::new(path) + .define("Z3_TRUST_SYSTEM_VERSION", "ON") + .no_build_target(true) + .build() + .join("build") + } +} + +#[cfg(feature = "build")] +pub use build::build_symcc; diff --git a/libafl_concolic/symcc_runtime/Cargo.toml b/libafl_concolic/symcc_runtime/Cargo.toml index 4085ba4907..cba873f69a 100644 --- a/libafl_concolic/symcc_runtime/Cargo.toml +++ b/libafl_concolic/symcc_runtime/Cargo.toml @@ -22,3 +22,4 @@ bindgen = "0.58" regex = "1" lazy_static = "1.4" which = "4.1" +symcc_libafl = {path = "../symcc_libafl"} \ No newline at end of file diff --git a/libafl_concolic/symcc_runtime/build.rs b/libafl_concolic/symcc_runtime/build.rs index 822a1410a8..2bdc07e93b 100644 --- a/libafl_concolic/symcc_runtime/build.rs +++ b/libafl_concolic/symcc_runtime/build.rs @@ -1,16 +1,14 @@ use std::{ env, fs::File, - io::{stdout, Write}, + io::Write, path::{Path, PathBuf}, - process::{exit, Command}, + process::exit, }; use lazy_static::lazy_static; use regex::{Regex, RegexBuilder}; - -const SYMCC_REPO_URL: &str = "https://github.com/AFLplusplus/symcc.git"; -const SYMCC_REPO_COMMIT: &str = "45cde0269ae22aef4cca2e1fb98c3b24f7bb2984"; +use symcc_libafl::clone_symcc; const SYMCC_RUNTIME_FUNCTION_NAME_PREFIX: &str = "_cpp_"; @@ -108,38 +106,10 @@ fn write_cpp_function_export_macro(out_path: &Path, cpp_bindings: &bindgen::Bind fn checkout_symcc(out_path: &Path) -> PathBuf { let repo_dir = out_path.join("libafl_symcc_src"); - if repo_dir.exists() { - repo_dir - } else { - build_dep_check(&["git"]); - let mut cmd = Command::new("git"); - cmd.arg("clone").arg(SYMCC_REPO_URL).arg(&repo_dir); - let output = cmd.output().expect("failed to execute git clone"); - if output.status.success() { - let mut cmd = Command::new("git"); - cmd.arg("checkout") - .arg(SYMCC_REPO_COMMIT) - .current_dir(&repo_dir); - let output = cmd.output().expect("failed to execute git checkout"); - if output.status.success() { - repo_dir - } else { - println!("failed to checkout symcc git repository commit:"); - let mut stdout = stdout(); - stdout - .write_all(&output.stderr) - .expect("failed to write git error message to stdout"); - exit(1) - } - } else { - println!("failed to clone symcc git repository:"); - let mut stdout = stdout(); - stdout - .write_all(&output.stderr) - .expect("failed to write git error message to stdout"); - exit(1) - } + if !repo_dir.exists() { + clone_symcc(&repo_dir); } + repo_dir } fn write_rust_runtime_macro_file(out_path: &Path, symcc_src_path: &Path) {