Move bitfields to bitbybit (#2688)

* move to bitbybit

* Restore bitbybit dependent code

* Clippy
This commit is contained in:
Marco C. 2024-11-13 20:28:25 +01:00 committed by GitHub
parent d334860148
commit b7889a5996
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 122 additions and 99 deletions

View File

@ -54,8 +54,10 @@ license = "MIT OR Apache-2.0"
[workspace.dependencies] [workspace.dependencies]
ahash = { version = "0.8.11", default-features = false } # The hash function already used in hashbrown ahash = { version = "0.8.11", default-features = false } # The hash function already used in hashbrown
arbitrary-int = "1.2.7" # arbitrary sized integers, useful in combination with bitfields (bitbybit crate)
backtrace = { version = "0.3.74", default-features = false } # Used to get the stacktrace in StacktraceObserver backtrace = { version = "0.3.74", default-features = false } # Used to get the stacktrace in StacktraceObserver
bindgen = "0.70.1" bindgen = "0.70.1"
bitbybit = "1.3.2" # bitfields, use this for bit fields and bit enums
clap = "4.5.18" clap = "4.5.18"
cc = "1.1.21" cc = "1.1.21"
cmake = "0.1.51" cmake = "0.1.51"

View File

@ -221,7 +221,8 @@ num-traits = { workspace = true, default-features = false }
serde = { workspace = true, features = ["alloc"] } # serialization lib serde = { workspace = true, features = ["alloc"] } # serialization lib
postcard = { workspace = true } # no_std compatible serde serialization format postcard = { workspace = true } # no_std compatible serde serialization format
bincode = { version = "1.3.3", optional = true } bincode = { version = "1.3.3", optional = true }
c2rust-bitfields = { version = "0.19.0", features = ["no_std"] } bitbybit = { workspace = true }
arbitrary-int = { workspace = true }
ahash = { workspace = true } # The hash function already used in hashbrown ahash = { workspace = true } # The hash function already used in hashbrown
meminterval = { workspace = true, features = ["serde"] } meminterval = { workspace = true, features = ["serde"] }
backtrace = { workspace = true, optional = true } # Used to get the stacktrace in StacktraceObserver backtrace = { workspace = true, optional = true } # Used to get the stacktrace in StacktraceObserver

View File

@ -1410,7 +1410,7 @@ where
None => input_len - cmp_buf_idx, None => input_len - cmp_buf_idx,
}; };
let hshape = (header.shape() + 1) as usize; let hshape = (header.shape().value() + 1) as usize;
match (&orig_val[cmp_h_idx], &new_val[cmp_h_idx]) { match (&orig_val[cmp_h_idx], &new_val[cmp_h_idx]) {
(CmpValues::U8(_orig), CmpValues::U8(_new)) => { (CmpValues::U8(_orig), CmpValues::U8(_new)) => {
@ -1506,7 +1506,7 @@ where
} }
(CmpValues::U16(orig), CmpValues::U16(new)) => { (CmpValues::U16(orig), CmpValues::U16(new)) => {
let (orig_v0, orig_v1, new_v0, new_v1) = (orig.0, orig.1, new.0, new.1); let (orig_v0, orig_v1, new_v0, new_v1) = (orig.0, orig.1, new.0, new.1);
let attribute: u8 = header.attribute() as u8; let attribute: u8 = header.attribute().value();
if new_v0 != orig_v0 && orig_v0 != orig_v1 { if new_v0 != orig_v0 && orig_v0 != orig_v1 {
// Compare v0 against v1 // Compare v0 against v1
@ -1594,7 +1594,7 @@ where
} }
(CmpValues::U32(orig), CmpValues::U32(new)) => { (CmpValues::U32(orig), CmpValues::U32(new)) => {
let (orig_v0, orig_v1, new_v0, new_v1) = (orig.0, orig.1, new.0, new.1); let (orig_v0, orig_v1, new_v0, new_v1) = (orig.0, orig.1, new.0, new.1);
let attribute = header.attribute() as u8; let attribute = header.attribute().value();
let mut cmp_found = false; let mut cmp_found = false;
if new_v0 != orig_v0 && orig_v0 != orig_v1 { if new_v0 != orig_v0 && orig_v0 != orig_v1 {
@ -1687,7 +1687,7 @@ where
} }
(CmpValues::U64(orig), CmpValues::U64(new)) => { (CmpValues::U64(orig), CmpValues::U64(new)) => {
let (orig_v0, orig_v1, new_v0, new_v1) = (orig.0, orig.1, new.0, new.1); let (orig_v0, orig_v1, new_v0, new_v1) = (orig.0, orig.1, new.0, new.1);
let attribute = header.attribute() as u8; let attribute = header.attribute().value();
let mut cmp_found = false; let mut cmp_found = false;
if new_v0 != orig_v0 && orig_v0 != orig_v1 { if new_v0 != orig_v0 && orig_v0 != orig_v1 {

View File

@ -5,7 +5,8 @@ use core::{
ops::{Deref, DerefMut}, ops::{Deref, DerefMut},
}; };
use c2rust_bitfields::BitfieldStruct; use arbitrary_int::{u1, u4, u5, u6};
use bitbybit::bitfield;
use hashbrown::HashMap; use hashbrown::HashMap;
use libafl_bolts::{ownedref::OwnedRefMut, AsSlice, HasLen, Named}; use libafl_bolts::{ownedref::OwnedRefMut, AsSlice, HasLen, Named};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -404,8 +405,6 @@ impl AFLppCmpValuesMetadata {
} }
} }
#[derive(Debug, Copy, Clone, BitfieldStruct)]
#[repr(C, packed)]
/// Comparison header, used to describe a set of comparison values efficiently. /// Comparison header, used to describe a set of comparison values efficiently.
/// ///
/// # Bitfields /// # Bitfields
@ -413,17 +412,36 @@ impl AFLppCmpValuesMetadata {
/// - hits: The number of hits of a particular comparison /// - hits: The number of hits of a particular comparison
/// - id: Unused by ``LibAFL``, a unique ID for a particular comparison /// - id: Unused by ``LibAFL``, a unique ID for a particular comparison
/// - shape: Whether a comparison is u8/u8, u16/u16, etc. /// - shape: Whether a comparison is u8/u8, u16/u16, etc.
/// - _type: Whether the comparison value represents an instruction (like a `cmp`) or function /// - type_: Whether the comparison value represents an instruction (like a `cmp`) or function
/// call arguments /// call arguments
/// - attribute: OR-ed bitflags describing whether the comparison is <, >, =, <=, >=, or transform /// - attribute: OR-ed bitflags describing whether the comparison is <, >, =, <=, >=, or transform
/// - overflow: Whether the comparison overflows /// - overflow: Whether the comparison overflows
/// - reserved: Reserved for future use /// - reserved: Reserved for future use
#[bitfield(u32)]
#[derive(Debug)]
pub struct AFLppCmpLogHeader { pub struct AFLppCmpLogHeader {
/// The header values /// The number of hits of a particular comparison
#[bitfield(name = "hits", ty = "u32", bits = "0..=5")] // 6 bits up to 63 entries, we have CMP_MAP_H = 32 (so using half of it) ///
#[bitfield(name = "shape", ty = "u32", bits = "6..=10")] // 31 + 1 bytes max /// 6 bits up to 63 entries, we have CMP_MAP_H = 32 (so using half of it)
#[bitfield(name = "_type", ty = "u8", bits = "11..=11")] // 2: cmp, rtn #[bits(0..=5, r)]
#[bitfield(name = "attribute", ty = "u32", bits = "12..=15")] hits: u6,
// 16 types for arithmetic comparison types /// Whether a comparison is u8/u8, u16/u16, etc.
pub data: [u8; 2], ///
/// 31 + 1 bytes max
#[bits(6..=10, r)]
shape: u5,
/// Whether the comparison value represents an instruction (like a `cmp`) or function call
/// arguments
///
/// 2: cmp, rtn
#[bit(11, r)]
type_: u1,
/// OR-ed bitflags describing whether the comparison is <, >, =, <=, >=, or transform
///
/// 16 types for arithmetic comparison types
#[bits(12..=15, r)]
attribute: u4,
/// data
#[bits(16..=31, r)]
data: u16,
} }

View File

@ -24,8 +24,8 @@ nix = { workspace = true }
proc-maps = "0.4.0" proc-maps = "0.4.0"
[dependencies] [dependencies]
#arbitrary-int = { version = "1.2.7" } arbitrary-int = { workspace = true }
#bitbybit = { version = "1.3.2" } bitbybit = { workspace = true }
libafl_bolts = { path = "../libafl_bolts", default-features = false } libafl_bolts = { path = "../libafl_bolts", default-features = false }
libc = { workspace = true } libc = { workspace = true }
libipt = { workspace = true, optional = true } libipt = { workspace = true, optional = true }

View File

@ -32,10 +32,10 @@ use std::{
sync::LazyLock, sync::LazyLock,
}; };
// #[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
// use arbitrary_int::u4; use arbitrary_int::u4;
// #[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
// use bitbybit::bitfield; use bitbybit::bitfield;
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
use caps::{CapSet, Capability}; use caps::{CapSet, Capability};
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
@ -641,19 +641,19 @@ impl IntelPTBuilder {
} }
} }
// /// Perf event config for `IntelPT` /// Perf event config for `IntelPT`
// /// ///
// /// (This is almost mapped to `IA32_RTIT_CTL MSR` by perf) /// (This is almost mapped to `IA32_RTIT_CTL MSR` by perf)
// #[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
// #[bitfield(u64, default = 0)] #[bitfield(u64, default = 0)]
// struct PtConfig { struct PtConfig {
// /// Disable call return address compression. AKA DisRETC in Intel SDM. /// Disable call return address compression. AKA DisRETC in Intel SDM.
// #[bit(11, rw)] #[bit(11, rw)]
// noretcomp: bool, noretcomp: bool,
// /// Indicates the frequency of PSB packets. AKA PSBFreq in Intel SDM. /// Indicates the frequency of PSB packets. AKA PSBFreq in Intel SDM.
// #[bits(24..=27, rw)] #[bits(24..=27, rw)]
// psb_period: u4, psb_period: u4,
// } }
/// Number of address filters available on the running CPU /// Number of address filters available on the running CPU
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
@ -815,12 +815,11 @@ fn new_perf_event_attr_intel_pt() -> Result<perf_event_attr, Error> {
Ok(t) => Ok(*t), Ok(t) => Ok(*t),
Err(e) => Err(Error::unsupported(e.clone())), Err(e) => Err(Error::unsupported(e.clone())),
}?; }?;
// let config = PtConfig::builder() let config = PtConfig::builder()
// .with_noretcomp(true) .with_noretcomp(true)
// .with_psb_period(u4::new(0)) .with_psb_period(u4::new(0))
// .build() .build()
// .raw_value; .raw_value;
let config = 0x08_00; // noretcomp
let mut attr = perf_event_attr { let mut attr = perf_event_attr {
size: size_of::<perf_event_attr>() as u32, size: size_of::<perf_event_attr>() as u32,
@ -940,8 +939,8 @@ const fn wrap_aux_pointer(ptr: u64, perf_aux_buffer_size: usize) -> u64 {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
// #[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
// use arbitrary_int::Number; use arbitrary_int::Number;
use static_assertions::assert_eq_size; use static_assertions::assert_eq_size;
use super::*; use super::*;
@ -949,7 +948,7 @@ mod test {
// Only 64-bit systems are supported, ensure we can use usize and u64 interchangeably // Only 64-bit systems are supported, ensure we can use usize and u64 interchangeably
assert_eq_size!(usize, u64); assert_eq_size!(usize, u64);
/// Quick way to check if your machine is compatible with Intl PT's features used by libafl /// 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 `cargo test intel_pt_check_availability -- --show-output`
#[test] #[test]
@ -979,52 +978,52 @@ mod test {
.unwrap(); .unwrap();
} }
// #[test] #[test]
// #[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
// fn intel_pt_pt_config_noretcomp_format() { fn intel_pt_pt_config_noretcomp_format() {
// let ptconfig_noretcomp = PtConfig::DEFAULT.with_noretcomp(true).raw_value; let ptconfig_noretcomp = PtConfig::DEFAULT.with_noretcomp(true).raw_value;
// let path = format!("{PT_EVENT_PATH}/format/noretcomp"); let path = format!("{PT_EVENT_PATH}/format/noretcomp");
// let s = fs::read_to_string(&path).expect("Failed to read Intel PT config noretcomp format"); let s = fs::read_to_string(&path).expect("Failed to read Intel PT config noretcomp format");
// assert!( assert!(
// s.starts_with("config:"), s.starts_with("config:"),
// "Unexpected Intel PT config noretcomp format" "Unexpected Intel PT config noretcomp format"
// ); );
// let bit = s["config:".len()..] let bit = s["config:".len()..]
// .trim() .trim()
// .parse::<u32>() .parse::<u32>()
// .expect("Failed to parse Intel PT config noretcomp format"); .expect("Failed to parse Intel PT config noretcomp format");
// assert_eq!( assert_eq!(
// ptconfig_noretcomp, ptconfig_noretcomp,
// 0b1 << bit, 0b1 << bit,
// "Unexpected Intel PT config noretcomp format" "Unexpected Intel PT config noretcomp format"
// ); );
// } }
//
// #[test] #[test]
// #[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
// fn intel_pt_pt_config_psb_period_format() { fn intel_pt_pt_config_psb_period_format() {
// let ptconfig_psb_period = PtConfig::DEFAULT.with_psb_period(u4::MAX).raw_value; let ptconfig_psb_period = PtConfig::DEFAULT.with_psb_period(u4::MAX).raw_value;
// let path = format!("{PT_EVENT_PATH}/format/psb_period"); let path = format!("{PT_EVENT_PATH}/format/psb_period");
// let s = let s =
// fs::read_to_string(&path).expect("Failed to read Intel PT config psb_period format"); fs::read_to_string(&path).expect("Failed to read Intel PT config psb_period format");
// assert!( assert!(
// s.starts_with("config:"), s.starts_with("config:"),
// "Unexpected Intel PT config psb_period format" "Unexpected Intel PT config psb_period format"
// ); );
// let from = s["config:".len().."config:".len() + 2] let from = s["config:".len().."config:".len() + 2]
// .parse::<u32>() .parse::<u32>()
// .expect("Failed to parse Intel PT config psb_period format"); .expect("Failed to parse Intel PT config psb_period format");
// let to = s["config:".len() + 3..] let to = s["config:".len() + 3..]
// .trim() .trim()
// .parse::<u32>() .parse::<u32>()
// .expect("Failed to parse Intel PT config psb_period format"); .expect("Failed to parse Intel PT config psb_period format");
// let mut format = 0; let mut format = 0;
// for bit in from..=to { for bit in from..=to {
// format |= 0b1 << bit; format |= 0b1 << bit;
// } }
// assert_eq!( assert_eq!(
// ptconfig_psb_period, format, ptconfig_psb_period, format,
// "Unexpected Intel PT config psb_period format" "Unexpected Intel PT config psb_period format"
// ); );
// } }
} }

View File

@ -442,7 +442,7 @@ pub static mut libafl_cmplog_map: CmpLogMap = CmpLogMap {
#[cfg(feature = "cmplog_extended_instrumentation")] #[cfg(feature = "cmplog_extended_instrumentation")]
#[allow(clippy::large_stack_arrays)] #[allow(clippy::large_stack_arrays)]
pub static mut libafl_cmplog_map_extended: AFLppCmpLogMap = AFLppCmpLogMap { pub static mut libafl_cmplog_map_extended: AFLppCmpLogMap = AFLppCmpLogMap {
headers: [AFLppCmpLogHeader { data: [0; 2] }; CMPLOG_MAP_W], headers: [AFLppCmpLogHeader::new_with_raw_value(0); CMPLOG_MAP_W],
vals: AFLppCmpLogVals { vals: AFLppCmpLogVals {
operands: [[AFLppCmpLogOperands { operands: [[AFLppCmpLogOperands {
v0: 0, v0: 0,
@ -463,7 +463,7 @@ pub use libafl_cmplog_map as CMPLOG_MAP;
pub use libafl_cmplog_map_extended as CMPLOG_MAP_EXTENDED; pub use libafl_cmplog_map_extended as CMPLOG_MAP_EXTENDED;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
#[repr(C, packed)] #[repr(C)]
/// Comparison map compatible with AFL++ cmplog instrumentation /// Comparison map compatible with AFL++ cmplog instrumentation
pub struct AFLppCmpLogMap { pub struct AFLppCmpLogMap {
headers: [AFLppCmpLogHeader; CMPLOG_MAP_W], headers: [AFLppCmpLogHeader; CMPLOG_MAP_W],
@ -478,6 +478,7 @@ impl HasLen for AFLppCmpLogMap {
impl AFLppCmpLogMap { impl AFLppCmpLogMap {
#[must_use] #[must_use]
#[allow(clippy::cast_ptr_alignment)]
/// Instantiate a new boxed zeroed `AFLppCmpLogMap`. This should be used to create a new /// Instantiate a new boxed zeroed `AFLppCmpLogMap`. This should be used to create a new
/// map, because it is so large it cannot be allocated on the stack with default /// map, because it is so large it cannot be allocated on the stack with default
/// runtime configuration. /// runtime configuration.
@ -524,6 +525,7 @@ impl Serialize for AFLppCmpLogMap {
} }
impl<'de> Deserialize<'de> for AFLppCmpLogMap { impl<'de> Deserialize<'de> for AFLppCmpLogMap {
#[allow(clippy::cast_ptr_alignment)]
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where where
D: Deserializer<'de>, D: Deserializer<'de>,
@ -540,11 +542,11 @@ impl CmpMap for AFLppCmpLogMap {
} }
fn executions_for(&self, idx: usize) -> usize { fn executions_for(&self, idx: usize) -> usize {
self.headers[idx].hits() as usize self.headers[idx].hits().value() as usize
} }
fn usable_executions_for(&self, idx: usize) -> usize { fn usable_executions_for(&self, idx: usize) -> usize {
if self.headers[idx]._type() == CMPLOG_KIND_INS { if self.headers[idx].type_().value() == CMPLOG_KIND_INS {
if self.executions_for(idx) < CMPLOG_MAP_H { if self.executions_for(idx) < CMPLOG_MAP_H {
self.executions_for(idx) self.executions_for(idx)
} else { } else {
@ -558,9 +560,10 @@ impl CmpMap for AFLppCmpLogMap {
} }
fn values_of(&self, idx: usize, execution: usize) -> Option<CmpValues> { fn values_of(&self, idx: usize, execution: usize) -> Option<CmpValues> {
if self.headers[idx]._type() == CMPLOG_KIND_INS { let header = self.headers[idx];
if header.type_().value() == CMPLOG_KIND_INS {
unsafe { unsafe {
match self.headers[idx].shape() { match self.headers[idx].shape().value() {
0 => Some(CmpValues::U8(( 0 => Some(CmpValues::U8((
self.vals.operands[idx][execution].v0 as u8, self.vals.operands[idx][execution].v0 as u8,
self.vals.operands[idx][execution].v1 as u8, self.vals.operands[idx][execution].v1 as u8,
@ -600,7 +603,7 @@ impl CmpMap for AFLppCmpLogMap {
fn reset(&mut self) -> Result<(), Error> { fn reset(&mut self) -> Result<(), Error> {
// For performance, we reset just the headers // For performance, we reset just the headers
self.headers.fill(AFLppCmpLogHeader { data: [0; 2] }); self.headers.fill(AFLppCmpLogHeader::new_with_raw_value(0));
Ok(()) Ok(())
} }