Skip to content

intel PT: check for availability in VMX #3328

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jul 9, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions crates/libafl_intelpt/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,6 @@
This module is a wrapper around the `IntelPT` kernel driver, exposing functionalities specifically crafted for `LibAFL`.

At the moment only `Linux` hosts are supported.

You can run `sudo -E cargo test intel_pt_check_availability -- --show-output` to check if your host has all the features
used by this crate.
8 changes: 7 additions & 1 deletion crates/libafl_intelpt/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,12 @@ pub fn availability_in_qemu_kvm() -> Result<(), String> {

#[cfg(target_os = "linux")]
{
match availability_in_vmx() {
Err(reason) => reasons.push(reason),
Ok(false) => reasons.push("Intel PT is not available for VMX operations.".to_owned()),
Ok(true) => {}
}

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
Expand Down Expand Up @@ -123,7 +129,7 @@ mod test {

/// Quick way to check if your machine is compatible with Intel PT's features used by libafl
///
/// Simply run `cargo test intel_pt_check_availability -- --show-output`
/// Simply run `sudo -E cargo test intel_pt_check_availability -- --show-output`
#[test]
fn intel_pt_check_availability() {
print!("Intel PT availability:\t\t\t");
Expand Down
29 changes: 26 additions & 3 deletions crates/libafl_intelpt/src/linux.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use alloc::{
use core::{ffi::CStr, fmt::Debug, ops::RangeInclusive, ptr};
use std::{
fs,
io::{Read, Seek, SeekFrom},
os::{
fd::{AsRawFd, FromRawFd, OwnedFd},
raw::c_void,
Expand Down Expand Up @@ -544,7 +545,7 @@ impl Default for IntelPTBuilder {
/// .inherit(false)
/// .perf_buffer_size(128 * PAGE_SIZE + PAGE_SIZE)
/// .unwrap()
/// .perf_aux_buffer_size(2 * 1024 * 1024)
/// .perf_aux_buffer_size(4 * 1024 * 1024)
/// .unwrap()
/// .ip_filters(&[]);
/// assert_eq!(builder, IntelPTBuilder::default());
Expand All @@ -557,7 +558,7 @@ impl Default for IntelPTBuilder {
exclude_hv: true,
inherit: false,
perf_buffer_size: 128 * PAGE_SIZE + PAGE_SIZE,
perf_aux_buffer_size: 2 * 1024 * 1024,
perf_aux_buffer_size: 4 * 1024 * 1024,
ip_filters: Vec::new(),
}
}
Expand Down Expand Up @@ -833,6 +834,28 @@ pub(crate) fn availability_in_linux() -> Result<(), String> {
}
}

/// Check if the `MSR IA32_VMX_MISC` has bit 14 set, which indicates that Intel PT is
/// available in VMX operations.
pub(crate) fn availability_in_vmx() -> Result<bool, String> {
let error_prefix = "Failed to check for PT availability in VM operations: ";
let mut msrs = fs::OpenOptions::new()
.read(true)
.open("/dev/cpu/0/msr")
.map_err(|e| {
format!(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not use proper errors here?

Copy link
Contributor Author

@Marcondiro Marcondiro Jul 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you mean an enum of some kind?
In the end the availability checks are made to display possible reasons of PT unavailability to users, not to other parts of the code so adding enums with display method ecc feels just useless bilerplate

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just the normal libafl Error, returning a String as error is dirty

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wrapping everything in an Unsupported or Unknown libafl error just to call to_string in the other side isn't cleaner nor useful since it adds useless stuff in the print :D

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's more like OsError right? Still a better love story than Strings IMHO

"{error_prefix} Failed to open /dev/cpu/0/msr: {e} \
Make sure you have the `msr` kernel module loaded"
)
})?;
msrs.seek(SeekFrom::Start(0x485))
.map_err(|e| format!("{error_prefix} Failed to seek in /dev/cpu/0/msr: {e}"))?;
let mut buf = [0u8; 8];
msrs.read_exact(&mut buf)
.map_err(|e| format!("{error_prefix} Failed to read MSR 0x485: {e}"))?;

Ok(buf[1] & 0b0100_0000 != 0)
}

fn new_perf_event_attr_intel_pt() -> Result<perf_event_attr, Error> {
let type_ = match &*PERF_EVENT_TYPE {
Ok(t) => Ok(*t),
Expand Down Expand Up @@ -909,7 +932,7 @@ fn linux_version() -> Result<(usize, usize, usize), ()> {
let release = unsafe { CStr::from_ptr(uname_data.release.as_ptr()) };
let mut parts = release
.to_bytes()
.split(|&c| c == b'.' || c == b'-')
.split(|&c| matches!(c, b'.' | b'-' | b'+'))
.take(3)
.map(|s| String::from_utf8_lossy(s).parse::<usize>());
if let (Some(Ok(major)), Some(Ok(minor)), Some(Ok(patch))) =
Expand Down
Loading