diff --git a/fuzzers/fuzzbench/Cargo.toml b/fuzzers/fuzzbench/Cargo.toml index b0c9fac9ed..ac8c43c0cf 100644 --- a/fuzzers/fuzzbench/Cargo.toml +++ b/fuzzers/fuzzbench/Cargo.toml @@ -31,14 +31,3 @@ nix = "0.20.0" [lib] name = "fuzzbench" crate-type = ["staticlib"] - -[[bin]] -# For c binaries -name = "libafl_cc" -path = "src/bin/libafl_cc.rs" - -[[bin]] -# For cpp binaries -name = "libafl_cxx" -path = "src/bin/libafl_cc.rs" - diff --git a/fuzzers/fuzzbench/src/bin/libafl_cc.rs b/fuzzers/fuzzbench/src/bin/libafl_cc.rs index d2b0630717..29ed3e3757 100644 --- a/fuzzers/fuzzbench/src/bin/libafl_cc.rs +++ b/fuzzers/fuzzbench/src/bin/libafl_cc.rs @@ -1,7 +1,7 @@ -use libafl_cc::{ClangWrapper, CompilerWrapper}; +use libafl_cc::{ClangWrapper, CompilerWrapper, LLVMPasses}; use std::env; -fn main() { +pub fn main() { let args: Vec = env::args().collect(); if args.len() > 1 { let mut dir = env::current_exe().unwrap(); @@ -15,20 +15,18 @@ fn main() { dir.pop(); - let mut cc = ClangWrapper::new("clang", "clang++"); - + let mut cc = ClangWrapper::new(); if let Some(code) = cc - .is_cpp(is_cpp) - .from_args(&args) - .unwrap() - .link_staticlib(&dir, "fuzzbench".into()) - .unwrap() - .add_arg("-fsanitize-coverage=trace-pc-guard,trace-cmp".into()) - .unwrap() + .cpp(is_cpp) // silence the compiler wrapper output, needed for some configure scripts. - .silence() + .silence(true) + .from_args(&args) + .expect("Failed to parse the command line".into()) + .link_staticlib(&dir, "fuzzbench") + .add_arg("-fsanitize-coverage=trace-pc-guard,trace-cmp") + .add_pass(LLVMPasses::CmpLogRtn) .run() - .unwrap() + .expect("Failed to run the wrapped compiler".into()) { std::process::exit(code); } diff --git a/fuzzers/fuzzbench/src/bin/libafl_cxx.rs b/fuzzers/fuzzbench/src/bin/libafl_cxx.rs new file mode 100644 index 0000000000..ce786239b0 --- /dev/null +++ b/fuzzers/fuzzbench/src/bin/libafl_cxx.rs @@ -0,0 +1,5 @@ +pub mod libafl_cc; + +fn main() { + libafl_cc::main() +} diff --git a/fuzzers/generic_inmemory/src/bin/libafl_cc.rs b/fuzzers/generic_inmemory/src/bin/libafl_cc.rs index 0305a00b66..2bee42e73d 100644 --- a/fuzzers/generic_inmemory/src/bin/libafl_cc.rs +++ b/fuzzers/generic_inmemory/src/bin/libafl_cc.rs @@ -1,21 +1,35 @@ -use libafl_cc::{ClangWrapper, CompilerWrapper}; +use libafl_cc::{ClangWrapper, CompilerWrapper, LLVMPasses}; use std::env; -fn main() { +pub fn main() { let args: Vec = env::args().collect(); if args.len() > 1 { let mut dir = env::current_exe().unwrap(); + let wrapper_name = dir.file_name().unwrap().to_str().unwrap(); + + let is_cpp = match wrapper_name[wrapper_name.len()-2..].to_lowercase().as_str() { + "cc" => false, + "++" | "pp" | "xx" => true, + _ => panic!("Could not figure out if c or c++ warpper was called. Expected {:?} to end with c or cxx", dir), + }; + dir.pop(); - let mut cc = ClangWrapper::new("clang", "clang++"); - cc.is_cpp(false) + let mut cc = ClangWrapper::new(); + if let Some(code) = cc + .cpp(is_cpp) + // silence the compiler wrapper output, needed for some configure scripts. + .silence(true) .from_args(&args) - .unwrap() - .link_staticlib(&dir, "generic_inmemory".into()) - .unwrap() - .add_arg("-fsanitize-coverage=trace-pc-guard,trace-cmp".into()) - .unwrap(); - cc.run().unwrap(); + .expect("Failed to parse the command line".into()) + .link_staticlib(&dir, "generic_inmemory") + .add_arg("-fsanitize-coverage=trace-pc-guard,trace-cmp") + .add_pass(LLVMPasses::CmpLogRtn) + .run() + .expect("Failed to run the wrapped compiler".into()) + { + std::process::exit(code); + } } else { panic!("LibAFL CC: No Arguments given"); } diff --git a/fuzzers/generic_inmemory/src/bin/libafl_cxx.rs b/fuzzers/generic_inmemory/src/bin/libafl_cxx.rs index 71886b5e02..ce786239b0 100644 --- a/fuzzers/generic_inmemory/src/bin/libafl_cxx.rs +++ b/fuzzers/generic_inmemory/src/bin/libafl_cxx.rs @@ -1,22 +1,5 @@ -use libafl_cc::{ClangWrapper, CompilerWrapper}; -use std::env; +pub mod libafl_cc; fn main() { - let args: Vec = env::args().collect(); - if args.len() > 1 { - let mut dir = env::current_exe().unwrap(); - dir.pop(); - - let mut cc = ClangWrapper::new("clang", "clang++"); - cc.is_cpp(true) - .from_args(&args) - .unwrap() - .link_staticlib(&dir, "generic_inmemory".into()) - .unwrap() - .add_arg("-fsanitize-coverage=trace-pc-guard,trace-cmp".into()) - .unwrap(); - cc.run().unwrap(); - } else { - panic!("LibAFL CC: No Arguments given"); - } + libafl_cc::main() } diff --git a/fuzzers/libfuzzer_libmozjpeg/src/bin/libafl_cc.rs b/fuzzers/libfuzzer_libmozjpeg/src/bin/libafl_cc.rs index ef610ccba0..5fcd7a96cb 100644 --- a/fuzzers/libfuzzer_libmozjpeg/src/bin/libafl_cc.rs +++ b/fuzzers/libfuzzer_libmozjpeg/src/bin/libafl_cc.rs @@ -1,21 +1,34 @@ -use libafl_cc::{ClangWrapper, CompilerWrapper}; +use libafl_cc::{ClangWrapper, CompilerWrapper, LLVMPasses}; use std::env; -fn main() { +pub fn main() { let args: Vec = env::args().collect(); if args.len() > 1 { let mut dir = env::current_exe().unwrap(); + let wrapper_name = dir.file_name().unwrap().to_str().unwrap(); + + let is_cpp = match wrapper_name[wrapper_name.len()-2..].to_lowercase().as_str() { + "cc" => false, + "++" | "pp" | "xx" => true, + _ => panic!("Could not figure out if c or c++ warpper was called. Expected {:?} to end with c or cxx", dir), + }; + dir.pop(); - let mut cc = ClangWrapper::new("clang", "clang++"); - cc.is_cpp(false) + let mut cc = ClangWrapper::new(); + if let Some(code) = cc + .cpp(is_cpp) + // silence the compiler wrapper output, needed for some configure scripts. + .silence(true) .from_args(&args) - .unwrap() - .link_staticlib(&dir, "libfuzzer_libmozjpeg".into()) - .unwrap() - .add_arg("-fsanitize-coverage=trace-pc-guard,trace-cmp".into()) - .unwrap(); - cc.run().unwrap(); + .expect("Failed to parse the command line".into()) + .link_staticlib(&dir, "libfuzzer_libmozjpeg") + .add_arg("-fsanitize-coverage=trace-pc-guard,trace-cmp") + .run() + .expect("Failed to run the wrapped compiler".into()) + { + std::process::exit(code); + } } else { panic!("LibAFL CC: No Arguments given"); } diff --git a/fuzzers/libfuzzer_libmozjpeg/src/bin/libafl_cxx.rs b/fuzzers/libfuzzer_libmozjpeg/src/bin/libafl_cxx.rs index c042724c97..ce786239b0 100644 --- a/fuzzers/libfuzzer_libmozjpeg/src/bin/libafl_cxx.rs +++ b/fuzzers/libfuzzer_libmozjpeg/src/bin/libafl_cxx.rs @@ -1,22 +1,5 @@ -use libafl_cc::{ClangWrapper, CompilerWrapper}; -use std::env; +pub mod libafl_cc; fn main() { - let args: Vec = env::args().collect(); - if args.len() > 1 { - let mut dir = env::current_exe().unwrap(); - dir.pop(); - - let mut cc = ClangWrapper::new("clang", "clang++"); - cc.is_cpp(true) - .from_args(&args) - .unwrap() - .link_staticlib(&dir, "libfuzzer_libmozjpeg".into()) - .unwrap() - .add_arg("-fsanitize-coverage=trace-pc-guard,trace-cmp".into()) - .unwrap(); - cc.run().unwrap(); - } else { - panic!("LibAFL CC: No Arguments given"); - } + libafl_cc::main() } diff --git a/fuzzers/libfuzzer_libpng/src/bin/libafl_cc.rs b/fuzzers/libfuzzer_libpng/src/bin/libafl_cc.rs index 4e39dd0224..c2a105ee57 100644 --- a/fuzzers/libfuzzer_libpng/src/bin/libafl_cc.rs +++ b/fuzzers/libfuzzer_libpng/src/bin/libafl_cc.rs @@ -1,21 +1,34 @@ -use libafl_cc::{ClangWrapper, CompilerWrapper}; +use libafl_cc::{ClangWrapper, CompilerWrapper, LLVMPasses}; use std::env; -fn main() { +pub fn main() { let args: Vec = env::args().collect(); if args.len() > 1 { let mut dir = env::current_exe().unwrap(); + let wrapper_name = dir.file_name().unwrap().to_str().unwrap(); + + let is_cpp = match wrapper_name[wrapper_name.len()-2..].to_lowercase().as_str() { + "cc" => false, + "++" | "pp" | "xx" => true, + _ => panic!("Could not figure out if c or c++ warpper was called. Expected {:?} to end with c or cxx", dir), + }; + dir.pop(); - let mut cc = ClangWrapper::new("clang", "clang++"); - cc.is_cpp(false) + let mut cc = ClangWrapper::new(); + if let Some(code) = cc + .cpp(is_cpp) + // silence the compiler wrapper output, needed for some configure scripts. + .silence(true) .from_args(&args) - .unwrap() - .link_staticlib(&dir, "libfuzzer_libpng".into()) - .unwrap() - .add_arg("-fsanitize-coverage=trace-pc-guard".into()) - .unwrap(); - cc.run().unwrap(); + .expect("Failed to parse the command line".into()) + .link_staticlib(&dir, "libfuzzer_libpng") + .add_arg("-fsanitize-coverage=trace-pc-guard") + .run() + .expect("Failed to run the wrapped compiler".into()) + { + std::process::exit(code); + } } else { panic!("LibAFL CC: No Arguments given"); } diff --git a/fuzzers/libfuzzer_libpng/src/bin/libafl_cxx.rs b/fuzzers/libfuzzer_libpng/src/bin/libafl_cxx.rs index 0b053064e9..ce786239b0 100644 --- a/fuzzers/libfuzzer_libpng/src/bin/libafl_cxx.rs +++ b/fuzzers/libfuzzer_libpng/src/bin/libafl_cxx.rs @@ -1,22 +1,5 @@ -use libafl_cc::{ClangWrapper, CompilerWrapper}; -use std::env; +pub mod libafl_cc; fn main() { - let args: Vec = env::args().collect(); - if args.len() > 1 { - let mut dir = env::current_exe().unwrap(); - dir.pop(); - - let mut cc = ClangWrapper::new("clang", "clang++"); - cc.is_cpp(true) - .from_args(&args) - .unwrap() - .link_staticlib(&dir, "libfuzzer_libpng".into()) - .unwrap() - .add_arg("-fsanitize-coverage=trace-pc-guard".into()) - .unwrap(); - cc.run().unwrap(); - } else { - panic!("LibAFL CC: No Arguments given"); - } + libafl_cc::main() } diff --git a/fuzzers/libfuzzer_libpng_launcher/src/bin/libafl_cc.rs b/fuzzers/libfuzzer_libpng_launcher/src/bin/libafl_cc.rs index 4e39dd0224..c2a105ee57 100644 --- a/fuzzers/libfuzzer_libpng_launcher/src/bin/libafl_cc.rs +++ b/fuzzers/libfuzzer_libpng_launcher/src/bin/libafl_cc.rs @@ -1,21 +1,34 @@ -use libafl_cc::{ClangWrapper, CompilerWrapper}; +use libafl_cc::{ClangWrapper, CompilerWrapper, LLVMPasses}; use std::env; -fn main() { +pub fn main() { let args: Vec = env::args().collect(); if args.len() > 1 { let mut dir = env::current_exe().unwrap(); + let wrapper_name = dir.file_name().unwrap().to_str().unwrap(); + + let is_cpp = match wrapper_name[wrapper_name.len()-2..].to_lowercase().as_str() { + "cc" => false, + "++" | "pp" | "xx" => true, + _ => panic!("Could not figure out if c or c++ warpper was called. Expected {:?} to end with c or cxx", dir), + }; + dir.pop(); - let mut cc = ClangWrapper::new("clang", "clang++"); - cc.is_cpp(false) + let mut cc = ClangWrapper::new(); + if let Some(code) = cc + .cpp(is_cpp) + // silence the compiler wrapper output, needed for some configure scripts. + .silence(true) .from_args(&args) - .unwrap() - .link_staticlib(&dir, "libfuzzer_libpng".into()) - .unwrap() - .add_arg("-fsanitize-coverage=trace-pc-guard".into()) - .unwrap(); - cc.run().unwrap(); + .expect("Failed to parse the command line".into()) + .link_staticlib(&dir, "libfuzzer_libpng") + .add_arg("-fsanitize-coverage=trace-pc-guard") + .run() + .expect("Failed to run the wrapped compiler".into()) + { + std::process::exit(code); + } } else { panic!("LibAFL CC: No Arguments given"); } diff --git a/fuzzers/libfuzzer_libpng_launcher/src/bin/libafl_cxx.rs b/fuzzers/libfuzzer_libpng_launcher/src/bin/libafl_cxx.rs index 0b053064e9..ce786239b0 100644 --- a/fuzzers/libfuzzer_libpng_launcher/src/bin/libafl_cxx.rs +++ b/fuzzers/libfuzzer_libpng_launcher/src/bin/libafl_cxx.rs @@ -1,22 +1,5 @@ -use libafl_cc::{ClangWrapper, CompilerWrapper}; -use std::env; +pub mod libafl_cc; fn main() { - let args: Vec = env::args().collect(); - if args.len() > 1 { - let mut dir = env::current_exe().unwrap(); - dir.pop(); - - let mut cc = ClangWrapper::new("clang", "clang++"); - cc.is_cpp(true) - .from_args(&args) - .unwrap() - .link_staticlib(&dir, "libfuzzer_libpng".into()) - .unwrap() - .add_arg("-fsanitize-coverage=trace-pc-guard".into()) - .unwrap(); - cc.run().unwrap(); - } else { - panic!("LibAFL CC: No Arguments given"); - } + libafl_cc::main() } diff --git a/fuzzers/libfuzzer_reachability/src/bin/libafl_cc.rs b/fuzzers/libfuzzer_reachability/src/bin/libafl_cc.rs index 4e39dd0224..c2a105ee57 100644 --- a/fuzzers/libfuzzer_reachability/src/bin/libafl_cc.rs +++ b/fuzzers/libfuzzer_reachability/src/bin/libafl_cc.rs @@ -1,21 +1,34 @@ -use libafl_cc::{ClangWrapper, CompilerWrapper}; +use libafl_cc::{ClangWrapper, CompilerWrapper, LLVMPasses}; use std::env; -fn main() { +pub fn main() { let args: Vec = env::args().collect(); if args.len() > 1 { let mut dir = env::current_exe().unwrap(); + let wrapper_name = dir.file_name().unwrap().to_str().unwrap(); + + let is_cpp = match wrapper_name[wrapper_name.len()-2..].to_lowercase().as_str() { + "cc" => false, + "++" | "pp" | "xx" => true, + _ => panic!("Could not figure out if c or c++ warpper was called. Expected {:?} to end with c or cxx", dir), + }; + dir.pop(); - let mut cc = ClangWrapper::new("clang", "clang++"); - cc.is_cpp(false) + let mut cc = ClangWrapper::new(); + if let Some(code) = cc + .cpp(is_cpp) + // silence the compiler wrapper output, needed for some configure scripts. + .silence(true) .from_args(&args) - .unwrap() - .link_staticlib(&dir, "libfuzzer_libpng".into()) - .unwrap() - .add_arg("-fsanitize-coverage=trace-pc-guard".into()) - .unwrap(); - cc.run().unwrap(); + .expect("Failed to parse the command line".into()) + .link_staticlib(&dir, "libfuzzer_libpng") + .add_arg("-fsanitize-coverage=trace-pc-guard") + .run() + .expect("Failed to run the wrapped compiler".into()) + { + std::process::exit(code); + } } else { panic!("LibAFL CC: No Arguments given"); } diff --git a/fuzzers/libfuzzer_reachability/src/bin/libafl_cxx.rs b/fuzzers/libfuzzer_reachability/src/bin/libafl_cxx.rs index 0b053064e9..ce786239b0 100644 --- a/fuzzers/libfuzzer_reachability/src/bin/libafl_cxx.rs +++ b/fuzzers/libfuzzer_reachability/src/bin/libafl_cxx.rs @@ -1,22 +1,5 @@ -use libafl_cc::{ClangWrapper, CompilerWrapper}; -use std::env; +pub mod libafl_cc; fn main() { - let args: Vec = env::args().collect(); - if args.len() > 1 { - let mut dir = env::current_exe().unwrap(); - dir.pop(); - - let mut cc = ClangWrapper::new("clang", "clang++"); - cc.is_cpp(true) - .from_args(&args) - .unwrap() - .link_staticlib(&dir, "libfuzzer_libpng".into()) - .unwrap() - .add_arg("-fsanitize-coverage=trace-pc-guard".into()) - .unwrap(); - cc.run().unwrap(); - } else { - panic!("LibAFL CC: No Arguments given"); - } + libafl_cc::main() } diff --git a/libafl_cc/Cargo.toml b/libafl_cc/Cargo.toml index 098655f0b7..22975fdca3 100644 --- a/libafl_cc/Cargo.toml +++ b/libafl_cc/Cargo.toml @@ -12,4 +12,7 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[build-dependencies] +cc = { version = "1.0", features = ["parallel"] } + [dependencies] diff --git a/libafl_cc/build.rs b/libafl_cc/build.rs new file mode 100644 index 0000000000..311865885b --- /dev/null +++ b/libafl_cc/build.rs @@ -0,0 +1,74 @@ +use std::{env, fs::File, io::Write, path::Path, process::Command, str}; + +fn main() { + let out_dir = env::var_os("OUT_DIR").unwrap(); + let out_dir = Path::new(&out_dir); + let src_dir = Path::new("src"); + + let dest_path = Path::new(&out_dir).join("clang_constants.rs"); + let mut clang_constants_file = File::create(&dest_path).expect("Could not create file"); + + let llvm_config = env::var("LLVM_CONFIG").unwrap_or_else(|_| "llvm-config".into()); + if let Ok(output) = Command::new(&llvm_config).args(&["--bindir"]).output() { + let llvm_bindir = Path::new( + str::from_utf8(&output.stdout) + .expect("Invalid llvm-config output") + .trim(), + ); + + write!( + &mut clang_constants_file, + "// These constants are autogenerated by build.rs + +pub const CLANG_PATH: &str = {:?}; +pub const CLANGXX_PATH: &str = {:?}; + ", + llvm_bindir.join("clang"), + llvm_bindir.join("clang++") + ) + .expect("Could not write file"); + + println!("cargo:rerun-if-changed=src/cmplog-routines-pass.cc"); + + let output = Command::new(&llvm_config) + .args(&["--cxxflags"]) + .output() + .expect("Failed to execute llvm-config"); + let cxxflags = str::from_utf8(&output.stdout).expect("Invalid llvm-config output"); + + let output = Command::new(&llvm_config) + .args(&["--ldflags"]) + .output() + .expect("Failed to execute llvm-config"); + let ldflags = str::from_utf8(&output.stdout).expect("Invalid llvm-config output"); + + let cxxflags: Vec<&str> = cxxflags.trim().split_whitespace().collect(); + let ldflags: Vec<&str> = ldflags.trim().split_whitespace().collect(); + + let _ = Command::new(llvm_bindir.join("clang++")) + .args(&cxxflags) + .arg(src_dir.join("cmplog-routines-pass.cc")) + .args(&ldflags) + .args(&["-fPIC", "-shared", "-o"]) + .arg(out_dir.join("cmplog-routines-pass.so")) + .status() + .expect("Failed to compile cmplog-routines-pass.cc"); + } else { + write!( + &mut clang_constants_file, + "// These constants are autogenerated by build.rs + +pub const CLANG_PATH: &str = \"clang\"; +pub const CLANGXX_PATH: &str = \"clang++\"; + " + ) + .expect("Could not write file"); + + println!( + "cargo:warning=Failed to locate the LLVM path using {}, we will not build LLVM passes", + llvm_config + ); + } + + println!("cargo:rerun-if-changed=build.rs"); +} diff --git a/libafl_cc/src/clang.rs b/libafl_cc/src/clang.rs new file mode 100644 index 0000000000..ab69698c16 --- /dev/null +++ b/libafl_cc/src/clang.rs @@ -0,0 +1,284 @@ +//! LLVM compiler Wrapper from `LibAFL` + +use std::{ + convert::Into, + path::{Path, PathBuf}, + string::String, + vec::Vec, +}; + +use crate::{CompilerWrapper, Error, LIB_EXT, LIB_PREFIX}; + +include!(concat!(env!("OUT_DIR"), "/clang_constants.rs")); + +#[allow(clippy::upper_case_acronyms)] +pub enum LLVMPasses { + //CmpLogIns, + CmpLogRtn, +} + +impl LLVMPasses { + #[must_use] + pub fn path(&self) -> PathBuf { + match self { + LLVMPasses::CmpLogRtn => PathBuf::from(env!("OUT_DIR")).join("cmplog-routines-pass.so"), + } + } +} + +/// Wrap Clang +#[allow(clippy::struct_excessive_bools)] +pub struct ClangWrapper { + is_silent: bool, + optimize: bool, + wrapped_cc: String, + wrapped_cxx: String, + + name: String, + is_cpp: bool, + linking: bool, + x_set: bool, + bit_mode: u32, + + from_args_called: bool, + base_args: Vec, + cc_args: Vec, + link_args: Vec, + passes: Vec, +} + +#[allow(clippy::match_same_arms)] // for the linking = false wip for "shared" +impl CompilerWrapper for ClangWrapper { + fn from_args(&mut self, args: &[S]) -> Result<&'_ mut Self, Error> + where + S: AsRef, + { + let mut new_args: Vec = vec![]; + if args.is_empty() { + return Err(Error::InvalidArguments( + "The number of arguments cannot be 0".to_string(), + )); + } + + if self.from_args_called { + return Err(Error::Unknown( + "CompilerWrapper::from_args cannot be called twice on the same instance" + .to_string(), + )); + } + self.from_args_called = true; + + if args.len() == 1 { + return Err(Error::InvalidArguments( + "LibAFL Compiler wrapper - no commands specified. Use me as compiler.".to_string(), + )); + } + + self.name = args[0].as_ref().to_string(); + // Detect C++ compiler looking at the wrapper name + self.is_cpp = self.is_cpp || self.name.ends_with("++"); + + // Sancov flag + // new_args.push("-fsanitize-coverage=trace-pc-guard".into()); + + let mut linking = true; + // Detect stray -v calls from ./configure scripts. + if args.len() > 1 && args[1].as_ref() == "-v" { + linking = false; + } + + for arg in &args[1..] { + match arg.as_ref() { + "-x" => self.x_set = true, + "-m32" => self.bit_mode = 32, + "-m64" => self.bit_mode = 64, + "-c" | "-S" | "-E" => linking = false, + "-shared" => linking = false, // TODO dynamic list? + "-Wl,-z,defs" | "-Wl,--no-undefined" => continue, + _ => (), + }; + new_args.push(arg.as_ref().to_string()); + } + self.linking = linking; + + if self.optimize { + new_args.push("-g".into()); + new_args.push("-O3".into()); + new_args.push("-funroll-loops".into()); + } + + // Fuzzing define common among tools + new_args.push("-DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION=1".into()); + + // Libraries needed by libafl on Windows + #[cfg(windows)] + if linking { + new_args.push("-lws2_32".into()); + new_args.push("-lBcrypt".into()); + new_args.push("-lAdvapi32".into()); + } + + self.base_args = new_args; + Ok(self) + } + + fn add_arg(&mut self, arg: S) -> &'_ mut Self + where + S: AsRef, + { + self.base_args.push(arg.as_ref().to_string()); + self + } + + fn add_cc_arg(&mut self, arg: S) -> &'_ mut Self + where + S: AsRef, + { + self.cc_args.push(arg.as_ref().to_string()); + self + } + + fn add_link_arg(&mut self, arg: S) -> &'_ mut Self + where + S: AsRef, + { + self.link_args.push(arg.as_ref().to_string()); + self + } + + fn link_staticlib(&mut self, dir: &Path, name: S) -> &'_ mut Self + where + S: AsRef, + { + if cfg!(any(target_os = "macos", target_os = "ios")) { + //self.add_link_arg("-force_load".into())?; + } else { + self.add_link_arg("-Wl,--whole-archive"); + } + self.add_link_arg( + dir.join(format!("{}{}.{}", LIB_PREFIX, name.as_ref(), LIB_EXT)) + .into_os_string() + .into_string() + .unwrap(), + ); + if cfg!(any(target_os = "macos", target_os = "ios")) { + self + } else { + self.add_link_arg("-Wl,-no-whole-archive") + } + } + + fn command(&mut self) -> Result, Error> { + let mut args = vec![]; + if self.is_cpp { + args.push(self.wrapped_cxx.clone()); + } else { + args.push(self.wrapped_cc.clone()); + } + args.extend_from_slice(self.base_args.as_slice()); + for pass in &self.passes { + args.push("-Xclang".into()); + args.push("-load".into()); + args.push("-Xclang".into()); + args.push(pass.path().into_os_string().into_string().unwrap()); + } + if self.linking { + if self.x_set { + args.push("-x".into()); + args.push("none".into()); + } + + args.extend_from_slice(self.link_args.as_slice()); + } else { + args.extend_from_slice(self.cc_args.as_slice()); + } + + Ok(args) + } + + fn is_linking(&self) -> bool { + self.linking + } + + fn silence(&mut self, value: bool) -> &'_ mut Self { + self.is_silent = value; + self + } + + fn is_silent(&self) -> bool { + self.is_silent + } +} + +impl Default for ClangWrapper { + /// Create a new Clang Wrapper + #[must_use] + fn default() -> Self { + Self::new() + } +} + +impl ClangWrapper { + /// Create a new Clang Wrapper + #[must_use] + pub fn new() -> Self { + Self { + optimize: true, + wrapped_cc: CLANG_PATH.into(), + wrapped_cxx: CLANGXX_PATH.into(), + name: "".into(), + is_cpp: false, + linking: false, + x_set: false, + bit_mode: 0, + from_args_called: false, + base_args: vec![], + cc_args: vec![], + link_args: vec![], + passes: vec![], + is_silent: false, + } + } + + pub fn wrapped_cc(&mut self, cc: String) -> &'_ mut Self { + self.wrapped_cc = cc; + self + } + + pub fn wrapped_cxx(&mut self, cxx: String) -> &'_ mut Self { + self.wrapped_cxx = cxx; + self + } + + /// Disable optimizations + pub fn dont_optimize(&mut self) -> &'_ mut Self { + self.optimize = false; + self + } + + /// Set cpp mode + pub fn cpp(&mut self, value: bool) -> &'_ mut Self { + self.is_cpp = value; + self + } + + // Add LLVM pass + pub fn add_pass(&mut self, pass: LLVMPasses) -> &'_ mut Self { + self.passes.push(pass); + self + } +} + +#[cfg(test)] +mod tests { + use crate::{ClangWrapper, CompilerWrapper}; + + #[test] + fn test_clang_version() { + ClangWrapper::new() + .from_args(&["my-clang", "-v"]) + .unwrap() + .run() + .unwrap(); + } +} diff --git a/libafl_cc/src/cmplog-routines-pass.cc b/libafl_cc/src/cmplog-routines-pass.cc new file mode 100644 index 0000000000..0a0fbd2655 --- /dev/null +++ b/libafl_cc/src/cmplog-routines-pass.cc @@ -0,0 +1,469 @@ +/* + american fuzzy lop++ - LLVM CmpLog instrumentation + -------------------------------------------------- + + Written by Andrea Fioraldi + + Copyright 2015, 2016 Google Inc. All rights reserved. + Copyright 2019-2020 AFLplusplus Project. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at: + + http://www.apache.org/licenses/LICENSE-2.0 + +*/ + +#include +#include +#include + +#include +#include +#include +#include +#include "llvm/Config/llvm-config.h" + +#include "llvm/ADT/Statistic.h" +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/LegacyPassManager.h" +#include "llvm/IR/Module.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Transforms/IPO/PassManagerBuilder.h" +#include "llvm/Transforms/Utils/BasicBlockUtils.h" +#include "llvm/Pass.h" +#include "llvm/Analysis/ValueTracking.h" + +#if LLVM_VERSION_MAJOR > 3 || \ + (LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR > 4) + #include "llvm/IR/Verifier.h" + #include "llvm/IR/DebugInfo.h" +#else + #include "llvm/Analysis/Verifier.h" + #include "llvm/DebugInfo.h" + #define nullptr 0 +#endif + +#include + +using namespace llvm; + +namespace { + +/* Function that we never instrument or analyze */ +/* Note: this ignore check is also called in isInInstrumentList() */ +bool isIgnoreFunction(const llvm::Function *F) { + + // Starting from "LLVMFuzzer" these are functions used in libfuzzer based + // fuzzing campaign installations, e.g. oss-fuzz + + static constexpr const char *ignoreList[] = { + + "asan.", + "llvm.", + "sancov.", + "__ubsan", + "ign.", + "__afl", + "_fini", + "__libc_", + "__asan", + "__msan", + "__cmplog", + "__sancov", + "__san", + "__cxx_", + "__decide_deferred", + "_GLOBAL", + "_ZZN6__asan", + "_ZZN6__lsan", + "msan.", + "LLVMFuzzerM", + "LLVMFuzzerC", + "LLVMFuzzerI", + "maybe_duplicate_stderr", + "discard_output", + "close_stdout", + "dup_and_close_stderr", + "maybe_close_fd_mask", + "ExecuteFilesOnyByOne" + + }; + + for (auto const &ignoreListFunc : ignoreList) { + + if (F->getName().startswith(ignoreListFunc)) { return true; } + + } + + static constexpr const char *ignoreSubstringList[] = { + + "__asan", "__msan", "__ubsan", "__lsan", + "__san", "__sanitize", "__cxx", "_GLOBAL__", + "DebugCounter", "DwarfDebug", "DebugLoc" + + }; + + for (auto const &ignoreListFunc : ignoreSubstringList) { + + // hexcoder: F->getName().contains() not avaiilable in llvm 3.8.0 + if (StringRef::npos != F->getName().find(ignoreListFunc)) { return true; } + + } + + return false; + +} + +class CmpLogRoutines : public ModulePass { + + public: + static char ID; + CmpLogRoutines() : ModulePass(ID) {} + + bool runOnModule(Module &M) override; + +#if LLVM_VERSION_MAJOR < 4 + const char *getPassName() const override { + +#else + StringRef getPassName() const override { + +#endif + return "cmplog routines"; + + } + + private: + bool hookRtns(Module &M); + +}; + +} // namespace + +char CmpLogRoutines::ID = 0; + +bool CmpLogRoutines::hookRtns(Module &M) { + + std::vector calls, llvmStdStd, llvmStdC, gccStdStd, gccStdC; + LLVMContext & C = M.getContext(); + + Type *VoidTy = Type::getVoidTy(C); + // PointerType *VoidPtrTy = PointerType::get(VoidTy, 0); + IntegerType *Int8Ty = IntegerType::getInt8Ty(C); + PointerType *i8PtrTy = PointerType::get(Int8Ty, 0); + +#if LLVM_VERSION_MAJOR < 9 + Constant * +#else + FunctionCallee +#endif + c = M.getOrInsertFunction("__cmplog_rtn_hook", VoidTy, i8PtrTy, i8PtrTy +#if LLVM_VERSION_MAJOR < 5 + , + NULL +#endif + ); +#if LLVM_VERSION_MAJOR < 9 + Function *cmplogHookFn = cast(c); +#else + FunctionCallee cmplogHookFn = c; +#endif + +#if LLVM_VERSION_MAJOR < 9 + Constant * +#else + FunctionCallee +#endif + c1 = M.getOrInsertFunction("__cmplog_rtn_llvm_stdstring_stdstring", + VoidTy, i8PtrTy, i8PtrTy +#if LLVM_VERSION_MAJOR < 5 + , + NULL +#endif + ); +#if LLVM_VERSION_MAJOR < 9 + Function *cmplogLlvmStdStd = cast(c1); +#else + FunctionCallee cmplogLlvmStdStd = c1; +#endif + +#if LLVM_VERSION_MAJOR < 9 + Constant * +#else + FunctionCallee +#endif + c2 = M.getOrInsertFunction("__cmplog_rtn_llvm_stdstring_cstring", VoidTy, + i8PtrTy, i8PtrTy +#if LLVM_VERSION_MAJOR < 5 + , + NULL +#endif + ); +#if LLVM_VERSION_MAJOR < 9 + Function *cmplogLlvmStdC = cast(c2); +#else + FunctionCallee cmplogLlvmStdC = c2; +#endif + +#if LLVM_VERSION_MAJOR < 9 + Constant * +#else + FunctionCallee +#endif + c3 = M.getOrInsertFunction("__cmplog_rtn_gcc_stdstring_stdstring", VoidTy, + i8PtrTy, i8PtrTy +#if LLVM_VERSION_MAJOR < 5 + , + NULL +#endif + ); +#if LLVM_VERSION_MAJOR < 9 + Function *cmplogGccStdStd = cast(c3); +#else + FunctionCallee cmplogGccStdStd = c3; +#endif + +#if LLVM_VERSION_MAJOR < 9 + Constant * +#else + FunctionCallee +#endif + c4 = M.getOrInsertFunction("__cmplog_rtn_gcc_stdstring_cstring", VoidTy, + i8PtrTy, i8PtrTy +#if LLVM_VERSION_MAJOR < 5 + , + NULL +#endif + ); +#if LLVM_VERSION_MAJOR < 9 + Function *cmplogGccStdC = cast(c4); +#else + FunctionCallee cmplogGccStdC = c4; +#endif + + /* iterate over all functions, bbs and instruction and add suitable calls */ + for (auto &F : M) { + + if (!isIgnoreFunction(&F)) continue; + + for (auto &BB : F) { + + for (auto &IN : BB) { + + CallInst *callInst = nullptr; + + if ((callInst = dyn_cast(&IN))) { + + Function *Callee = callInst->getCalledFunction(); + if (!Callee) continue; + if (callInst->getCallingConv() != llvm::CallingConv::C) continue; + + FunctionType *FT = Callee->getFunctionType(); + + bool isPtrRtn = FT->getNumParams() >= 2 && + !FT->getReturnType()->isVoidTy() && + FT->getParamType(0) == FT->getParamType(1) && + FT->getParamType(0)->isPointerTy(); + + bool isGccStdStringStdString = + Callee->getName().find("__is_charIT_EE7__value") != + std::string::npos && + Callee->getName().find( + "St7__cxx1112basic_stringIS2_St11char_traits") != + std::string::npos && + FT->getNumParams() >= 2 && + FT->getParamType(0) == FT->getParamType(1) && + FT->getParamType(0)->isPointerTy(); + + bool isGccStdStringCString = + Callee->getName().find( + "St7__cxx1112basic_stringIcSt11char_" + "traitsIcESaIcEE7compareEPK") != std::string::npos && + FT->getNumParams() >= 2 && FT->getParamType(0)->isPointerTy() && + FT->getParamType(1)->isPointerTy(); + + bool isLlvmStdStringStdString = + Callee->getName().find("_ZNSt3__1eqI") != std::string::npos && + Callee->getName().find("_12basic_stringI") != std::string::npos && + Callee->getName().find("_11char_traits") != std::string::npos && + FT->getNumParams() >= 2 && FT->getParamType(0)->isPointerTy() && + FT->getParamType(1)->isPointerTy(); + + bool isLlvmStdStringCString = + Callee->getName().find("_ZNSt3__1eqI") != std::string::npos && + Callee->getName().find("_12basic_stringI") != std::string::npos && + FT->getNumParams() >= 2 && FT->getParamType(0)->isPointerTy() && + FT->getParamType(1)->isPointerTy(); + + /* + { + + fprintf(stderr, "F:%s C:%s argc:%u\n", + F.getName().str().c_str(), + Callee->getName().str().c_str(), FT->getNumParams()); + fprintf(stderr, "ptr0:%u ptr1:%u ptr2:%u\n", + FT->getParamType(0)->isPointerTy(), + FT->getParamType(1)->isPointerTy(), + FT->getNumParams() > 2 ? + FT->getParamType(2)->isPointerTy() : 22 ); + + } + + */ + + if (isGccStdStringCString || isGccStdStringStdString || + isLlvmStdStringStdString || isLlvmStdStringCString) { + + isPtrRtn = false; + + } + + if (isPtrRtn) { calls.push_back(callInst); } + if (isGccStdStringStdString) { gccStdStd.push_back(callInst); } + if (isGccStdStringCString) { gccStdC.push_back(callInst); } + if (isLlvmStdStringStdString) { llvmStdStd.push_back(callInst); } + if (isLlvmStdStringCString) { llvmStdC.push_back(callInst); } + + } + + } + + } + + } + + if (!calls.size() && !gccStdStd.size() && !gccStdC.size() && + !llvmStdStd.size() && !llvmStdC.size()) + return false; + + for (auto &callInst : calls) { + + Value *v1P = callInst->getArgOperand(0), *v2P = callInst->getArgOperand(1); + + IRBuilder<> IRB(callInst->getParent()); + IRB.SetInsertPoint(callInst); + + std::vector args; + Value * v1Pcasted = IRB.CreatePointerCast(v1P, i8PtrTy); + Value * v2Pcasted = IRB.CreatePointerCast(v2P, i8PtrTy); + args.push_back(v1Pcasted); + args.push_back(v2Pcasted); + + IRB.CreateCall(cmplogHookFn, args); + + // errs() << callInst->getCalledFunction()->getName() << "\n"; + + } + + for (auto &callInst : gccStdStd) { + + Value *v1P = callInst->getArgOperand(0), *v2P = callInst->getArgOperand(1); + + IRBuilder<> IRB(callInst->getParent()); + IRB.SetInsertPoint(callInst); + + std::vector args; + Value * v1Pcasted = IRB.CreatePointerCast(v1P, i8PtrTy); + Value * v2Pcasted = IRB.CreatePointerCast(v2P, i8PtrTy); + args.push_back(v1Pcasted); + args.push_back(v2Pcasted); + + IRB.CreateCall(cmplogGccStdStd, args); + + // errs() << callInst->getCalledFunction()->getName() << "\n"; + + } + + for (auto &callInst : gccStdC) { + + Value *v1P = callInst->getArgOperand(0), *v2P = callInst->getArgOperand(1); + + IRBuilder<> IRB(callInst->getParent()); + IRB.SetInsertPoint(callInst); + + std::vector args; + Value * v1Pcasted = IRB.CreatePointerCast(v1P, i8PtrTy); + Value * v2Pcasted = IRB.CreatePointerCast(v2P, i8PtrTy); + args.push_back(v1Pcasted); + args.push_back(v2Pcasted); + + IRB.CreateCall(cmplogGccStdC, args); + + // errs() << callInst->getCalledFunction()->getName() << "\n"; + + } + + for (auto &callInst : llvmStdStd) { + + Value *v1P = callInst->getArgOperand(0), *v2P = callInst->getArgOperand(1); + + IRBuilder<> IRB(callInst->getParent()); + IRB.SetInsertPoint(callInst); + + std::vector args; + Value * v1Pcasted = IRB.CreatePointerCast(v1P, i8PtrTy); + Value * v2Pcasted = IRB.CreatePointerCast(v2P, i8PtrTy); + args.push_back(v1Pcasted); + args.push_back(v2Pcasted); + + IRB.CreateCall(cmplogLlvmStdStd, args); + + // errs() << callInst->getCalledFunction()->getName() << "\n"; + + } + + for (auto &callInst : llvmStdC) { + + Value *v1P = callInst->getArgOperand(0), *v2P = callInst->getArgOperand(1); + + IRBuilder<> IRB(callInst->getParent()); + IRB.SetInsertPoint(callInst); + + std::vector args; + Value * v1Pcasted = IRB.CreatePointerCast(v1P, i8PtrTy); + Value * v2Pcasted = IRB.CreatePointerCast(v2P, i8PtrTy); + args.push_back(v1Pcasted); + args.push_back(v2Pcasted); + + IRB.CreateCall(cmplogLlvmStdC, args); + + // errs() << callInst->getCalledFunction()->getName() << "\n"; + + } + + return true; + +} + +bool CmpLogRoutines::runOnModule(Module &M) { + + hookRtns(M); + verifyModule(M); + + return true; + +} + +static void registerCmpLogRoutinesPass(const PassManagerBuilder &, + legacy::PassManagerBase &PM) { + + auto p = new CmpLogRoutines(); + PM.add(p); + +} + +static RegisterStandardPasses RegisterCmpLogRoutinesPass( + PassManagerBuilder::EP_OptimizerLast, registerCmpLogRoutinesPass); + +static RegisterStandardPasses RegisterCmpLogRoutinesPass0( + PassManagerBuilder::EP_EnabledOnOptLevel0, registerCmpLogRoutinesPass); + +#if LLVM_VERSION_MAJOR >= 11 +static RegisterStandardPasses RegisterCmpLogRoutinesPassLTO( + PassManagerBuilder::EP_FullLinkTimeOptimizationLast, + registerCmpLogRoutinesPass); +#endif + diff --git a/libafl_cc/src/lib.rs b/libafl_cc/src/lib.rs index fcaf37bbc8..f79d94aa89 100644 --- a/libafl_cc/src/lib.rs +++ b/libafl_cc/src/lib.rs @@ -1,6 +1,9 @@ //! Compiler Wrapper from `LibAFL` -use std::{path::Path, process::Command, string::String, vec::Vec}; +use std::{convert::Into, path::Path, process::Command, string::String, vec::Vec}; + +pub mod clang; +pub use clang::{ClangWrapper, LLVMPasses}; /// `LibAFL` CC Error Type #[derive(Debug)] @@ -31,19 +34,62 @@ pub const LIB_PREFIX: &str = "lib"; /// Wrap a compiler hijacking its arguments pub trait CompilerWrapper { /// Set the wrapper arguments parsing a command line set of arguments - fn from_args(&mut self, args: &[String]) -> Result<&'_ mut Self, Error>; + fn from_args(&mut self, args: &[S]) -> Result<&'_ mut Self, Error> + where + S: AsRef; /// Add a compiler argument - fn add_arg(&mut self, arg: String) -> Result<&'_ mut Self, Error>; + fn add_arg(&mut self, arg: S) -> &'_ mut Self + where + S: AsRef; /// Add a compiler argument only when compiling - fn add_cc_arg(&mut self, arg: String) -> Result<&'_ mut Self, Error>; + fn add_cc_arg(&mut self, arg: S) -> &'_ mut Self + where + S: AsRef; /// Add a compiler argument only when linking - fn add_link_arg(&mut self, arg: String) -> Result<&'_ mut Self, Error>; + fn add_link_arg(&mut self, arg: S) -> &'_ mut Self + where + S: AsRef; + + /// Add compiler arguments + fn add_args(&mut self, args: &[S]) -> &'_ mut Self + where + S: AsRef, + { + for arg in args { + self.add_arg(arg); + } + self + } + + /// Add compiler arguments only when compiling + fn add_cc_args(&mut self, args: &[S]) -> &'_ mut Self + where + S: AsRef, + { + for arg in args { + self.add_cc_arg(arg); + } + self + } + + /// Add compiler arguments only when linking + fn add_link_args(&mut self, args: &[S]) -> &'_ mut Self + where + S: AsRef, + { + for arg in args { + self.add_link_arg(arg); + } + self + } /// Link static C lib - fn link_staticlib(&mut self, dir: &Path, name: String) -> Result<&'_ mut Self, Error>; + fn link_staticlib(&mut self, dir: &Path, name: S) -> &'_ mut Self + where + S: AsRef; /// Command to run the compiler fn command(&mut self) -> Result, Error>; @@ -52,9 +98,9 @@ pub trait CompilerWrapper { fn is_linking(&self) -> bool; /// Silences `libafl_cc` output - fn silence(&mut self) -> &'_ mut Self; + fn silence(&mut self, value: bool) -> &'_ mut Self; - /// Returns `true` if `silence` was called + /// Returns `true` if `silence` was called with `true` fn is_silent(&self) -> bool; /// Run the compiler @@ -79,202 +125,3 @@ pub trait CompilerWrapper { Ok(status.code()) } } - -/// Wrap Clang -#[allow(clippy::struct_excessive_bools)] -pub struct ClangWrapper { - is_silent: bool, - optimize: bool, - wrapped_cc: String, - wrapped_cxx: String, - - name: String, - is_cpp: bool, - linking: bool, - x_set: bool, - bit_mode: u32, - - base_args: Vec, - cc_args: Vec, - link_args: Vec, -} - -#[allow(clippy::match_same_arms)] // for the linking = false wip for "shared" -impl CompilerWrapper for ClangWrapper { - fn from_args<'a>(&'a mut self, args: &[String]) -> Result<&'a mut Self, Error> { - let mut new_args = vec![]; - if args.is_empty() { - return Err(Error::InvalidArguments( - "The number of arguments cannot be 0".to_string(), - )); - } - - if args.len() == 1 { - return Err(Error::InvalidArguments( - "LibAFL Compiler wrapper - no commands specified. Use me as compiler.".to_string(), - )); - } - - self.name = args[0].clone(); - // Detect C++ compiler looking at the wrapper name - self.is_cpp = self.is_cpp || self.name.ends_with("++"); - - // Sancov flag - // new_args.push("-fsanitize-coverage=trace-pc-guard".into()); - - let mut linking = true; - // Detect stray -v calls from ./configure scripts. - if args.len() > 1 && args[1] == "-v" { - linking = false; - } - - for arg in &args[1..] { - match arg.as_str() { - "-x" => self.x_set = true, - "-m32" => self.bit_mode = 32, - "-m64" => self.bit_mode = 64, - "-c" | "-S" | "-E" => linking = false, - "-shared" => linking = false, // TODO dynamic list? - "-Wl,-z,defs" | "-Wl,--no-undefined" => continue, - _ => (), - }; - new_args.push(arg.clone()); - } - self.linking = linking; - - if self.optimize { - new_args.push("-g".into()); - new_args.push("-O3".into()); - new_args.push("-funroll-loops".into()); - } - - // Fuzzing define common among tools - new_args.push("-DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION=1".into()); - - // Libraries needed by libafl on Windows - #[cfg(windows)] - if linking { - new_args.push("-lws2_32".into()); - new_args.push("-lBcrypt".into()); - new_args.push("-lAdvapi32".into()); - } - - self.base_args = new_args; - Ok(self) - } - - fn add_arg(&mut self, arg: String) -> Result<&'_ mut Self, Error> { - self.base_args.push(arg); - Ok(self) - } - - fn add_cc_arg(&mut self, arg: String) -> Result<&'_ mut Self, Error> { - self.cc_args.push(arg); - Ok(self) - } - - fn add_link_arg(&mut self, arg: String) -> Result<&'_ mut Self, Error> { - self.link_args.push(arg); - Ok(self) - } - - fn link_staticlib(&mut self, dir: &Path, name: String) -> Result<&'_ mut Self, Error> { - if cfg!(any(target_os = "macos", target_os = "ios")) { - //self.add_link_arg("-force_load".into())?; - } else { - self.add_link_arg("-Wl,--whole-archive".into())?; - } - self.add_link_arg( - dir.join(format!("{}{}.{}", LIB_PREFIX, name, LIB_EXT)) - .display() - .to_string(), - )?; - if cfg!(any(target_os = "macos", target_os = "ios")) { - Ok(self) - } else { - self.add_link_arg("-Wl,-no-whole-archive".into()) - } - } - - fn command(&mut self) -> Result, Error> { - let mut args = vec![]; - if self.is_cpp { - args.push(self.wrapped_cxx.clone()); - } else { - args.push(self.wrapped_cc.clone()); - } - args.extend_from_slice(self.base_args.as_slice()); - if self.linking { - if self.x_set { - args.push("-x".into()); - args.push("none".into()); - } - - args.extend_from_slice(self.link_args.as_slice()); - } else { - args.extend_from_slice(self.cc_args.as_slice()); - } - - Ok(args) - } - - fn is_linking(&self) -> bool { - self.linking - } - - fn silence(&mut self) -> &'_ mut Self { - self.is_silent = true; - self - } - - fn is_silent(&self) -> bool { - self.is_silent - } -} - -impl ClangWrapper { - /// Create a new Clang Wrapper - #[must_use] - pub fn new(wrapped_cc: &str, wrapped_cxx: &str) -> Self { - Self { - optimize: true, - wrapped_cc: wrapped_cc.into(), - wrapped_cxx: wrapped_cxx.into(), - name: "".into(), - is_cpp: false, - linking: false, - x_set: false, - bit_mode: 0, - base_args: vec![], - cc_args: vec![], - link_args: vec![], - is_silent: false, - } - } - - /// Disable optimizations - pub fn dont_optimize(&mut self) -> &'_ mut Self { - self.optimize = false; - self - } - - /// Set cpp mode - pub fn is_cpp(&mut self, value: bool) -> &'_ mut Self { - self.is_cpp = value; - self - } -} - -#[cfg(test)] -mod tests { - use crate::{ClangWrapper, CompilerWrapper}; - - #[test] - fn test_clang_version() { - ClangWrapper::new("clang", "clang++") - .from_args(&["my-clang".into(), "-v".into()]) - .unwrap() - .run() - .unwrap(); - } -} diff --git a/libafl_frida/src/cmplog_rt.rs b/libafl_frida/src/cmplog_rt.rs index e97934c514..6f5e796b1a 100644 --- a/libafl_frida/src/cmplog_rt.rs +++ b/libafl_frida/src/cmplog_rt.rs @@ -4,7 +4,7 @@ use std::ffi::c_void; extern crate libafl_targets; extern "C" { - pub fn libafl_targets_cmplog_wrapper(k: u64, shape: u8, arg1: u64, arg2: u64); + pub fn __libafl_targets_cmplog_instructions(k: u64, shape: u8, arg1: u64, arg2: u64); } pub struct CmpLogRuntime { @@ -31,7 +31,7 @@ impl CmpLogRuntime { k &= (CMPLOG_MAP_W as u64) - 1; unsafe { - libafl_targets_cmplog_wrapper(k, 8, op1, op2); + __libafl_targets_cmplog_instructions(k, 8, op1, op2); } } diff --git a/libafl_targets/build.rs b/libafl_targets/build.rs index 0f332f0aa7..e1964839d7 100644 --- a/libafl_targets/build.rs +++ b/libafl_targets/build.rs @@ -64,7 +64,6 @@ pub const CMPLOG_MAP_H: usize = {}; #[cfg(feature = "sancov_cmplog")] { sancov_cmp.define("SANCOV_CMPLOG", "1"); - println!("cargo:rerun-if-changed=src/cmplog.h"); } sancov_cmp @@ -84,6 +83,15 @@ pub const CMPLOG_MAP_H: usize = {}; .compile("libfuzzer_compatibility"); } + println!("cargo:rerun-if-changed=src/cmplog.h"); + println!("cargo:rerun-if-changed=src/cmplog.c"); + + cc::Build::new() + .define("CMPLOG_MAP_W", Some(&*format!("{}", cmplog_map_w))) + .define("CMPLOG_MAP_H", Some(&*format!("{}", cmplog_map_h))) + .file(_src_dir.join("cmplog.c")) + .compile("cmplog"); + println!("cargo:rustc-link-search=native={}", &out_dir); println!("cargo:rerun-if-changed=build.rs"); diff --git a/libafl_targets/src/cmplog.c b/libafl_targets/src/cmplog.c new file mode 100644 index 0000000000..bdc1bc3587 --- /dev/null +++ b/libafl_targets/src/cmplog.c @@ -0,0 +1,207 @@ +// From AFL++'s afl-compiler-rt.c + +#define CMPLOG_MODULE +#include "common.h" +#include "cmplog.h" + +#if defined(_WIN32) + +#include + +void *__libafl_asan_region_is_poisoned(void *beg, size_t size) { + + (void)beg; + (void)size; + return NULL; + +} + +#pragma comment(linker, "/alternatename:__asan_region_is_poisoned=__libafl_asan_region_is_poisoned") + +#elif defined(__unix__) || (defined(__APPLE__) && defined(__MACH__)) + +#include +#include +#include + +static int dummy_fd[2] = {2, 2}; +static int dymmy_initialized = 0; + +__attribute__((weak)) void *__asan_region_is_poisoned(void *beg, size_t size) { + + (void)beg; + (void)size; + return NULL; + +} + +#endif + +void __libafl_targets_cmplog_instructions(uintptr_t k, uint8_t shape, uint64_t arg1, uint64_t arg2) { + + STATIC_ASSERT(sizeof(libafl_cmplog_map.vals.operands) == sizeof(libafl_cmplog_map.vals.routines)); + + __libafl_targets_cmplog(k, shape, arg1, arg2); + +} + +// POSIX shenanigan to see if an area is mapped. +// If it is mapped as X-only, we have a problem, so maybe we should add a check +// to avoid to call it on .text addresses +static long area_is_valid(void *ptr, size_t len) { + + if (!ptr || __asan_region_is_poisoned(ptr, len)) return 0; + + long valid_len; + +#if defined(_WIN32) + if (IsBadReadPtr(ptr, len)) return 0; + valid_len = (long)len; +#elif defined(__unix__) || (defined(__APPLE__) && defined(__MACH__)) + if (!dymmy_initialized) { + if ((dummy_fd[1] = open("/dev/null", O_WRONLY)) < 0) { + if (pipe(dummy_fd) < 0) + dummy_fd[1] = 1; + } + dymmy_initialized = 1; + } + + valid_len = syscall(SYS_write, dummy_fd[1], ptr, len); + + if (valid_len <= 0 || valid_len > (long)len) return 0; +#endif + + // even if the write succeed this can be a false positive if we cross + // a page boundary. who knows why. + + char *p = (char *)ptr; +#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__)) + long page_size = sysconf(_SC_PAGE_SIZE); +#else + long page_size = 4096; // Yolo +#endif + char *page = (char *)((uintptr_t)p & ~(page_size - 1)) + page_size; + + if (page > p + len) { + // no, not crossing a page boundary + return valid_len; + } else { + // yes it crosses a boundary, hence we can only return the length of + // rest of the first page, we cannot detect if the next page is valid + // or not, neither by SYS_write nor msync() :-( + return (long)(page - p); + } + +} + +void __libafl_targets_cmplog_routines(uintptr_t k, uint8_t *ptr1, uint8_t *ptr2) { + + if (!libafl_cmplog_enabled) return; + + int l1, l2; + if ((l1 = area_is_valid(ptr1, CMPLOG_RTN_LEN)) <= 0 || + (l2 = area_is_valid(ptr2, CMPLOG_RTN_LEN)) <= 0) + return; + int len = MIN(l1, l2); + + uint32_t hits; + + if (libafl_cmplog_map.headers[k].kind != CMPLOG_KIND_RTN) { + libafl_cmplog_map.headers[k].kind = CMPLOG_KIND_RTN; + libafl_cmplog_map.headers[k].hits = 1; + libafl_cmplog_map.headers[k].shape = len - 1; + hits = 0; + } else { + hits = libafl_cmplog_map.headers[k].hits++; + if (libafl_cmplog_map.headers[k].shape < len) + libafl_cmplog_map.headers[k].shape = len - 1; + } + + hits &= CMPLOG_MAP_RTN_H - 1; + MEMCPY(libafl_cmplog_map.vals.routines[k][hits].v0, ptr1, len); + MEMCPY(libafl_cmplog_map.vals.routines[k][hits].v1, ptr2, len); + +} + +void __cmplog_rtn_hook(uint8_t *ptr1, uint8_t *ptr2) { + + uintptr_t k = RETADDR; + k = (k >> 4) ^ (k << 8); + k &= CMPLOG_MAP_W - 1; + + __libafl_targets_cmplog_routines(k, ptr1, ptr2); + +} + +// gcc libstdc++ +// _ZNKSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE7compareEPKc +static uint8_t *get_gcc_stdstring(uint8_t *string) { + + uint32_t *len = (uint32_t *)(string + 8); + + if (*len < 16) { // in structure + return (string + 16); + } else { // in memory + uint8_t **ptr = (uint8_t **)string; + return (*ptr); + } + +} + +// llvm libc++ _ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocator +// IcEEE7compareEmmPKcm +static uint8_t *get_llvm_stdstring(uint8_t *string) { + + // length is in: if ((string[0] & 1) == 0) uint8_t len = (string[0] >> 1); + // or: if (string[0] & 1) uint32_t *len = (uint32_t *) (string + 8); + + if (string[0] & 1) { // in memory + uint8_t **ptr = (uint8_t **)(string + 16); + return (*ptr); + } else { // in structure + return (string + 1); + } + +} + +void __cmplog_rtn_gcc_stdstring_cstring(uint8_t *stdstring, uint8_t *cstring) { + + if (!libafl_cmplog_enabled) return; + if (area_is_valid(stdstring, 32) <= 0) + return; + + __cmplog_rtn_hook(get_gcc_stdstring(stdstring), cstring); + +} + +void __cmplog_rtn_gcc_stdstring_stdstring(uint8_t *stdstring1, uint8_t *stdstring2) { + + if (!libafl_cmplog_enabled) return; + if (area_is_valid(stdstring1, 32) <= 0 || area_is_valid(stdstring2, 32) <= 0) + return; + + __cmplog_rtn_hook(get_gcc_stdstring(stdstring1), + get_gcc_stdstring(stdstring2)); + +} + +void __cmplog_rtn_llvm_stdstring_cstring(uint8_t *stdstring, uint8_t *cstring) { + + if (!libafl_cmplog_enabled) return; + if (area_is_valid(stdstring, 32) <= 0) + return; + + __cmplog_rtn_hook(get_llvm_stdstring(stdstring), cstring); + +} + +void __cmplog_rtn_llvm_stdstring_stdstring(uint8_t *stdstring1, uint8_t *stdstring2) { + + if (!libafl_cmplog_enabled) return; + if (area_is_valid(stdstring1, 32) <= 0 || area_is_valid(stdstring2, 32) <= 0) + return; + + __cmplog_rtn_hook(get_llvm_stdstring(stdstring1), + get_llvm_stdstring(stdstring2)); + +} diff --git a/libafl_targets/src/cmplog.h b/libafl_targets/src/cmplog.h index f3a30ad251..7807221f82 100644 --- a/libafl_targets/src/cmplog.h +++ b/libafl_targets/src/cmplog.h @@ -10,6 +10,10 @@ #define CMPLOG_MAP_H 32 #endif +#define CMPLOG_RTN_LEN 32 + +#define CMPLOG_MAP_RTN_H ((CMPLOG_MAP_H * sizeof(CmpLogOperands)) / sizeof(CmpLogRoutine)) + #define CMPLOG_KIND_INS 0 #define CMPLOG_KIND_RTN 1 @@ -24,19 +28,31 @@ typedef struct CmpLogOperands { uint64_t v1; } CmpLogOperands; +typedef struct CmpLogRoutine { + uint8_t v0[CMPLOG_RTN_LEN]; + uint8_t v1[CMPLOG_RTN_LEN]; +} CmpLogRoutine; + typedef struct CmpLogMap { CmpLogHeader headers[CMPLOG_MAP_W]; - CmpLogOperands operands[CMPLOG_MAP_W][CMPLOG_MAP_H]; + union { + CmpLogOperands operands[CMPLOG_MAP_W][CMPLOG_MAP_H]; + CmpLogRoutine routines[CMPLOG_MAP_W][CMPLOG_MAP_RTN_H]; + } vals; } CmpLogMap; extern CmpLogMap libafl_cmplog_map; extern uint8_t libafl_cmplog_enabled; -static void __libafl_targets_cmplog(uintptr_t k, uint8_t shape, uint64_t arg1, uint64_t arg2) { +void __libafl_targets_cmplog_instructions(uintptr_t k, uint8_t shape, uint64_t arg1, uint64_t arg2); + +void __libafl_targets_cmplog_routines(uintptr_t k, uint8_t *ptr1, uint8_t *ptr2); + +static inline void __libafl_targets_cmplog(uintptr_t k, uint8_t shape, uint64_t arg1, uint64_t arg2) { if (!libafl_cmplog_enabled) return; - + uint16_t hits; if (libafl_cmplog_map.headers[k].kind != CMPLOG_KIND_INS) { libafl_cmplog_map.headers[k].kind = CMPLOG_KIND_INS; @@ -51,8 +67,8 @@ static void __libafl_targets_cmplog(uintptr_t k, uint8_t shape, uint64_t arg1, u } hits &= CMPLOG_MAP_H - 1; - libafl_cmplog_map.operands[k][hits].v0 = arg1; - libafl_cmplog_map.operands[k][hits].v1 = arg2; + libafl_cmplog_map.vals.operands[k][hits].v0 = arg1; + libafl_cmplog_map.vals.operands[k][hits].v1 = arg2; } diff --git a/libafl_targets/src/common.h b/libafl_targets/src/common.h index 25f01dd74c..ac4668f05e 100644 --- a/libafl_targets/src/common.h +++ b/libafl_targets/src/common.h @@ -20,10 +20,21 @@ _a > _b ? _a : _b; \ \ }) + #define MIN(a, b) \ + ({ \ + \ + __typeof__(a) _a = (a); \ + __typeof__(b) _b = (b); \ + _a < _b ? _a : _b; \ + \ + }) + #define MEMCPY __builtin_memcpy #else #define MAX(a, b) (((a) > (b)) ? (a) : (b)) + #define MIN(a, b) (((a) < (b)) ? (a) : (b)) + #define MEMCPY memcpy #endif - +#define STATIC_ASSERT(pred) switch(0){case 0:case pred:;} #endif diff --git a/libafl_targets/src/sancov_cmp.c b/libafl_targets/src/sancov_cmp.c index 6f0e46f6ce..edee036092 100644 --- a/libafl_targets/src/sancov_cmp.c +++ b/libafl_targets/src/sancov_cmp.c @@ -72,12 +72,6 @@ void __sanitizer_cov_trace_cmp8(uint64_t arg1, uint64_t arg2) { } -#ifdef SANCOV_CMPLOG -void libafl_targets_cmplog_wrapper(uintptr_t k, uint8_t shape, uint64_t arg1, uint64_t arg2){ - return __libafl_targets_cmplog(k, shape, arg1, arg2); -} -#endif - void __sanitizer_cov_trace_switch(uint64_t val, uint64_t *cases) { uintptr_t rt = RETADDR;