Skip to content

Commit

Permalink
Add plugins feature
Browse files Browse the repository at this point in the history
  • Loading branch information
brunoproduit authored and smoelius committed Nov 11, 2023
1 parent f704286 commit 785db30
Show file tree
Hide file tree
Showing 6 changed files with 176 additions and 33 deletions.
34 changes: 28 additions & 6 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,13 @@ jobs:
matrix:
environment: [ubuntu-latest, macos-latest]
toolchain: [stable, nightly]
features: [default, plugins]
cc: [cc, clang]
exclude:
- environment: macos-latest
features: plugins
- toolchain: stable
features: plugins
include:
- cc: cc
cxx: c++
Expand All @@ -45,21 +51,37 @@ jobs:
CC: ${{ matrix.cc }}
CXX: ${{ matrix.cxx }}
steps:
- if: ${{ matrix.environment == 'ubuntu-latest' }}
run: sudo apt-get install llvm
- uses: actions/checkout@v4
with:
submodules: true
- name: Rustup
run: rustup default ${{ matrix.toolchain }}
- name: Install LLVM
id: install-llvm
run: |
LLVM_VERSION="$(rustc --version -v | grep '^LLVM version:' | grep -o '[0-9]\+' | head -n 1)"
if [[ ${{ matrix.environment }} = 'macos-latest' ]]; then
brew update
brew install llvm@"$LLVM_VERSION" || true
echo "PATH=/usr/local/opt/llvm/bin:$PATH" >> "$GITHUB_OUTPUT"
else
wget https://apt.llvm.org/llvm.sh
chmod +x llvm.sh
sudo ./llvm.sh "$LLVM_VERSION"
echo "PATH=$PATH" >> "$GITHUB_OUTPUT"
fi
env:
HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK: 1
- name: Build
run: cargo build -vv
run: cargo build --features=${{ matrix.features }} -vv
env:
PATH: ${{ steps.install-llvm.outputs.path }}
- name: Run afl-system-config
run: cargo run -- afl system-config
run: cargo run --features=${{ matrix.features }} -- afl system-config
- name: Build examples (with AFL instrumentation)
run: cargo run -- afl build --examples -vv
run: cargo run --features=${{ matrix.features }} -- afl build --examples -vv
- name: Run tests
run: cargo test -p cargo-afl -vv
run: cargo test --features=${{ matrix.features }} -p cargo-afl -vv
all-checks:
needs: [lint, build]
runs-on: ubuntu-latest
Expand Down
9 changes: 8 additions & 1 deletion afl/examples/cmplog.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ fn main() {
// This fuzz harness demonstrates the capabilities of CmpLog.
// Simply run the fuzzer and it should find the crash immediately.
afl::fuzz!(|data: &[u8]| {
if data.len() != 16 {
if data.len() < 29 {
return;
}
if data[0] != b'A' {
Expand All @@ -26,6 +26,13 @@ fn main() {
return;
};

let slice = &data[16..];
let match_string = "Hello, world!";
let compare_string = String::from_utf8(slice.to_vec()).unwrap_or_default();
if compare_string != match_string {
return;
}

panic!("BOOM");
});
}
4 changes: 4 additions & 0 deletions cargo-afl/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,7 @@ assert_cmd = "2.0"
ctor = "0.2"
predicates = "3.0"
tempfile = "3.8"

[features]
default = []
plugins = []
81 changes: 78 additions & 3 deletions cargo-afl/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,21 +60,34 @@ fn main() {

build_afl(&work_dir, base.as_deref());
build_afl_llvm_runtime(&work_dir, base.as_deref());

if cfg!(feature = "plugins") {
copy_afl_llvm_plugins(&work_dir, base.as_deref());
}
}

fn build_afl(work_dir: &Path, base: Option<&Path>) {
// if you had already installed cargo-afl previously you **must** clean AFL++
let mut command = Command::new("make");
command
.current_dir(work_dir)
.args(["clean", "all", "install"])
.args(["clean", "install"])
// skip the checks for the legacy x86 afl-gcc compiler
.env("AFL_NO_X86", "1")
// build just the runtime to avoid troubles with Xcode clang on macOS
.env("NO_BUILD", "1")
//.env("NO_BUILD", "1")
.env("DESTDIR", common::afl_dir(base))
.env("PREFIX", "")
.env_remove("DEBUG");
let status = command.status().expect("could not run 'make'");

if cfg!(feature = "plugins") {
let llvm_config = check_llvm_and_get_config();
command.env("LLVM_CONFIG", llvm_config);
}

let status = command
.status()
.expect("could not run 'make clean install'");
assert!(status.success());
}

Expand All @@ -94,6 +107,68 @@ fn build_afl_llvm_runtime(work_dir: &Path, base: Option<&Path>) {
assert!(status.success());
}

fn copy_afl_llvm_plugins(work_dir: &Path, base: Option<&Path>) {
// Iterate over the files in the directory.
if let Ok(entries) = work_dir.read_dir() {
for entry in entries.flatten() {
let file_name = entry.file_name();
let file_name_str = file_name.to_string_lossy();

// Get the file extension.
if let Some(extension) = file_name_str.split('.').last() {
// Only copy the files that are shared objects
if extension == "so" {
// Attempt to copy the shared object file.
std::fs::copy(
work_dir.join(&file_name),
common::afl_llvm_dir(base).join(&file_name),
)
.unwrap_or_else(|_| {
panic!("Couldn't copy shared object file {file_name_str}",)
});
}
}
}
} else {
eprintln!("Failed to read the work directory. Aborting.");
}
}

fn check_llvm_and_get_config() -> String {
// Make sure we are on nightly for the -Z flags
assert!(
rustc_version::version_meta().unwrap().channel == rustc_version::Channel::Nightly,
"cargo-afl must be compiled with nightly for the plugins feature"
);
let version_meta = rustc_version::version_meta().unwrap();
let llvm_version = version_meta.llvm_version.unwrap().major.to_string();

// Fetch the llvm version of the rust toolchain and set the LLVM_CONFIG environment variable to the same version
// This is needed to compile the llvm plugins (needed for cmplog) from afl with the right LLVM version
let llvm_config = if cfg!(target_os = "macos") {
"llvm-config".to_string()
} else {
format!("llvm-config-{llvm_version}")
};

// check if llvm tools are installed and with the good version for the plugin compilation
let mut command = Command::new(llvm_config.clone());
command.args(["--version"]);
let out = command
.output()
.unwrap_or_else(|_| panic!("could not run {llvm_config} --version"));

let version = String::from_utf8(out.stdout)
.expect("could not convert llvm-config --version output to utf8");
let major = version
.split('.')
.next()
.expect("could not get major from llvm-config --version output");
assert!(major == llvm_version);

llvm_config
}

#[cfg(unix)]
mod sys {
use std::fs::File;
Expand Down
73 changes: 54 additions & 19 deletions cargo-afl/src/bin/cargo-afl.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
use clap::crate_version;

use std::collections::HashMap;
use std::env;
use std::ffi::{OsStr, OsString};
use std::process::{self, Command, Stdio};
Expand Down Expand Up @@ -99,22 +98,28 @@ fn main() {
fn clap_app() -> clap::Command {
use clap::{value_parser, Arg, Command};

let help = "In addition to the subcommands above, Cargo subcommands are also \
const HELP: &str = "In addition to the subcommands above, Cargo subcommands are also \
supported (see `cargo help` for a list of all Cargo subcommands).";

const VERSION: &str = if cfg!(feature = "plugins") {
concat!(env!("CARGO_PKG_VERSION"), " [feature=plugins]")
} else {
env!("CARGO_PKG_VERSION")
};

Command::new("cargo afl")
.display_name("cargo")
.subcommand_required(true)
.arg_required_else_help(true)
.subcommand(
Command::new("afl")
.version(crate_version!())
.version(VERSION)
.subcommand_required(true)
.arg_required_else_help(true)
.allow_external_subcommands(true)
.external_subcommand_value_parser(value_parser!(OsString))
.override_usage("cargo afl [SUBCOMMAND or Cargo SUBCOMMAND]")
.after_help(help)
.after_help(HELP)
.subcommand(
Command::new("addseeds")
.about("Invoke afl-addseeds")
Expand Down Expand Up @@ -316,18 +321,48 @@ where

// `-C codegen-units=1` is needed to work around link errors
// https://github.com/rust-fuzz/afl.rs/pull/193#issuecomment-933550430

let binding = common::afl_llvm_dir(None);
let p = binding.display();

let mut rustflags = format!(
"-C debug-assertions \
-C overflow_checks \
-C passes={passes} \
-C codegen-units=1 \
-C llvm-args=-sanitizer-coverage-level=3 \
-C llvm-args=-sanitizer-coverage-trace-pc-guard \
-C llvm-args=-sanitizer-coverage-prune-blocks=0 \
-C llvm-args=-sanitizer-coverage-trace-compares \
-C opt-level=3 \
-C target-cpu=native "
-C overflow_checks \
-C passes={passes} \
-C codegen-units=1 \
-C opt-level=3 \
-C target-cpu=native "
);
let mut environment_variables = HashMap::<&str, String>::new();
environment_variables.insert("ASAN_OPTIONS", asan_options);
environment_variables.insert("TSAN_OPTIONS", tsan_options);

if cfg!(feature = "plugins") {
// Make sure we are on nightly for the -Z flags
assert!(
rustc_version::version_meta().unwrap().channel == rustc_version::Channel::Nightly,
"cargo-afl must be compiled with nightly for CMPLOG and other advanced AFL++ features"
);

rustflags.push_str(&format!(
"-Z llvm-plugins={p}/cmplog-instructions-pass.so \
-Z llvm-plugins={p}/cmplog-routines-pass.so \
-Z llvm-plugins={p}/cmplog-switches-pass.so \
-Z llvm-plugins={p}/SanitizerCoveragePCGUARD.so \
-Z llvm-plugins={p}/afl-llvm-dict2file.so
"
));

environment_variables.insert("AFL_QUIET", "1".to_string());
} else {
rustflags.push_str(
"-C llvm-args=-sanitizer-coverage-level=3 \
-C llvm-args=-sanitizer-coverage-trace-pc-guard \
-C llvm-args=-sanitizer-coverage-prune-blocks=0 \
-C llvm-args=-sanitizer-coverage-trace-compares
",
);
}

let no_cfg_fuzzing = env::var("AFL_NO_CFG_FUZZING").is_ok();
if no_cfg_fuzzing {
Expand All @@ -353,20 +388,20 @@ where
rustflags.push_str(&format!(
"-l afl-llvm-rt \
-L {} ",
common::afl_llvm_rt_dir(None).display()
common::afl_llvm_dir(None).display()
));

// add user provided flags
rustflags.push_str(&env::var("RUSTFLAGS").unwrap_or_default());
rustdocflags.push_str(&env::var("RUSTDOCFLAGS").unwrap_or_default());

environment_variables.insert("RUSTFLAGS", rustflags);
environment_variables.insert("RUSTDOCFLAGS", rustdocflags);

let status = Command::new(cargo_path)
.arg(subcommand)
.args(args)
.env("RUSTFLAGS", &rustflags)
.env("RUSTDOCFLAGS", &rustdocflags)
.env("ASAN_OPTIONS", asan_options)
.env("TSAN_OPTIONS", tsan_options)
.envs(&environment_variables)
.status()
.unwrap();
process::exit(status.code().unwrap_or(1));
Expand Down
8 changes: 4 additions & 4 deletions cargo-afl/src/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,18 +57,18 @@ pub fn afl_dir(base: Option<&Path>) -> PathBuf {

#[allow(dead_code)]
#[must_use]
pub fn afl_llvm_rt_dir(base: Option<&Path>) -> PathBuf {
data_dir(base, "afl-llvm-rt")
pub fn afl_llvm_dir(base: Option<&Path>) -> PathBuf {
data_dir(base, "afl-llvm")
}

#[allow(dead_code)]
#[must_use]
pub fn object_file_path(base: Option<&Path>) -> PathBuf {
afl_llvm_rt_dir(base).join("libafl-llvm-rt.o")
afl_llvm_dir(base).join("libafl-llvm-rt.o")
}

#[allow(dead_code)]
#[must_use]
pub fn archive_file_path(base: Option<&Path>) -> PathBuf {
afl_llvm_rt_dir(base).join("libafl-llvm-rt.a")
afl_llvm_dir(base).join("libafl-llvm-rt.a")
}

0 comments on commit 785db30

Please sign in to comment.