Fix drcov path parsing (#2884)
* fix drcov path parsing * refactoring of drcov tool * add the possibility to sort addresses in drcov tools * more aggressive clippy. it now catches more warnings as errors than before * reduce the number of unfixable warnings displayed.
This commit is contained in:
parent
c5b7c7c235
commit
4083f0ba73
@ -126,12 +126,15 @@ z3 = "0.12.1"
|
|||||||
|
|
||||||
|
|
||||||
[workspace.lints.rust]
|
[workspace.lints.rust]
|
||||||
|
# Deny
|
||||||
|
warnings = { level = "deny", priority = -1 }
|
||||||
|
|
||||||
# Forbid
|
# Forbid
|
||||||
unexpected_cfgs = "forbid"
|
unexpected_cfgs = "forbid"
|
||||||
|
|
||||||
# Allow
|
# Allow
|
||||||
incomplete_features = "allow"
|
incomplete_features = "allow"
|
||||||
ambiguous_glob_reexports = "allow"
|
# ambiguous_glob_reexports = "allow"
|
||||||
|
|
||||||
|
|
||||||
[workspace.lints.clippy]
|
[workspace.lints.clippy]
|
||||||
@ -142,9 +145,10 @@ cargo_common_metadata = "deny"
|
|||||||
|
|
||||||
# Warn
|
# Warn
|
||||||
cargo = { level = "warn", priority = -1 }
|
cargo = { level = "warn", priority = -1 }
|
||||||
negative_feature_names = "warn"
|
|
||||||
|
|
||||||
# Allow
|
# Allow
|
||||||
|
negative_feature_names = "allow" # TODO: turn into 'warn' when working
|
||||||
|
multiple_crate_versions = "allow" # TODO: turn into `warn` when working
|
||||||
unreadable_literal = "allow"
|
unreadable_literal = "allow"
|
||||||
type_repetition_in_bounds = "allow"
|
type_repetition_in_bounds = "allow"
|
||||||
missing_errors_doc = "allow"
|
missing_errors_doc = "allow"
|
||||||
|
@ -30,5 +30,7 @@ pyo3-build-config = { workspace = true }
|
|||||||
name = "pylibafl"
|
name = "pylibafl"
|
||||||
crate-type = ["cdylib"]
|
crate-type = ["cdylib"]
|
||||||
|
|
||||||
[profile.dev]
|
# TODO: find a way to fix this when a solution is found
|
||||||
panic = "abort"
|
# https://github.com/rust-lang/cargo/issues/9330
|
||||||
|
# [profile.dev]
|
||||||
|
# panic = "abort"
|
||||||
|
@ -244,7 +244,7 @@ windows_alias = "unsupported"
|
|||||||
command = "${TARGET_DIR}/${PROFILE_DIR}/qemu_coverage-${CARGO_MAKE_PROFILE}"
|
command = "${TARGET_DIR}/${PROFILE_DIR}/qemu_coverage-${CARGO_MAKE_PROFILE}"
|
||||||
args = [
|
args = [
|
||||||
"--coverage-path",
|
"--coverage-path",
|
||||||
"${TARGET_DIR}/drcov.log",
|
"${TARGET_DIR}/cov.drcov",
|
||||||
"--input-dir",
|
"--input-dir",
|
||||||
"./corpus",
|
"./corpus",
|
||||||
"--",
|
"--",
|
||||||
@ -294,11 +294,11 @@ script = '''
|
|||||||
cargo make ${FEATURE} || exit 1
|
cargo make ${FEATURE} || exit 1
|
||||||
|
|
||||||
cargo run --manifest-path ../../../utils/drcov_utils/Cargo.toml --bin drcov_merge -- \
|
cargo run --manifest-path ../../../utils/drcov_utils/Cargo.toml --bin drcov_merge -- \
|
||||||
-i ${TARGET_DIR}/drcov-000.log ${TARGET_DIR}/drcov-001.log ${TARGET_DIR}/drcov-002.log ${TARGET_DIR}/drcov-003.log \
|
-i ${TARGET_DIR}/cov-000.drcov ${TARGET_DIR}/cov-001.drcov ${TARGET_DIR}/cov-002.drcov ${TARGET_DIR}/cov-003.drcov \
|
||||||
--output ${TARGET_DIR}/drcov-merged.log || exit 1
|
--output ${TARGET_DIR}/cov-merged.drcov || exit 1
|
||||||
|
|
||||||
TMP=$(cargo run --manifest-path ../../../utils/drcov_utils/Cargo.toml --bin drcov_dump_addrs -- \
|
TMP=$(cargo run --manifest-path ../../../utils/drcov_utils/Cargo.toml --bin drcov_dump_addrs -- \
|
||||||
-i ${TARGET_DIR}/drcov-merged.log | wc -l || exit 1)
|
-i ${TARGET_DIR}/cov-merged.drcov -a | wc -l || exit 1)
|
||||||
|
|
||||||
NB_BLOCKS=$((TMP - 1))
|
NB_BLOCKS=$((TMP - 1))
|
||||||
|
|
||||||
|
@ -135,10 +135,7 @@ pub fn fuzz() {
|
|||||||
cov_path.set_file_name(format!("{coverage_name}-{core:03}.{coverage_extension}"));
|
cov_path.set_file_name(format!("{coverage_name}-{core:03}.{coverage_extension}"));
|
||||||
|
|
||||||
let emulator_modules = tuple_list!(
|
let emulator_modules = tuple_list!(
|
||||||
DrCovModule::builder()
|
DrCovModule::builder().filename(cov_path.clone()).build(),
|
||||||
.filename(cov_path.clone())
|
|
||||||
.full_trace(false)
|
|
||||||
.build(),
|
|
||||||
SnapshotModule::new()
|
SnapshotModule::new()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -86,6 +86,8 @@ pub use libafl_bolts::{nonzero, Error};
|
|||||||
/// The purpose of this module is to alleviate imports of many components by adding a glob import.
|
/// The purpose of this module is to alleviate imports of many components by adding a glob import.
|
||||||
#[cfg(feature = "prelude")]
|
#[cfg(feature = "prelude")]
|
||||||
pub mod prelude {
|
pub mod prelude {
|
||||||
|
#![expect(ambiguous_glob_reexports)]
|
||||||
|
|
||||||
pub use super::{
|
pub use super::{
|
||||||
corpus::*, events::*, executors::*, feedbacks::*, fuzzer::*, generators::*, inputs::*,
|
corpus::*, events::*, executors::*, feedbacks::*, fuzzer::*, generators::*, inputs::*,
|
||||||
monitors::*, mutators::*, observers::*, schedulers::*, stages::*, state::*, *,
|
monitors::*, mutators::*, observers::*, schedulers::*, stages::*, state::*, *,
|
||||||
|
@ -678,6 +678,8 @@ impl From<pyo3::PyErr> for Error {
|
|||||||
/// The purpose of this module is to alleviate imports of many components by adding a glob import.
|
/// The purpose of this module is to alleviate imports of many components by adding a glob import.
|
||||||
#[cfg(feature = "prelude")]
|
#[cfg(feature = "prelude")]
|
||||||
pub mod prelude {
|
pub mod prelude {
|
||||||
|
#![allow(ambiguous_glob_reexports)]
|
||||||
|
|
||||||
pub use super::{bolts_prelude::*, *};
|
pub use super::{bolts_prelude::*, *};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,15 +3,12 @@
|
|||||||
#[cfg(feature = "alloc")]
|
#[cfg(feature = "alloc")]
|
||||||
use alloc::{borrow::Cow, vec::Vec};
|
use alloc::{borrow::Cow, vec::Vec};
|
||||||
#[cfg(feature = "alloc")]
|
#[cfg(feature = "alloc")]
|
||||||
use core::ops::{Deref, DerefMut};
|
|
||||||
use core::{
|
use core::{
|
||||||
any::{type_name, TypeId},
|
any::type_name,
|
||||||
cell::Cell,
|
|
||||||
fmt::{Debug, Formatter},
|
fmt::{Debug, Formatter},
|
||||||
marker::PhantomData,
|
ops::{Deref, DerefMut, Index, IndexMut},
|
||||||
mem::transmute,
|
|
||||||
ops::{Index, IndexMut},
|
|
||||||
};
|
};
|
||||||
|
use core::{any::TypeId, cell::Cell, marker::PhantomData, mem::transmute};
|
||||||
|
|
||||||
#[cfg(feature = "alloc")]
|
#[cfg(feature = "alloc")]
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
@ -1,11 +1,17 @@
|
|||||||
use std::{
|
#[cfg(any(
|
||||||
env,
|
target_vendor = "apple",
|
||||||
fs::File,
|
feature = "ddg-instr",
|
||||||
io::Write,
|
feature = "function-logging",
|
||||||
path::{Path, PathBuf},
|
feature = "cmplog-routines",
|
||||||
process::Command,
|
feature = "autotokens",
|
||||||
str,
|
feature = "coverage-accounting",
|
||||||
};
|
feature = "cmplog-instructions",
|
||||||
|
feature = "ctx",
|
||||||
|
feature = "dump-cfg",
|
||||||
|
feature = "profiling",
|
||||||
|
))]
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use std::{env, fs::File, io::Write, path::Path, process::Command, str};
|
||||||
|
|
||||||
#[cfg(target_vendor = "apple")]
|
#[cfg(target_vendor = "apple")]
|
||||||
use glob::glob;
|
use glob::glob;
|
||||||
@ -20,6 +26,17 @@ const LLVM_VERSION_MAX: u32 = 33;
|
|||||||
const LLVM_VERSION_MIN: u32 = 6;
|
const LLVM_VERSION_MIN: u32 = 6;
|
||||||
|
|
||||||
/// Get the extension for a shared object
|
/// Get the extension for a shared object
|
||||||
|
#[cfg(any(
|
||||||
|
feature = "ddg-instr",
|
||||||
|
feature = "function-logging",
|
||||||
|
feature = "cmplog-routines",
|
||||||
|
feature = "autotokens",
|
||||||
|
feature = "coverage-accounting",
|
||||||
|
feature = "cmplog-instructions",
|
||||||
|
feature = "ctx",
|
||||||
|
feature = "dump-cfg",
|
||||||
|
feature = "profiling",
|
||||||
|
))]
|
||||||
fn dll_extension<'a>() -> &'a str {
|
fn dll_extension<'a>() -> &'a str {
|
||||||
if let Ok(vendor) = env::var("CARGO_CFG_TARGET_VENDOR") {
|
if let Ok(vendor) = env::var("CARGO_CFG_TARGET_VENDOR") {
|
||||||
if vendor == "apple" {
|
if vendor == "apple" {
|
||||||
@ -143,6 +160,17 @@ fn find_llvm_version() -> Option<i32> {
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(any(
|
||||||
|
feature = "ddg-instr",
|
||||||
|
feature = "function-logging",
|
||||||
|
feature = "cmplog-routines",
|
||||||
|
feature = "autotokens",
|
||||||
|
feature = "coverage-accounting",
|
||||||
|
feature = "cmplog-instructions",
|
||||||
|
feature = "ctx",
|
||||||
|
feature = "dump-cfg",
|
||||||
|
feature = "profiling",
|
||||||
|
))]
|
||||||
#[expect(clippy::too_many_arguments)]
|
#[expect(clippy::too_many_arguments)]
|
||||||
fn build_pass(
|
fn build_pass(
|
||||||
bindir_path: &Path,
|
bindir_path: &Path,
|
||||||
|
@ -40,6 +40,8 @@ pub mod cpp_runtime {
|
|||||||
#![allow(non_upper_case_globals)]
|
#![allow(non_upper_case_globals)]
|
||||||
#![allow(non_camel_case_types)]
|
#![allow(non_camel_case_types)]
|
||||||
#![allow(non_snake_case)]
|
#![allow(non_snake_case)]
|
||||||
|
#![allow(unused_attributes)]
|
||||||
|
|
||||||
include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
|
include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
//! Tracing of expressions in a serialized form.
|
//! Tracing of expressions in a serialized form.
|
||||||
|
#![allow(no_mangle_generic_items)]
|
||||||
|
|
||||||
pub use libafl::observers::concolic::serialization_format::StdShMemMessageFileWriter;
|
pub use libafl::observers::concolic::serialization_format::StdShMemMessageFileWriter;
|
||||||
use libafl::observers::concolic::SymExpr;
|
use libafl::observers::concolic::SymExpr;
|
||||||
|
@ -39,9 +39,9 @@ use arbitrary_int::u4;
|
|||||||
use bitbybit::bitfield;
|
use bitbybit::bitfield;
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
use caps::{CapSet, Capability};
|
use caps::{CapSet, Capability};
|
||||||
|
use libafl_bolts::Error;
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
use libafl_bolts::ownedref::OwnedRefMut;
|
use libafl_bolts::{hash_64_fast, ownedref::OwnedRefMut};
|
||||||
use libafl_bolts::{hash_64_fast, Error};
|
|
||||||
use libipt::PtError;
|
use libipt::PtError;
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
use libipt::{
|
use libipt::{
|
||||||
|
@ -102,36 +102,39 @@ fn main() -> Result<(), Box<dyn Error>> {
|
|||||||
{
|
{
|
||||||
todo!("copy all the source files"); // we don't support libafl_libfuzzer for others rn
|
todo!("copy all the source files"); // we don't support libafl_libfuzzer for others rn
|
||||||
}
|
}
|
||||||
let mut template: toml::Value =
|
#[cfg(unix)]
|
||||||
toml::from_str(&fs::read_to_string("runtime/Cargo.toml.template")?)?;
|
{
|
||||||
let toml::Value::Table(root) = &mut template else {
|
let mut template: toml::Value =
|
||||||
unreachable!("Invalid Cargo.toml");
|
toml::from_str(&fs::read_to_string("runtime/Cargo.toml.template")?)?;
|
||||||
};
|
let toml::Value::Table(root) = &mut template else {
|
||||||
root.insert(
|
unreachable!("Invalid Cargo.toml");
|
||||||
"workspace".to_string(),
|
};
|
||||||
toml::Value::Table(toml::Table::new()),
|
root.insert(
|
||||||
);
|
"workspace".to_string(),
|
||||||
let Some(toml::Value::Table(deps)) = root.get_mut("dependencies") else {
|
toml::Value::Table(toml::Table::new()),
|
||||||
unreachable!("Invalid Cargo.toml");
|
);
|
||||||
};
|
let Some(toml::Value::Table(deps)) = root.get_mut("dependencies") else {
|
||||||
let version = env!("CARGO_PKG_VERSION");
|
unreachable!("Invalid Cargo.toml");
|
||||||
for (_name, spec) in deps {
|
};
|
||||||
if let toml::Value::Table(spec) = spec {
|
let version = env!("CARGO_PKG_VERSION");
|
||||||
// replace all path deps with version deps
|
for (_name, spec) in deps {
|
||||||
if spec.remove("path").is_some() {
|
if let toml::Value::Table(spec) = spec {
|
||||||
spec.insert(
|
// replace all path deps with version deps
|
||||||
"version".to_string(),
|
if spec.remove("path").is_some() {
|
||||||
toml::Value::String(version.to_string()),
|
spec.insert(
|
||||||
);
|
"version".to_string(),
|
||||||
|
toml::Value::String(version.to_string()),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let serialized = toml::to_string(&template)?;
|
||||||
|
fs::write(custom_lib_dir.join("Cargo.toml"), serialized)?;
|
||||||
|
|
||||||
|
// build in this filled out template
|
||||||
|
command.current_dir(custom_lib_dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
let serialized = toml::to_string(&template)?;
|
|
||||||
fs::write(custom_lib_dir.join("Cargo.toml"), serialized)?;
|
|
||||||
|
|
||||||
// build in this filled out template
|
|
||||||
command.current_dir(custom_lib_dir);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
assert!(
|
assert!(
|
||||||
|
@ -115,7 +115,9 @@ pub fn build() {
|
|||||||
let cross_cc = if cfg!(feature = "usermode") && (qemu_asan || qemu_asan_guest) {
|
let cross_cc = if cfg!(feature = "usermode") && (qemu_asan || qemu_asan_guest) {
|
||||||
// TODO try to autodetect a cross compiler with the arch name (e.g. aarch64-linux-gnu-gcc)
|
// 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);
|
if cpu_target != env::consts::ARCH {
|
||||||
|
println!("cargo:warning=CROSS_CC is not set, default to cc (things can go wrong since the selected cpu target ({cpu_target}) is different from the host arch ({}))", env::consts::ARCH);
|
||||||
|
}
|
||||||
"cc".to_owned()
|
"cc".to_owned()
|
||||||
});
|
});
|
||||||
println!("cargo:rerun-if-env-changed=CROSS_CC");
|
println!("cargo:rerun-if-env-changed=CROSS_CC");
|
||||||
|
@ -133,7 +133,7 @@ impl crate::ArchExtras for crate::CPU {
|
|||||||
&self,
|
&self,
|
||||||
conv: CallingConvention,
|
conv: CallingConvention,
|
||||||
idx: i32,
|
idx: i32,
|
||||||
val: T,
|
_val: T,
|
||||||
) -> Result<(), QemuRWError>
|
) -> Result<(), QemuRWError>
|
||||||
where
|
where
|
||||||
T: Into<GuestReg>,
|
T: Into<GuestReg>,
|
||||||
|
@ -302,7 +302,6 @@ pub type QemuInProcessForkExecutor<'a, C, CM, ED, EM, ET, H, I, OT, S, SM, SP, Z
|
|||||||
StatefulInProcessForkExecutor<'a, EM, Emulator<C, CM, ED, ET, I, S, SM>, H, I, OT, S, SP, Z>;
|
StatefulInProcessForkExecutor<'a, EM, Emulator<C, CM, ED, ET, I, S, SM>, H, I, OT, S, SP, Z>;
|
||||||
|
|
||||||
#[cfg(feature = "fork")]
|
#[cfg(feature = "fork")]
|
||||||
#[expect(clippy::type_complexity)]
|
|
||||||
pub struct QemuForkExecutor<'a, C, CM, ED, EM, ET, H, I, OT, S, SM, SP, Z> {
|
pub struct QemuForkExecutor<'a, C, CM, ED, EM, ET, H, I, OT, S, SM, SP, Z> {
|
||||||
inner: QemuInProcessForkExecutor<'a, C, CM, ED, EM, ET, H, I, OT, S, SM, SP, Z>,
|
inner: QemuInProcessForkExecutor<'a, C, CM, ED, EM, ET, H, I, OT, S, SM, SP, Z>,
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,8 @@
|
|||||||
|
#[cfg(feature = "usermode")]
|
||||||
|
use std::{
|
||||||
|
cmp::{max, min},
|
||||||
|
ops::Range,
|
||||||
|
};
|
||||||
use std::{path::PathBuf, sync::Mutex};
|
use std::{path::PathBuf, sync::Mutex};
|
||||||
|
|
||||||
use hashbrown::{hash_map::Entry, HashMap};
|
use hashbrown::{hash_map::Entry, HashMap};
|
||||||
@ -19,8 +24,13 @@ use crate::{
|
|||||||
Qemu,
|
Qemu,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Trace of `block_id`s met at runtime
|
||||||
static DRCOV_IDS: Mutex<Option<Vec<u64>>> = Mutex::new(None);
|
static DRCOV_IDS: Mutex<Option<Vec<u64>>> = Mutex::new(None);
|
||||||
|
|
||||||
|
///Map of `pc` -> `block_id`
|
||||||
static DRCOV_MAP: Mutex<Option<HashMap<GuestAddr, u64>>> = Mutex::new(None);
|
static DRCOV_MAP: Mutex<Option<HashMap<GuestAddr, u64>>> = Mutex::new(None);
|
||||||
|
|
||||||
|
/// Map of `pc` -> `block_len`
|
||||||
static DRCOV_LENGTHS: Mutex<Option<HashMap<GuestAddr, GuestUsize>>> = Mutex::new(None);
|
static DRCOV_LENGTHS: Mutex<Option<HashMap<GuestAddr, GuestUsize>>> = Mutex::new(None);
|
||||||
|
|
||||||
#[cfg_attr(
|
#[cfg_attr(
|
||||||
@ -46,7 +56,7 @@ pub struct DrCovModuleBuilder<F> {
|
|||||||
filter: Option<F>,
|
filter: Option<F>,
|
||||||
module_mapping: Option<RangeMap<u64, (u16, String)>>,
|
module_mapping: Option<RangeMap<u64, (u16, String)>>,
|
||||||
filename: Option<PathBuf>,
|
filename: Option<PathBuf>,
|
||||||
full_trace: Option<bool>,
|
full_trace: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<F> DrCovModuleBuilder<F>
|
impl<F> DrCovModuleBuilder<F>
|
||||||
@ -58,7 +68,7 @@ where
|
|||||||
self.filter.unwrap(),
|
self.filter.unwrap(),
|
||||||
self.filename.unwrap(),
|
self.filename.unwrap(),
|
||||||
self.module_mapping,
|
self.module_mapping,
|
||||||
self.full_trace.unwrap(),
|
self.full_trace,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -97,7 +107,7 @@ where
|
|||||||
filter: self.filter,
|
filter: self.filter,
|
||||||
module_mapping: self.module_mapping,
|
module_mapping: self.module_mapping,
|
||||||
filename: self.filename,
|
filename: self.filename,
|
||||||
full_trace: Some(full_trace),
|
full_trace,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -111,20 +121,243 @@ pub struct DrCovModule<F> {
|
|||||||
drcov_len: usize,
|
drcov_len: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn gen_unique_block_ids<ET, F, I, S>(
|
||||||
|
_qemu: Qemu,
|
||||||
|
emulator_modules: &mut EmulatorModules<ET, I, S>,
|
||||||
|
state: Option<&mut S>,
|
||||||
|
pc: GuestAddr,
|
||||||
|
) -> Option<u64>
|
||||||
|
where
|
||||||
|
ET: EmulatorModuleTuple<I, S>,
|
||||||
|
F: AddressFilter,
|
||||||
|
I: Unpin,
|
||||||
|
S: Unpin + HasMetadata,
|
||||||
|
{
|
||||||
|
let drcov_module = emulator_modules.get::<DrCovModule<F>>().unwrap();
|
||||||
|
if !drcov_module.must_instrument(pc) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let state = state.expect("The gen_unique_block_ids hook works only for in-process fuzzing. Is the Executor initialized?");
|
||||||
|
if state
|
||||||
|
.metadata_map_mut()
|
||||||
|
.get_mut::<DrCovMetadata>()
|
||||||
|
.is_none()
|
||||||
|
{
|
||||||
|
state.add_metadata(DrCovMetadata::new());
|
||||||
|
}
|
||||||
|
|
||||||
|
let meta = state.metadata_map_mut().get_mut::<DrCovMetadata>().unwrap();
|
||||||
|
|
||||||
|
match DRCOV_MAP.lock().unwrap().as_mut().unwrap().entry(pc) {
|
||||||
|
Entry::Occupied(entry) => {
|
||||||
|
let id = *entry.get();
|
||||||
|
if drcov_module.full_trace {
|
||||||
|
Some(id)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Entry::Vacant(entry) => {
|
||||||
|
let id = meta.current_id;
|
||||||
|
entry.insert(id);
|
||||||
|
meta.current_id = id + 1;
|
||||||
|
if drcov_module.full_trace {
|
||||||
|
// GuestAddress is u32 for 32 bit guests
|
||||||
|
#[expect(clippy::unnecessary_cast)]
|
||||||
|
Some(id as u64)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::needless_pass_by_value)] // no longer a problem with nightly
|
||||||
|
pub fn gen_block_lengths<ET, F, I, S>(
|
||||||
|
_qemu: Qemu,
|
||||||
|
emulator_modules: &mut EmulatorModules<ET, I, S>,
|
||||||
|
_state: Option<&mut S>,
|
||||||
|
pc: GuestAddr,
|
||||||
|
block_length: GuestUsize,
|
||||||
|
) where
|
||||||
|
ET: EmulatorModuleTuple<I, S>,
|
||||||
|
F: AddressFilter,
|
||||||
|
I: Unpin,
|
||||||
|
S: Unpin + HasMetadata,
|
||||||
|
{
|
||||||
|
let drcov_module = emulator_modules.get::<DrCovModule<F>>().unwrap();
|
||||||
|
if !drcov_module.must_instrument(pc) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
DRCOV_LENGTHS
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.insert(pc, block_length);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::needless_pass_by_value)] // no longer a problem with nightly
|
||||||
|
pub fn exec_trace_block<ET, F, I, S>(
|
||||||
|
_qemu: Qemu,
|
||||||
|
emulator_modules: &mut EmulatorModules<ET, I, S>,
|
||||||
|
_state: Option<&mut S>,
|
||||||
|
id: u64,
|
||||||
|
) where
|
||||||
|
ET: EmulatorModuleTuple<I, S>,
|
||||||
|
F: AddressFilter,
|
||||||
|
I: Unpin,
|
||||||
|
S: Unpin + HasMetadata,
|
||||||
|
{
|
||||||
|
if emulator_modules.get::<DrCovModule<F>>().unwrap().full_trace {
|
||||||
|
DRCOV_IDS.lock().unwrap().as_mut().unwrap().push(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<F, I, S> EmulatorModule<I, S> for DrCovModule<F>
|
||||||
|
where
|
||||||
|
F: AddressFilter,
|
||||||
|
I: Unpin,
|
||||||
|
S: Unpin + HasMetadata,
|
||||||
|
{
|
||||||
|
#[cfg(feature = "usermode")]
|
||||||
|
fn first_exec<ET>(
|
||||||
|
&mut self,
|
||||||
|
qemu: Qemu,
|
||||||
|
emulator_modules: &mut EmulatorModules<ET, I, S>,
|
||||||
|
_state: &mut S,
|
||||||
|
) where
|
||||||
|
ET: EmulatorModuleTuple<I, S>,
|
||||||
|
{
|
||||||
|
if self.full_trace {
|
||||||
|
emulator_modules.blocks(
|
||||||
|
Hook::Function(gen_unique_block_ids::<ET, F, I, S>),
|
||||||
|
Hook::Function(gen_block_lengths::<ET, F, I, S>),
|
||||||
|
Hook::Function(exec_trace_block::<ET, F, I, S>),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
emulator_modules.blocks(
|
||||||
|
Hook::Function(gen_unique_block_ids::<ET, F, I, S>),
|
||||||
|
Hook::Function(gen_block_lengths::<ET, F, I, S>),
|
||||||
|
Hook::Empty,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
if self.module_mapping.is_none() {
|
||||||
|
log::info!("Auto-filling module mapping for DrCov module from QEMU mapping.");
|
||||||
|
|
||||||
|
let mut module_range_map: RangeMap<u64, (u16, String)> = RangeMap::new();
|
||||||
|
let mut module_path_map: HashMap<String, (Range<u64>, u16)> = HashMap::default();
|
||||||
|
|
||||||
|
// We are building a mapping of
|
||||||
|
// Path -> pc_start..pc_end, where pc_start is the smallest pc for the path and pc_end the biggest pc.
|
||||||
|
let mut i = 0;
|
||||||
|
for ((map_start, map_end), map_path) in qemu.mappings().filter_map(|m| {
|
||||||
|
m.path().filter(|p| !p.is_empty()).map(|p| {
|
||||||
|
(
|
||||||
|
(
|
||||||
|
u64::try_from(m.start()).unwrap(),
|
||||||
|
u64::try_from(m.end()).unwrap(),
|
||||||
|
),
|
||||||
|
p.to_string(),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}) {
|
||||||
|
// Check if path is already present
|
||||||
|
match module_path_map.entry(map_path) {
|
||||||
|
Entry::Occupied(mut entry) => {
|
||||||
|
// If present, try to widen the range if necessary
|
||||||
|
let (range, _) = entry.get_mut();
|
||||||
|
range.start = min(range.start, map_start);
|
||||||
|
range.end = max(range.end, map_end);
|
||||||
|
}
|
||||||
|
Entry::Vacant(entry) => {
|
||||||
|
entry.insert((map_start..map_end, i));
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// module_mapping.insert(r, (i as u16, p));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now, we can reorder the data by building a RangeMap and consume the old map.
|
||||||
|
for (path, (range, id)) in module_path_map {
|
||||||
|
module_range_map.insert(range, (id, path));
|
||||||
|
}
|
||||||
|
|
||||||
|
self.module_mapping = Some(module_range_map);
|
||||||
|
} else {
|
||||||
|
log::info!("Using user-provided module mapping for DrCov module.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "systemmode")]
|
||||||
|
fn first_exec<ET>(
|
||||||
|
&mut self,
|
||||||
|
_qemu: Qemu,
|
||||||
|
emulator_modules: &mut EmulatorModules<ET, I, S>,
|
||||||
|
_state: &mut S,
|
||||||
|
) where
|
||||||
|
ET: EmulatorModuleTuple<I, S>,
|
||||||
|
{
|
||||||
|
assert!(
|
||||||
|
self.module_mapping.is_some(),
|
||||||
|
"DrCov should have a module mapping already set."
|
||||||
|
);
|
||||||
|
|
||||||
|
if self.full_trace {
|
||||||
|
emulator_modules.blocks(
|
||||||
|
Hook::Function(gen_unique_block_ids::<ET, F, I, S>),
|
||||||
|
Hook::Function(gen_block_lengths::<ET, F, I, S>),
|
||||||
|
Hook::Function(exec_trace_block::<ET, F, I, S>),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
emulator_modules.blocks(
|
||||||
|
Hook::Function(gen_unique_block_ids::<ET, F, I, S>),
|
||||||
|
Hook::Function(gen_block_lengths::<ET, F, I, S>),
|
||||||
|
Hook::Empty,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn post_exec<OT, ET>(
|
||||||
|
&mut self,
|
||||||
|
_qemu: Qemu,
|
||||||
|
_emulator_modules: &mut EmulatorModules<ET, I, S>,
|
||||||
|
_state: &mut S,
|
||||||
|
_input: &I,
|
||||||
|
_observers: &mut OT,
|
||||||
|
_exit_kind: &mut ExitKind,
|
||||||
|
) where
|
||||||
|
OT: ObserversTuple<I, S>,
|
||||||
|
ET: EmulatorModuleTuple<I, S>,
|
||||||
|
{
|
||||||
|
self.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn on_crash(&mut self) {
|
||||||
|
self.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn on_timeout(&mut self) {
|
||||||
|
self.flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl DrCovModule<NopAddressFilter> {
|
impl DrCovModule<NopAddressFilter> {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn builder() -> DrCovModuleBuilder<NopAddressFilter> {
|
pub fn builder() -> DrCovModuleBuilder<NopAddressFilter> {
|
||||||
DrCovModuleBuilder {
|
DrCovModuleBuilder {
|
||||||
filter: Some(NopAddressFilter),
|
filter: Some(NopAddressFilter),
|
||||||
module_mapping: None,
|
module_mapping: None,
|
||||||
full_trace: None,
|
full_trace: false,
|
||||||
filename: None,
|
filename: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<F> DrCovModule<F> {
|
impl<F> DrCovModule<F> {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
#[expect(clippy::let_underscore_untyped)]
|
|
||||||
pub fn new(
|
pub fn new(
|
||||||
filter: F,
|
filter: F,
|
||||||
filename: PathBuf,
|
filename: PathBuf,
|
||||||
@ -132,10 +365,12 @@ impl<F> DrCovModule<F> {
|
|||||||
full_trace: bool,
|
full_trace: bool,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
if full_trace {
|
if full_trace {
|
||||||
let _ = DRCOV_IDS.lock().unwrap().insert(vec![]);
|
*DRCOV_IDS.lock().unwrap() = Some(vec![]);
|
||||||
}
|
}
|
||||||
let _ = DRCOV_MAP.lock().unwrap().insert(HashMap::new());
|
|
||||||
let _ = DRCOV_LENGTHS.lock().unwrap().insert(HashMap::new());
|
*DRCOV_MAP.lock().unwrap() = Some(HashMap::new());
|
||||||
|
*DRCOV_LENGTHS.lock().unwrap() = Some(HashMap::new());
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
filter,
|
filter,
|
||||||
module_mapping,
|
module_mapping,
|
||||||
@ -145,7 +380,7 @@ impl<F> DrCovModule<F> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write(&mut self) {
|
pub fn flush(&mut self) {
|
||||||
let lengths_opt = DRCOV_LENGTHS.lock().unwrap();
|
let lengths_opt = DRCOV_LENGTHS.lock().unwrap();
|
||||||
let lengths = lengths_opt.as_ref().unwrap();
|
let lengths = lengths_opt.as_ref().unwrap();
|
||||||
if self.full_trace {
|
if self.full_trace {
|
||||||
@ -225,8 +460,9 @@ impl<F> DrCovModule<F> {
|
|||||||
match lengths.get(pc) {
|
match lengths.get(pc) {
|
||||||
Some(block_length) => {
|
Some(block_length) => {
|
||||||
drcov_vec.push(DrCovBasicBlock::new(
|
drcov_vec.push(DrCovBasicBlock::new(
|
||||||
*pc as u64,
|
u64::try_from(*pc).unwrap(),
|
||||||
*pc as u64 + *block_length as u64,
|
u64::try_from(*pc).unwrap() as u64
|
||||||
|
+ u64::try_from(*block_length).unwrap(),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
@ -259,96 +495,6 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<F, I, S> EmulatorModule<I, S> for DrCovModule<F>
|
|
||||||
where
|
|
||||||
F: AddressFilter,
|
|
||||||
I: Unpin,
|
|
||||||
S: Unpin + HasMetadata,
|
|
||||||
{
|
|
||||||
fn post_qemu_init<ET>(&mut self, _qemu: Qemu, _emulator_modules: &mut EmulatorModules<ET, I, S>)
|
|
||||||
where
|
|
||||||
ET: EmulatorModuleTuple<I, S>,
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "usermode")]
|
|
||||||
fn first_exec<ET>(
|
|
||||||
&mut self,
|
|
||||||
qemu: Qemu,
|
|
||||||
emulator_modules: &mut EmulatorModules<ET, I, S>,
|
|
||||||
_state: &mut S,
|
|
||||||
) where
|
|
||||||
ET: EmulatorModuleTuple<I, S>,
|
|
||||||
{
|
|
||||||
emulator_modules.blocks(
|
|
||||||
Hook::Function(gen_unique_block_ids::<ET, F, I, S>),
|
|
||||||
Hook::Function(gen_block_lengths::<ET, F, I, S>),
|
|
||||||
Hook::Function(exec_trace_block::<ET, F, I, S>),
|
|
||||||
);
|
|
||||||
|
|
||||||
if self.module_mapping.is_none() {
|
|
||||||
log::info!("Auto-filling module mapping for DrCov module from QEMU mapping.");
|
|
||||||
|
|
||||||
let mut module_mapping: RangeMap<u64, (u16, String)> = RangeMap::new();
|
|
||||||
|
|
||||||
#[expect(clippy::unnecessary_cast)] // for GuestAddr -> u64
|
|
||||||
for (i, (r, p)) in qemu
|
|
||||||
.mappings()
|
|
||||||
.filter_map(|m| {
|
|
||||||
m.path()
|
|
||||||
.map(|p| ((m.start() as u64)..(m.end() as u64), p.to_string()))
|
|
||||||
.filter(|(_, p)| !p.is_empty())
|
|
||||||
})
|
|
||||||
.enumerate()
|
|
||||||
{
|
|
||||||
module_mapping.insert(r, (i as u16, p));
|
|
||||||
}
|
|
||||||
|
|
||||||
self.module_mapping = Some(module_mapping);
|
|
||||||
} else {
|
|
||||||
log::info!("Using user-provided module mapping for DrCov module.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "systemmode")]
|
|
||||||
fn first_exec<ET>(
|
|
||||||
&mut self,
|
|
||||||
_qemu: Qemu,
|
|
||||||
_emulator_modules: &mut EmulatorModules<ET, I, S>,
|
|
||||||
_state: &mut S,
|
|
||||||
) where
|
|
||||||
ET: EmulatorModuleTuple<I, S>,
|
|
||||||
{
|
|
||||||
assert!(
|
|
||||||
self.module_mapping.is_some(),
|
|
||||||
"DrCov should have a module mapping already set."
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn post_exec<OT, ET>(
|
|
||||||
&mut self,
|
|
||||||
_qemu: Qemu,
|
|
||||||
_emulator_modules: &mut EmulatorModules<ET, I, S>,
|
|
||||||
_state: &mut S,
|
|
||||||
_input: &I,
|
|
||||||
_observers: &mut OT,
|
|
||||||
_exit_kind: &mut ExitKind,
|
|
||||||
) where
|
|
||||||
OT: ObserversTuple<I, S>,
|
|
||||||
ET: EmulatorModuleTuple<I, S>,
|
|
||||||
{
|
|
||||||
self.write();
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn on_crash(&mut self) {
|
|
||||||
self.write();
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn on_timeout(&mut self) {
|
|
||||||
self.write();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<F> HasAddressFilter for DrCovModule<F>
|
impl<F> HasAddressFilter for DrCovModule<F>
|
||||||
where
|
where
|
||||||
F: AddressFilter,
|
F: AddressFilter,
|
||||||
@ -375,96 +521,3 @@ where
|
|||||||
unsafe { (&raw mut NOP_PAGE_FILTER).as_mut().unwrap().get_mut() }
|
unsafe { (&raw mut NOP_PAGE_FILTER).as_mut().unwrap().get_mut() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn gen_unique_block_ids<ET, F, I, S>(
|
|
||||||
_qemu: Qemu,
|
|
||||||
emulator_modules: &mut EmulatorModules<ET, I, S>,
|
|
||||||
state: Option<&mut S>,
|
|
||||||
pc: GuestAddr,
|
|
||||||
) -> Option<u64>
|
|
||||||
where
|
|
||||||
ET: EmulatorModuleTuple<I, S>,
|
|
||||||
F: AddressFilter,
|
|
||||||
I: Unpin,
|
|
||||||
S: Unpin + HasMetadata,
|
|
||||||
{
|
|
||||||
let drcov_module = emulator_modules.get::<DrCovModule<F>>().unwrap();
|
|
||||||
if !drcov_module.must_instrument(pc) {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
let state = state.expect("The gen_unique_block_ids hook works only for in-process fuzzing. Is the Executor initialized?");
|
|
||||||
if state
|
|
||||||
.metadata_map_mut()
|
|
||||||
.get_mut::<DrCovMetadata>()
|
|
||||||
.is_none()
|
|
||||||
{
|
|
||||||
state.add_metadata(DrCovMetadata::new());
|
|
||||||
}
|
|
||||||
let meta = state.metadata_map_mut().get_mut::<DrCovMetadata>().unwrap();
|
|
||||||
|
|
||||||
match DRCOV_MAP.lock().unwrap().as_mut().unwrap().entry(pc) {
|
|
||||||
Entry::Occupied(e) => {
|
|
||||||
let id = *e.get();
|
|
||||||
if drcov_module.full_trace {
|
|
||||||
Some(id)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Entry::Vacant(e) => {
|
|
||||||
let id = meta.current_id;
|
|
||||||
e.insert(id);
|
|
||||||
meta.current_id = id + 1;
|
|
||||||
if drcov_module.full_trace {
|
|
||||||
// GuestAddress is u32 for 32 bit guests
|
|
||||||
#[expect(clippy::unnecessary_cast)]
|
|
||||||
Some(id as u64)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(clippy::needless_pass_by_value)] // no longer a problem with nightly
|
|
||||||
pub fn gen_block_lengths<ET, F, I, S>(
|
|
||||||
_qemu: Qemu,
|
|
||||||
emulator_modules: &mut EmulatorModules<ET, I, S>,
|
|
||||||
_state: Option<&mut S>,
|
|
||||||
pc: GuestAddr,
|
|
||||||
block_length: GuestUsize,
|
|
||||||
) where
|
|
||||||
ET: EmulatorModuleTuple<I, S>,
|
|
||||||
F: AddressFilter,
|
|
||||||
I: Unpin,
|
|
||||||
S: Unpin + HasMetadata,
|
|
||||||
{
|
|
||||||
let drcov_module = emulator_modules.get::<DrCovModule<F>>().unwrap();
|
|
||||||
if !drcov_module.must_instrument(pc) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
DRCOV_LENGTHS
|
|
||||||
.lock()
|
|
||||||
.unwrap()
|
|
||||||
.as_mut()
|
|
||||||
.unwrap()
|
|
||||||
.insert(pc, block_length);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(clippy::needless_pass_by_value)] // no longer a problem with nightly
|
|
||||||
pub fn exec_trace_block<ET, F, I, S>(
|
|
||||||
_qemu: Qemu,
|
|
||||||
emulator_modules: &mut EmulatorModules<ET, I, S>,
|
|
||||||
_state: Option<&mut S>,
|
|
||||||
id: u64,
|
|
||||||
) where
|
|
||||||
ET: EmulatorModuleTuple<I, S>,
|
|
||||||
F: AddressFilter,
|
|
||||||
I: Unpin,
|
|
||||||
S: Unpin + HasMetadata,
|
|
||||||
{
|
|
||||||
if emulator_modules.get::<DrCovModule<F>>().unwrap().full_trace {
|
|
||||||
DRCOV_IDS.lock().unwrap().as_mut().unwrap().push(id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,10 +1,7 @@
|
|||||||
use core::{fmt::Debug, ops::Range};
|
use core::fmt::Debug;
|
||||||
|
|
||||||
use libafl::{executors::ExitKind, observers::ObserversTuple};
|
use libafl::{executors::ExitKind, observers::ObserversTuple};
|
||||||
use libafl_bolts::tuples::{MatchFirstType, SplitBorrowExtractFirstType};
|
use libafl_bolts::tuples::{MatchFirstType, SplitBorrowExtractFirstType};
|
||||||
use libafl_qemu_sys::GuestAddr;
|
|
||||||
#[cfg(feature = "systemmode")]
|
|
||||||
use libafl_qemu_sys::GuestPhysAddr;
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
emu::EmulatorModules,
|
emu::EmulatorModules,
|
||||||
@ -15,6 +12,7 @@ use crate::{
|
|||||||
#[cfg(feature = "usermode")]
|
#[cfg(feature = "usermode")]
|
||||||
pub mod usermode;
|
pub mod usermode;
|
||||||
#[cfg(feature = "usermode")]
|
#[cfg(feature = "usermode")]
|
||||||
|
#[cfg_attr(feature = "hexagon", allow(unused_imports))]
|
||||||
pub use usermode::*;
|
pub use usermode::*;
|
||||||
|
|
||||||
#[cfg(feature = "systemmode")]
|
#[cfg(feature = "systemmode")]
|
||||||
|
@ -34,6 +34,7 @@ use crate::{
|
|||||||
#[cfg(cpu_target = "hexagon")]
|
#[cfg(cpu_target = "hexagon")]
|
||||||
/// Hexagon syscalls are not currently supported by the `syscalls` crate, so we just paste this here for now.
|
/// Hexagon syscalls are not currently supported by the `syscalls` crate, so we just paste this here for now.
|
||||||
/// <https://github.com/qemu/qemu/blob/11be70677c70fdccd452a3233653949b79e97908/linux-user/hexagon/syscall_nr.h#L230>
|
/// <https://github.com/qemu/qemu/blob/11be70677c70fdccd452a3233653949b79e97908/linux-user/hexagon/syscall_nr.h#L230>
|
||||||
|
#[expect(non_upper_case_globals)]
|
||||||
const SYS_execve: u8 = 221;
|
const SYS_execve: u8 = 221;
|
||||||
|
|
||||||
/// Parses `injections.yaml`
|
/// Parses `injections.yaml`
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
#![allow(clippy::needless_pass_by_value)] // default compiler complains about Option<&mut T> otherwise, and this is used extensively.
|
#![allow(clippy::needless_pass_by_value)] // default compiler complains about Option<&mut T> otherwise, and this is used extensively.
|
||||||
use std::{cell::UnsafeCell, mem::MaybeUninit, sync::Mutex};
|
use std::{cell::UnsafeCell, mem::MaybeUninit, ops::Range, sync::Mutex};
|
||||||
|
|
||||||
use hashbrown::{HashMap, HashSet};
|
use hashbrown::{HashMap, HashSet};
|
||||||
use libafl_qemu_sys::{GuestAddr, MmapPerms};
|
use libafl_qemu_sys::{GuestAddr, MmapPerms};
|
||||||
@ -25,7 +25,7 @@ use crate::{
|
|||||||
modules::{
|
modules::{
|
||||||
asan::AsanModule,
|
asan::AsanModule,
|
||||||
utils::filters::{HasAddressFilter, NopAddressFilter, NOP_ADDRESS_FILTER},
|
utils::filters::{HasAddressFilter, NopAddressFilter, NOP_ADDRESS_FILTER},
|
||||||
EmulatorModule, EmulatorModuleTuple, Range,
|
EmulatorModule, EmulatorModuleTuple,
|
||||||
},
|
},
|
||||||
qemu::{Hook, SyscallHookResult},
|
qemu::{Hook, SyscallHookResult},
|
||||||
Qemu, SYS_brk, SYS_mprotect, SYS_mremap, SYS_munmap, SYS_pread64, SYS_read, SYS_readlinkat,
|
Qemu, SYS_brk, SYS_mprotect, SYS_mremap, SYS_munmap, SYS_pread64, SYS_read, SYS_readlinkat,
|
||||||
|
@ -311,6 +311,7 @@ impl PageFilter for NopPageFilter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "usermode")]
|
#[cfg(feature = "usermode")]
|
||||||
|
#[allow(dead_code)]
|
||||||
pub(crate) static mut NOP_ADDRESS_FILTER: UnsafeCell<NopAddressFilter> =
|
pub(crate) static mut NOP_ADDRESS_FILTER: UnsafeCell<NopAddressFilter> =
|
||||||
UnsafeCell::new(NopAddressFilter);
|
UnsafeCell::new(NopAddressFilter);
|
||||||
#[cfg(feature = "systemmode")]
|
#[cfg(feature = "systemmode")]
|
||||||
|
@ -251,6 +251,19 @@ fn parse_hex_to_u64(str: &str) -> Result<u64, ParseIntError> {
|
|||||||
u64::from_str_radix(&str[2..], 16)
|
u64::from_str_radix(&str[2..], 16)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn parse_path(s: &str) -> PathBuf {
|
||||||
|
let s = s.trim();
|
||||||
|
|
||||||
|
// If first and last character is a quote, let's remove them
|
||||||
|
let s = if s.starts_with('\"') && s.ends_with('\"'){
|
||||||
|
&s[1..s.len() - 1]
|
||||||
|
} else {
|
||||||
|
s
|
||||||
|
};
|
||||||
|
|
||||||
|
PathBuf::from(s)
|
||||||
|
}
|
||||||
|
|
||||||
impl DrCovReader {
|
impl DrCovReader {
|
||||||
/// Parse a `drcov` file to memory.
|
/// Parse a `drcov` file to memory.
|
||||||
pub fn read<P: AsRef<Path> + ?Sized>(file: &P) -> Result<Self, Error> {
|
pub fn read<P: AsRef<Path> + ?Sized>(file: &P) -> Result<Self, Error> {
|
||||||
@ -336,7 +349,7 @@ impl DrCovReader {
|
|||||||
return Err(err("timestamp"));
|
return Err(err("timestamp"));
|
||||||
};
|
};
|
||||||
|
|
||||||
let Some(path) = split.next().map(|s| PathBuf::from(s.trim())) else {
|
let Some(path) = split.next().map(parse_path) else {
|
||||||
return Err(err("path"));
|
return Err(err("path"));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -46,7 +46,7 @@
|
|||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate std;
|
extern crate std;
|
||||||
|
|
||||||
#[allow(unused_imports)] // for no-std
|
#[allow(unused_imports)]
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
|
|
||||||
|
@ -205,7 +205,7 @@ extern "C" {
|
|||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
#[allow(unused_assignments)] // cfg dependent
|
#[allow(unused_assignments)] // cfg dependent
|
||||||
pub unsafe extern "C" fn __sanitizer_cov_trace_pc_guard(guard: *mut u32) {
|
pub unsafe extern "C" fn __sanitizer_cov_trace_pc_guard(guard: *mut u32) {
|
||||||
#[allow(unused_mut)] // cfg dependent
|
#[allow(unused_variables, unused_mut)] // cfg dependent
|
||||||
let mut pos = *guard as usize;
|
let mut pos = *guard as usize;
|
||||||
|
|
||||||
#[cfg(any(feature = "sancov_ngram4", feature = "sancov_ngram8"))]
|
#[cfg(any(feature = "sancov_ngram4", feature = "sancov_ngram8"))]
|
||||||
|
@ -59,7 +59,7 @@ for project in "${PROJECTS[@]}"; do
|
|||||||
features="--features=clippy"
|
features="--features=clippy"
|
||||||
fi
|
fi
|
||||||
if [ -d "$project" ]; then
|
if [ -d "$project" ]; then
|
||||||
run_clippy "$project" $features
|
run_clippy "$project" "$features"
|
||||||
else
|
else
|
||||||
echo "Warning: Directory $project does not exist. Skipping."
|
echo "Warning: Directory $project does not exist. Skipping."
|
||||||
fi
|
fi
|
||||||
|
@ -9,6 +9,7 @@ categories = ["development-tools"]
|
|||||||
keywords = ["fuzzing", "libafl", "drcov"]
|
keywords = ["fuzzing", "libafl", "drcov"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
env_logger = "0.11.6"
|
||||||
libafl_targets = { workspace = true, default-features = true }
|
libafl_targets = { workspace = true, default-features = true }
|
||||||
clap = { workspace = true, features = ["derive", "wrap_help"] }
|
clap = { workspace = true, features = ["derive", "wrap_help"] }
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use std::{
|
use std::{
|
||||||
fs::{create_dir_all, File},
|
fs::{create_dir_all, File},
|
||||||
io::Write,
|
io::{Error, Write},
|
||||||
path::PathBuf,
|
path::PathBuf,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -17,15 +17,25 @@ use libafl_targets::drcov::DrCovReader;
|
|||||||
pub struct Opt {
|
pub struct Opt {
|
||||||
#[arg(short, long, help = "DrCov traces to read", required = true)]
|
#[arg(short, long, help = "DrCov traces to read", required = true)]
|
||||||
pub inputs: Vec<PathBuf>,
|
pub inputs: Vec<PathBuf>,
|
||||||
|
|
||||||
#[arg(
|
#[arg(
|
||||||
short,
|
short,
|
||||||
long,
|
long,
|
||||||
help = "Output folder to write address files to. If none is set, this will output all addresses to stdout."
|
help = "Output folder to write address files to. If none is set, this will output all addresses to stdout."
|
||||||
)]
|
)]
|
||||||
pub out_dir: Option<PathBuf>,
|
pub out_dir: Option<PathBuf>,
|
||||||
|
|
||||||
|
#[arg(short, long, help = "Print the metadata of the drcov file.")]
|
||||||
|
pub metadata: bool,
|
||||||
|
|
||||||
|
#[arg(short, long, help = "Dump the addresses.")]
|
||||||
|
pub addrs: bool,
|
||||||
|
|
||||||
|
#[arg(short, long, help = "Sort the addresses from smallest to biggest.")]
|
||||||
|
pub sort: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() -> Result<(), Error> {
|
||||||
let opts = Opt::parse();
|
let opts = Opt::parse();
|
||||||
|
|
||||||
if let Some(out_dir) = &opts.out_dir {
|
if let Some(out_dir) = &opts.out_dir {
|
||||||
@ -45,7 +55,13 @@ fn main() {
|
|||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(out_dir) = &opts.out_dir {
|
let mut blocks = drcov.basic_block_addresses_u64();
|
||||||
|
|
||||||
|
if opts.sort {
|
||||||
|
blocks.sort_unstable();
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut writer: Box<dyn Write> = if let Some(out_dir) = &opts.out_dir {
|
||||||
// Write files to a directory
|
// Write files to a directory
|
||||||
let out_file = out_dir.join(
|
let out_file = out_dir.join(
|
||||||
input
|
input
|
||||||
@ -53,27 +69,47 @@ fn main() {
|
|||||||
.expect("File without filename shouldn't exist"),
|
.expect("File without filename shouldn't exist"),
|
||||||
);
|
);
|
||||||
|
|
||||||
let Ok(mut file) = File::create_new(&out_file).map_err(|err| {
|
let Ok(file) = File::create_new(&out_file).map_err(|err| {
|
||||||
eprintln!("Could not create file {out_file:?} - continuing: {err:?}");
|
eprintln!("Could not create file {out_file:?} - continuing: {err:?}");
|
||||||
}) else {
|
}) else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
|
||||||
println!("Dumping addresses from drcov file {input:?} to {out_file:?}");
|
println!("Dumping traces from drcov file {input:?} to {out_file:?}",);
|
||||||
|
|
||||||
for line in drcov.basic_block_addresses_u64() {
|
Box::new(file)
|
||||||
file.write_all(format!("{line:#x}\n").as_bytes())
|
|
||||||
.expect("Could not write to file");
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// dump to stdout
|
Box::new(std::io::stdout())
|
||||||
println!("# Blocks covered in {input:?}:");
|
};
|
||||||
|
|
||||||
for line in drcov.basic_block_addresses_u64() {
|
// dump to stdout
|
||||||
println!("{line:#x}");
|
let modules = &drcov.module_entries;
|
||||||
|
|
||||||
|
if opts.metadata {
|
||||||
|
writeln!(writer, "# {} Modules:", modules.len())?;
|
||||||
|
for module in &drcov.module_entries {
|
||||||
|
writeln!(
|
||||||
|
writer,
|
||||||
|
"\t{} - [{:#020x}-{:#020x}] {}",
|
||||||
|
module.id,
|
||||||
|
module.base,
|
||||||
|
module.end,
|
||||||
|
module.path.display()
|
||||||
|
)?;
|
||||||
}
|
}
|
||||||
|
writeln!(writer, "# {} Blocks covered in {input:?}.", blocks.len())?;
|
||||||
|
|
||||||
println!();
|
if opts.addrs {
|
||||||
|
writeln!(writer)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.addrs {
|
||||||
|
for line in blocks {
|
||||||
|
writeln!(writer, "{line:#x}")?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -24,6 +24,7 @@ pub struct Opt {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
env_logger::init();
|
||||||
let opts = Opt::parse();
|
let opts = Opt::parse();
|
||||||
|
|
||||||
assert!(
|
assert!(
|
||||||
|
Loading…
x
Reference in New Issue
Block a user