libafl_qemu_sys and libafl_qemu_build to have bindgen with QEMU (#915)
* build and sys qemu crates * working libafl_qemu_build * libafl_qemu_sys * switch libafl_qemu to use libafl_qemu_sys * fix * use sys * fmt * mmu lookup * fix * autofix * clippy * fix * allow * cl * docker * docker * fix * mem access info in mem hooks * fmt * fix * kill libafl_page_size * fix * clippy * default bindings for docs.rs * macos * fix arm build * fix * plugins * fix * fix fuzzer * Correct PC on breakpoint Co-authored-by: Dominik Maier <domenukk@gmail.com>
This commit is contained in:
parent
5252097819
commit
71dd58396c
@ -26,6 +26,8 @@ exclude = [
|
|||||||
"fuzzers",
|
"fuzzers",
|
||||||
"bindings",
|
"bindings",
|
||||||
"scripts",
|
"scripts",
|
||||||
|
"libafl_qemu/libafl_qemu_build",
|
||||||
|
"libafl_qemu/libafl_qemu_sys"
|
||||||
]
|
]
|
||||||
|
|
||||||
[workspace.package]
|
[workspace.package]
|
||||||
|
10
Dockerfile
10
Dockerfile
@ -39,6 +39,12 @@ COPY libafl_frida/src/gettls.c libafl_frida/src/gettls.c
|
|||||||
COPY libafl_qemu/Cargo.toml libafl_qemu/build.rs libafl_qemu/
|
COPY libafl_qemu/Cargo.toml libafl_qemu/build.rs libafl_qemu/
|
||||||
COPY scripts/dummy.rs libafl_qemu/src/lib.rs
|
COPY scripts/dummy.rs libafl_qemu/src/lib.rs
|
||||||
|
|
||||||
|
COPY libafl_qemu/libafl_qemu_build/Cargo.toml libafl_qemu/libafl_qemu_build/
|
||||||
|
COPY scripts/dummy.rs libafl_qemu/libafl_qemu_build/src/lib.rs
|
||||||
|
|
||||||
|
COPY libafl_qemu/libafl_qemu_sys/Cargo.toml libafl_qemu/libafl_qemu_sys/build.rs libafl_qemu/libafl_qemu_sys/
|
||||||
|
COPY scripts/dummy.rs libafl_qemu/libafl_qemu_sys/src/lib.rs
|
||||||
|
|
||||||
COPY libafl_sugar/Cargo.toml libafl_sugar/
|
COPY libafl_sugar/Cargo.toml libafl_sugar/
|
||||||
COPY scripts/dummy.rs libafl_sugar/src/lib.rs
|
COPY scripts/dummy.rs libafl_sugar/src/lib.rs
|
||||||
|
|
||||||
@ -91,6 +97,10 @@ RUN touch libafl/src/lib.rs
|
|||||||
COPY libafl_targets/src libafl_targets/src
|
COPY libafl_targets/src libafl_targets/src
|
||||||
RUN touch libafl_targets/src/lib.rs
|
RUN touch libafl_targets/src/lib.rs
|
||||||
COPY libafl_frida/src libafl_frida/src
|
COPY libafl_frida/src libafl_frida/src
|
||||||
|
RUN touch libafl_qemu/libafl_qemu_build/src/lib.rs
|
||||||
|
COPY libafl_qemu/libafl_qemu_build/src libafl_qemu/libafl_qemu_build/src
|
||||||
|
RUN touch libafl_qemu/libafl_qemu_sys/src/lib.rs
|
||||||
|
COPY libafl_qemu/libafl_qemu_sys/src libafl_qemu/libafl_qemu_sys/src
|
||||||
RUN touch libafl_qemu/src/lib.rs
|
RUN touch libafl_qemu/src/lib.rs
|
||||||
COPY libafl_qemu/src libafl_qemu/src
|
COPY libafl_qemu/src libafl_qemu/src
|
||||||
RUN touch libafl_frida/src/lib.rs
|
RUN touch libafl_frida/src/lib.rs
|
||||||
|
@ -31,8 +31,8 @@ use libafl::{
|
|||||||
//prelude::{SimpleMonitor, SimpleEventManager},
|
//prelude::{SimpleMonitor, SimpleEventManager},
|
||||||
};
|
};
|
||||||
use libafl_qemu::{
|
use libafl_qemu::{
|
||||||
edges, edges::QemuEdgeCoverageHelper, elf::EasyElf, emu::Emulator, QemuExecutor, QemuHooks,
|
edges, edges::QemuEdgeCoverageHelper, elf::EasyElf, emu::Emulator, GuestPhysAddr, QemuExecutor,
|
||||||
Regs,
|
QemuHooks, Regs,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub static mut MAX_INPUT_SIZE: usize = 50;
|
pub static mut MAX_INPUT_SIZE: usize = 50;
|
||||||
@ -60,7 +60,7 @@ pub fn fuzz() {
|
|||||||
&env::var("FUZZ_INPUT").unwrap_or_else(|_| "FUZZ_INPUT".to_owned()),
|
&env::var("FUZZ_INPUT").unwrap_or_else(|_| "FUZZ_INPUT".to_owned()),
|
||||||
0,
|
0,
|
||||||
)
|
)
|
||||||
.expect("Symbol or env FUZZ_INPUT not found");
|
.expect("Symbol or env FUZZ_INPUT not found") as GuestPhysAddr;
|
||||||
println!("FUZZ_INPUT @ {:#x}", input_addr);
|
println!("FUZZ_INPUT @ {:#x}", input_addr);
|
||||||
|
|
||||||
let main_addr = elf
|
let main_addr = elf
|
||||||
@ -88,7 +88,7 @@ pub fn fuzz() {
|
|||||||
}
|
}
|
||||||
emu.remove_breakpoint(main_addr);
|
emu.remove_breakpoint(main_addr);
|
||||||
|
|
||||||
emu.save_snapshot("start", true);
|
// emu.save_snapshot("start", true);
|
||||||
|
|
||||||
emu.set_breakpoint(breakpoint); // BREAKPOINT
|
emu.set_breakpoint(breakpoint); // BREAKPOINT
|
||||||
|
|
||||||
@ -99,7 +99,7 @@ pub fn fuzz() {
|
|||||||
// saved_regs.push(emu.cpu_from_index(0).read_reg(r).unwrap());
|
// saved_regs.push(emu.cpu_from_index(0).read_reg(r).unwrap());
|
||||||
//}
|
//}
|
||||||
|
|
||||||
let saved_cpu_states: Vec<_> = (0..emu.num_cpus())
|
let mut saved_cpu_states: Vec<_> = (0..emu.num_cpus())
|
||||||
.map(|i| emu.cpu_from_index(i).save_state())
|
.map(|i| emu.cpu_from_index(i).save_state())
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
@ -161,7 +161,7 @@ pub fn fuzz() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// A feedback to choose if an input is a solution or not
|
// A feedback to choose if an input is a solution or not
|
||||||
let mut objective = feedback_or_fast!(CrashFeedback::new(), TimeoutFeedback::new());
|
let mut objective = CrashFeedback::new(); //feedback_or_fast!(CrashFeedback::new(), TimeoutFeedback::new());
|
||||||
|
|
||||||
// If not restarting, create a State from scratch
|
// If not restarting, create a State from scratch
|
||||||
let mut state = state.unwrap_or_else(|| {
|
let mut state = state.unwrap_or_else(|| {
|
||||||
|
@ -17,22 +17,23 @@ python = ["pyo3", "pyo3-build-config"]
|
|||||||
fork = ["libafl/fork"]
|
fork = ["libafl/fork"]
|
||||||
|
|
||||||
# The following architecture features are mutually exclusive.
|
# The following architecture features are mutually exclusive.
|
||||||
x86_64 = [] # build qemu for x86_64 (default)
|
x86_64 = ["libafl_qemu_sys/x86_64"] # build qemu for x86_64 (default)
|
||||||
i386 = [] # build qemu for i386
|
i386 = ["libafl_qemu_sys/i386"] # build qemu for i386
|
||||||
arm = [] # build qemu for arm
|
arm = ["libafl_qemu_sys/arm"] # build qemu for arm
|
||||||
aarch64 = [] # build qemu for aarch64
|
aarch64 = ["libafl_qemu_sys/aarch64"] # build qemu for aarch64
|
||||||
be = []
|
be = ["libafl_qemu_sys/be"]
|
||||||
|
|
||||||
usermode = []
|
usermode = ["libafl_qemu_sys/usermode"]
|
||||||
systemmode = []
|
systemmode = ["libafl_qemu_sys/systemmode"]
|
||||||
|
|
||||||
slirp = [ "systemmode" ] # build qemu with host libslirp (for user networking)
|
slirp = [ "systemmode", "libafl_qemu_sys/slirp" ] # build qemu with host libslirp (for user networking)
|
||||||
|
|
||||||
clippy = [] # special feature for clippy, don't use in normal projects§
|
clippy = [] # special feature for clippy, don't use in normal projects§
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
libafl = { path = "../libafl", version = "0.8.2", default-features = false, features = ["std", "derive", "llmp_compression"] }
|
libafl = { path = "../libafl", version = "0.8.2", default-features = false, features = ["std", "derive", "llmp_compression"] }
|
||||||
libafl_targets = { path = "../libafl_targets", version = "0.8.2" }
|
libafl_targets = { path = "../libafl_targets", version = "0.8.2" }
|
||||||
|
libafl_qemu_sys = { path = "./libafl_qemu_sys", version = "0.8.2" }
|
||||||
|
|
||||||
serde = { version = "1.0", default-features = false, features = ["alloc"] } # serialization lib
|
serde = { version = "1.0", default-features = false, features = ["alloc"] } # serialization lib
|
||||||
hashbrown = { version = "0.12", features = ["serde", "ahash-compile-time-rng"] } # A faster hashmap, nostd compatible
|
hashbrown = { version = "0.12", features = ["serde", "ahash-compile-time-rng"] } # A faster hashmap, nostd compatible
|
||||||
@ -51,8 +52,6 @@ pyo3 = { version = "0.17", features = ["pyproto"], optional = true }
|
|||||||
rangemap = "1.0"
|
rangemap = "1.0"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
cc = { version = "1.0" }
|
|
||||||
which = "4.2"
|
|
||||||
pyo3-build-config = { version = "0.15", optional = true }
|
pyo3-build-config = { version = "0.15", optional = true }
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
|
@ -1,43 +1,14 @@
|
|||||||
use std::{env, fs, path::Path, process::Command};
|
use std::{env, fs, path::Path, process::Command};
|
||||||
|
|
||||||
use which::which;
|
|
||||||
|
|
||||||
const QEMU_URL: &str = "https://github.com/AFLplusplus/qemu-libafl-bridge";
|
|
||||||
const QEMU_DIRNAME: &str = "qemu-libafl-bridge";
|
|
||||||
const QEMU_REVISION: &str = "6db12fe4df0eb1305261cf07d1995f21eb262392";
|
|
||||||
|
|
||||||
fn build_dep_check(tools: &[&str]) {
|
|
||||||
for tool in tools {
|
|
||||||
which(tool).unwrap_or_else(|_| panic!("Build tool {tool} not found"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! assert_unique_feature {
|
|
||||||
() => {};
|
|
||||||
($first:tt $(,$rest:tt)*) => {
|
|
||||||
$(
|
|
||||||
#[cfg(not(feature = "clippy"))] // ignore multiple definition for clippy
|
|
||||||
#[cfg(all(feature = $first, feature = $rest))]
|
|
||||||
compile_error!(concat!("features \"", $first, "\" and \"", $rest, "\" cannot be used together"));
|
|
||||||
)*
|
|
||||||
assert_unique_feature!($($rest),*);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(clippy::too_many_lines)]
|
|
||||||
pub fn build() {
|
pub fn build() {
|
||||||
// Make sure that exactly one qemu mode is set
|
// Note: Unique features are checked in libafl_qemu_sys
|
||||||
assert_unique_feature!("usermode", "systemmode");
|
|
||||||
let emulation_mode = if cfg!(feature = "usermode") {
|
let emulation_mode = if cfg!(feature = "usermode") {
|
||||||
"usermode".to_string()
|
"usermode".to_string()
|
||||||
} else if cfg!(feature = "systemmode") {
|
} else if cfg!(feature = "systemmode") {
|
||||||
"systemmode".to_string()
|
"systemmode".to_string()
|
||||||
} else {
|
} else {
|
||||||
env::var("EMULATION_MODE").unwrap_or_else(|_| {
|
env::var("EMULATION_MODE").unwrap_or_else(|_| {
|
||||||
println!(
|
|
||||||
"cargo:warning=No emulation mode feature enabled or EMULATION_MODE env specified for libafl_qemu, supported: usermode, systemmmode - defaulting to usermode"
|
|
||||||
);
|
|
||||||
"usermode".to_string()
|
"usermode".to_string()
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
@ -45,17 +16,9 @@ pub fn build() {
|
|||||||
println!("cargo:rerun-if-env-changed=EMULATION_MODE");
|
println!("cargo:rerun-if-env-changed=EMULATION_MODE");
|
||||||
|
|
||||||
println!("cargo:rerun-if-changed=build.rs");
|
println!("cargo:rerun-if-changed=build.rs");
|
||||||
|
println!("cargo:rerun-if-changed=build_linux.rs");
|
||||||
|
|
||||||
// Make sure we have at most one architecutre feature set
|
let cpu_target = if cfg!(feature = "x86_64") {
|
||||||
// Else, we default to `x86_64` - having a default makes CI easier :)
|
|
||||||
assert_unique_feature!("arm", "aarch64", "i386", "i86_64");
|
|
||||||
|
|
||||||
// Make sure that we don't have BE set for any architecture other than arm
|
|
||||||
// Sure aarch64 may support BE, but its not in common usage and we don't
|
|
||||||
// need it yet and so haven't tested it
|
|
||||||
assert_unique_feature!("be", "aarch64", "i386", "i86_64");
|
|
||||||
|
|
||||||
let mut cpu_target = if cfg!(feature = "x86_64") {
|
|
||||||
"x86_64".to_string()
|
"x86_64".to_string()
|
||||||
} else if cfg!(feature = "arm") {
|
} else if cfg!(feature = "arm") {
|
||||||
"arm".to_string()
|
"arm".to_string()
|
||||||
@ -65,17 +28,14 @@ pub fn build() {
|
|||||||
"i386".to_string()
|
"i386".to_string()
|
||||||
} else {
|
} else {
|
||||||
env::var("CPU_TARGET").unwrap_or_else(|_| {
|
env::var("CPU_TARGET").unwrap_or_else(|_| {
|
||||||
println!(
|
|
||||||
"cargo:warning=No architecture feature enabled or CPU_TARGET env specified for libafl_qemu, supported: arm, aarch64, i386, x86_64 - defaulting to x86_64"
|
|
||||||
);
|
|
||||||
"x86_64".to_string()
|
"x86_64".to_string()
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
println!("cargo:rerun-if-env-changed=CPU_TARGET");
|
println!("cargo:rerun-if-env-changed=CPU_TARGET");
|
||||||
|
println!("cargo:rustc-cfg=cpu_target=\"{cpu_target}\"");
|
||||||
let jobs = env::var("NUM_JOBS");
|
|
||||||
|
|
||||||
let cross_cc = if emulation_mode == "usermode" {
|
let cross_cc = if emulation_mode == "usermode" {
|
||||||
|
// TODO try to autodetect a cross compiler with the arch name (e.g. aarch64-linux-gnu-gcc)
|
||||||
let cross_cc = env::var("CROSS_CC").unwrap_or_else(|_| {
|
let cross_cc = env::var("CROSS_CC").unwrap_or_else(|_| {
|
||||||
println!("cargo:warning=CROSS_CC is not set, default to cc (things can go wrong if the selected cpu target ({cpu_target}) is not the host arch ({}))", env::consts::ARCH);
|
println!("cargo:warning=CROSS_CC is not set, default to cc (things can go wrong if the selected cpu target ({cpu_target}) is not the host arch ({}))", env::consts::ARCH);
|
||||||
"cc".to_owned()
|
"cc".to_owned()
|
||||||
@ -87,422 +47,17 @@ pub fn build() {
|
|||||||
String::new()
|
String::new()
|
||||||
};
|
};
|
||||||
|
|
||||||
println!("cargo:rustc-cfg=cpu_target=\"{cpu_target}\"");
|
|
||||||
|
|
||||||
// qemu-system-arm supports both big and little endian configurations and so
|
|
||||||
// therefore the "be" feature should ignored in this configuration. Also
|
|
||||||
// ignore the feature if we are running in clippy which enables all the
|
|
||||||
// features at once (disabling the check for mutually exclusive options)
|
|
||||||
// resulting in cpu_target being set to 'x86_64' above which obviously
|
|
||||||
// doesn't support BE.
|
|
||||||
if cfg!(feature = "be")
|
|
||||||
&& cfg!(feature = "arm")
|
|
||||||
&& cfg!(feature = "usermode")
|
|
||||||
&& !cfg!(feature = "clippy")
|
|
||||||
{
|
|
||||||
// We have told rustc which CPU target to use above (it doesn't need
|
|
||||||
// to make any changes for endianness), however, we need QEMU to be
|
|
||||||
// built for the right endian-ness, so we update the cpu_target for
|
|
||||||
// here on down
|
|
||||||
cpu_target += "eb";
|
|
||||||
}
|
|
||||||
|
|
||||||
if std::env::var("DOCS_RS").is_ok() {
|
if std::env::var("DOCS_RS").is_ok() {
|
||||||
return; // only build when we're not generating docs
|
return; // only build when we're not generating docs
|
||||||
}
|
}
|
||||||
|
|
||||||
let custum_qemu_dir = env::var_os("CUSTOM_QEMU_DIR").map(|x| x.to_string_lossy().to_string());
|
let out_dir = env::var("OUT_DIR").unwrap();
|
||||||
let custum_qemu_no_build = env::var("CUSTOM_QEMU_NO_BUILD").is_ok();
|
|
||||||
println!("cargo:rerun-if-env-changed=CUSTOM_QEMU_DIR");
|
|
||||||
println!("cargo:rerun-if-env-changed=CUSTOM_QEMU_NO_BUILD");
|
|
||||||
|
|
||||||
let out_dir = env::var_os("OUT_DIR").unwrap();
|
|
||||||
let out_dir = out_dir.to_string_lossy().to_string();
|
|
||||||
let out_dir_path = Path::new(&out_dir);
|
let out_dir_path = Path::new(&out_dir);
|
||||||
let mut target_dir = out_dir_path.to_path_buf();
|
let mut target_dir = out_dir_path.to_path_buf();
|
||||||
target_dir.pop();
|
target_dir.pop();
|
||||||
target_dir.pop();
|
target_dir.pop();
|
||||||
target_dir.pop();
|
target_dir.pop();
|
||||||
|
|
||||||
println!("cargo:rerun-if-changed=libqasan");
|
|
||||||
|
|
||||||
build_dep_check(&["git", "make"]);
|
|
||||||
|
|
||||||
let qemu_path = if let Some(qemu_dir) = custum_qemu_dir.as_ref() {
|
|
||||||
Path::new(&qemu_dir).to_path_buf()
|
|
||||||
} else {
|
|
||||||
let qemu_path = target_dir.join(QEMU_DIRNAME);
|
|
||||||
|
|
||||||
let qemu_rev = target_dir.join("QEMU_REVISION");
|
|
||||||
if qemu_rev.exists()
|
|
||||||
&& fs::read_to_string(&qemu_rev).expect("Failed to read QEMU_REVISION") != QEMU_REVISION
|
|
||||||
{
|
|
||||||
drop(fs::remove_dir_all(&qemu_path));
|
|
||||||
}
|
|
||||||
|
|
||||||
if !qemu_path.is_dir() {
|
|
||||||
println!(
|
|
||||||
"cargo:warning=Qemu not found, cloning with git ({})...",
|
|
||||||
QEMU_REVISION
|
|
||||||
);
|
|
||||||
fs::create_dir_all(&qemu_path).unwrap();
|
|
||||||
Command::new("git")
|
|
||||||
.current_dir(&qemu_path)
|
|
||||||
.arg("init")
|
|
||||||
.status()
|
|
||||||
.unwrap();
|
|
||||||
Command::new("git")
|
|
||||||
.current_dir(&qemu_path)
|
|
||||||
.arg("remote")
|
|
||||||
.arg("add")
|
|
||||||
.arg("origin")
|
|
||||||
.arg(QEMU_URL)
|
|
||||||
.status()
|
|
||||||
.unwrap();
|
|
||||||
Command::new("git")
|
|
||||||
.current_dir(&qemu_path)
|
|
||||||
.arg("fetch")
|
|
||||||
.arg("--depth")
|
|
||||||
.arg("1")
|
|
||||||
.arg("origin")
|
|
||||||
.arg(QEMU_REVISION)
|
|
||||||
.status()
|
|
||||||
.unwrap();
|
|
||||||
Command::new("git")
|
|
||||||
.current_dir(&qemu_path)
|
|
||||||
.arg("checkout")
|
|
||||||
.arg("FETCH_HEAD")
|
|
||||||
.status()
|
|
||||||
.unwrap();
|
|
||||||
fs::write(&qemu_rev, QEMU_REVISION).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
qemu_path
|
|
||||||
};
|
|
||||||
|
|
||||||
let build_dir = qemu_path.join("build");
|
|
||||||
|
|
||||||
let target_suffix = if emulation_mode == "usermode" {
|
|
||||||
"linux-user".to_string()
|
|
||||||
} else {
|
|
||||||
"softmmu".to_string()
|
|
||||||
};
|
|
||||||
|
|
||||||
let output_lib = if emulation_mode == "usermode" {
|
|
||||||
build_dir.join(format!("libqemu-{cpu_target}.so"))
|
|
||||||
} else {
|
|
||||||
build_dir.join(format!("libqemu-system-{cpu_target}.so"))
|
|
||||||
};
|
|
||||||
|
|
||||||
println!("cargo:rerun-if-changed={}", output_lib.to_string_lossy());
|
|
||||||
|
|
||||||
if !output_lib.is_file() || (custum_qemu_dir.is_some() && !custum_qemu_no_build) {
|
|
||||||
/*drop(
|
|
||||||
Command::new("make")
|
|
||||||
.current_dir(&qemu_path)
|
|
||||||
.arg("distclean")
|
|
||||||
.status(),
|
|
||||||
);*/
|
|
||||||
if emulation_mode == "usermode" {
|
|
||||||
Command::new("./configure")
|
|
||||||
.current_dir(&qemu_path)
|
|
||||||
//.arg("--as-static-lib")
|
|
||||||
.arg("--as-shared-lib")
|
|
||||||
.arg(&format!("--target-list={cpu_target}-{target_suffix}"))
|
|
||||||
.args([
|
|
||||||
"--disable-blobs",
|
|
||||||
"--disable-bsd-user",
|
|
||||||
"--disable-fdt",
|
|
||||||
"--disable-system",
|
|
||||||
])
|
|
||||||
.status()
|
|
||||||
.expect("Configure failed");
|
|
||||||
} else {
|
|
||||||
Command::new("./configure")
|
|
||||||
.current_dir(&qemu_path)
|
|
||||||
//.arg("--as-static-lib")
|
|
||||||
.arg("--as-shared-lib")
|
|
||||||
.arg(&format!("--target-list={cpu_target}-{target_suffix}"))
|
|
||||||
.arg(if cfg!(feature = "slirp") {
|
|
||||||
"--enable-slirp"
|
|
||||||
} else {
|
|
||||||
"--disable-slirp"
|
|
||||||
})
|
|
||||||
.arg("--enable-fdt=internal")
|
|
||||||
.arg("--audio-drv-list=")
|
|
||||||
.arg("--disable-alsa")
|
|
||||||
.arg("--disable-attr")
|
|
||||||
.arg("--disable-auth-pam")
|
|
||||||
.arg("--disable-dbus-display")
|
|
||||||
.arg("--disable-blobs")
|
|
||||||
.arg("--disable-bochs")
|
|
||||||
.arg("--disable-bpf")
|
|
||||||
.arg("--disable-brlapi")
|
|
||||||
.arg("--disable-bsd-user")
|
|
||||||
.arg("--disable-bzip2")
|
|
||||||
.arg("--disable-cap-ng")
|
|
||||||
.arg("--disable-canokey")
|
|
||||||
.arg("--disable-cloop")
|
|
||||||
.arg("--disable-cocoa")
|
|
||||||
.arg("--disable-coreaudio")
|
|
||||||
.arg("--disable-curl")
|
|
||||||
.arg("--disable-curses")
|
|
||||||
.arg("--disable-dmg")
|
|
||||||
.arg("--disable-docs")
|
|
||||||
.arg("--disable-dsound")
|
|
||||||
.arg("--disable-fuse")
|
|
||||||
.arg("--disable-fuse-lseek")
|
|
||||||
.arg("--disable-gcrypt")
|
|
||||||
.arg("--disable-gettext")
|
|
||||||
.arg("--disable-gio")
|
|
||||||
.arg("--disable-glusterfs")
|
|
||||||
.arg("--disable-gnutls")
|
|
||||||
.arg("--disable-gtk")
|
|
||||||
.arg("--disable-guest-agent")
|
|
||||||
.arg("--disable-guest-agent-msi")
|
|
||||||
.arg("--disable-hax")
|
|
||||||
.arg("--disable-hvf")
|
|
||||||
.arg("--disable-iconv")
|
|
||||||
.arg("--disable-jack")
|
|
||||||
.arg("--disable-keyring")
|
|
||||||
.arg("--disable-kvm")
|
|
||||||
.arg("--disable-libdaxctl")
|
|
||||||
.arg("--disable-libiscsi")
|
|
||||||
.arg("--disable-libnfs")
|
|
||||||
.arg("--disable-libpmem")
|
|
||||||
.arg("--disable-libssh")
|
|
||||||
.arg("--disable-libudev")
|
|
||||||
.arg("--disable-libusb")
|
|
||||||
.arg("--disable-linux-aio")
|
|
||||||
.arg("--disable-linux-io-uring")
|
|
||||||
.arg("--disable-linux-user")
|
|
||||||
.arg("--disable-live-block-migration")
|
|
||||||
.arg("--disable-lzfse")
|
|
||||||
.arg("--disable-lzo")
|
|
||||||
.arg("--disable-l2tpv3")
|
|
||||||
.arg("--disable-malloc-trim")
|
|
||||||
.arg("--disable-mpath")
|
|
||||||
.arg("--disable-multiprocess")
|
|
||||||
.arg("--disable-netmap")
|
|
||||||
.arg("--disable-nettle")
|
|
||||||
.arg("--disable-numa")
|
|
||||||
.arg("--disable-nvmm")
|
|
||||||
.arg("--disable-opengl")
|
|
||||||
.arg("--disable-oss")
|
|
||||||
.arg("--disable-pa")
|
|
||||||
.arg("--disable-parallels")
|
|
||||||
.arg("--disable-plugins")
|
|
||||||
.arg("--disable-png")
|
|
||||||
.arg("--disable-pvrdma")
|
|
||||||
.arg("--disable-qcow1")
|
|
||||||
.arg("--disable-qed")
|
|
||||||
.arg("--disable-qga-vss")
|
|
||||||
.arg("--disable-rbd")
|
|
||||||
.arg("--disable-rdma")
|
|
||||||
.arg("--disable-replication")
|
|
||||||
.arg("--disable-sdl")
|
|
||||||
.arg("--disable-sdl-image")
|
|
||||||
.arg("--disable-seccomp")
|
|
||||||
.arg("--disable-selinux")
|
|
||||||
.arg("--disable-slirp-smbd")
|
|
||||||
.arg("--disable-smartcard")
|
|
||||||
.arg("--disable-snappy")
|
|
||||||
.arg("--disable-sparse")
|
|
||||||
.arg("--disable-spice")
|
|
||||||
.arg("--disable-spice-protocol")
|
|
||||||
.arg("--disable-tools")
|
|
||||||
.arg("--disable-tpm")
|
|
||||||
.arg("--disable-usb-redir")
|
|
||||||
.arg("--disable-user")
|
|
||||||
.arg("--disable-u2f")
|
|
||||||
.arg("--disable-vde")
|
|
||||||
.arg("--disable-vdi")
|
|
||||||
.arg("--disable-vduse-blk-export")
|
|
||||||
.arg("--disable-vhost-crypto")
|
|
||||||
.arg("--disable-vhost-kernel")
|
|
||||||
.arg("--disable-vhost-net")
|
|
||||||
.arg("--disable-vhost-user-blk-server")
|
|
||||||
.arg("--disable-vhost-vdpa")
|
|
||||||
.arg("--disable-virglrenderer")
|
|
||||||
.arg("--disable-virtfs")
|
|
||||||
.arg("--disable-virtiofsd")
|
|
||||||
.arg("--disable-vmnet")
|
|
||||||
.arg("--disable-vnc")
|
|
||||||
.arg("--disable-vnc-jpeg")
|
|
||||||
.arg("--disable-vnc-sasl")
|
|
||||||
.arg("--disable-vte")
|
|
||||||
.arg("--disable-vvfat")
|
|
||||||
.arg("--disable-whpx")
|
|
||||||
.arg("--disable-xen")
|
|
||||||
.arg("--disable-xen-pci-passthrough")
|
|
||||||
.arg("--disable-xkbcommon")
|
|
||||||
.arg("--disable-zstd")
|
|
||||||
.status()
|
|
||||||
.expect("Configure failed");
|
|
||||||
}
|
|
||||||
if let Ok(j) = jobs {
|
|
||||||
Command::new("make")
|
|
||||||
.current_dir(&build_dir)
|
|
||||||
.arg("-j")
|
|
||||||
.arg(&j)
|
|
||||||
.status()
|
|
||||||
.expect("Make failed");
|
|
||||||
} else {
|
|
||||||
Command::new("make")
|
|
||||||
.current_dir(&build_dir)
|
|
||||||
.arg("-j")
|
|
||||||
.status()
|
|
||||||
.expect("Make failed");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut objects = vec![];
|
|
||||||
for dir in &[
|
|
||||||
build_dir.join("libcommon.fa.p"),
|
|
||||||
build_dir.join(format!("libqemu-{cpu_target}-{target_suffix}.fa.p")),
|
|
||||||
] {
|
|
||||||
for path in fs::read_dir(dir).unwrap() {
|
|
||||||
let path = path.unwrap().path();
|
|
||||||
if path.is_file() {
|
|
||||||
if let Some(name) = path.file_name() {
|
|
||||||
if name.to_string_lossy().starts_with("stubs") {
|
|
||||||
continue;
|
|
||||||
} else if let Some(ext) = path.extension() {
|
|
||||||
if ext == "o" {
|
|
||||||
objects.push(path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if emulation_mode == "usermode" {
|
|
||||||
Command::new("ld")
|
|
||||||
.current_dir(out_dir_path)
|
|
||||||
.arg("-o")
|
|
||||||
.arg("libqemu-partially-linked.o")
|
|
||||||
.arg("-r")
|
|
||||||
.args(objects)
|
|
||||||
.arg("--start-group")
|
|
||||||
.arg("--whole-archive")
|
|
||||||
.arg(format!("{}/libhwcore.fa", build_dir.display()))
|
|
||||||
.arg(format!("{}/libqom.fa", build_dir.display()))
|
|
||||||
.arg(format!("{}/libevent-loop-base.a", build_dir.display()))
|
|
||||||
.arg("--no-whole-archive")
|
|
||||||
.arg(format!("{}/libqemuutil.a", build_dir.display()))
|
|
||||||
.arg(format!("{}/libhwcore.fa", build_dir.display()))
|
|
||||||
.arg(format!("{}/libqom.fa", build_dir.display()))
|
|
||||||
.arg(format!(
|
|
||||||
"--dynamic-list={}/plugins/qemu-plugins.symbols",
|
|
||||||
qemu_path.display()
|
|
||||||
))
|
|
||||||
.arg("--end-group")
|
|
||||||
.status()
|
|
||||||
.expect("Partial linked failure");
|
|
||||||
} else {
|
|
||||||
Command::new("ld")
|
|
||||||
.current_dir(out_dir_path)
|
|
||||||
.arg("-o")
|
|
||||||
.arg("libqemu-partially-linked.o")
|
|
||||||
.arg("-r")
|
|
||||||
.args(objects)
|
|
||||||
.arg("--start-group")
|
|
||||||
.arg("--whole-archive")
|
|
||||||
.arg(format!("{}/libhwcore.fa", build_dir.display()))
|
|
||||||
.arg(format!("{}/libqom.fa", build_dir.display()))
|
|
||||||
.arg(format!("{}/libevent-loop-base.a", build_dir.display()))
|
|
||||||
.arg(format!("{}/libio.fa", build_dir.display()))
|
|
||||||
.arg(format!("{}/libcrypto.fa", build_dir.display()))
|
|
||||||
.arg(format!("{}/libauthz.fa", build_dir.display()))
|
|
||||||
.arg(format!("{}/libblockdev.fa", build_dir.display()))
|
|
||||||
.arg(format!("{}/libblock.fa", build_dir.display()))
|
|
||||||
.arg(format!("{}/libchardev.fa", build_dir.display()))
|
|
||||||
.arg(format!("{}/libqmp.fa", build_dir.display()))
|
|
||||||
.arg("--no-whole-archive")
|
|
||||||
.arg(format!("{}/libqemuutil.a", build_dir.display()))
|
|
||||||
.arg(format!(
|
|
||||||
"{}/subprojects/libvhost-user/libvhost-user-glib.a",
|
|
||||||
build_dir.display()
|
|
||||||
))
|
|
||||||
.arg(format!(
|
|
||||||
"{}/subprojects/libvhost-user/libvhost-user.a",
|
|
||||||
build_dir.display()
|
|
||||||
))
|
|
||||||
.arg(format!(
|
|
||||||
"{}/subprojects/libvduse/libvduse.a",
|
|
||||||
build_dir.display()
|
|
||||||
))
|
|
||||||
.arg(format!("{}/libfdt.a", build_dir.display()))
|
|
||||||
.arg(format!("{}/libmigration.fa", build_dir.display()))
|
|
||||||
.arg(format!("{}/libhwcore.fa", build_dir.display()))
|
|
||||||
.arg(format!("{}/libqom.fa", build_dir.display()))
|
|
||||||
.arg(format!("{}/libio.fa", build_dir.display()))
|
|
||||||
.arg(format!("{}/libcrypto.fa", build_dir.display()))
|
|
||||||
.arg(format!("{}/libauthz.fa", build_dir.display()))
|
|
||||||
.arg(format!("{}/libblockdev.fa", build_dir.display()))
|
|
||||||
.arg(format!("{}/libblock.fa", build_dir.display()))
|
|
||||||
.arg(format!("{}/libchardev.fa", build_dir.display()))
|
|
||||||
.arg(format!("{}/libqmp.fa", build_dir.display()))
|
|
||||||
.arg(format!(
|
|
||||||
"--dynamic-list={}/plugins/qemu-plugins.symbols",
|
|
||||||
qemu_path.display()
|
|
||||||
))
|
|
||||||
.arg("--end-group")
|
|
||||||
.status()
|
|
||||||
.expect("Partial linked failure");
|
|
||||||
}
|
|
||||||
|
|
||||||
Command::new("ar")
|
|
||||||
.current_dir(out_dir_path)
|
|
||||||
.arg("crs")
|
|
||||||
.arg("libqemu-partially-linked.a")
|
|
||||||
.arg("libqemu-partially-linked.o")
|
|
||||||
.status()
|
|
||||||
.expect("Ar creation");
|
|
||||||
|
|
||||||
println!("cargo:rustc-link-search=native={out_dir}");
|
|
||||||
println!("cargo:rustc-link-lib=static=qemu-partially-linked");
|
|
||||||
println!("cargo:rustc-link-lib=rt");
|
|
||||||
println!("cargo:rustc-link-lib=gmodule-2.0");
|
|
||||||
println!("cargo:rustc-link-lib=glib-2.0");
|
|
||||||
println!("cargo:rustc-link-lib=stdc++");
|
|
||||||
println!("cargo:rustc-link-lib=z");
|
|
||||||
#[cfg(all(feature = "slirp", feature = "systemmode"))]
|
|
||||||
println!("cargo:rustc-link-lib=slirp");
|
|
||||||
|
|
||||||
if emulation_mode == "systemmode" {
|
|
||||||
println!("cargo:rustc-link-lib=pixman-1");
|
|
||||||
|
|
||||||
fs::create_dir_all(target_dir.join("pc-bios")).unwrap();
|
|
||||||
for path in fs::read_dir(build_dir.join("pc-bios")).unwrap() {
|
|
||||||
let path = path.unwrap().path();
|
|
||||||
if path.is_file() {
|
|
||||||
if let Some(name) = path.file_name() {
|
|
||||||
fs::copy(&path, target_dir.join("pc-bios").join(name))
|
|
||||||
.expect("Failed to copy a pc-bios folder file");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* #[cfg(not(feature = "python"))]
|
|
||||||
{
|
|
||||||
fs::copy(
|
|
||||||
build_dir.join(&format!("libqemu-{}.so", cpu_target)),
|
|
||||||
target_dir.join(&format!("libqemu-{}.so", cpu_target)),
|
|
||||||
)
|
|
||||||
.expect("Failed to copy the QEMU shared object");
|
|
||||||
|
|
||||||
println!(
|
|
||||||
"cargo:rustc-link-search=native={}",
|
|
||||||
&target_dir.to_string_lossy()
|
|
||||||
);
|
|
||||||
println!("cargo:rustc-link-lib=qemu-{}", cpu_target);
|
|
||||||
|
|
||||||
println!("cargo:rustc-env=LD_LIBRARY_PATH={}", target_dir.display());
|
|
||||||
} */
|
|
||||||
|
|
||||||
if emulation_mode == "usermode" {
|
if emulation_mode == "usermode" {
|
||||||
let qasan_dir = Path::new("libqasan");
|
let qasan_dir = Path::new("libqasan");
|
||||||
let qasan_dir = fs::canonicalize(qasan_dir).unwrap();
|
let qasan_dir = fs::canonicalize(qasan_dir).unwrap();
|
||||||
|
24
libafl_qemu/libafl_qemu_build/Cargo.toml
Normal file
24
libafl_qemu/libafl_qemu_build/Cargo.toml
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
[package]
|
||||||
|
name = "libafl_qemu_build"
|
||||||
|
version = "0.8.2"
|
||||||
|
authors = ["Andrea Fioraldi <andreafioraldi@gmail.com>"]
|
||||||
|
description = "Builder for LibAFL QEMU"
|
||||||
|
documentation = "https://docs.rs/libafl_qemu_build"
|
||||||
|
repository = "https://github.com/AFLplusplus/LibAFL/"
|
||||||
|
readme = "./README.md"
|
||||||
|
license = "MIT OR Apache-2.0"
|
||||||
|
keywords = ["fuzzing", "qemu", "instrumentation"]
|
||||||
|
edition = "2021"
|
||||||
|
categories = ["development-tools::testing", "emulators", "embedded", "os", "no-std"]
|
||||||
|
|
||||||
|
[features]
|
||||||
|
slirp = [] # build qemu with host libslirp (for user networking)
|
||||||
|
|
||||||
|
clippy = [] # special feature for clippy, don't use in normal projects§
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
bindgen = "0.63"
|
||||||
|
cc = "1.0"
|
||||||
|
which = "4.2"
|
||||||
|
json = "0.12"
|
||||||
|
shell-words = "1.1"
|
3
libafl_qemu/libafl_qemu_build/README.md
Normal file
3
libafl_qemu/libafl_qemu_build/README.md
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# QEMU LibAFL bridge builder
|
||||||
|
|
||||||
|
Parts of the codebase are originally taken from the Hoedur fuzzer, (c) Simon Wörner.
|
126
libafl_qemu/libafl_qemu_build/src/bindings.rs
Normal file
126
libafl_qemu/libafl_qemu_build/src/bindings.rs
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
use std::{fs, path::Path};
|
||||||
|
|
||||||
|
use bindgen::{BindgenError, Bindings};
|
||||||
|
|
||||||
|
const WRAPPER_HEADER: &str = r#"
|
||||||
|
|
||||||
|
// QEMU_BUILD_BUG* cause an infinite recursion in bindgen when target is arm
|
||||||
|
#include "qemu/compiler.h"
|
||||||
|
|
||||||
|
#undef QEMU_BUILD_BUG_MSG
|
||||||
|
#undef QEMU_BUILD_BUG_ON_STRUCT
|
||||||
|
#undef QEMU_BUILD_BUG_ON
|
||||||
|
#undef QEMU_BUILD_BUG_ON_ZERO
|
||||||
|
|
||||||
|
#define QEMU_BUILD_BUG_MSG(x, msg)
|
||||||
|
#define QEMU_BUILD_BUG_ON_STRUCT(x)
|
||||||
|
#define QEMU_BUILD_BUG_ON(x)
|
||||||
|
#define QEMU_BUILD_BUG_ON_ZERO(x)
|
||||||
|
|
||||||
|
#include "qemu/osdep.h"
|
||||||
|
#include "qapi/error.h"
|
||||||
|
|
||||||
|
#include "exec/target_page.h"
|
||||||
|
#include "hw/qdev-core.h"
|
||||||
|
#include "hw/qdev-properties.h"
|
||||||
|
#include "qemu/error-report.h"
|
||||||
|
|
||||||
|
#ifdef CONFIG_USER_ONLY
|
||||||
|
|
||||||
|
#include "qemu.h"
|
||||||
|
#include "user-internals.h"
|
||||||
|
#include "strace.h"
|
||||||
|
#include "signal-common.h"
|
||||||
|
#include "loader.h"
|
||||||
|
#include "user-mmap.h"
|
||||||
|
#include "user/safe-syscall.h"
|
||||||
|
#include "qemu/selfmap.h"
|
||||||
|
#include "cpu_loop-common.h"
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#include "migration/vmstate.h"
|
||||||
|
#include "hw/core/sysemu-cpu-ops.h"
|
||||||
|
#include "exec/address-spaces.h"
|
||||||
|
#include "sysemu/tcg.h"
|
||||||
|
#include "sysemu/replay.h"
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "exec/cpu-common.h"
|
||||||
|
#include "exec/exec-all.h"
|
||||||
|
#include "exec/translate-all.h"
|
||||||
|
#include "exec/log.h"
|
||||||
|
#include "trace/trace-root.h"
|
||||||
|
#include "qemu/accel.h"
|
||||||
|
#include "hw/core/accel-cpu.h"
|
||||||
|
|
||||||
|
#include "tcg/tcg.h"
|
||||||
|
#include "tcg/tcg-op.h"
|
||||||
|
#include "tcg/tcg-internal.h"
|
||||||
|
#include "exec/helper-head.h"
|
||||||
|
|
||||||
|
#include "qemu/plugin-memory.h"
|
||||||
|
|
||||||
|
"#;
|
||||||
|
|
||||||
|
pub fn generate(
|
||||||
|
build_dir: &Path,
|
||||||
|
cpu_target: &str,
|
||||||
|
clang_args: Vec<String>,
|
||||||
|
) -> Result<Bindings, BindgenError> {
|
||||||
|
let wrapper_h = build_dir.join("wrapper.h");
|
||||||
|
fs::write(&wrapper_h, WRAPPER_HEADER).expect("Unable to write wrapper.h");
|
||||||
|
|
||||||
|
let bindings = bindgen::Builder::default()
|
||||||
|
.derive_debug(true)
|
||||||
|
.derive_default(true)
|
||||||
|
.impl_debug(true)
|
||||||
|
.generate_comments(true)
|
||||||
|
.default_enum_style(bindgen::EnumVariation::NewType {
|
||||||
|
is_global: true,
|
||||||
|
is_bitfield: true,
|
||||||
|
})
|
||||||
|
.header(wrapper_h.display().to_string())
|
||||||
|
.clang_args(clang_args)
|
||||||
|
.allowlist_type("target_ulong")
|
||||||
|
.allowlist_type("target_long")
|
||||||
|
.allowlist_type("CPUState")
|
||||||
|
.allowlist_type("CPUArchState")
|
||||||
|
.allowlist_type("RAMBlock")
|
||||||
|
.allowlist_type("qemu_plugin_hwaddr")
|
||||||
|
.allowlist_type("qemu_plugin_meminfo_t")
|
||||||
|
.allowlist_type("qemu_plugin_mem_rw")
|
||||||
|
.allowlist_type("MemOpIdx")
|
||||||
|
.allowlist_type("MemOp")
|
||||||
|
.allowlist_function("qemu_user_init")
|
||||||
|
.allowlist_function("target_mmap")
|
||||||
|
.allowlist_function("target_mprotect")
|
||||||
|
.allowlist_function("target_munmap")
|
||||||
|
.allowlist_function("cpu_memory_rw_debug")
|
||||||
|
.allowlist_function("cpu_physical_memory_rw")
|
||||||
|
.allowlist_function("cpu_reset")
|
||||||
|
.allowlist_function("cpu_synchronize_state")
|
||||||
|
.allowlist_function("cpu_get_phys_page_attrs_debug")
|
||||||
|
.allowlist_function("tlb_plugin_lookup")
|
||||||
|
.allowlist_function("qemu_plugin_hwaddr_phys_addr")
|
||||||
|
.allowlist_function("qemu_plugin_get_hwaddr")
|
||||||
|
.blocklist_function("main_loop_wait") // bindgen issue #1313
|
||||||
|
.parse_callbacks(Box::new(bindgen::CargoCallbacks));
|
||||||
|
|
||||||
|
// arch specific functions
|
||||||
|
let bindings = if cpu_target == "i386" || cpu_target == "x86_64" {
|
||||||
|
bindings
|
||||||
|
.allowlist_type("CPUX86State")
|
||||||
|
.allowlist_type("X86CPU")
|
||||||
|
} else if cpu_target == "arssssm" {
|
||||||
|
bindings
|
||||||
|
.allowlist_type("ARMCPU")
|
||||||
|
.allowlist_type("ARMv7MState")
|
||||||
|
} else {
|
||||||
|
bindings
|
||||||
|
};
|
||||||
|
|
||||||
|
// generate + write bindings
|
||||||
|
bindings.generate()
|
||||||
|
}
|
422
libafl_qemu/libafl_qemu_build/src/build.rs
Normal file
422
libafl_qemu/libafl_qemu_build/src/build.rs
Normal file
@ -0,0 +1,422 @@
|
|||||||
|
use std::{
|
||||||
|
env, fs,
|
||||||
|
path::{Path, PathBuf},
|
||||||
|
process::Command,
|
||||||
|
};
|
||||||
|
|
||||||
|
use which::which;
|
||||||
|
|
||||||
|
const QEMU_URL: &str = "https://github.com/AFLplusplus/qemu-libafl-bridge";
|
||||||
|
const QEMU_DIRNAME: &str = "qemu-libafl-bridge";
|
||||||
|
const QEMU_REVISION: &str = "9707dd2d211221367915d5da21fe8693d6842eaf";
|
||||||
|
|
||||||
|
fn build_dep_check(tools: &[&str]) {
|
||||||
|
for tool in tools {
|
||||||
|
which(tool).unwrap_or_else(|_| panic!("Build tool {tool} not found"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::too_many_lines)]
|
||||||
|
#[must_use]
|
||||||
|
pub fn build(
|
||||||
|
cpu_target: &str,
|
||||||
|
is_big_endian: bool,
|
||||||
|
is_usermode: bool,
|
||||||
|
jobs: Option<u32>,
|
||||||
|
) -> (PathBuf, PathBuf) {
|
||||||
|
let mut cpu_target = cpu_target.to_string();
|
||||||
|
// qemu-system-arm supports both big and little endian configurations and so
|
||||||
|
// therefore the "be" feature should ignored in this configuration. Also
|
||||||
|
// ignore the feature if we are running in clippy which enables all the
|
||||||
|
// features at once (disabling the check for mutually exclusive options)
|
||||||
|
// resulting in cpu_target being set to 'x86_64' above which obviously
|
||||||
|
// doesn't support BE.
|
||||||
|
if is_big_endian && cpu_target == "arm" && is_usermode && !cfg!(feature = "clippy") {
|
||||||
|
// We have told rustc which CPU target to use above (it doesn't need
|
||||||
|
// to make any changes for endianness), however, we need QEMU to be
|
||||||
|
// built for the right endian-ness, so we update the cpu_target for
|
||||||
|
// here on down
|
||||||
|
cpu_target += "eb";
|
||||||
|
}
|
||||||
|
|
||||||
|
let custum_qemu_dir = env::var_os("CUSTOM_QEMU_DIR").map(|x| x.to_string_lossy().to_string());
|
||||||
|
let custum_qemu_no_build = env::var("CUSTOM_QEMU_NO_BUILD").is_ok();
|
||||||
|
let custum_qemu_no_configure = env::var("CUSTOM_QEMU_NO_CONFIGURE").is_ok();
|
||||||
|
println!("cargo:rerun-if-env-changed=CUSTOM_QEMU_DIR");
|
||||||
|
println!("cargo:rerun-if-env-changed=CUSTOM_QEMU_NO_BUILD");
|
||||||
|
println!("cargo:rerun-if-env-changed=CUSTOM_QEMU_NO_CONFIGURE");
|
||||||
|
|
||||||
|
let out_dir = env::var_os("OUT_DIR").unwrap();
|
||||||
|
let out_dir = out_dir.to_string_lossy().to_string();
|
||||||
|
let out_dir_path = Path::new(&out_dir);
|
||||||
|
let mut target_dir = out_dir_path.to_path_buf();
|
||||||
|
target_dir.pop();
|
||||||
|
target_dir.pop();
|
||||||
|
target_dir.pop();
|
||||||
|
|
||||||
|
build_dep_check(&["git", "make"]);
|
||||||
|
|
||||||
|
let qemu_path = if let Some(qemu_dir) = custum_qemu_dir.as_ref() {
|
||||||
|
Path::new(&qemu_dir).to_path_buf()
|
||||||
|
} else {
|
||||||
|
let qemu_path = target_dir.join(QEMU_DIRNAME);
|
||||||
|
|
||||||
|
let qemu_rev = target_dir.join("QEMU_REVISION");
|
||||||
|
if qemu_rev.exists()
|
||||||
|
&& fs::read_to_string(&qemu_rev).expect("Failed to read QEMU_REVISION") != QEMU_REVISION
|
||||||
|
{
|
||||||
|
drop(fs::remove_dir_all(&qemu_path));
|
||||||
|
}
|
||||||
|
|
||||||
|
if !qemu_path.is_dir() {
|
||||||
|
println!(
|
||||||
|
"cargo:warning=Qemu not found, cloning with git ({})...",
|
||||||
|
QEMU_REVISION
|
||||||
|
);
|
||||||
|
fs::create_dir_all(&qemu_path).unwrap();
|
||||||
|
Command::new("git")
|
||||||
|
.current_dir(&qemu_path)
|
||||||
|
.arg("init")
|
||||||
|
.status()
|
||||||
|
.unwrap();
|
||||||
|
Command::new("git")
|
||||||
|
.current_dir(&qemu_path)
|
||||||
|
.arg("remote")
|
||||||
|
.arg("add")
|
||||||
|
.arg("origin")
|
||||||
|
.arg(QEMU_URL)
|
||||||
|
.status()
|
||||||
|
.unwrap();
|
||||||
|
Command::new("git")
|
||||||
|
.current_dir(&qemu_path)
|
||||||
|
.arg("fetch")
|
||||||
|
.arg("--depth")
|
||||||
|
.arg("1")
|
||||||
|
.arg("origin")
|
||||||
|
.arg(QEMU_REVISION)
|
||||||
|
.status()
|
||||||
|
.unwrap();
|
||||||
|
Command::new("git")
|
||||||
|
.current_dir(&qemu_path)
|
||||||
|
.arg("checkout")
|
||||||
|
.arg("FETCH_HEAD")
|
||||||
|
.status()
|
||||||
|
.unwrap();
|
||||||
|
fs::write(&qemu_rev, QEMU_REVISION).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
qemu_path
|
||||||
|
};
|
||||||
|
|
||||||
|
let build_dir = qemu_path.join("build");
|
||||||
|
|
||||||
|
let target_suffix = if is_usermode {
|
||||||
|
"linux-user".to_string()
|
||||||
|
} else {
|
||||||
|
"softmmu".to_string()
|
||||||
|
};
|
||||||
|
|
||||||
|
let output_lib = if is_usermode {
|
||||||
|
build_dir.join(format!("libqemu-{cpu_target}.so"))
|
||||||
|
} else {
|
||||||
|
build_dir.join(format!("libqemu-system-{cpu_target}.so"))
|
||||||
|
};
|
||||||
|
|
||||||
|
println!("cargo:rerun-if-changed={}", output_lib.to_string_lossy());
|
||||||
|
|
||||||
|
if !output_lib.is_file() || (custum_qemu_dir.is_some() && !custum_qemu_no_build) {
|
||||||
|
/*drop(
|
||||||
|
Command::new("make")
|
||||||
|
.current_dir(&qemu_path)
|
||||||
|
.arg("distclean")
|
||||||
|
.status(),
|
||||||
|
);*/
|
||||||
|
if is_usermode && !custum_qemu_no_configure {
|
||||||
|
let mut cmd = Command::new("./configure");
|
||||||
|
cmd.current_dir(&qemu_path)
|
||||||
|
//.arg("--as-static-lib")
|
||||||
|
.arg("--as-shared-lib")
|
||||||
|
.arg(&format!("--target-list={cpu_target}-{target_suffix}"))
|
||||||
|
.args([
|
||||||
|
"--disable-blobs",
|
||||||
|
"--disable-bsd-user",
|
||||||
|
"--disable-fdt",
|
||||||
|
"--disable-system",
|
||||||
|
]);
|
||||||
|
if cfg!(feature = "debug_assertions") {
|
||||||
|
cmd.arg("--enable-debug");
|
||||||
|
}
|
||||||
|
cmd.status().expect("Configure failed");
|
||||||
|
} else if !custum_qemu_no_configure {
|
||||||
|
let mut cmd = Command::new("./configure");
|
||||||
|
cmd.current_dir(&qemu_path)
|
||||||
|
//.arg("--as-static-lib")
|
||||||
|
.arg("--as-shared-lib")
|
||||||
|
.arg(&format!("--target-list={cpu_target}-{target_suffix}"))
|
||||||
|
.arg(if cfg!(feature = "slirp") {
|
||||||
|
"--enable-slirp"
|
||||||
|
} else {
|
||||||
|
"--disable-slirp"
|
||||||
|
})
|
||||||
|
.arg("--enable-fdt=internal")
|
||||||
|
.arg("--audio-drv-list=")
|
||||||
|
.arg("--disable-alsa")
|
||||||
|
.arg("--disable-attr")
|
||||||
|
.arg("--disable-auth-pam")
|
||||||
|
.arg("--disable-dbus-display")
|
||||||
|
.arg("--disable-blobs")
|
||||||
|
.arg("--disable-bochs")
|
||||||
|
.arg("--disable-bpf")
|
||||||
|
.arg("--disable-brlapi")
|
||||||
|
.arg("--disable-bsd-user")
|
||||||
|
.arg("--disable-bzip2")
|
||||||
|
.arg("--disable-cap-ng")
|
||||||
|
.arg("--disable-canokey")
|
||||||
|
.arg("--disable-cloop")
|
||||||
|
.arg("--disable-cocoa")
|
||||||
|
.arg("--disable-coreaudio")
|
||||||
|
.arg("--disable-curl")
|
||||||
|
.arg("--disable-curses")
|
||||||
|
.arg("--disable-dmg")
|
||||||
|
.arg("--disable-docs")
|
||||||
|
.arg("--disable-dsound")
|
||||||
|
.arg("--disable-fuse")
|
||||||
|
.arg("--disable-fuse-lseek")
|
||||||
|
.arg("--disable-gcrypt")
|
||||||
|
.arg("--disable-gettext")
|
||||||
|
.arg("--disable-gio")
|
||||||
|
.arg("--disable-glusterfs")
|
||||||
|
.arg("--disable-gnutls")
|
||||||
|
.arg("--disable-gtk")
|
||||||
|
.arg("--disable-guest-agent")
|
||||||
|
.arg("--disable-guest-agent-msi")
|
||||||
|
.arg("--disable-hax")
|
||||||
|
.arg("--disable-hvf")
|
||||||
|
.arg("--disable-iconv")
|
||||||
|
.arg("--disable-jack")
|
||||||
|
.arg("--disable-keyring")
|
||||||
|
.arg("--disable-kvm")
|
||||||
|
.arg("--disable-libdaxctl")
|
||||||
|
.arg("--disable-libiscsi")
|
||||||
|
.arg("--disable-libnfs")
|
||||||
|
.arg("--disable-libpmem")
|
||||||
|
.arg("--disable-libssh")
|
||||||
|
.arg("--disable-libudev")
|
||||||
|
.arg("--disable-libusb")
|
||||||
|
.arg("--disable-linux-aio")
|
||||||
|
.arg("--disable-linux-io-uring")
|
||||||
|
.arg("--disable-linux-user")
|
||||||
|
.arg("--disable-live-block-migration")
|
||||||
|
.arg("--disable-lzfse")
|
||||||
|
.arg("--disable-lzo")
|
||||||
|
.arg("--disable-l2tpv3")
|
||||||
|
.arg("--disable-malloc-trim")
|
||||||
|
.arg("--disable-mpath")
|
||||||
|
.arg("--disable-multiprocess")
|
||||||
|
.arg("--disable-netmap")
|
||||||
|
.arg("--disable-nettle")
|
||||||
|
.arg("--disable-numa")
|
||||||
|
.arg("--disable-nvmm")
|
||||||
|
.arg("--disable-opengl")
|
||||||
|
.arg("--disable-oss")
|
||||||
|
.arg("--disable-pa")
|
||||||
|
.arg("--disable-parallels")
|
||||||
|
.arg("--disable-png")
|
||||||
|
.arg("--disable-pvrdma")
|
||||||
|
.arg("--disable-qcow1")
|
||||||
|
.arg("--disable-qed")
|
||||||
|
.arg("--disable-qga-vss")
|
||||||
|
.arg("--disable-rbd")
|
||||||
|
.arg("--disable-rdma")
|
||||||
|
.arg("--disable-replication")
|
||||||
|
.arg("--disable-sdl")
|
||||||
|
.arg("--disable-sdl-image")
|
||||||
|
.arg("--disable-seccomp")
|
||||||
|
.arg("--disable-selinux")
|
||||||
|
.arg("--disable-slirp-smbd")
|
||||||
|
.arg("--disable-smartcard")
|
||||||
|
.arg("--disable-snappy")
|
||||||
|
.arg("--disable-sparse")
|
||||||
|
.arg("--disable-spice")
|
||||||
|
.arg("--disable-spice-protocol")
|
||||||
|
.arg("--disable-tools")
|
||||||
|
.arg("--disable-tpm")
|
||||||
|
.arg("--disable-usb-redir")
|
||||||
|
.arg("--disable-user")
|
||||||
|
.arg("--disable-u2f")
|
||||||
|
.arg("--disable-vde")
|
||||||
|
.arg("--disable-vdi")
|
||||||
|
.arg("--disable-vduse-blk-export")
|
||||||
|
.arg("--disable-vhost-crypto")
|
||||||
|
.arg("--disable-vhost-kernel")
|
||||||
|
.arg("--disable-vhost-net")
|
||||||
|
.arg("--disable-vhost-user-blk-server")
|
||||||
|
.arg("--disable-vhost-vdpa")
|
||||||
|
.arg("--disable-virglrenderer")
|
||||||
|
.arg("--disable-virtfs")
|
||||||
|
.arg("--disable-virtiofsd")
|
||||||
|
.arg("--disable-vmnet")
|
||||||
|
.arg("--disable-vnc")
|
||||||
|
.arg("--disable-vnc-jpeg")
|
||||||
|
.arg("--disable-vnc-sasl")
|
||||||
|
.arg("--disable-vte")
|
||||||
|
.arg("--disable-vvfat")
|
||||||
|
.arg("--disable-whpx")
|
||||||
|
.arg("--disable-xen")
|
||||||
|
.arg("--disable-xen-pci-passthrough")
|
||||||
|
.arg("--disable-xkbcommon")
|
||||||
|
.arg("--disable-zstd");
|
||||||
|
if cfg!(feature = "debug_assertions") {
|
||||||
|
cmd.arg("--enable-debug");
|
||||||
|
}
|
||||||
|
cmd.status().expect("Configure failed");
|
||||||
|
}
|
||||||
|
if let Some(j) = jobs {
|
||||||
|
Command::new("make")
|
||||||
|
.current_dir(&build_dir)
|
||||||
|
.arg("-j")
|
||||||
|
.arg(&format!("{j}"))
|
||||||
|
.status()
|
||||||
|
.expect("Make failed");
|
||||||
|
} else {
|
||||||
|
Command::new("make")
|
||||||
|
.current_dir(&build_dir)
|
||||||
|
.arg("-j")
|
||||||
|
.status()
|
||||||
|
.expect("Make failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut objects = vec![];
|
||||||
|
for dir in &[
|
||||||
|
build_dir.join("libcommon.fa.p"),
|
||||||
|
build_dir.join(format!("libqemu-{cpu_target}-{target_suffix}.fa.p")),
|
||||||
|
] {
|
||||||
|
for path in fs::read_dir(dir).unwrap() {
|
||||||
|
let path = path.unwrap().path();
|
||||||
|
if path.is_file() {
|
||||||
|
if let Some(name) = path.file_name() {
|
||||||
|
if name.to_string_lossy().starts_with("stubs") {
|
||||||
|
continue;
|
||||||
|
} else if let Some(ext) = path.extension() {
|
||||||
|
if ext == "o" {
|
||||||
|
objects.push(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if is_usermode {
|
||||||
|
Command::new("ld")
|
||||||
|
.current_dir(out_dir_path)
|
||||||
|
.arg("-o")
|
||||||
|
.arg("libqemu-partially-linked.o")
|
||||||
|
.arg("-r")
|
||||||
|
.args(objects)
|
||||||
|
.arg("--start-group")
|
||||||
|
.arg("--whole-archive")
|
||||||
|
.arg(format!("{}/libhwcore.fa", build_dir.display()))
|
||||||
|
.arg(format!("{}/libqom.fa", build_dir.display()))
|
||||||
|
.arg(format!("{}/libevent-loop-base.a", build_dir.display()))
|
||||||
|
.arg("--no-whole-archive")
|
||||||
|
.arg(format!("{}/libqemuutil.a", build_dir.display()))
|
||||||
|
.arg(format!("{}/libhwcore.fa", build_dir.display()))
|
||||||
|
.arg(format!("{}/libqom.fa", build_dir.display()))
|
||||||
|
.arg(format!(
|
||||||
|
"--dynamic-list={}/plugins/qemu-plugins.symbols",
|
||||||
|
qemu_path.display()
|
||||||
|
))
|
||||||
|
.arg("--end-group")
|
||||||
|
.status()
|
||||||
|
.expect("Partial linked failure");
|
||||||
|
} else {
|
||||||
|
Command::new("ld")
|
||||||
|
.current_dir(out_dir_path)
|
||||||
|
.arg("-o")
|
||||||
|
.arg("libqemu-partially-linked.o")
|
||||||
|
.arg("-r")
|
||||||
|
.args(objects)
|
||||||
|
.arg("--start-group")
|
||||||
|
.arg("--whole-archive")
|
||||||
|
.arg(format!("{}/libhwcore.fa", build_dir.display()))
|
||||||
|
.arg(format!("{}/libqom.fa", build_dir.display()))
|
||||||
|
.arg(format!("{}/libevent-loop-base.a", build_dir.display()))
|
||||||
|
.arg(format!("{}/libio.fa", build_dir.display()))
|
||||||
|
.arg(format!("{}/libcrypto.fa", build_dir.display()))
|
||||||
|
.arg(format!("{}/libauthz.fa", build_dir.display()))
|
||||||
|
.arg(format!("{}/libblockdev.fa", build_dir.display()))
|
||||||
|
.arg(format!("{}/libblock.fa", build_dir.display()))
|
||||||
|
.arg(format!("{}/libchardev.fa", build_dir.display()))
|
||||||
|
.arg(format!("{}/libqmp.fa", build_dir.display()))
|
||||||
|
.arg("--no-whole-archive")
|
||||||
|
.arg(format!("{}/libqemuutil.a", build_dir.display()))
|
||||||
|
.arg(format!(
|
||||||
|
"{}/subprojects/libvhost-user/libvhost-user-glib.a",
|
||||||
|
build_dir.display()
|
||||||
|
))
|
||||||
|
.arg(format!(
|
||||||
|
"{}/subprojects/libvhost-user/libvhost-user.a",
|
||||||
|
build_dir.display()
|
||||||
|
))
|
||||||
|
.arg(format!(
|
||||||
|
"{}/subprojects/libvduse/libvduse.a",
|
||||||
|
build_dir.display()
|
||||||
|
))
|
||||||
|
.arg(format!("{}/libfdt.a", build_dir.display()))
|
||||||
|
.arg(format!("{}/libmigration.fa", build_dir.display()))
|
||||||
|
.arg(format!("{}/libhwcore.fa", build_dir.display()))
|
||||||
|
.arg(format!("{}/libqom.fa", build_dir.display()))
|
||||||
|
.arg(format!("{}/libio.fa", build_dir.display()))
|
||||||
|
.arg(format!("{}/libcrypto.fa", build_dir.display()))
|
||||||
|
.arg(format!("{}/libauthz.fa", build_dir.display()))
|
||||||
|
.arg(format!("{}/libblockdev.fa", build_dir.display()))
|
||||||
|
.arg(format!("{}/libblock.fa", build_dir.display()))
|
||||||
|
.arg(format!("{}/libchardev.fa", build_dir.display()))
|
||||||
|
.arg(format!("{}/libqmp.fa", build_dir.display()))
|
||||||
|
.arg(format!(
|
||||||
|
"--dynamic-list={}/plugins/qemu-plugins.symbols",
|
||||||
|
qemu_path.display()
|
||||||
|
))
|
||||||
|
.arg("--end-group")
|
||||||
|
.status()
|
||||||
|
.expect("Partial linked failure");
|
||||||
|
}
|
||||||
|
|
||||||
|
Command::new("ar")
|
||||||
|
.current_dir(out_dir_path)
|
||||||
|
.arg("crs")
|
||||||
|
.arg("libqemu-partially-linked.a")
|
||||||
|
.arg("libqemu-partially-linked.o")
|
||||||
|
.status()
|
||||||
|
.expect("Ar creation");
|
||||||
|
|
||||||
|
println!("cargo:rustc-link-search=native={out_dir}");
|
||||||
|
println!("cargo:rustc-link-lib=static=qemu-partially-linked");
|
||||||
|
println!("cargo:rustc-link-lib=rt");
|
||||||
|
println!("cargo:rustc-link-lib=gmodule-2.0");
|
||||||
|
println!("cargo:rustc-link-lib=glib-2.0");
|
||||||
|
println!("cargo:rustc-link-lib=stdc++");
|
||||||
|
println!("cargo:rustc-link-lib=z");
|
||||||
|
|
||||||
|
if !is_usermode {
|
||||||
|
println!("cargo:rustc-link-lib=pixman-1");
|
||||||
|
if env::var("LINK_SLIRP").is_ok() || cfg!(feature = "slirp") {
|
||||||
|
println!("cargo:rustc-link-lib=slirp");
|
||||||
|
}
|
||||||
|
|
||||||
|
fs::create_dir_all(target_dir.join("pc-bios")).unwrap();
|
||||||
|
for path in fs::read_dir(build_dir.join("pc-bios")).unwrap() {
|
||||||
|
let path = path.unwrap().path();
|
||||||
|
if path.is_file() {
|
||||||
|
if let Some(name) = path.file_name() {
|
||||||
|
fs::copy(&path, target_dir.join("pc-bios").join(name))
|
||||||
|
.expect("Failed to copy a pc-bios folder file");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(qemu_path, build_dir)
|
||||||
|
}
|
115
libafl_qemu/libafl_qemu_build/src/lib.rs
Normal file
115
libafl_qemu/libafl_qemu_build/src/lib.rs
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
use std::{
|
||||||
|
fs,
|
||||||
|
path::{Path, PathBuf},
|
||||||
|
};
|
||||||
|
|
||||||
|
mod bindings;
|
||||||
|
mod build;
|
||||||
|
|
||||||
|
pub use build::build;
|
||||||
|
|
||||||
|
pub fn build_with_bindings(
|
||||||
|
cpu_target: &str,
|
||||||
|
is_big_endian: bool,
|
||||||
|
is_usermode: bool,
|
||||||
|
jobs: Option<u32>,
|
||||||
|
bindings_file: &Path,
|
||||||
|
) {
|
||||||
|
println!("cargo:rerun-if-changed={}", bindings_file.display());
|
||||||
|
|
||||||
|
let (qemu_dir, build_dir) = build::build(cpu_target, is_big_endian, is_usermode, jobs);
|
||||||
|
let clang_args = qemu_bindgen_clang_args(&qemu_dir, &build_dir, cpu_target, is_usermode);
|
||||||
|
|
||||||
|
let bind = bindings::generate(&build_dir, cpu_target, clang_args)
|
||||||
|
.expect("Failed to generate the bindings");
|
||||||
|
bind.write_to_file(bindings_file)
|
||||||
|
.expect("Faield to write to the bindings file");
|
||||||
|
}
|
||||||
|
|
||||||
|
//linux-user_main.c.o libqemu-x86_64-linux-user.fa.p
|
||||||
|
|
||||||
|
fn qemu_bindgen_clang_args(
|
||||||
|
qemu_dir: &Path,
|
||||||
|
build_dir: &Path,
|
||||||
|
cpu_target: &str,
|
||||||
|
is_usermode: bool,
|
||||||
|
) -> Vec<String> {
|
||||||
|
// load compile commands
|
||||||
|
let compile_commands_string = &fs::read_to_string(build_dir.join("compile_commands.json"))
|
||||||
|
.expect("failed to read compile commands");
|
||||||
|
|
||||||
|
let compile_commands =
|
||||||
|
json::parse(compile_commands_string).expect("Failed to parse compile commands");
|
||||||
|
|
||||||
|
let (main_file, main_obj) = if is_usermode {
|
||||||
|
(
|
||||||
|
"/linux-user/main.c",
|
||||||
|
format!("libqemu-{cpu_target}-linux-user.fa.p/linux-user_main.c.o"),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
(
|
||||||
|
"/softmmu/main.c",
|
||||||
|
format!("qemu-system-{cpu_target}.p/softmmu_main.c.o"),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
// find main object
|
||||||
|
let entry = compile_commands
|
||||||
|
.members()
|
||||||
|
.find(|entry| {
|
||||||
|
entry["output"] == main_obj
|
||||||
|
|| entry["file"]
|
||||||
|
.as_str()
|
||||||
|
.map_or(false, |file| file.ends_with(main_file))
|
||||||
|
})
|
||||||
|
.expect("Didn't find compile command for qemu-system-arm");
|
||||||
|
|
||||||
|
// get main object build command
|
||||||
|
let command = entry["command"].as_str().expect("Command is a string");
|
||||||
|
|
||||||
|
// filter define and include args
|
||||||
|
let mut clang_args = vec![];
|
||||||
|
let mut include_arg = false;
|
||||||
|
for arg in shell_words::split(command)
|
||||||
|
.expect("failed to parse command")
|
||||||
|
.into_iter()
|
||||||
|
.skip(1)
|
||||||
|
{
|
||||||
|
if arg.starts_with("-D") {
|
||||||
|
clang_args.push(arg);
|
||||||
|
} else if let Some(incpath) = arg.strip_prefix("-I") {
|
||||||
|
clang_args.push(format!("-I{}", include_path(build_dir, incpath)));
|
||||||
|
} else if arg == "-iquote" || arg == "-isystem" {
|
||||||
|
include_arg = true;
|
||||||
|
clang_args.push(arg);
|
||||||
|
} else if include_arg {
|
||||||
|
include_arg = false;
|
||||||
|
clang_args.push(include_path(build_dir, &arg));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let target_arch_dir = match cpu_target {
|
||||||
|
"x86_64" => format!("-I{}/target/i386", qemu_dir.display()),
|
||||||
|
"aarch64" => format!("-I{}/target/arm", qemu_dir.display()),
|
||||||
|
_ => format!("-I{}/target/{cpu_target}", qemu_dir.display()),
|
||||||
|
};
|
||||||
|
|
||||||
|
// add include dirs
|
||||||
|
clang_args.push(format!("-I{}", qemu_dir.display()));
|
||||||
|
clang_args.push(format!("-I{}/include", qemu_dir.display()));
|
||||||
|
clang_args.push(format!("-I{}/quote", qemu_dir.display()));
|
||||||
|
clang_args.push(target_arch_dir);
|
||||||
|
|
||||||
|
clang_args
|
||||||
|
}
|
||||||
|
|
||||||
|
fn include_path(build_dir: &Path, path: &str) -> String {
|
||||||
|
let include_path = PathBuf::from(path);
|
||||||
|
|
||||||
|
if include_path.is_absolute() {
|
||||||
|
path.to_string()
|
||||||
|
} else {
|
||||||
|
// make include path absolute
|
||||||
|
build_dir.join(include_path).display().to_string()
|
||||||
|
}
|
||||||
|
}
|
9
libafl_qemu/libafl_qemu_build/src/main.rs
Normal file
9
libafl_qemu/libafl_qemu_build/src/main.rs
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
use libafl_qemu_build::build_with_bindings;
|
||||||
|
|
||||||
|
// RUST_BACKTRACE=1 OUT_DIR=/tmp/foo/a/b/c cargo run
|
||||||
|
fn main() {
|
||||||
|
let bfile = PathBuf::from("generated_qemu_bindings.rs");
|
||||||
|
build_with_bindings("arm", false, true, None, &bfile);
|
||||||
|
}
|
32
libafl_qemu/libafl_qemu_sys/Cargo.toml
Normal file
32
libafl_qemu/libafl_qemu_sys/Cargo.toml
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
[package]
|
||||||
|
name = "libafl_qemu_sys"
|
||||||
|
version = "0.8.2"
|
||||||
|
authors = ["Andrea Fioraldi <andreafioraldi@gmail.com>"]
|
||||||
|
description = "C to Rust bindings for the LibAFL QEMU bridge"
|
||||||
|
documentation = "https://docs.rs/libafl_qemu_sys"
|
||||||
|
repository = "https://github.com/AFLplusplus/LibAFL/"
|
||||||
|
readme = "../../../README.md"
|
||||||
|
license = "MIT OR Apache-2.0"
|
||||||
|
keywords = ["fuzzing", "qemu", "instrumentation"]
|
||||||
|
edition = "2021"
|
||||||
|
categories = ["development-tools::testing", "emulators", "embedded", "os", "no-std"]
|
||||||
|
|
||||||
|
[features]
|
||||||
|
# The following architecture features are mutually exclusive.
|
||||||
|
x86_64 = [] # build qemu for x86_64 (default)
|
||||||
|
i386 = [] # build qemu for i386
|
||||||
|
arm = [] # build qemu for arm
|
||||||
|
aarch64 = [] # build qemu for aarch64
|
||||||
|
be = []
|
||||||
|
|
||||||
|
usermode = []
|
||||||
|
systemmode = []
|
||||||
|
|
||||||
|
slirp = [ "systemmode", "libafl_qemu_build/slirp" ] # build qemu with host libslirp (for user networking)
|
||||||
|
|
||||||
|
clippy = [ "libafl_qemu_build/clippy" ] # special feature for clippy, don't use in normal projects§
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
libafl_qemu_build = { path = "../libafl_qemu_build", version = "0.8.2" }
|
13
libafl_qemu/libafl_qemu_sys/build.rs
Normal file
13
libafl_qemu/libafl_qemu_sys/build.rs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
mod host_specific {
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
include!("build_linux.rs");
|
||||||
|
|
||||||
|
#[cfg(not(target_os = "linux"))]
|
||||||
|
pub fn build() {
|
||||||
|
println!("cargo:warning=libafl_qemu_sys only builds on Linux hosts ATM");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
host_specific::build();
|
||||||
|
}
|
88
libafl_qemu/libafl_qemu_sys/build_linux.rs
Normal file
88
libafl_qemu/libafl_qemu_sys/build_linux.rs
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
use std::{env, fs::copy, path::PathBuf};
|
||||||
|
|
||||||
|
use libafl_qemu_build::build_with_bindings;
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! assert_unique_feature {
|
||||||
|
() => {};
|
||||||
|
($first:tt $(,$rest:tt)*) => {
|
||||||
|
$(
|
||||||
|
#[cfg(not(feature = "clippy"))] // ignore multiple definition for clippy
|
||||||
|
#[cfg(all(feature = $first, feature = $rest))]
|
||||||
|
compile_error!(concat!("features \"", $first, "\" and \"", $rest, "\" cannot be used together"));
|
||||||
|
)*
|
||||||
|
assert_unique_feature!($($rest),*);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build() {
|
||||||
|
// Make sure that exactly one qemu mode is set
|
||||||
|
assert_unique_feature!("usermode", "systemmode");
|
||||||
|
let emulation_mode = if cfg!(feature = "usermode") {
|
||||||
|
"usermode".to_string()
|
||||||
|
} else if cfg!(feature = "systemmode") {
|
||||||
|
"systemmode".to_string()
|
||||||
|
} else {
|
||||||
|
env::var("EMULATION_MODE").unwrap_or_else(|_| {
|
||||||
|
println!(
|
||||||
|
"cargo:warning=No emulation mode feature enabled or EMULATION_MODE env specified for libafl_qemu, supported: usermode, systemmmode - defaulting to usermode"
|
||||||
|
);
|
||||||
|
"usermode".to_string()
|
||||||
|
})
|
||||||
|
};
|
||||||
|
println!("cargo:rustc-cfg=emulation_mode=\"{emulation_mode}\"");
|
||||||
|
println!("cargo:rerun-if-env-changed=EMULATION_MODE");
|
||||||
|
|
||||||
|
println!("cargo:rerun-if-changed=build.rs");
|
||||||
|
println!("cargo:rerun-if-changed=build_linux.rs");
|
||||||
|
|
||||||
|
// Make sure we have at most one architecutre feature set
|
||||||
|
// Else, we default to `x86_64` - having a default makes CI easier :)
|
||||||
|
assert_unique_feature!("arm", "aarch64", "i386", "i86_64");
|
||||||
|
|
||||||
|
// Make sure that we don't have BE set for any architecture other than arm
|
||||||
|
// Sure aarch64 may support BE, but its not in common usage and we don't
|
||||||
|
// need it yet and so haven't tested it
|
||||||
|
assert_unique_feature!("be", "aarch64", "i386", "i86_64");
|
||||||
|
|
||||||
|
let cpu_target = if cfg!(feature = "x86_64") {
|
||||||
|
"x86_64".to_string()
|
||||||
|
} else if cfg!(feature = "arm") {
|
||||||
|
"arm".to_string()
|
||||||
|
} else if cfg!(feature = "aarch64") {
|
||||||
|
"aarch64".to_string()
|
||||||
|
} else if cfg!(feature = "i386") {
|
||||||
|
"i386".to_string()
|
||||||
|
} else {
|
||||||
|
env::var("CPU_TARGET").unwrap_or_else(|_| {
|
||||||
|
println!(
|
||||||
|
"cargo:warning=No architecture feature enabled or CPU_TARGET env specified for libafl_qemu, supported: arm, aarch64, i386, x86_64 - defaulting to x86_64"
|
||||||
|
);
|
||||||
|
"x86_64".to_string()
|
||||||
|
})
|
||||||
|
};
|
||||||
|
println!("cargo:rerun-if-env-changed=CPU_TARGET");
|
||||||
|
println!("cargo:rustc-cfg=cpu_target=\"{cpu_target}\"");
|
||||||
|
|
||||||
|
let jobs = env::var("NUM_JOBS")
|
||||||
|
.ok()
|
||||||
|
.map(|x| str::parse::<u32>(&x).expect("The number of jobs is not a valid integer!"));
|
||||||
|
|
||||||
|
let out_dir = env::var("OUT_DIR").unwrap();
|
||||||
|
let out_dir = PathBuf::from(out_dir);
|
||||||
|
let bindings_file = out_dir.join("bindings.rs");
|
||||||
|
|
||||||
|
if std::env::var("DOCS_RS").is_ok() || cfg!(feature = "clippy") {
|
||||||
|
// Only build when we're not generating docs and not in clippy
|
||||||
|
copy("src/x86_64_stub_bindings.rs", bindings_file).expect("Failed to copy the bindings stub");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
build_with_bindings(
|
||||||
|
&cpu_target,
|
||||||
|
cfg!(feature = "be"),
|
||||||
|
emulation_mode == "usermode",
|
||||||
|
jobs,
|
||||||
|
&bindings_file,
|
||||||
|
);
|
||||||
|
}
|
38
libafl_qemu/libafl_qemu_sys/src/lib.rs
Normal file
38
libafl_qemu/libafl_qemu_sys/src/lib.rs
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
#![allow(non_upper_case_globals)]
|
||||||
|
#![allow(non_camel_case_types)]
|
||||||
|
#![allow(non_snake_case)]
|
||||||
|
#![allow(improper_ctypes)]
|
||||||
|
#![allow(unused_mut)]
|
||||||
|
#![allow(clippy::all)]
|
||||||
|
#![allow(clippy::pedantic)]
|
||||||
|
|
||||||
|
#[cfg(all(not(feature = "clippy"), target_os = "linux"))]
|
||||||
|
include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
|
||||||
|
|
||||||
|
#[cfg(all(feature = "clippy", target_os = "linux"))]
|
||||||
|
mod x86_64_stub_bindings;
|
||||||
|
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
use core::ops::BitAnd;
|
||||||
|
|
||||||
|
#[cfg(all(feature = "clippy", target_os = "linux"))]
|
||||||
|
pub use x86_64_stub_bindings::*;
|
||||||
|
|
||||||
|
// from include/exec/memop.h
|
||||||
|
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
pub fn memop_size(op: MemOp) -> u32 {
|
||||||
|
1 << op.bitand(MemOp_MO_SIZE).0
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
pub fn memop_big_endian(op: MemOp) -> bool {
|
||||||
|
op.bitand(MemOp_MO_BSWAP) == MemOp_MO_BE
|
||||||
|
}
|
||||||
|
|
||||||
|
// from include/qemu/plugin.h
|
||||||
|
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
pub fn make_plugin_meminfo(oi: MemOpIdx, rw: qemu_plugin_mem_rw) -> qemu_plugin_meminfo_t {
|
||||||
|
oi | (rw.0 << 16)
|
||||||
|
}
|
11801
libafl_qemu/libafl_qemu_sys/src/x86_64_stub_bindings.rs
Normal file
11801
libafl_qemu/libafl_qemu_sys/src/x86_64_stub_bindings.rs
Normal file
File diff suppressed because it is too large
Load Diff
@ -14,7 +14,7 @@ use meminterval::{Interval, IntervalTree};
|
|||||||
use num_enum::{IntoPrimitive, TryFromPrimitive};
|
use num_enum::{IntoPrimitive, TryFromPrimitive};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
emu::{Emulator, SyscallHookResult},
|
emu::{Emulator, MemAccessInfo, SyscallHookResult},
|
||||||
helper::{QemuHelper, QemuHelperTuple, QemuInstrumentationFilter},
|
helper::{QemuHelper, QemuHelperTuple, QemuInstrumentationFilter},
|
||||||
hooks::QemuHooks,
|
hooks::QemuHooks,
|
||||||
GuestAddr,
|
GuestAddr,
|
||||||
@ -185,6 +185,7 @@ impl AsanGiovese {
|
|||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
|
#[allow(clippy::cast_sign_loss)]
|
||||||
pub fn is_invalid_access(emu: &Emulator, addr: GuestAddr, n: usize) -> bool {
|
pub fn is_invalid_access(emu: &Emulator, addr: GuestAddr, n: usize) -> bool {
|
||||||
unsafe {
|
unsafe {
|
||||||
if n == 0 {
|
if n == 0 {
|
||||||
@ -236,6 +237,7 @@ impl AsanGiovese {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
#[allow(clippy::cast_sign_loss)]
|
||||||
pub fn poison(&mut self, emu: &Emulator, addr: GuestAddr, n: usize, poison_byte: i8) -> bool {
|
pub fn poison(&mut self, emu: &Emulator, addr: GuestAddr, n: usize, poison_byte: i8) -> bool {
|
||||||
unsafe {
|
unsafe {
|
||||||
if n == 0 {
|
if n == 0 {
|
||||||
@ -281,6 +283,7 @@ impl AsanGiovese {
|
|||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
#[allow(clippy::must_use_candidate)]
|
#[allow(clippy::must_use_candidate)]
|
||||||
|
#[allow(clippy::cast_sign_loss)]
|
||||||
pub fn unpoison(emu: &Emulator, addr: GuestAddr, n: usize) -> bool {
|
pub fn unpoison(emu: &Emulator, addr: GuestAddr, n: usize) -> bool {
|
||||||
unsafe {
|
unsafe {
|
||||||
let n = n as isize;
|
let n = n as isize;
|
||||||
@ -719,7 +722,7 @@ pub fn gen_readwrite_asan<QT, S>(
|
|||||||
hooks: &mut QemuHooks<'_, QT, S>,
|
hooks: &mut QemuHooks<'_, QT, S>,
|
||||||
_state: Option<&mut S>,
|
_state: Option<&mut S>,
|
||||||
pc: GuestAddr,
|
pc: GuestAddr,
|
||||||
_size: usize,
|
_info: MemAccessInfo,
|
||||||
) -> Option<u64>
|
) -> Option<u64>
|
||||||
where
|
where
|
||||||
S: UsesInput,
|
S: UsesInput,
|
||||||
@ -732,6 +735,7 @@ where
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn trace_read1_asan<QT, S>(
|
pub fn trace_read1_asan<QT, S>(
|
||||||
hooks: &mut QemuHooks<'_, QT, S>,
|
hooks: &mut QemuHooks<'_, QT, S>,
|
||||||
_state: Option<&mut S>,
|
_state: Option<&mut S>,
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
//! Expose QEMU user `LibAFL` C api to Rust
|
//! Expose QEMU user `LibAFL` C api to Rust
|
||||||
|
|
||||||
use core::mem::MaybeUninit;
|
|
||||||
use core::{
|
use core::{
|
||||||
convert::Into,
|
convert::Into,
|
||||||
ffi::c_void,
|
ffi::c_void,
|
||||||
|
mem::MaybeUninit,
|
||||||
ptr::{addr_of, copy_nonoverlapping, null},
|
ptr::{addr_of, copy_nonoverlapping, null},
|
||||||
};
|
};
|
||||||
#[cfg(emulation_mode = "systemmode")]
|
#[cfg(emulation_mode = "systemmode")]
|
||||||
@ -16,23 +16,79 @@ use num_enum::{IntoPrimitive, TryFromPrimitive};
|
|||||||
use num_traits::Num;
|
use num_traits::Num;
|
||||||
use strum_macros::EnumIter;
|
use strum_macros::EnumIter;
|
||||||
|
|
||||||
#[cfg(not(any(cpu_target = "x86_64", cpu_target = "aarch64")))]
|
pub type GuestAddr = libafl_qemu_sys::target_ulong;
|
||||||
/// `GuestAddr` is u32 for 32-bit targets
|
pub type GuestUsize = libafl_qemu_sys::target_ulong;
|
||||||
pub type GuestAddr = u32;
|
pub type GuestIsize = libafl_qemu_sys::target_long;
|
||||||
|
pub type GuestVirtAddr = libafl_qemu_sys::hwaddr;
|
||||||
|
pub type GuestPhysAddr = libafl_qemu_sys::hwaddr;
|
||||||
|
|
||||||
#[cfg(any(cpu_target = "x86_64", cpu_target = "aarch64"))]
|
pub type GuestHwAddrInfo = libafl_qemu_sys::qemu_plugin_hwaddr;
|
||||||
/// `GuestAddr` is u64 for 64-bit targets
|
|
||||||
pub type GuestAddr = u64;
|
|
||||||
|
|
||||||
pub type GuestUsize = GuestAddr;
|
#[repr(transparent)]
|
||||||
|
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
|
||||||
|
pub struct MemAccessInfo {
|
||||||
|
oi: libafl_qemu_sys::MemOpIdx,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MemAccessInfo {
|
||||||
|
#[must_use]
|
||||||
|
pub fn memop(&self) -> libafl_qemu_sys::MemOp {
|
||||||
|
libafl_qemu_sys::MemOp(self.oi >> 4)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn memopidx(&self) -> libafl_qemu_sys::MemOpIdx {
|
||||||
|
self.oi
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn mmu_index(&self) -> u32 {
|
||||||
|
self.oi & 15
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn size(&self) -> usize {
|
||||||
|
libafl_qemu_sys::memop_size(self.memop()) as usize
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn is_big_endian(&self) -> bool {
|
||||||
|
libafl_qemu_sys::memop_big_endian(self.memop())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn encode_with(&self, other: u32) -> u64 {
|
||||||
|
(u64::from(self.oi) << 32) | u64::from(other)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn decode_from(encoded: u64) -> (Self, u32) {
|
||||||
|
let low = (encoded & 0xFFFFFFFF) as u32;
|
||||||
|
let high = (encoded >> 32) as u32;
|
||||||
|
(Self { oi: high }, low)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn new(oi: libafl_qemu_sys::MemOpIdx) -> Self {
|
||||||
|
Self { oi }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<libafl_qemu_sys::MemOpIdx> for MemAccessInfo {
|
||||||
|
fn from(oi: libafl_qemu_sys::MemOpIdx) -> Self {
|
||||||
|
Self { oi }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "python")]
|
#[cfg(feature = "python")]
|
||||||
use pyo3::{prelude::*, PyIterProtocol};
|
use pyo3::{prelude::*, PyIterProtocol};
|
||||||
|
|
||||||
pub const SKIP_EXEC_HOOK: u64 = u64::MAX;
|
pub const SKIP_EXEC_HOOK: u64 = u64::MAX;
|
||||||
|
|
||||||
type CPUStatePtr = *mut c_void;
|
pub use libafl_qemu_sys::{CPUArchState, CPUState};
|
||||||
type CPUArchStatePtr = *mut c_void;
|
|
||||||
|
pub type CPUStatePtr = *mut libafl_qemu_sys::CPUState;
|
||||||
|
pub type CPUArchStatePtr = *mut libafl_qemu_sys::CPUArchState;
|
||||||
|
|
||||||
#[derive(IntoPrimitive, TryFromPrimitive, Debug, Clone, Copy, EnumIter, PartialEq, Eq)]
|
#[derive(IntoPrimitive, TryFromPrimitive, Debug, Clone, Copy, EnumIter, PartialEq, Eq)]
|
||||||
#[repr(i32)]
|
#[repr(i32)]
|
||||||
@ -197,16 +253,6 @@ extern "C" {
|
|||||||
fn libafl_get_brk() -> u64;
|
fn libafl_get_brk() -> u64;
|
||||||
fn libafl_set_brk(brk: u64) -> u64;
|
fn libafl_set_brk(brk: u64) -> u64;
|
||||||
|
|
||||||
/// abi_long target_mmap(abi_ulong start, abi_ulong len, int target_prot, int flags, int fd, abi_ulong offset)
|
|
||||||
fn target_mmap(start: u64, len: u64, target_prot: i32, flags: i32, fd: i32, offset: u64)
|
|
||||||
-> u64;
|
|
||||||
|
|
||||||
/// int target_mprotect(abi_ulong start, abi_ulong len, int prot)
|
|
||||||
fn target_mprotect(start: u64, len: u64, target_prot: i32) -> i32;
|
|
||||||
|
|
||||||
/// int target_munmap(abi_ulong start, abi_ulong len)
|
|
||||||
fn target_munmap(start: u64, len: u64) -> i32;
|
|
||||||
|
|
||||||
fn read_self_maps() -> *const c_void;
|
fn read_self_maps() -> *const c_void;
|
||||||
fn free_self_maps(map_info: *const c_void);
|
fn free_self_maps(map_info: *const c_void);
|
||||||
|
|
||||||
@ -232,17 +278,6 @@ extern "C" {
|
|||||||
fn qemu_main_loop();
|
fn qemu_main_loop();
|
||||||
fn qemu_cleanup();
|
fn qemu_cleanup();
|
||||||
|
|
||||||
// int cpu_memory_rw_debug(CPUState *cpu, target_ulong addr,
|
|
||||||
// uint8_t *buf, int len, int is_write);
|
|
||||||
fn cpu_memory_rw_debug(
|
|
||||||
cpu: CPUStatePtr,
|
|
||||||
addr: GuestAddr,
|
|
||||||
buf: *mut u8,
|
|
||||||
len: i32,
|
|
||||||
is_write: i32,
|
|
||||||
);
|
|
||||||
fn cpu_physical_memory_rw(addr: GuestAddr, buf: *mut u8, len: i32, iswrite: bool);
|
|
||||||
|
|
||||||
fn libafl_save_qemu_snapshot(name: *const u8, sync: bool);
|
fn libafl_save_qemu_snapshot(name: *const u8, sync: bool);
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
fn libafl_load_qemu_snapshot(name: *const u8, sync: bool);
|
fn libafl_load_qemu_snapshot(name: *const u8, sync: bool);
|
||||||
@ -256,7 +291,8 @@ extern "C" fn qemu_cleanup_atexit() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
fn libafl_qemu_arch_state_size() -> usize;
|
//static libafl_page_size: GuestUsize;
|
||||||
|
fn libafl_page_from_addr(addr: GuestAddr) -> GuestAddr;
|
||||||
|
|
||||||
// CPUState* libafl_qemu_get_cpu(int cpu_index);
|
// CPUState* libafl_qemu_get_cpu(int cpu_index);
|
||||||
fn libafl_qemu_get_cpu(cpu_index: i32) -> CPUStatePtr;
|
fn libafl_qemu_get_cpu(cpu_index: i32) -> CPUStatePtr;
|
||||||
@ -309,7 +345,7 @@ extern "C" {
|
|||||||
// void (*exec_n)(uint64_t id, target_ulong addr, size_t size, uint64_t data),
|
// void (*exec_n)(uint64_t id, target_ulong addr, size_t size, uint64_t data),
|
||||||
// uint64_t data);
|
// uint64_t data);
|
||||||
fn libafl_add_read_hook(
|
fn libafl_add_read_hook(
|
||||||
gen: Option<extern "C" fn(GuestAddr, usize, u64) -> u64>,
|
gen: Option<extern "C" fn(GuestAddr, MemAccessInfo, u64) -> u64>,
|
||||||
exec1: Option<extern "C" fn(u64, GuestAddr, u64)>,
|
exec1: Option<extern "C" fn(u64, GuestAddr, u64)>,
|
||||||
exec2: Option<extern "C" fn(u64, GuestAddr, u64)>,
|
exec2: Option<extern "C" fn(u64, GuestAddr, u64)>,
|
||||||
exec4: Option<extern "C" fn(u64, GuestAddr, u64)>,
|
exec4: Option<extern "C" fn(u64, GuestAddr, u64)>,
|
||||||
@ -326,7 +362,7 @@ extern "C" {
|
|||||||
// void (*exec_n)(uint64_t id, target_ulong addr, size_t size, uint64_t data),
|
// void (*exec_n)(uint64_t id, target_ulong addr, size_t size, uint64_t data),
|
||||||
// uint64_t data);
|
// uint64_t data);
|
||||||
fn libafl_add_write_hook(
|
fn libafl_add_write_hook(
|
||||||
gen: Option<extern "C" fn(GuestAddr, usize, u64) -> u64>,
|
gen: Option<extern "C" fn(GuestAddr, MemAccessInfo, u64) -> u64>,
|
||||||
exec1: Option<extern "C" fn(u64, GuestAddr, u64)>,
|
exec1: Option<extern "C" fn(u64, GuestAddr, u64)>,
|
||||||
exec2: Option<extern "C" fn(u64, GuestAddr, u64)>,
|
exec2: Option<extern "C" fn(u64, GuestAddr, u64)>,
|
||||||
exec4: Option<extern "C" fn(u64, GuestAddr, u64)>,
|
exec4: Option<extern "C" fn(u64, GuestAddr, u64)>,
|
||||||
@ -359,11 +395,6 @@ extern "C" {
|
|||||||
data: *const (),
|
data: *const (),
|
||||||
);
|
);
|
||||||
fn libafl_qemu_gdb_reply(buf: *const u8, len: usize);
|
fn libafl_qemu_gdb_reply(buf: *const u8, len: usize);
|
||||||
|
|
||||||
fn libafl_qemu_cpu_arch_state(cpu: CPUStatePtr) -> CPUArchStatePtr;
|
|
||||||
// fn libafl_qemu_arch_state_cpu(env: CPUArchStatePtr) -> CPUStatePtr;
|
|
||||||
|
|
||||||
fn cpu_reset(cpu: CPUStatePtr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(emulation_mode = "usermode")]
|
#[cfg(emulation_mode = "usermode")]
|
||||||
@ -444,28 +475,8 @@ extern "C" fn gdb_cmd(buf: *const u8, len: usize, data: *const ()) -> i32 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct SavedCPUState {
|
|
||||||
data: Vec<u8>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SavedCPUState {
|
|
||||||
#[must_use]
|
|
||||||
#[allow(clippy::uninit_vec)]
|
|
||||||
fn uninit() -> Self {
|
|
||||||
unsafe {
|
|
||||||
let len = libafl_qemu_arch_state_size();
|
|
||||||
let mut data = Vec::with_capacity(len);
|
|
||||||
data.set_len(len);
|
|
||||||
|
|
||||||
Self { data }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
#[repr(C)]
|
#[repr(transparent)]
|
||||||
pub struct CPU {
|
pub struct CPU {
|
||||||
ptr: CPUStatePtr,
|
ptr: CPUStatePtr,
|
||||||
}
|
}
|
||||||
@ -501,6 +512,53 @@ impl CPU {
|
|||||||
unsafe { (addr as usize - guest_base) as GuestAddr }
|
unsafe { (addr as usize - guest_base) as GuestAddr }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(emulation_mode = "systemmode")]
|
||||||
|
#[must_use]
|
||||||
|
pub fn get_phys_addr(&self, vaddr: GuestAddr) -> Option<GuestPhysAddr> {
|
||||||
|
unsafe {
|
||||||
|
let page = libafl_page_from_addr(vaddr);
|
||||||
|
let mut attrs = MaybeUninit::<libafl_qemu_sys::MemTxAttrs>::uninit();
|
||||||
|
let paddr = libafl_qemu_sys::cpu_get_phys_page_attrs_debug(
|
||||||
|
self.ptr,
|
||||||
|
page as GuestVirtAddr,
|
||||||
|
attrs.as_mut_ptr(),
|
||||||
|
);
|
||||||
|
if paddr == (-1i64 as GuestPhysAddr) {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(paddr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(emulation_mode = "systemmode")]
|
||||||
|
#[must_use]
|
||||||
|
pub fn get_phys_addr_tlb(
|
||||||
|
&self,
|
||||||
|
vaddr: GuestAddr,
|
||||||
|
info: MemAccessInfo,
|
||||||
|
is_store: bool,
|
||||||
|
) -> Option<GuestPhysAddr> {
|
||||||
|
unsafe {
|
||||||
|
let pminfo = libafl_qemu_sys::make_plugin_meminfo(
|
||||||
|
info.oi,
|
||||||
|
if is_store {
|
||||||
|
libafl_qemu_sys::qemu_plugin_mem_rw_QEMU_PLUGIN_MEM_W
|
||||||
|
} else {
|
||||||
|
libafl_qemu_sys::qemu_plugin_mem_rw_QEMU_PLUGIN_MEM_R
|
||||||
|
},
|
||||||
|
);
|
||||||
|
let phwaddr = libafl_qemu_sys::qemu_plugin_get_hwaddr(pminfo, vaddr as GuestVirtAddr);
|
||||||
|
if phwaddr.is_null() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(libafl_qemu_sys::qemu_plugin_hwaddr_phys_addr(phwaddr) as GuestPhysAddr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO expose tlb_set_dirty and tlb_reset_dirty
|
||||||
|
|
||||||
/// Write a value to a guest address.
|
/// Write a value to a guest address.
|
||||||
///
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
@ -513,8 +571,15 @@ impl CPU {
|
|||||||
let host_addr = Emulator::new_empty().g2h(addr);
|
let host_addr = Emulator::new_empty().g2h(addr);
|
||||||
copy_nonoverlapping(buf.as_ptr(), host_addr, buf.len());
|
copy_nonoverlapping(buf.as_ptr(), host_addr, buf.len());
|
||||||
}
|
}
|
||||||
|
// TODO use gdbstub's target_cpu_memory_rw_debug
|
||||||
#[cfg(emulation_mode = "systemmode")]
|
#[cfg(emulation_mode = "systemmode")]
|
||||||
cpu_memory_rw_debug(self.ptr, addr, buf.as_ptr() as *mut u8, buf.len() as i32, 1);
|
libafl_qemu_sys::cpu_memory_rw_debug(
|
||||||
|
self.ptr,
|
||||||
|
addr as GuestVirtAddr,
|
||||||
|
buf.as_ptr() as *mut _,
|
||||||
|
buf.len(),
|
||||||
|
true,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Read a value from a guest address.
|
/// Read a value from a guest address.
|
||||||
@ -529,8 +594,15 @@ impl CPU {
|
|||||||
let host_addr = Emulator::new_empty().g2h(addr);
|
let host_addr = Emulator::new_empty().g2h(addr);
|
||||||
copy_nonoverlapping(host_addr, buf.as_mut_ptr(), buf.len());
|
copy_nonoverlapping(host_addr, buf.as_mut_ptr(), buf.len());
|
||||||
}
|
}
|
||||||
|
// TODO use gdbstub's target_cpu_memory_rw_debug
|
||||||
#[cfg(emulation_mode = "systemmode")]
|
#[cfg(emulation_mode = "systemmode")]
|
||||||
cpu_memory_rw_debug(self.ptr, addr, buf.as_mut_ptr(), buf.len() as i32, 0);
|
libafl_qemu_sys::cpu_memory_rw_debug(
|
||||||
|
self.ptr,
|
||||||
|
addr as GuestVirtAddr,
|
||||||
|
buf.as_mut_ptr() as *mut _,
|
||||||
|
buf.len(),
|
||||||
|
false,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
@ -568,31 +640,28 @@ impl CPU {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn reset(&self) {
|
pub fn reset(&self) {
|
||||||
unsafe { cpu_reset(self.ptr) };
|
unsafe { libafl_qemu_sys::cpu_reset(self.ptr) };
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn save_state(&self) -> SavedCPUState {
|
pub fn save_state(&self) -> CPUArchState {
|
||||||
let mut saved = SavedCPUState::uninit();
|
|
||||||
unsafe {
|
unsafe {
|
||||||
copy_nonoverlapping(
|
let mut saved = MaybeUninit::<CPUArchState>::uninit();
|
||||||
libafl_qemu_cpu_arch_state(self.ptr) as *mut u8,
|
copy_nonoverlapping(self.ptr.as_mut().unwrap().env_ptr, saved.as_mut_ptr(), 1);
|
||||||
saved.data.as_mut_ptr(),
|
saved.assume_init()
|
||||||
saved.data.len(),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
saved
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn restore_state(&self, saved: &SavedCPUState) {
|
pub fn restore_state(&self, saved: &CPUArchState) {
|
||||||
unsafe {
|
unsafe {
|
||||||
copy_nonoverlapping(
|
copy_nonoverlapping(saved, self.ptr.as_mut().unwrap().env_ptr, 1);
|
||||||
saved.data.as_ptr(),
|
|
||||||
libafl_qemu_cpu_arch_state(self.ptr) as *mut u8,
|
|
||||||
saved.data.len(),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn raw_ptr(&self) -> CPUStatePtr {
|
||||||
|
self.ptr
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static mut EMULATOR_IS_INITIALIZED: bool = false;
|
static mut EMULATOR_IS_INITIALIZED: bool = false;
|
||||||
@ -684,6 +753,16 @@ impl Emulator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn page_from_addr(addr: GuestAddr) -> GuestAddr {
|
||||||
|
unsafe { libafl_page_from_addr(addr) }
|
||||||
|
}
|
||||||
|
|
||||||
|
//#[must_use]
|
||||||
|
/*pub fn page_size() -> GuestUsize {
|
||||||
|
unsafe { libafl_page_size }
|
||||||
|
}*/
|
||||||
|
|
||||||
#[cfg(emulation_mode = "usermode")]
|
#[cfg(emulation_mode = "usermode")]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn g2h<T>(&self, addr: GuestAddr) -> *mut T {
|
pub fn g2h<T>(&self, addr: GuestAddr) -> *mut T {
|
||||||
@ -710,15 +789,24 @@ impl Emulator {
|
|||||||
|
|
||||||
/// Write a value to a phsical guest address, including ROM areas.
|
/// Write a value to a phsical guest address, including ROM areas.
|
||||||
#[cfg(emulation_mode = "systemmode")]
|
#[cfg(emulation_mode = "systemmode")]
|
||||||
pub unsafe fn write_phys_mem(&self, addr: GuestAddr, buf: &[u8]) {
|
pub unsafe fn write_phys_mem(&self, paddr: GuestPhysAddr, buf: &[u8]) {
|
||||||
cpu_physical_memory_rw(addr, buf.as_ptr() as *mut u8, buf.len() as i32, true);
|
libafl_qemu_sys::cpu_physical_memory_rw(
|
||||||
|
paddr,
|
||||||
|
buf.as_ptr() as *mut _,
|
||||||
|
buf.len() as u64,
|
||||||
|
true,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Read a value from a physical guest address.
|
/// Read a value from a physical guest address.
|
||||||
#[cfg(emulation_mode = "systemmode")]
|
#[cfg(emulation_mode = "systemmode")]
|
||||||
pub unsafe fn read_phys_mem(&self, addr: GuestAddr, buf: &mut [u8]) {
|
pub unsafe fn read_phys_mem(&self, paddr: GuestPhysAddr, buf: &mut [u8]) {
|
||||||
#[cfg(emulation_mode = "systemmode")]
|
libafl_qemu_sys::cpu_physical_memory_rw(
|
||||||
cpu_physical_memory_rw(addr, buf.as_mut_ptr(), buf.len() as i32, false);
|
paddr,
|
||||||
|
buf.as_mut_ptr() as *mut _,
|
||||||
|
buf.len() as u64,
|
||||||
|
false,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
@ -819,18 +907,21 @@ impl Emulator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(emulation_mode = "usermode")]
|
#[cfg(emulation_mode = "usermode")]
|
||||||
|
#[allow(clippy::cast_sign_loss)]
|
||||||
fn mmap(
|
fn mmap(
|
||||||
&self,
|
&self,
|
||||||
addr: GuestAddr,
|
addr: GuestAddr,
|
||||||
size: usize,
|
size: usize,
|
||||||
perms: MmapPerms,
|
perms: MmapPerms,
|
||||||
flags: c_int,
|
flags: c_int,
|
||||||
) -> Result<u64, ()> {
|
) -> Result<GuestAddr, ()> {
|
||||||
let res = unsafe { target_mmap(addr.into(), size as u64, perms.into(), flags, -1, 0) };
|
let res = unsafe {
|
||||||
if res == 0 {
|
libafl_qemu_sys::target_mmap(addr, size as GuestUsize, perms.into(), flags, -1, 0)
|
||||||
|
};
|
||||||
|
if res <= 0 {
|
||||||
Err(())
|
Err(())
|
||||||
} else {
|
} else {
|
||||||
Ok(res)
|
Ok(res as GuestAddr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -865,7 +956,9 @@ impl Emulator {
|
|||||||
|
|
||||||
#[cfg(emulation_mode = "usermode")]
|
#[cfg(emulation_mode = "usermode")]
|
||||||
pub fn mprotect(&self, addr: GuestAddr, size: usize, perms: MmapPerms) -> Result<(), String> {
|
pub fn mprotect(&self, addr: GuestAddr, size: usize, perms: MmapPerms) -> Result<(), String> {
|
||||||
let res = unsafe { target_mprotect(addr.into(), size as u64, perms.into()) };
|
let res = unsafe {
|
||||||
|
libafl_qemu_sys::target_mprotect(addr.into(), size as GuestUsize, perms.into())
|
||||||
|
};
|
||||||
if res == 0 {
|
if res == 0 {
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
@ -875,7 +968,7 @@ impl Emulator {
|
|||||||
|
|
||||||
#[cfg(emulation_mode = "usermode")]
|
#[cfg(emulation_mode = "usermode")]
|
||||||
pub fn unmap(&self, addr: GuestAddr, size: usize) -> Result<(), String> {
|
pub fn unmap(&self, addr: GuestAddr, size: usize) -> Result<(), String> {
|
||||||
if unsafe { target_munmap(addr.into(), size as u64) } == 0 {
|
if unsafe { libafl_qemu_sys::target_munmap(addr.into(), size as GuestUsize) } == 0 {
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err(format!("Failed to unmap {addr}"))
|
Err(format!("Failed to unmap {addr}"))
|
||||||
@ -908,7 +1001,7 @@ impl Emulator {
|
|||||||
|
|
||||||
pub fn add_read_hooks(
|
pub fn add_read_hooks(
|
||||||
&self,
|
&self,
|
||||||
gen: Option<extern "C" fn(GuestAddr, usize, u64) -> u64>,
|
gen: Option<extern "C" fn(GuestAddr, MemAccessInfo, u64) -> u64>,
|
||||||
exec1: Option<extern "C" fn(u64, GuestAddr, u64)>,
|
exec1: Option<extern "C" fn(u64, GuestAddr, u64)>,
|
||||||
exec2: Option<extern "C" fn(u64, GuestAddr, u64)>,
|
exec2: Option<extern "C" fn(u64, GuestAddr, u64)>,
|
||||||
exec4: Option<extern "C" fn(u64, GuestAddr, u64)>,
|
exec4: Option<extern "C" fn(u64, GuestAddr, u64)>,
|
||||||
@ -919,9 +1012,10 @@ impl Emulator {
|
|||||||
unsafe { libafl_add_read_hook(gen, exec1, exec2, exec4, exec8, exec_n, data) }
|
unsafe { libafl_add_read_hook(gen, exec1, exec2, exec4, exec8, exec_n, data) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO add MemOp info
|
||||||
pub fn add_write_hooks(
|
pub fn add_write_hooks(
|
||||||
&self,
|
&self,
|
||||||
gen: Option<extern "C" fn(GuestAddr, usize, u64) -> u64>,
|
gen: Option<extern "C" fn(GuestAddr, MemAccessInfo, u64) -> u64>,
|
||||||
exec1: Option<extern "C" fn(u64, GuestAddr, u64)>,
|
exec1: Option<extern "C" fn(u64, GuestAddr, u64)>,
|
||||||
exec2: Option<extern "C" fn(u64, GuestAddr, u64)>,
|
exec2: Option<extern "C" fn(u64, GuestAddr, u64)>,
|
||||||
exec4: Option<extern "C" fn(u64, GuestAddr, u64)>,
|
exec4: Option<extern "C" fn(u64, GuestAddr, u64)>,
|
||||||
|
@ -13,7 +13,7 @@ use libafl::{executors::inprocess::inprocess_get_state, inputs::UsesInput};
|
|||||||
|
|
||||||
pub use crate::emu::SyscallHookResult;
|
pub use crate::emu::SyscallHookResult;
|
||||||
use crate::{
|
use crate::{
|
||||||
emu::{Emulator, FatPtr, SKIP_EXEC_HOOK},
|
emu::{Emulator, FatPtr, MemAccessInfo, SKIP_EXEC_HOOK},
|
||||||
helper::QemuHelperTuple,
|
helper::QemuHelperTuple,
|
||||||
GuestAddr,
|
GuestAddr,
|
||||||
};
|
};
|
||||||
@ -189,7 +189,7 @@ where
|
|||||||
static mut READ_HOOKS: Vec<(Hook, Hook, Hook, Hook, Hook, Hook)> = vec![];
|
static mut READ_HOOKS: Vec<(Hook, Hook, Hook, Hook, Hook, Hook)> = vec![];
|
||||||
static mut WRITE_HOOKS: Vec<(Hook, Hook, Hook, Hook, Hook, Hook)> = vec![];
|
static mut WRITE_HOOKS: Vec<(Hook, Hook, Hook, Hook, Hook, Hook)> = vec![];
|
||||||
|
|
||||||
extern "C" fn gen_read_hook_wrapper<QT, S>(pc: GuestAddr, size: usize, index: u64) -> u64
|
extern "C" fn gen_read_hook_wrapper<QT, S>(pc: GuestAddr, info: MemAccessInfo, index: u64) -> u64
|
||||||
where
|
where
|
||||||
S: UsesInput,
|
S: UsesInput,
|
||||||
QT: QemuHelperTuple<S>,
|
QT: QemuHelperTuple<S>,
|
||||||
@ -203,9 +203,9 @@ where
|
|||||||
&mut QemuHooks<'_, QT, S>,
|
&mut QemuHooks<'_, QT, S>,
|
||||||
Option<&mut S>,
|
Option<&mut S>,
|
||||||
GuestAddr,
|
GuestAddr,
|
||||||
usize,
|
MemAccessInfo,
|
||||||
) -> Option<u64> = transmute(*ptr);
|
) -> Option<u64> = transmute(*ptr);
|
||||||
(func)(hooks, inprocess_get_state::<S>(), pc, size).map_or(SKIP_EXEC_HOOK, |id| id)
|
(func)(hooks, inprocess_get_state::<S>(), pc, info).map_or(SKIP_EXEC_HOOK, |id| id)
|
||||||
}
|
}
|
||||||
Hook::Closure(ptr) => {
|
Hook::Closure(ptr) => {
|
||||||
let func: &mut Box<
|
let func: &mut Box<
|
||||||
@ -213,17 +213,17 @@ where
|
|||||||
&mut QemuHooks<'_, QT, S>,
|
&mut QemuHooks<'_, QT, S>,
|
||||||
Option<&mut S>,
|
Option<&mut S>,
|
||||||
GuestAddr,
|
GuestAddr,
|
||||||
usize,
|
MemAccessInfo,
|
||||||
) -> Option<u64>,
|
) -> Option<u64>,
|
||||||
> = transmute(ptr);
|
> = transmute(ptr);
|
||||||
(func)(hooks, inprocess_get_state::<S>(), pc, size).map_or(SKIP_EXEC_HOOK, |id| id)
|
(func)(hooks, inprocess_get_state::<S>(), pc, info).map_or(SKIP_EXEC_HOOK, |id| id)
|
||||||
}
|
}
|
||||||
_ => 0,
|
_ => 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" fn gen_write_hook_wrapper<QT, S>(pc: GuestAddr, size: usize, index: u64) -> u64
|
extern "C" fn gen_write_hook_wrapper<QT, S>(pc: GuestAddr, info: MemAccessInfo, index: u64) -> u64
|
||||||
where
|
where
|
||||||
S: UsesInput,
|
S: UsesInput,
|
||||||
QT: QemuHelperTuple<S>,
|
QT: QemuHelperTuple<S>,
|
||||||
@ -237,9 +237,9 @@ where
|
|||||||
&mut QemuHooks<'_, QT, S>,
|
&mut QemuHooks<'_, QT, S>,
|
||||||
Option<&mut S>,
|
Option<&mut S>,
|
||||||
GuestAddr,
|
GuestAddr,
|
||||||
usize,
|
MemAccessInfo,
|
||||||
) -> Option<u64> = transmute(*ptr);
|
) -> Option<u64> = transmute(*ptr);
|
||||||
(func)(hooks, inprocess_get_state::<S>(), pc, size).map_or(SKIP_EXEC_HOOK, |id| id)
|
(func)(hooks, inprocess_get_state::<S>(), pc, info).map_or(SKIP_EXEC_HOOK, |id| id)
|
||||||
}
|
}
|
||||||
Hook::Closure(ptr) => {
|
Hook::Closure(ptr) => {
|
||||||
let func: &mut Box<
|
let func: &mut Box<
|
||||||
@ -247,10 +247,10 @@ where
|
|||||||
&mut QemuHooks<'_, QT, S>,
|
&mut QemuHooks<'_, QT, S>,
|
||||||
Option<&mut S>,
|
Option<&mut S>,
|
||||||
GuestAddr,
|
GuestAddr,
|
||||||
usize,
|
MemAccessInfo,
|
||||||
) -> Option<u64>,
|
) -> Option<u64>,
|
||||||
> = transmute(ptr);
|
> = transmute(ptr);
|
||||||
(func)(hooks, inprocess_get_state::<S>(), pc, size).map_or(SKIP_EXEC_HOOK, |id| id)
|
(func)(hooks, inprocess_get_state::<S>(), pc, info).map_or(SKIP_EXEC_HOOK, |id| id)
|
||||||
}
|
}
|
||||||
_ => 0,
|
_ => 0,
|
||||||
}
|
}
|
||||||
@ -942,7 +942,7 @@ where
|
|||||||
pub fn reads(
|
pub fn reads(
|
||||||
&self,
|
&self,
|
||||||
generation_hook: Option<
|
generation_hook: Option<
|
||||||
fn(&mut Self, Option<&mut S>, pc: GuestAddr, size: usize) -> Option<u64>,
|
fn(&mut Self, Option<&mut S>, pc: GuestAddr, info: MemAccessInfo) -> Option<u64>,
|
||||||
>,
|
>,
|
||||||
execution_hook1: Option<fn(&mut Self, Option<&mut S>, id: u64, addr: GuestAddr)>,
|
execution_hook1: Option<fn(&mut Self, Option<&mut S>, id: u64, addr: GuestAddr)>,
|
||||||
execution_hook2: Option<fn(&mut Self, Option<&mut S>, id: u64, addr: GuestAddr)>,
|
execution_hook2: Option<fn(&mut Self, Option<&mut S>, id: u64, addr: GuestAddr)>,
|
||||||
@ -1013,7 +1013,9 @@ where
|
|||||||
pub unsafe fn reads_closures(
|
pub unsafe fn reads_closures(
|
||||||
&self,
|
&self,
|
||||||
generation_hook: Option<
|
generation_hook: Option<
|
||||||
Box<dyn FnMut(&'a mut Self, Option<&'a mut S>, GuestAddr, usize) -> Option<u64>>,
|
Box<
|
||||||
|
dyn FnMut(&'a mut Self, Option<&'a mut S>, GuestAddr, MemAccessInfo) -> Option<u64>,
|
||||||
|
>,
|
||||||
>,
|
>,
|
||||||
execution_hook1: Option<Box<dyn FnMut(&'a mut Self, Option<&'a mut S>, u64, GuestAddr)>>,
|
execution_hook1: Option<Box<dyn FnMut(&'a mut Self, Option<&'a mut S>, u64, GuestAddr)>>,
|
||||||
execution_hook2: Option<Box<dyn FnMut(&'a mut Self, Option<&'a mut S>, u64, GuestAddr)>>,
|
execution_hook2: Option<Box<dyn FnMut(&'a mut Self, Option<&'a mut S>, u64, GuestAddr)>>,
|
||||||
@ -1070,7 +1072,7 @@ where
|
|||||||
pub fn reads_raw(
|
pub fn reads_raw(
|
||||||
&self,
|
&self,
|
||||||
generation_hook: Option<
|
generation_hook: Option<
|
||||||
fn(&mut Self, Option<&mut S>, pc: GuestAddr, size: usize) -> Option<u64>,
|
fn(&mut Self, Option<&mut S>, pc: GuestAddr, info: MemAccessInfo) -> Option<u64>,
|
||||||
>,
|
>,
|
||||||
execution_hook1: Option<extern "C" fn(id: u64, addr: GuestAddr, data: u64)>,
|
execution_hook1: Option<extern "C" fn(id: u64, addr: GuestAddr, data: u64)>,
|
||||||
execution_hook2: Option<extern "C" fn(id: u64, addr: GuestAddr, data: u64)>,
|
execution_hook2: Option<extern "C" fn(id: u64, addr: GuestAddr, data: u64)>,
|
||||||
@ -1109,7 +1111,7 @@ where
|
|||||||
pub fn writes(
|
pub fn writes(
|
||||||
&self,
|
&self,
|
||||||
generation_hook: Option<
|
generation_hook: Option<
|
||||||
fn(&mut Self, Option<&mut S>, pc: GuestAddr, size: usize) -> Option<u64>,
|
fn(&mut Self, Option<&mut S>, pc: GuestAddr, info: MemAccessInfo) -> Option<u64>,
|
||||||
>,
|
>,
|
||||||
execution_hook1: Option<fn(&mut Self, Option<&mut S>, id: u64, addr: GuestAddr)>,
|
execution_hook1: Option<fn(&mut Self, Option<&mut S>, id: u64, addr: GuestAddr)>,
|
||||||
execution_hook2: Option<fn(&mut Self, Option<&mut S>, id: u64, addr: GuestAddr)>,
|
execution_hook2: Option<fn(&mut Self, Option<&mut S>, id: u64, addr: GuestAddr)>,
|
||||||
@ -1180,7 +1182,9 @@ where
|
|||||||
pub unsafe fn writes_closures(
|
pub unsafe fn writes_closures(
|
||||||
&self,
|
&self,
|
||||||
generation_hook: Option<
|
generation_hook: Option<
|
||||||
Box<dyn FnMut(&'a mut Self, Option<&'a mut S>, GuestAddr, usize) -> Option<u64>>,
|
Box<
|
||||||
|
dyn FnMut(&'a mut Self, Option<&'a mut S>, GuestAddr, MemAccessInfo) -> Option<u64>,
|
||||||
|
>,
|
||||||
>,
|
>,
|
||||||
execution_hook1: Option<Box<dyn FnMut(&'a mut Self, Option<&'a mut S>, u64, GuestAddr)>>,
|
execution_hook1: Option<Box<dyn FnMut(&'a mut Self, Option<&'a mut S>, u64, GuestAddr)>>,
|
||||||
execution_hook2: Option<Box<dyn FnMut(&'a mut Self, Option<&'a mut S>, u64, GuestAddr)>>,
|
execution_hook2: Option<Box<dyn FnMut(&'a mut Self, Option<&'a mut S>, u64, GuestAddr)>>,
|
||||||
@ -1237,7 +1241,7 @@ where
|
|||||||
pub fn writes_raw(
|
pub fn writes_raw(
|
||||||
&self,
|
&self,
|
||||||
generation_hook: Option<
|
generation_hook: Option<
|
||||||
fn(&mut Self, Option<&mut S>, pc: GuestAddr, size: usize) -> Option<u64>,
|
fn(&mut Self, Option<&mut S>, pc: GuestAddr, info: MemAccessInfo) -> Option<u64>,
|
||||||
>,
|
>,
|
||||||
execution_hook1: Option<extern "C" fn(id: u64, addr: GuestAddr, data: u64)>,
|
execution_hook1: Option<extern "C" fn(id: u64, addr: GuestAddr, data: u64)>,
|
||||||
execution_hook2: Option<extern "C" fn(id: u64, addr: GuestAddr, data: u64)>,
|
execution_hook2: Option<extern "C" fn(id: u64, addr: GuestAddr, data: u64)>,
|
||||||
|
@ -14,9 +14,14 @@
|
|||||||
#![allow(clippy::borrow_deref_ref)]
|
#![allow(clippy::borrow_deref_ref)]
|
||||||
// Allow only ATM, it will be evetually removed
|
// Allow only ATM, it will be evetually removed
|
||||||
#![allow(clippy::missing_safety_doc)]
|
#![allow(clippy::missing_safety_doc)]
|
||||||
|
// libafl_qemu_sys export types with empty struct markers (e.g. struct {} start_init_save)
|
||||||
|
// This causes bindgen to generate empty Rust struct that are generally not FFI-safe due to C++ having empty structs with size 1
|
||||||
|
// As the QEMU codebase is C, it is FFI-safe and we just ignore the warning
|
||||||
|
#![allow(improper_ctypes)]
|
||||||
|
|
||||||
use std::env;
|
use std::env;
|
||||||
|
|
||||||
|
pub use libafl_qemu_sys as sys;
|
||||||
pub use strum::IntoEnumIterator;
|
pub use strum::IntoEnumIterator;
|
||||||
|
|
||||||
#[cfg(cpu_target = "aarch64")]
|
#[cfg(cpu_target = "aarch64")]
|
||||||
|
@ -20,6 +20,7 @@ use crate::{SYS_fstatat64, SYS_mmap2};
|
|||||||
#[cfg(not(cpu_target = "arm"))]
|
#[cfg(not(cpu_target = "arm"))]
|
||||||
use crate::{SYS_mmap, SYS_newfstatat};
|
use crate::{SYS_mmap, SYS_newfstatat};
|
||||||
|
|
||||||
|
// TODO use the functions provided by Emulator
|
||||||
pub const SNAPSHOT_PAGE_SIZE: usize = 4096;
|
pub const SNAPSHOT_PAGE_SIZE: usize = 4096;
|
||||||
pub const SNAPSHOT_PAGE_MASK: GuestAddr = !(SNAPSHOT_PAGE_SIZE as GuestAddr - 1);
|
pub const SNAPSHOT_PAGE_MASK: GuestAddr = !(SNAPSHOT_PAGE_SIZE as GuestAddr - 1);
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user