Llvm passes (#185)

* enable llvm passes in libafl_cc

* cmplog rtn pass in fuzzbench fuzzer

* improve libafl_cc

* silence fuzzbench compiler wrapper

* instrumentation and runtime for rtn cmplog

* fix test

* fix test

* fuck clippy

* remove anon union in CmpLogMap

* windows.h

* remove libafl_targets_cmplog_wrapper

* no inline linking

* adapt fuzzers/
This commit is contained in:
Andrea Fioraldi 2021-06-23 09:38:15 +02:00 committed by GitHub
parent 37f641f79b
commit 5b54f0f068
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 1277 additions and 391 deletions

View File

@ -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"

View File

@ -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<String> = 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);
}

View File

@ -0,0 +1,5 @@
pub mod libafl_cc;
fn main() {
libafl_cc::main()
}

View File

@ -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<String> = 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");
}

View File

@ -1,22 +1,5 @@
use libafl_cc::{ClangWrapper, CompilerWrapper};
use std::env;
pub mod libafl_cc;
fn main() {
let args: Vec<String> = 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()
}

View File

@ -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<String> = 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");
}

View File

@ -1,22 +1,5 @@
use libafl_cc::{ClangWrapper, CompilerWrapper};
use std::env;
pub mod libafl_cc;
fn main() {
let args: Vec<String> = 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()
}

View File

@ -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<String> = 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");
}

View File

@ -1,22 +1,5 @@
use libafl_cc::{ClangWrapper, CompilerWrapper};
use std::env;
pub mod libafl_cc;
fn main() {
let args: Vec<String> = 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()
}

View File

@ -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<String> = 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");
}

View File

@ -1,22 +1,5 @@
use libafl_cc::{ClangWrapper, CompilerWrapper};
use std::env;
pub mod libafl_cc;
fn main() {
let args: Vec<String> = 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()
}

View File

@ -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<String> = 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");
}

View File

@ -1,22 +1,5 @@
use libafl_cc::{ClangWrapper, CompilerWrapper};
use std::env;
pub mod libafl_cc;
fn main() {
let args: Vec<String> = 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()
}

View File

@ -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]

74
libafl_cc/build.rs Normal file
View File

@ -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");
}

284
libafl_cc/src/clang.rs Normal file
View File

@ -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<String>,
cc_args: Vec<String>,
link_args: Vec<String>,
passes: Vec<LLVMPasses>,
}
#[allow(clippy::match_same_arms)] // for the linking = false wip for "shared"
impl CompilerWrapper for ClangWrapper {
fn from_args<S>(&mut self, args: &[S]) -> Result<&'_ mut Self, Error>
where
S: AsRef<str>,
{
let mut new_args: Vec<String> = 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<S>(&mut self, arg: S) -> &'_ mut Self
where
S: AsRef<str>,
{
self.base_args.push(arg.as_ref().to_string());
self
}
fn add_cc_arg<S>(&mut self, arg: S) -> &'_ mut Self
where
S: AsRef<str>,
{
self.cc_args.push(arg.as_ref().to_string());
self
}
fn add_link_arg<S>(&mut self, arg: S) -> &'_ mut Self
where
S: AsRef<str>,
{
self.link_args.push(arg.as_ref().to_string());
self
}
fn link_staticlib<S>(&mut self, dir: &Path, name: S) -> &'_ mut Self
where
S: AsRef<str>,
{
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<Vec<String>, 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();
}
}

View File

@ -0,0 +1,469 @@
/*
american fuzzy lop++ - LLVM CmpLog instrumentation
--------------------------------------------------
Written by Andrea Fioraldi <andreafioraldi@gmail.com>
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 <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <list>
#include <string>
#include <fstream>
#include <sys/time.h>
#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 <set>
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<CallInst *> 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<Function>(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<Function>(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<Function>(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<Function>(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<Function>(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<CallInst>(&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<Value *> 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<Value *> 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<Value *> 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<Value *> 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<Value *> 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

View File

@ -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<S>(&mut self, args: &[S]) -> Result<&'_ mut Self, Error>
where
S: AsRef<str>;
/// Add a compiler argument
fn add_arg(&mut self, arg: String) -> Result<&'_ mut Self, Error>;
fn add_arg<S>(&mut self, arg: S) -> &'_ mut Self
where
S: AsRef<str>;
/// Add a compiler argument only when compiling
fn add_cc_arg(&mut self, arg: String) -> Result<&'_ mut Self, Error>;
fn add_cc_arg<S>(&mut self, arg: S) -> &'_ mut Self
where
S: AsRef<str>;
/// Add a compiler argument only when linking
fn add_link_arg(&mut self, arg: String) -> Result<&'_ mut Self, Error>;
fn add_link_arg<S>(&mut self, arg: S) -> &'_ mut Self
where
S: AsRef<str>;
/// Add compiler arguments
fn add_args<S>(&mut self, args: &[S]) -> &'_ mut Self
where
S: AsRef<str>,
{
for arg in args {
self.add_arg(arg);
}
self
}
/// Add compiler arguments only when compiling
fn add_cc_args<S>(&mut self, args: &[S]) -> &'_ mut Self
where
S: AsRef<str>,
{
for arg in args {
self.add_cc_arg(arg);
}
self
}
/// Add compiler arguments only when linking
fn add_link_args<S>(&mut self, args: &[S]) -> &'_ mut Self
where
S: AsRef<str>,
{
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<S>(&mut self, dir: &Path, name: S) -> &'_ mut Self
where
S: AsRef<str>;
/// Command to run the compiler
fn command(&mut self) -> Result<Vec<String>, 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<String>,
cc_args: Vec<String>,
link_args: Vec<String>,
}
#[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<Vec<String>, 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();
}
}

View File

@ -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);
}
}

View File

@ -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");

207
libafl_targets/src/cmplog.c Normal file
View File

@ -0,0 +1,207 @@
// From AFL++'s afl-compiler-rt.c
#define CMPLOG_MODULE
#include "common.h"
#include "cmplog.h"
#if defined(_WIN32)
#include <windows.h>
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 <unistd.h>
#include <sys/syscall.h>
#include <fcntl.h>
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));
}

View File

@ -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;
}

View File

@ -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

View File

@ -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;