Rusty LibAFL fmt (#2271)
This commit is contained in:
parent
b1bec42044
commit
14263b9c69
@ -35,6 +35,7 @@ exclude = [
|
||||
"libafl_qemu/libafl_qemu_sys",
|
||||
"utils/noaslr",
|
||||
"utils/gdb_qemu",
|
||||
"utils/libafl_fmt",
|
||||
"scripts",
|
||||
]
|
||||
|
||||
|
@ -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));
|
||||
|
@ -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 :)"
|
||||
|
16
utils/libafl_fmt/Cargo.toml
Normal file
16
utils/libafl_fmt/Cargo.toml
Normal 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"
|
185
utils/libafl_fmt/src/main.rs
Normal file
185
utils/libafl_fmt/src/main.rs
Normal 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(())
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user