Skip to content

Commit

Permalink
Add config subcommand
Browse files Browse the repository at this point in the history
  • Loading branch information
smoelius committed Dec 4, 2023
1 parent ed53a16 commit fff490d
Show file tree
Hide file tree
Showing 6 changed files with 298 additions and 216 deletions.
18 changes: 11 additions & 7 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,13 @@ jobs:
matrix:
environment: [ubuntu-latest, macos-latest]
toolchain: [stable, nightly]
features: [default, plugins]
plugins: [true, false]
cc: [cc, clang]
exclude:
- environment: macos-latest
features: plugins
plugins: true
- toolchain: stable
features: plugins
plugins: true
include:
- cc: cc
cxx: c++
Expand Down Expand Up @@ -71,13 +71,17 @@ jobs:
env:
HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK: 1
- name: Build
run: cargo build --features=${{ matrix.features }} -vv
run: cargo build -vv
- name: Run `cargo afl config`
run: |
PLUGINS="$(${{ matrix.plugins }} && echo --plugins)" || true
cargo run -- afl config --build $PLUGINS
- name: Run afl-system-config
run: cargo run --features=${{ matrix.features }} -- afl system-config
run: cargo run -- afl system-config
- name: Build examples (with AFL instrumentation)
run: cargo run --features=${{ matrix.features }} -- afl build --examples -vv
run: cargo run -- afl build --examples -vv
- name: Run tests
run: cargo test --features=${{ matrix.features }} -p cargo-afl -vv
run: cargo test -p cargo-afl -vv
all-checks:
needs: [lint, build]
runs-on: ubuntu-latest
Expand Down
4 changes: 4 additions & 0 deletions cargo-afl/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ homepage = "https://github.com/rust-fuzz/afl.rs"
edition = "2021"

[build-dependencies]
clap = { version = "4.4", features = ["cargo", "derive"] }
fs_extra = "1.3"
home = "0.5"
libc = "0.2"
Expand All @@ -23,8 +24,11 @@ xdg = "2.5"

[dependencies]
clap = { version = "4.4", features = ["cargo", "derive"] }
fs_extra = "1.3"
home = "0.5"
libc = "0.2"
rustc_version = "0.4"
tempfile = "3.8"
xdg = "2.5"

[dev-dependencies]
Expand Down
193 changes: 12 additions & 181 deletions cargo-afl/build.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,12 @@
use std::env;
use std::ffi::OsStr;
use std::path::{Path, PathBuf};
use std::process::Command;

static AFL_SRC_PATH: &str = "AFLplusplus";

// https://github.com/rust-fuzz/afl.rs/issues/148
#[cfg(target_os = "macos")]
static AR_CMD: &str = "/usr/bin/ar";
#[cfg(not(target_os = "macos"))]
static AR_CMD: &str = "ar";
use std::path::Path;

#[path = "src/common.rs"]
mod common;

#[path = "src/config.rs"]
mod config;

fn main() {
let installing = home::cargo_home()
.map(|path| Path::new(env!("CARGO_MANIFEST_DIR")).starts_with(path))
Expand All @@ -22,175 +15,13 @@ fn main() {

let building_on_docs_rs = env::var("DOCS_RS").is_ok();

let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());

// smoelius: Build AFLplusplus in a temporary directory when installing or when building on docs.rs.
let work_dir = if installing || building_on_docs_rs {
let tempdir = tempfile::tempdir_in(&out_dir).unwrap();
if Path::new(AFL_SRC_PATH).join(".git").is_dir() {
let status = Command::new("git")
.args(["clone", AFL_SRC_PATH, &*tempdir.path().to_string_lossy()])
.status()
.expect("could not run 'git'");
assert!(status.success());
} else {
fs_extra::dir::copy(
AFL_SRC_PATH,
tempdir.path(),
&fs_extra::dir::CopyOptions {
content_only: true,
..Default::default()
},
)
.unwrap();
}
tempdir.into_path()
} else {
PathBuf::from(AFL_SRC_PATH)
};

let base = if building_on_docs_rs {
Some(out_dir)
} else {
None
};

// smoelius: Lock `work_dir` until the build script exits.
#[cfg(unix)]
let _file = sys::lock_path(&work_dir).unwrap();

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", "install"])
// skip the checks for the legacy x86 afl-gcc compiler
.env("AFL_NO_X86", "1")
.env("DESTDIR", common::afl_dir(base))
.env("PREFIX", "")
.env_remove("DEBUG");

if cfg!(feature = "plugins") {
let llvm_config = check_llvm_and_get_config();
command.env("LLVM_CONFIG", llvm_config);
} else {
// build just the runtime to avoid troubles with Xcode clang on macOS
// smoelius: `NO_BUILD=1` also makes `cargo build` significantly faster.
command.env("NO_BUILD", "1");
}

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

fn build_afl_llvm_runtime(work_dir: &Path, base: Option<&Path>) {
std::fs::copy(
work_dir.join("afl-compiler-rt.o"),
common::object_file_path(base),
)
.expect("Couldn't copy object file");

let status = Command::new(AR_CMD)
.arg("r")
.arg(common::archive_file_path(base))
.arg(common::object_file_path(base))
.status()
.expect("could not run 'ar'");
assert!(status.success());
}

fn copy_afl_llvm_plugins(work_dir: &Path, base: Option<&Path>) {
// Iterate over the files in the directory.
for result in work_dir.read_dir().unwrap() {
let entry = result.unwrap();
let file_name = entry.file_name();

// Get the file extension. Only copy the files that are shared objects.
if Path::new(&file_name).extension() == Some(OsStr::new("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(|error| {
panic!("Couldn't copy shared object file {file_name:?}: {error}")
});
}
}
}

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;
use std::io::{Error, Result};
use std::os::unix::io::AsRawFd;
use std::path::Path;

pub fn lock_path(path: &Path) -> Result<File> {
let file = File::open(path)?;
lock_exclusive(&file)?;
Ok(file)
}

// smoelius: `lock_exclusive` and `flock` were copied from:
// https://github.com/rust-lang/cargo/blob/ae91d4ed41da98bdfa16041dbc6cd30287920120/src/cargo/util/flock.rs

fn lock_exclusive(file: &File) -> Result<()> {
flock(file, libc::LOCK_EX)
}

fn flock(file: &File, flag: libc::c_int) -> Result<()> {
let ret = unsafe { libc::flock(file.as_raw_fd(), flag) };
if ret < 0 {
Err(Error::last_os_error())
} else {
Ok(())
}
// smoelius: Build AFLplusplus only when installing and not building on docs.rs.
if installing && !building_on_docs_rs {
config::config(&config::Args {
build: true,
force: true,
plugins: cfg!(feature = "plugins"),
..Default::default()
});
}
}
Loading

0 comments on commit fff490d

Please sign in to comment.