Intel PT minor fixes/improvements (#2724)
* waitpid_filtered to ignore SIGWINCH * Fix warnings unused manifest key: *.version * Add export_raw feature to libafl_intelpt * derive Debug for IntelPTHook * Clippy * Update target program ELF offsets * Add comment to KVM pt_mode check * refactor * Add intel_pt_export_raw feature in libafl * map_error instead of unwrap * borrow checker friendly join_split_trace and copy trace before deocde to prevent decoding failures * Set ip_filters (also) with builder * Move trace to file * Fix Cargo.toml docs * Ignore blocks with no instruction most likely they are filtered out
This commit is contained in:
parent
95d87bd7d8
commit
36734083f9
@ -84,24 +84,25 @@ pub fn main() {
|
|||||||
// A fuzzer with feedbacks and a corpus scheduler
|
// A fuzzer with feedbacks and a corpus scheduler
|
||||||
let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective);
|
let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective);
|
||||||
|
|
||||||
let mut intel_pt = IntelPT::builder().cpu(cpu.0).inherit(true).build().unwrap();
|
|
||||||
|
|
||||||
// The target is a ET_DYN elf, it will be relocated by the loader with this offset.
|
// The target is a ET_DYN elf, it will be relocated by the loader with this offset.
|
||||||
// see https://github.com/torvalds/linux/blob/c1e939a21eb111a6d6067b38e8e04b8809b64c4e/arch/x86/include/asm/elf.h#L234C1-L239C38
|
// see https://github.com/torvalds/linux/blob/c1e939a21eb111a6d6067b38e8e04b8809b64c4e/arch/x86/include/asm/elf.h#L234C1-L239C38
|
||||||
const DEFAULT_MAP_WINDOW: usize = (1 << 47) - PAGE_SIZE;
|
const DEFAULT_MAP_WINDOW: usize = (1 << 47) - PAGE_SIZE;
|
||||||
const ELF_ET_DYN_BASE: usize = DEFAULT_MAP_WINDOW / 3 * 2 & !(PAGE_SIZE - 1);
|
const ELF_ET_DYN_BASE: usize = (DEFAULT_MAP_WINDOW / 3 * 2) & !(PAGE_SIZE - 1);
|
||||||
|
|
||||||
// Set the instruction pointer (IP) filter and memory image of our target.
|
// Set the instruction pointer (IP) filter and memory image of our target.
|
||||||
// These information can be retrieved from `readelf -l` (for example)
|
// These information can be retrieved from `readelf -l` (for example)
|
||||||
let code_memory_addresses = ELF_ET_DYN_BASE + 0x14000..=ELF_ET_DYN_BASE + 0x14000 + 0x40000;
|
let code_memory_addresses = ELF_ET_DYN_BASE + 0x15000..=ELF_ET_DYN_BASE + 0x14000 + 0x41000;
|
||||||
|
|
||||||
intel_pt
|
let intel_pt = IntelPT::builder()
|
||||||
.set_ip_filters(&[code_memory_addresses.clone()])
|
.cpu(cpu.0)
|
||||||
|
.inherit(true)
|
||||||
|
.ip_filters(&[code_memory_addresses.clone()])
|
||||||
|
.build()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let sections = [Section {
|
let sections = [Section {
|
||||||
file_path: target_path.to_string_lossy().to_string(),
|
file_path: target_path.to_string_lossy().to_string(),
|
||||||
file_offset: 0x13000,
|
file_offset: 0x14000,
|
||||||
size: (*code_memory_addresses.end() - *code_memory_addresses.start() + 1) as u64,
|
size: (*code_memory_addresses.end() - *code_memory_addresses.start() + 1) as u64,
|
||||||
virtual_address: *code_memory_addresses.start() as u64,
|
virtual_address: *code_memory_addresses.start() as u64,
|
||||||
}];
|
}];
|
||||||
|
@ -115,6 +115,8 @@ intel_pt = [
|
|||||||
"dep:nix",
|
"dep:nix",
|
||||||
"dep:num_enum",
|
"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"]
|
||||||
|
|
||||||
## Enables features for corpus minimization
|
## Enables features for corpus minimization
|
||||||
cmin = ["z3"]
|
cmin = ["z3"]
|
||||||
|
@ -29,7 +29,19 @@ use libafl_bolts::{
|
|||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
use libc::STDIN_FILENO;
|
use libc::STDIN_FILENO;
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
use nix::unistd::Pid;
|
use nix::{
|
||||||
|
errno::Errno,
|
||||||
|
sys::{
|
||||||
|
ptrace,
|
||||||
|
signal::Signal,
|
||||||
|
wait::WaitStatus,
|
||||||
|
wait::{
|
||||||
|
waitpid, WaitPidFlag,
|
||||||
|
WaitStatus::{Exited, PtraceEvent, Signaled, Stopped},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
unistd::Pid,
|
||||||
|
};
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
use typed_builder::TypedBuilder;
|
use typed_builder::TypedBuilder;
|
||||||
|
|
||||||
@ -204,8 +216,6 @@ where
|
|||||||
match unsafe { fork() } {
|
match unsafe { fork() } {
|
||||||
Ok(ForkResult::Parent { child }) => Ok(child),
|
Ok(ForkResult::Parent { child }) => Ok(child),
|
||||||
Ok(ForkResult::Child) => {
|
Ok(ForkResult::Child) => {
|
||||||
ptrace::traceme().unwrap();
|
|
||||||
|
|
||||||
if let Some(c) = self.cpu {
|
if let Some(c) = self.cpu {
|
||||||
c.set_affinity_forced().unwrap();
|
c.set_affinity_forced().unwrap();
|
||||||
}
|
}
|
||||||
@ -241,6 +251,7 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ptrace::traceme().unwrap();
|
||||||
// After this STOP, the process is traced with PTrace (no hooks yet)
|
// After this STOP, the process is traced with PTrace (no hooks yet)
|
||||||
raise(Signal::SIGSTOP).unwrap();
|
raise(Signal::SIGSTOP).unwrap();
|
||||||
|
|
||||||
@ -405,13 +416,13 @@ where
|
|||||||
T: CommandConfigurator<<S::Corpus as Corpus>::Input>,
|
T: CommandConfigurator<<S::Corpus as Corpus>::Input>,
|
||||||
{
|
{
|
||||||
#[inline]
|
#[inline]
|
||||||
fn set_timeout(&mut self, timeout: Duration) {
|
fn timeout(&self) -> Duration {
|
||||||
*self.configurer.exec_timeout_mut() = timeout;
|
self.configurer.exec_timeout()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn timeout(&self) -> Duration {
|
fn set_timeout(&mut self, timeout: Duration) {
|
||||||
self.configurer.exec_timeout()
|
*self.configurer.exec_timeout_mut() = timeout;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -437,32 +448,28 @@ where
|
|||||||
_mgr: &mut EM,
|
_mgr: &mut EM,
|
||||||
input: &Self::Input,
|
input: &Self::Input,
|
||||||
) -> Result<ExitKind, Error> {
|
) -> Result<ExitKind, Error> {
|
||||||
use nix::sys::{
|
|
||||||
ptrace,
|
|
||||||
signal::Signal,
|
|
||||||
wait::{
|
|
||||||
waitpid, WaitPidFlag,
|
|
||||||
WaitStatus::{Exited, PtraceEvent, Signaled, Stopped},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
*state.executions_mut() += 1;
|
*state.executions_mut() += 1;
|
||||||
|
|
||||||
let child = self.configurer.spawn_child(input)?;
|
let child = self.configurer.spawn_child(input)?;
|
||||||
|
|
||||||
let wait_status = waitpid(child, Some(WaitPidFlag::WUNTRACED))?;
|
let wait_status = waitpid_filtered(child, Some(WaitPidFlag::WUNTRACED))?;
|
||||||
if !matches!(wait_status, Stopped(c, Signal::SIGSTOP) if c == child) {
|
if !matches!(wait_status, Stopped(c, Signal::SIGSTOP) if c == child) {
|
||||||
return Err(Error::unknown("Unexpected state of child process"));
|
return Err(Error::unknown(format!(
|
||||||
|
"Unexpected state of child process {wait_status:?} (while waiting for SIGSTOP)"
|
||||||
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
ptrace::setoptions(child, ptrace::Options::PTRACE_O_TRACEEXEC)?;
|
let options = ptrace::Options::PTRACE_O_TRACEEXEC | ptrace::Options::PTRACE_O_EXITKILL;
|
||||||
|
ptrace::setoptions(child, options)?;
|
||||||
ptrace::cont(child, None)?;
|
ptrace::cont(child, None)?;
|
||||||
|
|
||||||
let wait_status = waitpid(child, None)?;
|
let wait_status = waitpid_filtered(child, None)?;
|
||||||
if !matches!(wait_status, PtraceEvent(c, Signal::SIGTRAP, e)
|
if !matches!(wait_status, PtraceEvent(c, Signal::SIGTRAP, e)
|
||||||
if c == child && e == (ptrace::Event::PTRACE_EVENT_EXEC as i32)
|
if c == child && e == (ptrace::Event::PTRACE_EVENT_EXEC as i32)
|
||||||
) {
|
) {
|
||||||
return Err(Error::unknown("Unexpected state of child process"));
|
return Err(Error::unknown(format!(
|
||||||
|
"Unexpected state of child process {wait_status:?} (while waiting for SIGTRAP PTRACE_EVENT_EXEC)"
|
||||||
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
self.observers.pre_exec_child_all(state, input)?;
|
self.observers.pre_exec_child_all(state, input)?;
|
||||||
@ -471,6 +478,8 @@ where
|
|||||||
}
|
}
|
||||||
self.hooks.pre_exec_all(state, input);
|
self.hooks.pre_exec_all(state, input);
|
||||||
|
|
||||||
|
// todo: it might be better to keep the target ptraced in case the target handles sigalarm,
|
||||||
|
// breaking the libafl timeout
|
||||||
ptrace::detach(child, None)?;
|
ptrace::detach(child, None)?;
|
||||||
let res = match waitpid(child, None)? {
|
let res = match waitpid(child, None)? {
|
||||||
Exited(pid, 0) if pid == child => ExitKind::Ok,
|
Exited(pid, 0) if pid == child => ExitKind::Ok,
|
||||||
@ -478,9 +487,9 @@ where
|
|||||||
Signaled(pid, Signal::SIGALRM, _has_coredump) if pid == child => ExitKind::Timeout,
|
Signaled(pid, Signal::SIGALRM, _has_coredump) if pid == child => ExitKind::Timeout,
|
||||||
Signaled(pid, Signal::SIGABRT, _has_coredump) if pid == child => ExitKind::Crash,
|
Signaled(pid, Signal::SIGABRT, _has_coredump) if pid == child => ExitKind::Crash,
|
||||||
Signaled(pid, Signal::SIGKILL, _has_coredump) if pid == child => ExitKind::Oom,
|
Signaled(pid, Signal::SIGKILL, _has_coredump) if pid == child => ExitKind::Oom,
|
||||||
Stopped(pid, Signal::SIGALRM) if pid == child => ExitKind::Timeout,
|
// Stopped(pid, Signal::SIGALRM) if pid == child => ExitKind::Timeout,
|
||||||
Stopped(pid, Signal::SIGABRT) if pid == child => ExitKind::Crash,
|
// Stopped(pid, Signal::SIGABRT) if pid == child => ExitKind::Crash,
|
||||||
Stopped(pid, Signal::SIGKILL) if pid == child => ExitKind::Oom,
|
// Stopped(pid, Signal::SIGKILL) if pid == child => ExitKind::Oom,
|
||||||
s => {
|
s => {
|
||||||
// TODO other cases?
|
// TODO other cases?
|
||||||
return Err(Error::unsupported(
|
return Err(Error::unsupported(
|
||||||
@ -855,6 +864,22 @@ pub trait CommandConfigurator<I, C = Child>: Sized {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// waitpid wrapper that ignores some signals sent by the ptraced child
|
||||||
|
#[cfg(all(feature = "std", target_os = "linux"))]
|
||||||
|
fn waitpid_filtered(pid: Pid, options: Option<WaitPidFlag>) -> Result<WaitStatus, Errno> {
|
||||||
|
loop {
|
||||||
|
let wait_status = waitpid(pid, options);
|
||||||
|
let sig = match &wait_status {
|
||||||
|
// IGNORED
|
||||||
|
Ok(Stopped(c, Signal::SIGWINCH)) if *c == pid => Signal::SIGWINCH,
|
||||||
|
// RETURNED
|
||||||
|
Ok(ws) => break Ok(*ws),
|
||||||
|
Err(e) => break Err(*e),
|
||||||
|
};
|
||||||
|
ptrace::cont(pid, sig)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::{
|
use crate::{
|
||||||
|
@ -30,7 +30,7 @@ pub struct Section {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Hook to enable Intel Processor Trace (PT) tracing
|
/// Hook to enable Intel Processor Trace (PT) tracing
|
||||||
#[derive(TypedBuilder)]
|
#[derive(Debug, TypedBuilder)]
|
||||||
pub struct IntelPTHook<T> {
|
pub struct IntelPTHook<T> {
|
||||||
#[builder(default = IntelPT::builder().build().unwrap())]
|
#[builder(default = IntelPT::builder().build().unwrap())]
|
||||||
intel_pt: IntelPT,
|
intel_pt: IntelPT,
|
||||||
@ -40,17 +40,6 @@ pub struct IntelPTHook<T> {
|
|||||||
map_len: usize,
|
map_len: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
//fixme: just derive(Debug) once https://github.com/sum-catnip/libipt-rs/pull/4 will be on crates.io
|
|
||||||
impl<T> Debug for IntelPTHook<T> {
|
|
||||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
|
|
||||||
f.debug_struct("IntelPTHook")
|
|
||||||
.field("intel_pt", &self.intel_pt)
|
|
||||||
.field("map_ptr", &self.map_ptr)
|
|
||||||
.field("map_len", &self.map_len)
|
|
||||||
.finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<S, T> ExecutorHook<S> for IntelPTHook<T>
|
impl<S, T> ExecutorHook<S> for IntelPTHook<T>
|
||||||
where
|
where
|
||||||
S: UsesInput + Serialize,
|
S: UsesInput + Serialize,
|
||||||
@ -63,13 +52,19 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn post_exec(&mut self, _state: &mut S, _input: &S::Input) {
|
fn post_exec(&mut self, _state: &mut S, _input: &S::Input) {
|
||||||
self.intel_pt.disable_tracing().unwrap();
|
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 slice = unsafe { &mut *slice_from_raw_parts_mut(self.map_ptr, self.map_len) };
|
||||||
let _ = self
|
let _ = pt
|
||||||
.intel_pt
|
|
||||||
.decode_traces_into_map(&mut self.image.0, slice)
|
.decode_traces_into_map(&mut self.image.0, slice)
|
||||||
.inspect_err(|e| log::warn!("Intel PT trace decoding failed: {e}"));
|
.inspect_err(|e| log::warn!("Intel PT trace decoding failed: {e}"));
|
||||||
|
#[cfg(feature = "intel_pt_export_raw")]
|
||||||
|
{
|
||||||
|
let _ = pt
|
||||||
|
.dump_last_trace_to_file()
|
||||||
|
.inspect_err(|e| log::warn!("Intel PT trace save to file failed: {e}"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,7 +44,7 @@ cmake = { workspace = true }
|
|||||||
bindgen = { workspace = true }
|
bindgen = { workspace = true }
|
||||||
regex = { workspace = true }
|
regex = { workspace = true }
|
||||||
which = { workspace = true }
|
which = { workspace = true }
|
||||||
symcc_libafl = { workspace = true, default-features = true, version = "0.14.1" }
|
symcc_libafl = { workspace = true, default-features = true }
|
||||||
|
|
||||||
[lints]
|
[lints]
|
||||||
workspace = true
|
workspace = true
|
||||||
|
@ -15,6 +15,8 @@ default = ["std", "libipt"]
|
|||||||
std = ["libafl_bolts/std"]
|
std = ["libafl_bolts/std"]
|
||||||
|
|
||||||
libipt = ["std", "dep:libipt"]
|
libipt = ["std", "dep:libipt"]
|
||||||
|
## Export raw Intel PT traces on decode, useful for debug, disabled by default for best performance
|
||||||
|
export_raw = []
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
static_assertions = { workspace = true }
|
static_assertions = { workspace = true }
|
||||||
|
@ -19,6 +19,7 @@ use std::{
|
|||||||
};
|
};
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
use std::{
|
use std::{
|
||||||
|
boxed::Box,
|
||||||
ffi::{CStr, CString},
|
ffi::{CStr, CString},
|
||||||
fmt::Debug,
|
fmt::Debug,
|
||||||
format, fs,
|
format, fs,
|
||||||
@ -129,6 +130,8 @@ pub struct IntelPT {
|
|||||||
aux_tail: *mut u64,
|
aux_tail: *mut u64,
|
||||||
previous_decode_head: u64,
|
previous_decode_head: u64,
|
||||||
ip_filters: Vec<RangeInclusive<usize>>,
|
ip_filters: Vec<RangeInclusive<usize>>,
|
||||||
|
#[cfg(feature = "export_raw")]
|
||||||
|
last_decode_trace: Vec<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
@ -302,8 +305,12 @@ impl IntelPT {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Head pointer wrapped, the trace is split
|
// Head pointer wrapped, the trace is split
|
||||||
unsafe { self.join_split_trace(head_wrap, tail_wrap) }
|
OwnedRefMut::Owned(self.join_split_trace(head_wrap, tail_wrap))
|
||||||
};
|
};
|
||||||
|
#[cfg(feature = "export_raw")]
|
||||||
|
{
|
||||||
|
self.last_decode_trace = data.as_ref().to_vec();
|
||||||
|
}
|
||||||
|
|
||||||
let mut config = ConfigBuilder::new(data.as_mut()).map_err(error_from_pt_error)?;
|
let mut config = ConfigBuilder::new(data.as_mut()).map_err(error_from_pt_error)?;
|
||||||
config.filter(self.ip_filters_to_addr_filter());
|
config.filter(self.ip_filters_to_addr_filter());
|
||||||
@ -351,19 +358,17 @@ impl IntelPT {
|
|||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
unsafe fn join_split_trace(&self, head_wrap: u64, tail_wrap: u64) -> OwnedRefMut<[u8]> {
|
fn join_split_trace(&self, head_wrap: u64, tail_wrap: u64) -> Box<[u8]> {
|
||||||
let first_ptr = self.perf_aux_buffer.add(tail_wrap as usize) as *mut u8;
|
let first_ptr = unsafe { self.perf_aux_buffer.add(tail_wrap as usize) as *mut u8 };
|
||||||
let first_len = self.perf_aux_buffer_size - tail_wrap as usize;
|
let first_len = self.perf_aux_buffer_size - tail_wrap as usize;
|
||||||
|
|
||||||
let second_ptr = self.perf_aux_buffer as *mut u8;
|
let second_ptr = self.perf_aux_buffer as *mut u8;
|
||||||
let second_len = head_wrap as usize;
|
let second_len = head_wrap as usize;
|
||||||
OwnedRefMut::Owned(
|
|
||||||
[
|
let mut vec = Vec::with_capacity(first_len + second_len);
|
||||||
slice::from_raw_parts(first_ptr, first_len),
|
vec.extend_from_slice(unsafe { slice::from_raw_parts(first_ptr, first_len) });
|
||||||
slice::from_raw_parts(second_ptr, second_len),
|
vec.extend_from_slice(unsafe { slice::from_raw_parts(second_ptr, second_len) });
|
||||||
]
|
vec.into_boxed_slice()
|
||||||
.concat()
|
|
||||||
.into_boxed_slice(),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
@ -395,7 +400,7 @@ impl IntelPT {
|
|||||||
*status = s;
|
*status = s;
|
||||||
let offset = decoder.offset().map_err(error_from_pt_error)?;
|
let offset = decoder.offset().map_err(error_from_pt_error)?;
|
||||||
|
|
||||||
if !b.speculative() && skip < offset {
|
if b.ninsn() > 0 && !b.speculative() && skip < offset {
|
||||||
let id = hash_me(*previous_block_end_ip) ^ hash_me(b.ip());
|
let id = hash_me(*previous_block_end_ip) ^ hash_me(b.ip());
|
||||||
// SAFETY: the index is < map.len() since the modulo operation is applied
|
// SAFETY: the index is < map.len() since the modulo operation is applied
|
||||||
let map_loc = unsafe { map.get_unchecked_mut(id as usize % map.len()) };
|
let map_loc = unsafe { map.get_unchecked_mut(id as usize % map.len()) };
|
||||||
@ -416,6 +421,29 @@ impl IntelPT {
|
|||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the raw trace used in the last decoding
|
||||||
|
#[cfg(feature = "export_raw")]
|
||||||
|
pub fn last_decode_trace(&self) -> Vec<u8> {
|
||||||
|
self.last_decode_trace.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Dump the raw trace used in the last decoding to the file
|
||||||
|
/// /// `./traces/trace_<unix epoch in micros>`
|
||||||
|
#[cfg(feature = "export_raw")]
|
||||||
|
pub fn dump_last_trace_to_file(&self) -> Result<(), Error> {
|
||||||
|
use std::{fs, io::Write, path::Path, time};
|
||||||
|
|
||||||
|
let traces_dir = Path::new("traces");
|
||||||
|
fs::create_dir_all(traces_dir)?;
|
||||||
|
let timestamp = time::SystemTime::now()
|
||||||
|
.duration_since(time::UNIX_EPOCH)
|
||||||
|
.map_err(|e| Error::unknown(e.to_string()))?
|
||||||
|
.as_micros();
|
||||||
|
let mut file = fs::File::create(traces_dir.join(format!("trace_{timestamp}")))?;
|
||||||
|
file.write_all(&self.last_decode_trace())?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
@ -441,6 +469,7 @@ pub struct IntelPTBuilder {
|
|||||||
inherit: bool,
|
inherit: bool,
|
||||||
perf_buffer_size: usize,
|
perf_buffer_size: usize,
|
||||||
perf_aux_buffer_size: usize,
|
perf_aux_buffer_size: usize,
|
||||||
|
ip_filters: Vec<RangeInclusive<usize>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
@ -450,14 +479,15 @@ impl Default for IntelPTBuilder {
|
|||||||
/// The default configuration corresponds to:
|
/// The default configuration corresponds to:
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use libafl_intelpt::{IntelPTBuilder, PAGE_SIZE};
|
/// use libafl_intelpt::{IntelPTBuilder, PAGE_SIZE};
|
||||||
/// let builder = unsafe { std::mem::zeroed::<IntelPTBuilder>() }
|
/// let builder = IntelPTBuilder::default()
|
||||||
/// .pid(None)
|
/// .pid(None)
|
||||||
/// .all_cpus()
|
/// .all_cpus()
|
||||||
/// .exclude_kernel(true)
|
/// .exclude_kernel(true)
|
||||||
/// .exclude_hv(true)
|
/// .exclude_hv(true)
|
||||||
/// .inherit(false)
|
/// .inherit(false)
|
||||||
/// .perf_buffer_size(128 * PAGE_SIZE + PAGE_SIZE).unwrap()
|
/// .perf_buffer_size(128 * PAGE_SIZE + PAGE_SIZE).unwrap()
|
||||||
/// .perf_aux_buffer_size(2 * 1024 * 1024).unwrap();
|
/// .perf_aux_buffer_size(2 * 1024 * 1024).unwrap()
|
||||||
|
/// .ip_filters(&[]);
|
||||||
/// assert_eq!(builder, IntelPTBuilder::default());
|
/// assert_eq!(builder, IntelPTBuilder::default());
|
||||||
/// ```
|
/// ```
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
@ -469,6 +499,7 @@ impl Default for IntelPTBuilder {
|
|||||||
inherit: false,
|
inherit: false,
|
||||||
perf_buffer_size: 128 * PAGE_SIZE + PAGE_SIZE,
|
perf_buffer_size: 128 * PAGE_SIZE + PAGE_SIZE,
|
||||||
perf_aux_buffer_size: 2 * 1024 * 1024,
|
perf_aux_buffer_size: 2 * 1024 * 1024,
|
||||||
|
ip_filters: Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -532,9 +563,7 @@ impl IntelPTBuilder {
|
|||||||
let aux_head = unsafe { &raw mut (*buff_metadata).aux_head };
|
let aux_head = unsafe { &raw mut (*buff_metadata).aux_head };
|
||||||
let aux_tail = unsafe { &raw mut (*buff_metadata).aux_tail };
|
let aux_tail = unsafe { &raw mut (*buff_metadata).aux_tail };
|
||||||
|
|
||||||
let ip_filters = Vec::with_capacity(*NR_ADDR_FILTERS.as_ref().unwrap_or(&0) as usize);
|
let mut intel_pt = IntelPT {
|
||||||
|
|
||||||
Ok(IntelPT {
|
|
||||||
fd,
|
fd,
|
||||||
perf_buffer,
|
perf_buffer,
|
||||||
perf_aux_buffer,
|
perf_aux_buffer,
|
||||||
@ -543,8 +572,14 @@ impl IntelPTBuilder {
|
|||||||
aux_head,
|
aux_head,
|
||||||
aux_tail,
|
aux_tail,
|
||||||
previous_decode_head: 0,
|
previous_decode_head: 0,
|
||||||
ip_filters,
|
ip_filters: Vec::with_capacity(*NR_ADDR_FILTERS.as_ref().unwrap_or(&0) as usize),
|
||||||
})
|
#[cfg(feature = "export_raw")]
|
||||||
|
last_decode_trace: Vec::new(),
|
||||||
|
};
|
||||||
|
if !self.ip_filters.is_empty() {
|
||||||
|
intel_pt.set_ip_filters(&self.ip_filters)?;
|
||||||
|
}
|
||||||
|
Ok(intel_pt)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Warn if the configuration is not recommended
|
/// Warn if the configuration is not recommended
|
||||||
@ -638,6 +673,15 @@ impl IntelPTBuilder {
|
|||||||
self.perf_aux_buffer_size = perf_aux_buffer_size;
|
self.perf_aux_buffer_size = perf_aux_buffer_size;
|
||||||
Ok(self)
|
Ok(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
/// Set filters based on Instruction Pointer (IP)
|
||||||
|
///
|
||||||
|
/// Only instructions in `filters` ranges will be traced.
|
||||||
|
pub fn ip_filters(mut self, filters: &[RangeInclusive<usize>]) -> Self {
|
||||||
|
self.ip_filters = filters.to_vec();
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Perf event config for `IntelPT`
|
/// Perf event config for `IntelPT`
|
||||||
@ -717,6 +761,9 @@ pub fn availability_in_qemu_kvm() -> Result<(), String> {
|
|||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
{
|
{
|
||||||
let kvm_pt_mode_path = "/sys/module/kvm_intel/parameters/pt_mode";
|
let kvm_pt_mode_path = "/sys/module/kvm_intel/parameters/pt_mode";
|
||||||
|
// Ignore the case when the file does not exist since it has been removed.
|
||||||
|
// KVM default is `System` mode
|
||||||
|
// https://lore.kernel.org/all/20241101185031.1799556-1-seanjc@google.com/t/#u
|
||||||
if let Ok(s) = fs::read_to_string(kvm_pt_mode_path) {
|
if let Ok(s) = fs::read_to_string(kvm_pt_mode_path) {
|
||||||
match s.trim().parse::<i32>().map(TryInto::try_into) {
|
match s.trim().parse::<i32>().map(TryInto::try_into) {
|
||||||
Ok(Ok(KvmPTMode::System)) => (),
|
Ok(Ok(KvmPTMode::System)) => (),
|
||||||
|
@ -90,7 +90,7 @@ clippy = ["libafl_qemu_sys/clippy"]
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
libafl = { workspace = true, features = ["std", "derive", "regex"] }
|
libafl = { workspace = true, features = ["std", "derive", "regex"] }
|
||||||
libafl_bolts = { workspace = true, features = ["std", "derive"] }
|
libafl_bolts = { workspace = true, features = ["std", "derive"] }
|
||||||
libafl_targets = { workspace = true, default-features = true, version = "0.14.1" }
|
libafl_targets = { workspace = true, default-features = true }
|
||||||
libafl_qemu_sys = { workspace = true }
|
libafl_qemu_sys = { workspace = true }
|
||||||
libafl_derive = { workspace = true, default-features = true }
|
libafl_derive = { workspace = true, default-features = true }
|
||||||
|
|
||||||
@ -129,7 +129,7 @@ getset = "0.1.3"
|
|||||||
document-features = { workspace = true, optional = true }
|
document-features = { workspace = true, optional = true }
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
libafl_qemu_build = { workspace = true, default-features = true, version = "0.14.1" }
|
libafl_qemu_build = { workspace = true, default-features = true }
|
||||||
pyo3-build-config = { workspace = true, optional = true }
|
pyo3-build-config = { workspace = true, optional = true }
|
||||||
rustversion = { workspace = true }
|
rustversion = { workspace = true }
|
||||||
bindgen = { workspace = true }
|
bindgen = { workspace = true }
|
||||||
|
Loading…
x
Reference in New Issue
Block a user