From 93f2a5aa6219162690e928dda161838150476542 Mon Sep 17 00:00:00 2001 From: Marcondiro Date: Sun, 18 Aug 2024 12:54:17 +0000 Subject: [PATCH] trace decoding draft --- libafl_qemu/Cargo.toml | 3 +- .../src/modules/systemmode/intel_pt.rs | 95 ++++++------- libafl_qemu/src/qemu/systemmode/intel_pt.rs | 131 ++++++++++++++---- 3 files changed, 146 insertions(+), 83 deletions(-) diff --git a/libafl_qemu/Cargo.toml b/libafl_qemu/Cargo.toml index b8bd5e0665e..6bde648260b 100644 --- a/libafl_qemu/Cargo.toml +++ b/libafl_qemu/Cargo.toml @@ -131,7 +131,8 @@ memmap2 = "0.9" getset = "0.1" bitflags = "2.6" perf-event-open-sys = "4" # Uses Linux 5.19.4 headers, consider forking if we need to bump -caps = "0.5" # TODO: Mark deps used only in systemmode as optional +caps = "0.5" # TODO: Mark deps used only in systemmode as optional (requires fixing linux_build.rs) +libipt = {git = "https://github.com/sum-catnip/libipt-rs", rev = "1dc124b"} # v2 is not on crates.io # Document all features of this crate (for `cargo doc`) document-features = { version = "0.2", optional = true } diff --git a/libafl_qemu/src/modules/systemmode/intel_pt.rs b/libafl_qemu/src/modules/systemmode/intel_pt.rs index 706f2960e24..4328f3b505a 100644 --- a/libafl_qemu/src/modules/systemmode/intel_pt.rs +++ b/libafl_qemu/src/modules/systemmode/intel_pt.rs @@ -1,9 +1,11 @@ -use libafl_qemu_sys::GuestVirtAddr; -use libafl::inputs::UsesInput; -use libafl::observers::ObserversTuple; -use libafl_targets::EDGES_MAP; -use crate::{modules::{EmulatorModule, ExitKind}, qemu::intel_pt::IntelPT, CpuPreRunHook, EmulatorModules, NewThreadHook}; -use crate::modules::EmulatorModuleTuple; +use libafl::{inputs::UsesInput, observers::ObserversTuple, HasMetadata}; +use libafl_qemu_sys::{CPUArchStatePtr, GuestVirtAddr}; + +use crate::{ + modules::{EmulatorModule, EmulatorModuleTuple, ExitKind}, + qemu::intel_pt::IntelPT, + EmulatorModules, +}; #[derive(Debug)] pub struct IntelPTModule { @@ -16,70 +18,59 @@ impl IntelPTModule { } } -pub fn intel_pt_new_thread( - emulator_modules: &mut EmulatorModules, - state: Option<&mut S>, - env: CPUArchStatePtr, - tid: u32 -) -> Option -where - S: HasMetadata + Unpin + UsesInput, - ET: EmulatorModuleTuple, -{ - // do something each time a thread in created in QEMU - let intel_pt_module: IntelPTModule = match emulator_modules.modules().match_first_type_mut::(); +// pub fn intel_pt_new_thread( +// emulator_modules: &mut EmulatorModules, +// _state: Option<&mut S>, +// _env: CPUArchStatePtr, +// tid: u32 +// ) -> bool +// where +// S: HasMetadata + Unpin + UsesInput, +// ET: EmulatorModuleTuple, +// { +// let intel_pt_module = emulator_modules.modules().match_first_type_mut::().unwrap(); - if let Some(pt) = intel_pt_module.pt { - // update PT state - } -} +// if let Some(pt) = &mut intel_pt_module.pt { +// // update PT state +// } -pub fn intel_pt_pre_cpu_exec( - emulator_modules: &mut EmulatorModules, - state: Option<&mut S>, - cpu: CPUStatePtr, -) -> Option -where - S: HasMetadata + Unpin + UsesInput, - ET: EmulatorModuleTuple, -{ - // do something each time a thread in created in QEMU - let intel_pt_module: IntelPTModule = match emulator_modules.modules().match_first_type_mut::(); - - if let Some(pt) = intel_pt_module.pt { - // update PT state - } -} +// // Why a bool here? +// true +// } impl EmulatorModule for IntelPTModule where - S: Unpin + UsesInput, + S: Unpin + UsesInput + HasMetadata, { fn first_exec(&mut self, emulator_modules: &mut EmulatorModules) where ET: EmulatorModuleTuple, { - emulator_modules.thread_creation( - NewThreadHook::Function(intel_pt_new_thread::) - ); + // emulator_modules.thread_creation( + // NewThreadHook::Function(intel_pt_new_thread::) + // ); - emulator_modules.cpu_runs( - CpuPreRunHook::Function(...), - CpuPostRunHook::Function(...), - ); + // emulator_modules.cpu_runs( + // CpuPostRunHook::Function(...), + // ); } - fn post_exec(&mut self, _emulator_modules: &mut EmulatorModules, _input: &S::Input, _observers: &mut OT, _exit_kind: &mut ExitKind) - where + fn post_exec( + &mut self, + _emulator_modules: &mut EmulatorModules, + _input: &S::Input, + _observers: &mut OT, + _exit_kind: &mut ExitKind, + ) where OT: ObserversTuple, ET: EmulatorModuleTuple, { // 1. decode traces // Output: List of block's IPs we are going through during the fuzzer's run (after filtering) - let indexes: Vec = { - // result of decoding - // use libxdc... - }; + // let indexes: Vec = { + // // result of decoding + // // use libxdc... + // }; // 2. update map // for idx in indexes { diff --git a/libafl_qemu/src/qemu/systemmode/intel_pt.rs b/libafl_qemu/src/qemu/systemmode/intel_pt.rs index 2ae2dce9eac..be9da0f9623 100644 --- a/libafl_qemu/src/qemu/systemmode/intel_pt.rs +++ b/libafl_qemu/src/qemu/systemmode/intel_pt.rs @@ -16,6 +16,7 @@ use std::{ use bitflags::bitflags; use caps::{CapSet, Capability}; use libafl::Error; +use libipt::{block::BlockDecoder, ConfigBuilder}; use num_enum::TryFromPrimitive; use perf_event_open_sys::{ bindings::{perf_event_attr, perf_event_mmap_page, PERF_FLAG_FD_CLOEXEC}, @@ -46,39 +47,26 @@ bitflags! { } } - pub trait IntelPTDecoder { - fn decode(&mut self, traces: &IntelPTTraces) -> IntelPTTracesResult; -} - -pub struct IntelPTTracesResult {} - -pub struct IntelPTTraces {} - -/// Intel official Intel PT trace decoder -pub struct Libipt {} +// pub trait IntelPTDecoder { +// fn decode(&mut self, traces: &IntelPTTraces) -> Result((), Error); +// } -/// kAFL Intel PT trace decoder -pub struct Libxdc {} +// /// Intel official Intel PT trace decoder +// pub struct Libipt {} -impl IntelPTDecoder for Libipt { - fn decode(&mut self, traces: &IntelPTTraces) -> IntelPTTracesResult { - todo!() - } -} - -impl IntelPTDecoder for Libxdc { - fn decode(&mut self, traces: &IntelPTTraces) -> IntelPTTracesResult { - todo!() - } -} +// impl IntelPTDecoder for Libipt { +// fn decode(&mut self, traces: &IntelPTTraces) -> IntelPTTracesResult { +// todo!() +// } +// } +// TODO generic decoder: D, #[derive(Debug)] -pub struct IntelPT { +pub struct IntelPT { fd: OwnedFd, perf_buffer: *mut c_void, perf_aux_buffer: *mut c_void, buff_metadata: *mut perf_event_mmap_page, - decoder: D, } impl IntelPT { @@ -171,7 +159,9 @@ impl IntelPT { } } - pub fn read_trace_into(&self, buff: &mut T) -> Result<(), Error> { + // TODO remove + #[deprecated] + fn read_trace_into(&self, buff: &mut T) -> Result<(), Error> { // TODO: should we read also the normal buffer? let aux_head = unsafe { ptr::addr_of_mut!((*self.buff_metadata).aux_head) }; let aux_tail = unsafe { ptr::addr_of_mut!((*self.buff_metadata).aux_tail) }; @@ -195,6 +185,73 @@ impl IntelPT { Ok(()) } + pub fn decode(&mut self) -> Vec { + let mut ips = Vec::new(); + + let aux_head = unsafe { ptr::addr_of_mut!((*self.buff_metadata).aux_head) }; + let aux_tail = unsafe { ptr::addr_of_mut!((*self.buff_metadata).aux_tail) }; + + let head = wrap_aux_pointer(unsafe { aux_head.read_volatile() }); + let tail = wrap_aux_pointer(unsafe { aux_tail.read_volatile() }); + let data = unsafe { self.perf_aux_buffer.add(tail as usize) } as *mut u8; + let len = (head - tail) as usize; + + debug_assert!(head >= tail, "Intel PT: aux head is behind aux tail"); + + smp_rmb(); // TODO double check impl + + // TODO config.cpu = ; ? + // TODO handle decoding failures with config.decode.callback = ; config.decode.context = ; + // TODO remove unwrap() + let config = ConfigBuilder::new(unsafe { slice::from_raw_parts_mut(data, len) }) + .unwrap() + .finish(); + let mut decoder = BlockDecoder::new(&config).unwrap(); + + // TODO rewrite decently + loop { + let mut status = match decoder.sync_forward() { + Ok(s) => s, + Err(e) => break, + }; + + loop { + if loop { + if !status.event_pending() { + break Ok(()); + } + match decoder.event() { + Ok((_, s)) => { + status = s; + } + Err(e) => break Err(e), + }; + } + .is_err() + { + break; + } + + let packet = decoder.next(); + match packet { + Err(e) => { + println!("pterror in packet next {:?}", e); + break; + } + Ok((b, s)) => { + ips.push(b.ip()); + + if s.eos() { + break; + } + } + } + } + } + // TODO load the binary + ips + } + /// Check if Intel PT is available on the current system. /// /// This function can be helpful when `IntelPT::try_new` or `set_ip_filter` fail for an unclear @@ -239,6 +296,12 @@ impl IntelPT { )); } + // TODO check also the value of perf_event_paranoid, check which values are required by pt + // https://www.kernel.org/doc/Documentation/sysctl/kernel.txt + // also, looks like it is distribution dependent + // https://askubuntu.com/questions/1400874/what-does-perf-paranoia-level-four-do + // CAP_SYS_ADMIN might make this check useless + let kvm_pt_mode_path = "/sys/module/kvm_intel/parameters/pt_mode"; if let Ok(s) = fs::read_to_string(kvm_pt_mode_path) { match s.trim().parse::().map(|i| i.try_into()) { @@ -332,7 +395,7 @@ fn setup_perf_aux_buffer(fd: &OwnedFd, size: u64, offset: u64) -> Result<*mut c_ fn new_perf_event_attr_intel_pt() -> Result { let mut attr: perf_event_attr = unsafe { core::mem::zeroed() }; - attr.size = core::mem::size_of::() as u32; + attr.size = size_of::() as u32; attr.type_ = intel_pt_perf_type()?; attr.set_disabled(1); attr.config |= PtConfig::NORETCOMP.bits(); @@ -375,6 +438,13 @@ const fn wrap_aux_pointer(ptr: u64) -> u64 { ptr & (PERF_AUX_BUFFER_SIZE as u64 - 1) } +#[inline] +pub fn smp_rmb() { + unsafe { + core::arch::asm!("lfence", options(nostack, preserves_flags)); + } +} + #[cfg(test)] mod test { use std::{fs::OpenOptions, process}; @@ -459,9 +529,10 @@ mod test { waitpid(pid, None).expect("Failed to wait for the child process"); + // pt.read_trace_into(&mut file) + // .expect("Failed to write traces"); + // fs::remove_file(trace_path).expect("Failed to remove trace file"); + println!("Intel PT traces: {:?}", pt.decode()); pt.disable_tracing().expect("Failed to disable tracing"); - pt.read_trace_into(&mut file) - .expect("Failed to write traces"); - fs::remove_file(trace_path).expect("Failed to remove trace file"); } }