FRET-LibAFL/libafl_intelpt/tests/integration_tests_linux.rs
Marco C. f7f8dff6cd
Add Intel PT tracing support (#2471)
* WIP: IntelPT qemu systemmode

* use perf-event-open-sys instead of bindgen

* intelPT Add enable and disable tracing, add test

* Use static_assertions crate

* Fix volatiles, finish test

* Add Intel PT availability check

* Use LibAFL errors in Result

* Improve filtering

* Add KVM pt_mode check

* move static_assertions use

* Check for perf_event_open support

* Add (empty) IntelPT module

* Add IntelPTModule POC

* partial ideas to implement intel pt

* forgot smth

* trace decoding draft

* add libipt decoder

* use cpuid instead of reading /proc/cpuinfo

* investigating nondeterministic behaviour

* intel_pt module add thread creation hook

* Fully identify deps versions

Cargo docs: Although it looks like a specific version of the crate, it actually specifies a range of versions and allows SemVer compatible updates

* Move mem image to module, output to file for debug

* fixup! Use static_assertions crate

* Exclude host kernel from traces

* Bump libipt-rs

* Callback to get memory as an alterantive to image

* WIP Add bootloader fuzzer example

* Split availability check: add availability_with_qemu

* Move IntelPT to observer

* Improve test docs

* Clippy happy now

* Taplo happy now

* Add IntelPTObserver boilerplate

* Hook instead of Observer

* Clippy & Taplo

* Add psb_freq setting

* Extremely bad and dirty babyfuzzer stealing

* Use thread local cell instead of mutex

* Try a trace diff based naive feedback

* fix perf aux buffer wrap handling

* Use f64 for feedback score

* Fix clippy for cargo test

* Add config format tests

* WIP intelpt babyfuzzer with fork

* Fix not wrapped tail offset in split buffer

* Baby PT with raw traces diff working

* Cache nr_filters

* Use Lazy_lock for perf_type

* Add baby_fuzzer_intel_pt

* restore baby fuzzer

* baby_fuzzer with block decoder

* instruction decoder instead of block

* Fix after upstream merge

* OwnedRefMut instead of Cow

* Read mem directly instead of going through files

* Fix cache lifetime and tail update

* clippy

* Taplo

* Compile caps only on linux

* clippy

* Fail compilation on unsupported OSes

* Add baby_fuzzer_intel_pt to CI

* Cleanup

* Move intel pt + linux check

* fix baby pt

* rollback forkexecutor

* Remove unused dep

* Cleanup

* Lints

* Compute an edge id instead of using only block ip

* Binary only intelPT POC

* put linux specific code behind target_os=linux

* Clippy & Taplo

* fix CI

* Disable relocation

* No unwrap in decode

* No expect in decode

* Better logging, smaller aux buffer

* add IntelPTBuilder

* some lints

* Add exclude_hv config

* Per CPU tracing and inheritance

* Parametrize buffer size

* Try not to break commandExecutor API pt.1

* Try not to break commandExecutor API pt.2

* Try not to break commandExecutor API pt.3

* fix baby PT

* Support on_crash & on_timeout callbacks for libafl_qemu modules (#2620)

* support (unsafe) on_crash / on_timeout callbacks for modules

* use libc types in bindgen

* Move common code to bolts

* Cleanup

* Revert changes to backtrace_baby_fuzzers/command_executor

* Move intel_pt in one file

* Use workspace deps

* add nr_addr_filter fallback

* Cleaning

* Improve decode

* Clippy

* Improve errors and docs

* Impl from<PtError> for libafl::Error

* Merge hooks

* Docs

* Clean command executor

* fix baby PT

* fix baby PT warnings

* decoder fills the map with no vec alloc

* WIP command executor intel PT

* filter_map() instead of filter().map()

* fix docs

* fix windows?

* Baby lints

* Small cleanings

* Use personality to disable ASLR at runtime

* Fix nix dep

* Use prc-maps in babyfuzzer

* working ET_DYN elf

* Cleanup Cargo.toml

* Clean command executor

* introduce PtraceCommandConfigurator

* Fix clippy & taplo

* input via stdin

* libipt as workspace dep

* Check kernel version

* support Arg input location

* Reorder stuff

* File input

* timeout support for PtraceExec

* Lints

* Move out method not needing self form IntelPT

* unimplemented

* Lints

* Move intel_pt_baby_fuzzer

* Move intel_pt_command_executor

* Document the need for smp_rmb

* Better comment

* Readme and Makefile.toml instead of build.rs

* Move out from libafl_bolts to libafl_intelpt

* Fix hooks

* (Almost) fix intel_pt command exec

* fix intel_pt command exec debug

* Fix baby_fuzzer

* &raw over addr_of!

* cfg(target_os = "linux")

* bolts Cargo.toml leftover

* minimum wage README.md

* extract join_split_trace from decode

* extract decode_block from decode

* add 1 to `previous_block_ip` to avoid that all the recursive basic blocks map to 0

* More generic hook

* fix windows

* Update CI, fmt

* No bitbybit

* Fix docker?

* Fix Apple silicon?

* Use old libipt from crates.io

---------

Co-authored-by: Romain Malmain <romain.malmain@pm.me>
Co-authored-by: Dominik Maier <domenukk@gmail.com>
2024-11-12 22:34:46 -03:00

96 lines
3.1 KiB
Rust

#![cfg(feature = "std")]
#![cfg(feature = "libipt")]
#![cfg(target_os = "linux")]
use std::{arch::asm, process};
use libafl_intelpt::{availability, IntelPT};
use libipt::Image;
use nix::{
sys::{
signal::{kill, raise, Signal},
wait::{waitpid, WaitPidFlag},
},
unistd::{fork, ForkResult},
};
use proc_maps::get_process_maps;
/// To run this test ensure that the executable has the required capabilities.
/// This can be achieved with the script `./run_integration_tests_linux_with_caps.sh`
#[test]
fn intel_pt_trace_fork() {
if let Err(reason) = availability() {
// Mark as `skipped` once this will be possible https://github.com/rust-lang/rust/issues/68007
println!("Intel PT is not available, skipping test. Reasons:");
println!("{reason}");
return;
}
let pid = match unsafe { fork() } {
Ok(ForkResult::Parent { child }) => child,
Ok(ForkResult::Child) => {
raise(Signal::SIGSTOP).expect("Failed to stop the process");
// This will generate a sequence of tnt packets containing 255 taken branches
unsafe {
let mut count = 0;
asm!(
"2:",
"add {0:r}, 1",
"cmp {0:r}, 255",
"jle 2b",
inout(reg) count,
options(nostack)
);
let _ = count;
}
process::exit(0);
}
Err(e) => panic!("Fork failed {e}"),
};
let pt_builder = IntelPT::builder().pid(Some(pid.as_raw()));
let mut pt = pt_builder.build().expect("Failed to create IntelPT");
pt.enable_tracing().expect("Failed to enable tracing");
waitpid(pid, Some(WaitPidFlag::WUNTRACED)).expect("Failed to wait for the child process");
let maps = get_process_maps(pid.into()).unwrap();
kill(pid, Signal::SIGCONT).expect("Failed to continue the process");
waitpid(pid, None).expect("Failed to wait for the child process");
pt.disable_tracing().expect("Failed to disable tracing");
let mut image = Image::new(Some("test_trace_pid")).unwrap();
for map in maps {
if map.is_exec() && map.filename().is_some() {
match image.add_file(
map.filename().unwrap().to_str().unwrap(),
map.offset as u64,
map.size() as u64,
None,
map.start() as u64,
) {
Err(e) => println!(
"Error adding mapping for {:?}: {:?}, skipping",
map.filename().unwrap(),
e
),
Ok(()) => println!(
"mapping for {:?} added successfully {:#x} - {:#x}",
map.filename().unwrap(),
map.start(),
map.start() + map.size()
),
}
}
}
let mut map = vec![0u16; 0x10_00];
pt.decode_traces_into_map(&mut image, &mut map).unwrap();
let assembly_jump_id = map.iter().position(|count| *count >= 254);
assert!(
assembly_jump_id.is_some(),
"Assembly jumps not found in traces"
);
}