Skip to content

Commit

Permalink
Bump Libipt 0.3, add HW breakpoint support (#2984)
Browse files Browse the repository at this point in the history
* Decode with callback

* WIP restore intelpt module

* Fix build_target if target_dir doesn't exist

* WIP itelpt qemu/kvm example: bootloader

* qemu config refactoring

* Fix intel_pt_command_executor target dir

* * QEMU error refactoring*
* back to one QEMU init function
* other small things

* update test

* Bump libipt

* waitpid_filtered to ignore SIGWINCH

* Fix warnings unused manifest key: *.version

* Add export_raw feature to libafl_intelpt

* derive Debug for IntelPTHook

* Clippy

* Light refactor of EmulatorModules

* qemu is now a parameter to EmulatorModule callbacks and most function hooks.
* EmulatorModules is initialized before QEMU is initialized.

* Update target program ELF offsets

* fmt

* * asan fixed size accesses working with generics
* continue to propagate qemu argument as hook first parameter
* use pre_syscall* and post_syscall* everywhere
* fix some clippy stuff

* fmt

* Add comment to KVM pt_mode check

* refactor

* Add intel_pt_export_raw feature in libafl

* fix fuzzers

* * refactor asan and asanguest modules to avoid custom init of QEMU and use the module interface instead.
* adapt qemu_launcher example to fully work with emulator, since qemu must now be initialized by emulator.

* fmt

* clippy

* fix qemu_coverage

* fmt

* forgot qemu args in launcher

* map_error instead of unwrap

* use correct args

* Update to new libafl_qemu

* adapt api

* borrow checker friendly join_split_trace

and copy trace before deocde to prevent decoding failures

* testing stuff

* Set ip_filters (also) with builder

* Move trace to file

* Store a pt_builder in module

enable the setting of filters and other pt settings

* baby_bootloader target

* Best bootloader ever

* new builder?

* use closure for qemu config from emulator builder.

* better format

* clippy + fmt

* Fix build target

Create target directory if doesn't exist

* Remove filter on speculatively exec blocks

since also committed blocks can have this flag

* Add current ip_filters getter

* Fix possibile infinite loop in trace decode

* HW breakpoint + snapshot

* add snapshot and exit at first objective

* prefer raw pointers to slice_from_raw_parts_mut

since the latter is highly unsafe and allows more potentially dangerous reordering

* Add cpu option to QEMU config

* Add cpu option and minor improvements

* fix cargo run causing recompile

* no default devices

* windows clippy fix

* Exclude intel_pt feature from CI as all systemmode feats

* Add qemu_intel_pt_bootloader to CI

* Fix NopPageFilter

* Fix qemu_config

* Restore HW breakpoints

* Lints

* return Result for hw bp set/remove

* mark join_split_trace as unsafe

* Put the qcow2 in a tmpfs ramdisk

10x exec/sec

* Post merge fixes

* Try out libipt 0.3 alpha

* Try out libipt 0.3 alpha also in hook

* Clippy

* New libipt

* Post merge fixes

* Bump libipt

* Drive cache None

* Post merge fixes

* Use SectionInfo from libipt

* No slice::from_raw_parts_mut, just use raw pointer

* Cache the decoder builder

* Update qemu-bridge

* Add qemu -append param

* Move linux specific code to a mod, less #[cfg]s

* Add qemu initrd config

* Add qemu monitor tcp

* Add not enough ip filters message

* Fix wrong must_use

* Prevent possible infinite loop in block decoding in debug mode

* Clippy

* fix CI?

* Revert, keep libipt 0.3 and hw bp

---------

Co-authored-by: Romain Malmain <[email protected]>
Co-authored-by: Marco Cavenati <marco@lenovo300e>
  • Loading branch information
3 people authored Feb 17, 2025
1 parent 530a3cc commit 47f7978
Show file tree
Hide file tree
Showing 11 changed files with 1,260 additions and 1,026 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ document-features = "0.2.10"
fastbloom = { version = "0.8.0", default-features = false }
hashbrown = { version = "0.14.5", default-features = false } # A faster hashmap, nostd compatible
libc = "0.2.159" # For (*nix) libc
libipt = "0.2.0"
libipt = "0.3.0"
log = "0.4.22"
meminterval = "0.4.1"
mimalloc = { version = "0.1.43", default-features = false }
Expand Down
10 changes: 5 additions & 5 deletions fuzzers/binary_only/intel_pt_baby_fuzzer/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use libafl::{
corpus::{InMemoryCorpus, OnDiskCorpus},
events::SimpleEventManager,
executors::{
hooks::intel_pt::{IntelPTHook, Section},
hooks::intel_pt::{IntelPTHook, SectionInfo},
inprocess::GenericInProcessExecutor,
ExitKind,
},
Expand Down Expand Up @@ -100,10 +100,10 @@ pub fn main() {
let sections = process_maps
.iter()
.filter_map(|pm| {
if pm.is_exec() && pm.filename().is_some() {
Some(Section {
file_path: pm.filename().unwrap().to_string_lossy().to_string(),
file_offset: pm.offset as u64,
if pm.is_exec() && pm.filename().is_some() && pm.inode != 0 {
Some(SectionInfo {
filename: pm.filename().unwrap().to_string_lossy().to_string(),
offset: pm.offset as u64,
size: pm.size() as u64,
virtual_address: pm.start() as u64,
})
Expand Down
8 changes: 4 additions & 4 deletions fuzzers/binary_only/intel_pt_command_executor/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use libafl::{
events::SimpleEventManager,
executors::{
command::{CommandConfigurator, PTraceCommandConfigurator},
hooks::intel_pt::{IntelPTHook, Section},
hooks::intel_pt::{IntelPTHook, SectionInfo},
},
feedbacks::{CrashFeedback, MaxMapFeedback},
fuzzer::{Fuzzer, StdFuzzer},
Expand Down Expand Up @@ -105,9 +105,9 @@ pub fn main() {
.build()
.unwrap();

let sections = [Section {
file_path: target_path.to_string_lossy().to_string(),
file_offset: 0x14000,
let sections = [SectionInfo {
filename: target_path.to_string_lossy().to_string(),
offset: 0x14000,
size: (*code_memory_addresses.end() - *code_memory_addresses.start() + 1) as u64,
virtual_address: *code_memory_addresses.start() as u64,
}];
Expand Down
9 changes: 1 addition & 8 deletions libafl/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -111,13 +111,7 @@ regex = ["std", "dep:regex"]
casr = ["libcasr", "std", "regex"]

## Intel Processor Trace
intel_pt = [
"std",
"dep:libafl_intelpt",
"dep:libipt",
"dep:nix",
"dep:num_enum",
]
intel_pt = ["std", "dep:libafl_intelpt", "dep:nix", "dep:num_enum"]
## Save all the Intel PT raw traces to files, use only for debug
intel_pt_export_raw = ["intel_pt", "libafl_intelpt/export_raw"]

Expand Down Expand Up @@ -307,7 +301,6 @@ document-features = { workspace = true, optional = true }
# Optional
clap = { workspace = true, optional = true }
num_enum = { workspace = true, optional = true }
libipt = { workspace = true, optional = true }
fastbloom = { workspace = true, optional = true }

[lints]
Expand Down
66 changes: 10 additions & 56 deletions libafl/src/executors/hooks/intel_pt.rs
Original file line number Diff line number Diff line change
@@ -1,37 +1,24 @@
use core::fmt::Debug;
use std::{
ptr::slice_from_raw_parts_mut,
string::{String, ToString},
};

use libafl_intelpt::{error_from_pt_error, IntelPT};
use libipt::{Asid, Image, SectionCache};
pub use libafl_intelpt::SectionInfo;
use libafl_intelpt::{Image, IntelPT};
use num_traits::SaturatingAdd;
use serde::Serialize;
use typed_builder::TypedBuilder;

use crate::{executors::hooks::ExecutorHook, Error};

/// Info of a binary's section that can be used during `Intel PT` traces decoding
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Section {
/// Path of the binary
pub file_path: String,
/// Offset of the section in the file
pub file_offset: u64,
/// Size of the section
pub size: u64,
/// Start virtual address of the section once loaded in memory
pub virtual_address: u64,
}
use crate::executors::hooks::ExecutorHook;

/// Hook to enable Intel Processor Trace (PT) tracing
#[derive(Debug, TypedBuilder)]
pub struct IntelPTHook<T> {
#[builder(default = IntelPT::builder().build().unwrap())]
intel_pt: IntelPT,
#[builder(setter(transform = |sections: &[Section]| sections_to_image(sections).unwrap()))]
image: (Image<'static>, SectionCache<'static>),
#[builder(setter(transform = |sections: &[SectionInfo]| {
let mut i = Image::new(None).unwrap();
i.add_files_cached(sections, None).unwrap();
i
}))]
image: Image,
map_ptr: *mut T,
map_len: usize,
}
Expand All @@ -51,9 +38,8 @@ where
let pt = &mut self.intel_pt;
pt.disable_tracing().unwrap();

let slice = unsafe { &mut *slice_from_raw_parts_mut(self.map_ptr, self.map_len) };
let _ = pt
.decode_traces_into_map(&mut self.image.0, slice)
.decode_traces_into_map(&mut self.image, self.map_ptr, self.map_len)
.inspect_err(|e| log::warn!("Intel PT trace decoding failed: {e}"));
#[cfg(feature = "intel_pt_export_raw")]
{
Expand All @@ -63,35 +49,3 @@ where
}
}
}

// It would be nice to have this as a `TryFrom<IntoIter<Section>>`, but Rust's orphan rule doesn't
// like this (and `TryFromIter` is not a thing atm)
fn sections_to_image(
sections: &[Section],
) -> Result<(Image<'static>, SectionCache<'static>), Error> {
let mut image_cache = SectionCache::new(Some("image_cache")).map_err(error_from_pt_error)?;
let mut image = Image::new(Some("image")).map_err(error_from_pt_error)?;

for s in sections {
let isid = image_cache.add_file(&s.file_path, s.file_offset, s.size, s.virtual_address);
if let Err(e) = isid {
log::warn!(
"Error while caching {} {} - skipped",
s.file_path,
e.to_string()
);
continue;
}

if let Err(e) = image.add_cached(&mut image_cache, isid.unwrap(), Asid::default()) {
log::warn!(
"Error while adding cache to image {} {} - skipped",
s.file_path,
e.to_string()
);
continue;
}
}

Ok((image, image_cache))
}
Loading

0 comments on commit 47f7978

Please sign in to comment.