Autodetect llvm-config for QEMU bindings generation (#1610)

* Autodetect llvm-config for QEMU bindings generation

* fix ci

* Fix signal handlers without ucontext pointer

* ci
This commit is contained in:
Andrea Fioraldi 2023-10-10 15:26:32 +02:00 committed by GitHub
parent bbb999f4d5
commit 31f4669794
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 133 additions and 29 deletions

View File

@ -145,7 +145,7 @@ jobs:
# Skipping `python` as it has to be built with the `maturin` tool
# `agpl`, `nautilus` require nightly
# `sancov_pcguard_edges` is tested seperately
run: cargo hack check --each-feature --clean-per-run --exclude-features=prelude,agpl,nautilus,python,sancov_pcguard_edges,arm,aarch64,i386,be,systemmode --no-dev-deps
run: LLVM_CONFIG=llvm-config-15 cargo hack check --each-feature --clean-per-run --exclude-features=prelude,agpl,nautilus,python,sancov_pcguard_edges,arm,aarch64,i386,be,systemmode --no-dev-deps
- name: Check nightly features
run: cargo +nightly check --features=agpl && cargo +nightly check --features=nautilus
@ -179,7 +179,7 @@ jobs:
- uses: actions/checkout@v3
- uses: Swatinem/rust-cache@v2
- name: Run a maturin build
run: LLVM_CONFIG_PATH=llvm-config-15 cd ./bindings/pylibafl && python3 -m venv .env && . .env/bin/activate && pip install --upgrade --force-reinstall . && ./test.sh
run: export LLVM_CONFIG=llvm-config-15 && cd ./bindings/pylibafl && python3 -m venv .env && . .env/bin/activate && pip install --upgrade --force-reinstall . && ./test.sh
- name: Run python test
run: . ./bindings/pylibafl/.env/bin/activate && cd ./fuzzers/baby_fuzzer && python3 baby_fuzzer.py 2>&1 | grep "Bye"
@ -270,7 +270,7 @@ jobs:
run: sudo ln -s /usr/include/asm-generic /usr/include/asm
- name: Build and run example fuzzers (Linux)
if: runner.os == 'Linux'
run: RUN_ON_CI=1 ./scripts/test_all_fuzzers.sh
run: RUN_ON_CI=1 LLVM_CONFIG=llvm-config-15 ./scripts/test_all_fuzzers.sh
- name: Build and run example fuzzers (macOS)
if: runner.os == 'macOS' # use bash v4
run: /usr/local/bin/bash -c 'RUN_ON_CI=1 ./scripts/test_all_fuzzers.sh'

View File

@ -68,7 +68,7 @@ pub struct ShutdownSignalData {
/// Type for shutdown handler
#[cfg(all(unix, feature = "std"))]
pub type ShutdownFuncPtr =
unsafe fn(Signal, &mut siginfo_t, &mut ucontext_t, data: &mut ShutdownSignalData);
unsafe fn(Signal, &mut siginfo_t, Option<&mut ucontext_t>, data: &mut ShutdownSignalData);
/// Shutdown handler. `SigTerm`, `SigInterrupt`, `SigQuit` call this
/// We can't handle SIGKILL in the signal handler, this means that you shouldn't kill your fuzzer with `kill -9` because then the shmem segments are never freed
@ -80,7 +80,7 @@ pub type ShutdownFuncPtr =
pub unsafe fn shutdown_handler<SP>(
signal: Signal,
_info: &mut siginfo_t,
_context: &mut ucontext_t,
_context: Option<&mut ucontext_t>,
data: &ShutdownSignalData,
) where
SP: ShMemProvider,
@ -106,7 +106,7 @@ pub unsafe fn shutdown_handler<SP>(
#[cfg(all(unix, feature = "std"))]
impl Handler for ShutdownSignalData {
fn handle(&mut self, signal: Signal, info: &mut siginfo_t, context: &mut ucontext_t) {
fn handle(&mut self, signal: Signal, info: &mut siginfo_t, context: Option<&mut ucontext_t>) {
unsafe {
let data = &mut SHUTDOWN_SIGHANDLER_DATA;
if !data.shutdown_handler.is_null() {

View File

@ -659,21 +659,30 @@ pub mod unix_signal_handler {
state::{HasClientPerfMonitor, HasCorpus, HasSolutions},
};
pub(crate) type HandlerFuncPtr =
unsafe fn(Signal, &mut siginfo_t, &mut ucontext_t, data: &mut InProcessExecutorHandlerData);
pub(crate) type HandlerFuncPtr = unsafe fn(
Signal,
&mut siginfo_t,
Option<&mut ucontext_t>,
data: &mut InProcessExecutorHandlerData,
);
/// A handler that does nothing.
/*pub fn nop_handler(
_signal: Signal,
_info: &mut siginfo_t,
_context: &mut ucontext_t,
_context: Option<&mut ucontext_t>,
_data: &mut InProcessExecutorHandlerData,
) {
}*/
#[cfg(unix)]
impl Handler for InProcessExecutorHandlerData {
fn handle(&mut self, signal: Signal, info: &mut siginfo_t, context: &mut ucontext_t) {
fn handle(
&mut self,
signal: Signal,
info: &mut siginfo_t,
context: Option<&mut ucontext_t>,
) {
unsafe {
let data = &mut GLOBAL_STATE;
let in_handler = data.set_in_handler(true);
@ -759,7 +768,7 @@ pub mod unix_signal_handler {
pub unsafe fn inproc_timeout_handler<E, EM, OF, Z>(
_signal: Signal,
_info: &mut siginfo_t,
_context: &mut ucontext_t,
_context: Option<&mut ucontext_t>,
data: &mut InProcessExecutorHandlerData,
) where
E: HasObservers,
@ -809,7 +818,7 @@ pub mod unix_signal_handler {
pub unsafe fn inproc_crash_handler<E, EM, OF, Z>(
signal: Signal,
_info: &mut siginfo_t,
_context: &mut ucontext_t,
_context: Option<&mut ucontext_t>,
data: &mut InProcessExecutorHandlerData,
) where
E: Executor<EM, Z> + HasObservers,
@ -840,7 +849,12 @@ pub mod unix_signal_handler {
{
let mut writer = std::io::BufWriter::new(&mut bsod);
writeln!(writer, "input: {:?}", input.generate_name(0)).unwrap();
libafl_bolts::minibsod::generate_minibsod(&mut writer, signal, _info, _context)
libafl_bolts::minibsod::generate_minibsod(
&mut writer,
signal,
_info,
_context.as_deref(),
)
.unwrap();
writer.flush().unwrap();
}
@ -876,7 +890,7 @@ pub mod unix_signal_handler {
&mut writer,
signal,
_info,
_context,
_context.as_deref(),
)
.unwrap();
writer.flush().unwrap();
@ -1322,8 +1336,12 @@ pub mod windows_exception_handler {
/// The signature of the crash handler function
#[cfg(all(feature = "std", unix))]
pub(crate) type ForkHandlerFuncPtr =
unsafe fn(Signal, &mut siginfo_t, &mut ucontext_t, data: &mut InProcessForkExecutorGlobalData);
pub(crate) type ForkHandlerFuncPtr = unsafe fn(
Signal,
&mut siginfo_t,
Option<&mut ucontext_t>,
data: &mut InProcessForkExecutorGlobalData,
);
/// The inmem fork executor's handlers.
#[cfg(all(feature = "std", unix))]
@ -1463,7 +1481,7 @@ pub(crate) static mut FORK_EXECUTOR_GLOBAL_DATA: InProcessForkExecutorGlobalData
#[cfg(all(feature = "std", unix))]
impl Handler for InProcessForkExecutorGlobalData {
fn handle(&mut self, signal: Signal, info: &mut siginfo_t, context: &mut ucontext_t) {
fn handle(&mut self, signal: Signal, info: &mut siginfo_t, context: Option<&mut ucontext_t>) {
match signal {
Signal::SigUser2 | Signal::SigAlarm => unsafe {
if !FORK_EXECUTOR_GLOBAL_DATA.timeout_handler.is_null() {
@ -2076,7 +2094,7 @@ pub mod child_signal_handlers {
pub(crate) unsafe fn child_crash_handler<E>(
_signal: Signal,
_info: &mut siginfo_t,
_context: &mut ucontext_t,
_context: Option<&mut ucontext_t>,
data: &mut InProcessForkExecutorGlobalData,
) where
E: HasObservers,
@ -2098,7 +2116,7 @@ pub mod child_signal_handlers {
pub(crate) unsafe fn child_timeout_handler<E>(
_signal: Signal,
_info: &mut siginfo_t,
_context: &mut ucontext_t,
_context: Option<&mut ucontext_t>,
data: &mut InProcessForkExecutorGlobalData,
) where
E: HasObservers,

View File

@ -1939,7 +1939,12 @@ pub struct LlmpShutdownSignalHandler {
#[cfg(unix)]
impl Handler for LlmpShutdownSignalHandler {
fn handle(&mut self, _signal: Signal, _info: &mut siginfo_t, _context: &mut ucontext_t) {
fn handle(
&mut self,
_signal: Signal,
_info: &mut siginfo_t,
_context: Option<&mut ucontext_t>,
) {
unsafe {
ptr::write_volatile(&mut self.shutting_down, true);
}

View File

@ -837,12 +837,16 @@ pub fn generate_minibsod<W: Write>(
writer: &mut BufWriter<W>,
signal: Signal,
_siginfo: &siginfo_t,
ucontext: &ucontext_t,
ucontext: Option<&ucontext_t>,
) -> Result<(), std::io::Error> {
writeln!(writer, "{:━^100}", " CRASH ")?;
write_crash(writer, signal, ucontext)?;
if let Some(uctx) = ucontext {
write_crash(writer, signal, uctx)?;
writeln!(writer, "{:━^100}", " REGISTERS ")?;
dump_registers(writer, ucontext)?;
dump_registers(writer, uctx)?;
} else {
writeln!(writer, "Received signal {}", signal)?;
}
writeln!(writer, "{:━^100}", " BACKTRACE ")?;
writeln!(writer, "{:?}", backtrace::Backtrace::new())?;
writeln!(writer, "{:━^100}", " MAPS ")?;

View File

@ -378,7 +378,7 @@ impl Display for Signal {
#[cfg(feature = "alloc")]
pub trait Handler {
/// Handle a signal
fn handle(&mut self, signal: Signal, info: &mut siginfo_t, _context: &mut ucontext_t);
fn handle(&mut self, signal: Signal, info: &mut siginfo_t, _context: Option<&mut ucontext_t>);
/// Return a list of signals to handle
fn signals(&self) -> Vec<Signal>;
}
@ -425,7 +425,7 @@ unsafe fn handle_signal(sig: c_int, info: *mut siginfo_t, void: *mut c_void) {
handler.handle(
*signal,
&mut ptr::read_unaligned(info),
&mut ptr::read_unaligned(void as *mut ucontext_t),
(void as *mut ucontext_t).as_mut(),
);
}

View File

@ -1,14 +1,19 @@
#![allow(clippy::missing_panics_doc)]
use std::{
fs,
env, fs,
path::{Path, PathBuf},
process::Command,
};
use which::which;
mod bindings;
mod build;
pub use build::build;
const LLVM_VERSION_MAX: i32 = 33;
pub fn build_with_bindings(
cpu_target: &str,
is_big_endian: bool,
@ -25,6 +30,73 @@ pub fn build_with_bindings(
.expect("Faield to write to the bindings file");
}
// For bindgen, the llvm version must be >= of the rust llvm version
fn find_llvm_config() -> Result<String, String> {
if let Ok(var) = env::var("LLVM_CONFIG") {
return Ok(var);
}
let rustc_llvm_ver = find_rustc_llvm_version().unwrap();
for version in (rustc_llvm_ver..=LLVM_VERSION_MAX).rev() {
let llvm_config_name: String = format!("llvm-config-{version}");
if which(&llvm_config_name).is_ok() {
return Ok(llvm_config_name);
}
}
if which("llvm-config").is_ok() {
if let Some(ver) = find_llvm_version("llvm-config".to_owned()) {
if ver >= rustc_llvm_ver {
return Ok("llvm-config".to_owned());
}
}
}
Err("could not find llvm-config".to_owned())
}
fn exec_llvm_config(llvm_config: String, args: &[&str]) -> String {
match Command::new(llvm_config).args(args).output() {
Ok(output) => String::from_utf8(output.stdout)
.expect("Unexpected llvm-config output")
.trim()
.to_string(),
Err(e) => panic!("Could not execute llvm-config: {e}"),
}
}
fn find_llvm_version(llvm_config: String) -> Option<i32> {
let output = exec_llvm_config(llvm_config, &["--version"]);
if let Some(major) = output.split('.').collect::<Vec<&str>>().first() {
if let Ok(res) = major.parse::<i32>() {
return Some(res);
}
}
None
}
fn exec_rustc(args: &[&str]) -> String {
let rustc = env::var("RUSTC").unwrap();
match Command::new(rustc).args(args).output() {
Ok(output) => String::from_utf8(output.stdout)
.expect("Unexpected rustc output")
.trim()
.to_string(),
Err(e) => panic!("Could not execute rustc: {e}"),
}
}
fn find_rustc_llvm_version() -> Option<i32> {
let output = exec_rustc(&["--verbose", "--version"]);
let ver = output.split(':').last().unwrap().trim();
if let Some(major) = ver.split('.').collect::<Vec<&str>>().first() {
if let Ok(res) = major.parse::<i32>() {
return Some(res);
}
}
None
}
//linux-user_main.c.o libqemu-x86_64-linux-user.fa.p
fn qemu_bindgen_clang_args(
@ -33,6 +105,11 @@ fn qemu_bindgen_clang_args(
cpu_target: &str,
is_usermode: bool,
) -> Vec<String> {
if env::var("LLVM_CONFIG_PATH").is_err() {
let found = find_llvm_config().expect("Cannot find a suitable llvm-config, it must be a version equal or greater than the rustc LLVM version");
env::set_var("LLVM_CONFIG_PATH", found);
}
// load compile commands
let compile_commands_string = &fs::read_to_string(build_dir.join("compile_commands.json"))
.expect("failed to read compile commands");