Rusty LibAFL fmt (#2271)

This commit is contained in:
Romain Malmain 2024-06-07 23:41:50 +02:00 committed by GitHub
parent b1bec42044
commit 14263b9c69
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 205 additions and 47 deletions

View File

@ -35,6 +35,7 @@ exclude = [
"libafl_qemu/libafl_qemu_sys",
"utils/noaslr",
"utils/gdb_qemu",
"utils/libafl_fmt",
"scripts",
]

View File

@ -1,6 +1,6 @@
#![no_main]
use libfuzzer_sys::fuzz_target;
use cargo_fuzz_test::do_thing;
use libfuzzer_sys::fuzz_target;
fuzz_target!(|data: &[u8]| do_thing(data));

View File

@ -3,52 +3,8 @@
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
LIBAFL_DIR=$(realpath "$SCRIPT_DIR/..")
# TODO: This should be rewritten in rust, a Makefile, or some platform-independent language
if ! command -v parallel > /dev/null; then
echo "Parallel could not be found. Please install parallel (often found in the 'moreutils' package)."
exit 1
fi
if [ "$1" = "check" ]; then
CHECK=1
fi
# Find main rust crates
CRATES_TO_FMT=$(find "$LIBAFL_DIR" -type d \( -path "*/fuzzers/*" -o -path "*/target/*" -o -path "*/utils/noaslr" -o -path "*/utils/gdb_qemu" -o -path "*/docs/listings/baby_fuzzer/listing-*" \) -prune \
-o -name "Cargo.toml" -print \
| grep -v "$LIBAFL_DIR/Cargo.toml")$'\n'
# Find fuzzer crates
CRATES_TO_FMT+=$(find "$LIBAFL_DIR/fuzzers" "$LIBAFL_DIR/fuzzers/backtrace_baby_fuzzers" "$LIBAFL_DIR/libafl_libfuzzer/libafl_libfuzzer_runtime" -maxdepth 2 -name "Cargo.toml" -print)
echo "Welcome to the happy fmt script. :)"
if [ "$CHECK" ]; then
echo "Running fmt in check mode."
CARGO_FLAGS="--check"
CLANG_FLAGS="--dry-run"
fi
echo "[*] Formatting Rust crates..."
if ! echo "$CRATES_TO_FMT" | parallel --halt-on-error 1 "echo '[*] Running fmt for {}'; cargo +nightly fmt $CARGO_FLAGS --manifest-path {}"
then
echo "Rust format failed."
exit 1
fi
if command -v clang-format-18 > /dev/null; then
echo "[*] Formatting C(pp) files"
C_FILES=$(find "$LIBAFL_DIR" -type f \( -name '*.cpp' -o -iname '*.hpp' -o -name '*.cc' -o -name '*.cxx' -o -name '*.cc' -o -name '*.c' -o -name '*.h' \) | grep -v '/target/' | grep -v 'libpng-1\.6\.37' | grep -v 'stb_image\.h' | grep -v 'dlmalloc\.c' | grep -v 'QEMU-Nyx')
if ! clang-format-18 "$CLANG_FLAGS" -i --style=file "$C_FILES"
then
echo "C(pp) format failed."
exit 1
fi
cargo run --manifest-path "$LIBAFL_DIR/utils/libafl_fmt/Cargo.toml" --release -- -c
else
echo "Warning: clang-format-18 not found. C(pp) files formatting skipped."
cargo run --manifest-path "$LIBAFL_DIR/utils/libafl_fmt/Cargo.toml" --release
fi
echo "[*] Done :)"

View File

@ -0,0 +1,16 @@
[package]
name = "libafl_fmt"
version = "0.1.0"
edition = "2021"
description = "Format the LibAFL repository"
authors = ["Romain Malmain <romain.malmain@pm.me>"]
license = "MIT OR Apache-2.0"
[dependencies]
project-root = "0.2"
walkdir = "2.5"
regex = "1.10"
tokio = { version = "1.38", features = ["process", "rt", "rt-multi-thread", "macros"] }
clap = { version = "4.5", features = ["derive"] }
exitcode = "1.1"
which = "6.0"

View File

@ -0,0 +1,185 @@
use std::{io, io::ErrorKind, path::PathBuf, str::from_utf8};
use clap::Parser;
use regex::RegexSet;
use tokio::{process::Command, task::JoinSet};
use walkdir::WalkDir;
use which::which;
async fn run_cargo_fmt(path: PathBuf, is_check: bool, verbose: bool) -> io::Result<()> {
// Sanity Check
assert_eq!(path.file_name().unwrap().to_str().unwrap(), "Cargo.toml");
let task_str = if is_check { "Checking" } else { "Formatting" };
let mut fmt_command = Command::new("cargo");
fmt_command
.arg("+nightly")
.arg("fmt")
.arg("--manifest-path")
.arg(path.as_path());
if is_check {
fmt_command.arg("--check");
}
if verbose {
println!("[*] {} {}...", task_str, path.as_path().display());
}
let res = fmt_command.output().await?;
if !res.status.success() {
println!("{}", from_utf8(&res.stdout).unwrap());
return Err(io::Error::new(ErrorKind::Other, "Cargo fmt failed."));
}
Ok(())
}
async fn run_clang_fmt(
path: PathBuf,
clang: &str,
is_check: bool,
verbose: bool,
) -> io::Result<()> {
let task_str = if is_check { "Checking" } else { "Formatting" };
let mut fmt_command = Command::new(clang);
fmt_command
.arg("-i")
.arg("--style")
.arg("file")
.arg(path.as_path());
if is_check {
fmt_command.arg("-Werror").arg("--dry-run");
}
fmt_command.arg(path.as_path());
if verbose {
println!("[*] {} {}...", task_str, path.as_path().display());
}
let res = fmt_command.output().await?;
if !res.status.success() {
println!("{}", from_utf8(&res.stderr).unwrap());
return Err(io::Error::new(
ErrorKind::Other,
format!("{} failed.", clang),
));
}
Ok(())
}
#[derive(Parser)]
struct Cli {
#[arg(short, long)]
check: bool,
#[arg(short, long)]
verbose: bool,
}
#[tokio::main]
async fn main() -> io::Result<()> {
let cli = Cli::parse();
let libafl_root_dir = project_root::get_project_root().expect("Could not locate project root.");
let rust_excluded_directories = RegexSet::new([
r".*target.*",
r".*utils/noaslr.*",
r".*utils/gdb_qemu.*",
r".*docs/listings/baby_fuzzer/listing-.*",
r".*LibAFL/Cargo.toml.*",
])
.expect("Could not create the regex set from the given regex");
let c_excluded_directories = RegexSet::new([
r".*target.*",
r".*libpng-1\.6.*",
r".*stb_image\.h$",
r".*dlmalloc\.c$",
r".*QEMU-Nyx.*",
])
.expect("Could not create the regex set from the given regex");
let c_file_to_format = RegexSet::new([
r".*\.cpp$",
r".*\.hpp$",
r".*\.cc$",
r".*\.cxx$",
r".*\.c$",
r".*\.h$",
])
.expect("Could not create the regex set from the given regex");
let rust_projects_to_fmt: Vec<PathBuf> = WalkDir::new(&libafl_root_dir)
.into_iter()
.filter_map(|entry| entry.ok())
.filter(|e| !rust_excluded_directories.is_match(e.path().as_os_str().to_str().unwrap()))
.filter(|e| e.file_name() == "Cargo.toml")
.map(|e| e.into_path())
.collect();
let mut tokio_joinset = JoinSet::new();
for project in rust_projects_to_fmt {
tokio_joinset.spawn(run_cargo_fmt(project, cli.check, cli.verbose));
}
let (clang, warning) = if which("clang-format-18").is_ok() {
(Some("clang-format-18"), None)
} else if which("clang-format").is_ok() {
(
Some("clang-format"),
Some("using clang-format, could provide a different result from clang-format-18"),
)
} else {
(
None,
Some("clang-format not found. Skipping C formatting..."),
)
};
if let Some(clang) = clang {
let c_files_to_fmt: Vec<PathBuf> = WalkDir::new(&libafl_root_dir)
.into_iter()
.filter_map(|entry| entry.ok())
.filter(|e| !c_excluded_directories.is_match(e.path().as_os_str().to_str().unwrap()))
.filter(|e| e.file_type().is_file())
.filter(|e| c_file_to_format.is_match(e.file_name().to_str().unwrap()))
.map(|e| e.into_path())
.collect();
for c_file in c_files_to_fmt {
tokio_joinset.spawn(run_clang_fmt(c_file, clang, cli.check, cli.verbose));
}
}
while let Some(res) = tokio_joinset.join_next().await {
match res? {
Ok(_) => {}
Err(err) => {
println!("Error: {}", err);
std::process::exit(exitcode::IOERR)
}
}
}
if let Some(warning) = warning {
println!("Warning: {}", warning);
}
if cli.check {
println!("[*] Check finished successfully.")
} else {
println!("[*] Formatting finished successfully.")
}
Ok(())
}