Skip to content

Commit b7889a5

Browse files
authored
Move bitfields to bitbybit (#2688)
* move to bitbybit * Restore bitbybit dependent code * Clippy
1 parent d334860 commit b7889a5

7 files changed

Lines changed: 122 additions & 99 deletions

File tree

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,10 @@ license = "MIT OR Apache-2.0"
5454

5555
[workspace.dependencies]
5656
ahash = { version = "0.8.11", default-features = false } # The hash function already used in hashbrown
57+
arbitrary-int = "1.2.7" # arbitrary sized integers, useful in combination with bitfields (bitbybit crate)
5758
backtrace = { version = "0.3.74", default-features = false } # Used to get the stacktrace in StacktraceObserver
5859
bindgen = "0.70.1"
60+
bitbybit = "1.3.2" # bitfields, use this for bit fields and bit enums
5961
clap = "4.5.18"
6062
cc = "1.1.21"
6163
cmake = "0.1.51"

libafl/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,8 @@ num-traits = { workspace = true, default-features = false }
221221
serde = { workspace = true, features = ["alloc"] } # serialization lib
222222
postcard = { workspace = true } # no_std compatible serde serialization format
223223
bincode = { version = "1.3.3", optional = true }
224-
c2rust-bitfields = { version = "0.19.0", features = ["no_std"] }
224+
bitbybit = { workspace = true }
225+
arbitrary-int = { workspace = true }
225226
ahash = { workspace = true } # The hash function already used in hashbrown
226227
meminterval = { workspace = true, features = ["serde"] }
227228
backtrace = { workspace = true, optional = true } # Used to get the stacktrace in StacktraceObserver

libafl/src/mutators/token_mutations.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1410,7 +1410,7 @@ where
14101410
None => input_len - cmp_buf_idx,
14111411
};
14121412

1413-
let hshape = (header.shape() + 1) as usize;
1413+
let hshape = (header.shape().value() + 1) as usize;
14141414

14151415
match (&orig_val[cmp_h_idx], &new_val[cmp_h_idx]) {
14161416
(CmpValues::U8(_orig), CmpValues::U8(_new)) => {
@@ -1506,7 +1506,7 @@ where
15061506
}
15071507
(CmpValues::U16(orig), CmpValues::U16(new)) => {
15081508
let (orig_v0, orig_v1, new_v0, new_v1) = (orig.0, orig.1, new.0, new.1);
1509-
let attribute: u8 = header.attribute() as u8;
1509+
let attribute: u8 = header.attribute().value();
15101510

15111511
if new_v0 != orig_v0 && orig_v0 != orig_v1 {
15121512
// Compare v0 against v1
@@ -1594,7 +1594,7 @@ where
15941594
}
15951595
(CmpValues::U32(orig), CmpValues::U32(new)) => {
15961596
let (orig_v0, orig_v1, new_v0, new_v1) = (orig.0, orig.1, new.0, new.1);
1597-
let attribute = header.attribute() as u8;
1597+
let attribute = header.attribute().value();
15981598

15991599
let mut cmp_found = false;
16001600
if new_v0 != orig_v0 && orig_v0 != orig_v1 {
@@ -1687,7 +1687,7 @@ where
16871687
}
16881688
(CmpValues::U64(orig), CmpValues::U64(new)) => {
16891689
let (orig_v0, orig_v1, new_v0, new_v1) = (orig.0, orig.1, new.0, new.1);
1690-
let attribute = header.attribute() as u8;
1690+
let attribute = header.attribute().value();
16911691

16921692
let mut cmp_found = false;
16931693
if new_v0 != orig_v0 && orig_v0 != orig_v1 {

libafl/src/observers/cmp.rs

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ use core::{
55
ops::{Deref, DerefMut},
66
};
77

8-
use c2rust_bitfields::BitfieldStruct;
8+
use arbitrary_int::{u1, u4, u5, u6};
9+
use bitbybit::bitfield;
910
use hashbrown::HashMap;
1011
use libafl_bolts::{ownedref::OwnedRefMut, AsSlice, HasLen, Named};
1112
use serde::{Deserialize, Serialize};
@@ -404,26 +405,43 @@ impl AFLppCmpValuesMetadata {
404405
}
405406
}
406407

407-
#[derive(Debug, Copy, Clone, BitfieldStruct)]
408-
#[repr(C, packed)]
409408
/// Comparison header, used to describe a set of comparison values efficiently.
410409
///
411410
/// # Bitfields
412411
///
413412
/// - hits: The number of hits of a particular comparison
414413
/// - id: Unused by ``LibAFL``, a unique ID for a particular comparison
415414
/// - shape: Whether a comparison is u8/u8, u16/u16, etc.
416-
/// - _type: Whether the comparison value represents an instruction (like a `cmp`) or function
415+
/// - type_: Whether the comparison value represents an instruction (like a `cmp`) or function
417416
/// call arguments
418417
/// - attribute: OR-ed bitflags describing whether the comparison is <, >, =, <=, >=, or transform
419418
/// - overflow: Whether the comparison overflows
420419
/// - reserved: Reserved for future use
420+
#[bitfield(u32)]
421+
#[derive(Debug)]
421422
pub struct AFLppCmpLogHeader {
422-
/// The header values
423-
#[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)
424-
#[bitfield(name = "shape", ty = "u32", bits = "6..=10")] // 31 + 1 bytes max
425-
#[bitfield(name = "_type", ty = "u8", bits = "11..=11")] // 2: cmp, rtn
426-
#[bitfield(name = "attribute", ty = "u32", bits = "12..=15")]
427-
// 16 types for arithmetic comparison types
428-
pub data: [u8; 2],
423+
/// The number of hits of a particular comparison
424+
///
425+
/// 6 bits up to 63 entries, we have CMP_MAP_H = 32 (so using half of it)
426+
#[bits(0..=5, r)]
427+
hits: u6,
428+
/// Whether a comparison is u8/u8, u16/u16, etc.
429+
///
430+
/// 31 + 1 bytes max
431+
#[bits(6..=10, r)]
432+
shape: u5,
433+
/// Whether the comparison value represents an instruction (like a `cmp`) or function call
434+
/// arguments
435+
///
436+
/// 2: cmp, rtn
437+
#[bit(11, r)]
438+
type_: u1,
439+
/// OR-ed bitflags describing whether the comparison is <, >, =, <=, >=, or transform
440+
///
441+
/// 16 types for arithmetic comparison types
442+
#[bits(12..=15, r)]
443+
attribute: u4,
444+
/// data
445+
#[bits(16..=31, r)]
446+
data: u16,
429447
}

libafl_intelpt/Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ nix = { workspace = true }
2424
proc-maps = "0.4.0"
2525

2626
[dependencies]
27-
#arbitrary-int = { version = "1.2.7" }
28-
#bitbybit = { version = "1.3.2" }
27+
arbitrary-int = { workspace = true }
28+
bitbybit = { workspace = true }
2929
libafl_bolts = { path = "../libafl_bolts", default-features = false }
3030
libc = { workspace = true }
3131
libipt = { workspace = true, optional = true }

libafl_intelpt/src/lib.rs

Lines changed: 73 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,10 @@ use std::{
3232
sync::LazyLock,
3333
};
3434

35-
// #[cfg(target_os = "linux")]
36-
// use arbitrary_int::u4;
37-
// #[cfg(target_os = "linux")]
38-
// use bitbybit::bitfield;
35+
#[cfg(target_os = "linux")]
36+
use arbitrary_int::u4;
37+
#[cfg(target_os = "linux")]
38+
use bitbybit::bitfield;
3939
#[cfg(target_os = "linux")]
4040
use caps::{CapSet, Capability};
4141
#[cfg(target_os = "linux")]
@@ -641,19 +641,19 @@ impl IntelPTBuilder {
641641
}
642642
}
643643

644-
// /// Perf event config for `IntelPT`
645-
// ///
646-
// /// (This is almost mapped to `IA32_RTIT_CTL MSR` by perf)
647-
// #[cfg(target_os = "linux")]
648-
// #[bitfield(u64, default = 0)]
649-
// struct PtConfig {
650-
// /// Disable call return address compression. AKA DisRETC in Intel SDM.
651-
// #[bit(11, rw)]
652-
// noretcomp: bool,
653-
// /// Indicates the frequency of PSB packets. AKA PSBFreq in Intel SDM.
654-
// #[bits(24..=27, rw)]
655-
// psb_period: u4,
656-
// }
644+
/// Perf event config for `IntelPT`
645+
///
646+
/// (This is almost mapped to `IA32_RTIT_CTL MSR` by perf)
647+
#[cfg(target_os = "linux")]
648+
#[bitfield(u64, default = 0)]
649+
struct PtConfig {
650+
/// Disable call return address compression. AKA DisRETC in Intel SDM.
651+
#[bit(11, rw)]
652+
noretcomp: bool,
653+
/// Indicates the frequency of PSB packets. AKA PSBFreq in Intel SDM.
654+
#[bits(24..=27, rw)]
655+
psb_period: u4,
656+
}
657657

658658
/// Number of address filters available on the running CPU
659659
#[cfg(target_os = "linux")]
@@ -815,12 +815,11 @@ fn new_perf_event_attr_intel_pt() -> Result<perf_event_attr, Error> {
815815
Ok(t) => Ok(*t),
816816
Err(e) => Err(Error::unsupported(e.clone())),
817817
}?;
818-
// let config = PtConfig::builder()
819-
// .with_noretcomp(true)
820-
// .with_psb_period(u4::new(0))
821-
// .build()
822-
// .raw_value;
823-
let config = 0x08_00; // noretcomp
818+
let config = PtConfig::builder()
819+
.with_noretcomp(true)
820+
.with_psb_period(u4::new(0))
821+
.build()
822+
.raw_value;
824823

825824
let mut attr = perf_event_attr {
826825
size: size_of::<perf_event_attr>() as u32,
@@ -940,16 +939,16 @@ const fn wrap_aux_pointer(ptr: u64, perf_aux_buffer_size: usize) -> u64 {
940939

941940
#[cfg(test)]
942941
mod test {
943-
// #[cfg(target_os = "linux")]
944-
// use arbitrary_int::Number;
942+
#[cfg(target_os = "linux")]
943+
use arbitrary_int::Number;
945944
use static_assertions::assert_eq_size;
946945

947946
use super::*;
948947

949948
// Only 64-bit systems are supported, ensure we can use usize and u64 interchangeably
950949
assert_eq_size!(usize, u64);
951950

952-
/// Quick way to check if your machine is compatible with Intl PT's features used by libafl
951+
/// Quick way to check if your machine is compatible with Intel PT's features used by libafl
953952
///
954953
/// Simply run `cargo test intel_pt_check_availability -- --show-output`
955954
#[test]
@@ -979,52 +978,52 @@ mod test {
979978
.unwrap();
980979
}
981980

982-
// #[test]
983-
// #[cfg(target_os = "linux")]
984-
// fn intel_pt_pt_config_noretcomp_format() {
985-
// let ptconfig_noretcomp = PtConfig::DEFAULT.with_noretcomp(true).raw_value;
986-
// let path = format!("{PT_EVENT_PATH}/format/noretcomp");
987-
// let s = fs::read_to_string(&path).expect("Failed to read Intel PT config noretcomp format");
988-
// assert!(
989-
// s.starts_with("config:"),
990-
// "Unexpected Intel PT config noretcomp format"
991-
// );
992-
// let bit = s["config:".len()..]
993-
// .trim()
994-
// .parse::<u32>()
995-
// .expect("Failed to parse Intel PT config noretcomp format");
996-
// assert_eq!(
997-
// ptconfig_noretcomp,
998-
// 0b1 << bit,
999-
// "Unexpected Intel PT config noretcomp format"
1000-
// );
1001-
// }
1002-
//
1003-
// #[test]
1004-
// #[cfg(target_os = "linux")]
1005-
// fn intel_pt_pt_config_psb_period_format() {
1006-
// let ptconfig_psb_period = PtConfig::DEFAULT.with_psb_period(u4::MAX).raw_value;
1007-
// let path = format!("{PT_EVENT_PATH}/format/psb_period");
1008-
// let s =
1009-
// fs::read_to_string(&path).expect("Failed to read Intel PT config psb_period format");
1010-
// assert!(
1011-
// s.starts_with("config:"),
1012-
// "Unexpected Intel PT config psb_period format"
1013-
// );
1014-
// let from = s["config:".len().."config:".len() + 2]
1015-
// .parse::<u32>()
1016-
// .expect("Failed to parse Intel PT config psb_period format");
1017-
// let to = s["config:".len() + 3..]
1018-
// .trim()
1019-
// .parse::<u32>()
1020-
// .expect("Failed to parse Intel PT config psb_period format");
1021-
// let mut format = 0;
1022-
// for bit in from..=to {
1023-
// format |= 0b1 << bit;
1024-
// }
1025-
// assert_eq!(
1026-
// ptconfig_psb_period, format,
1027-
// "Unexpected Intel PT config psb_period format"
1028-
// );
1029-
// }
981+
#[test]
982+
#[cfg(target_os = "linux")]
983+
fn intel_pt_pt_config_noretcomp_format() {
984+
let ptconfig_noretcomp = PtConfig::DEFAULT.with_noretcomp(true).raw_value;
985+
let path = format!("{PT_EVENT_PATH}/format/noretcomp");
986+
let s = fs::read_to_string(&path).expect("Failed to read Intel PT config noretcomp format");
987+
assert!(
988+
s.starts_with("config:"),
989+
"Unexpected Intel PT config noretcomp format"
990+
);
991+
let bit = s["config:".len()..]
992+
.trim()
993+
.parse::<u32>()
994+
.expect("Failed to parse Intel PT config noretcomp format");
995+
assert_eq!(
996+
ptconfig_noretcomp,
997+
0b1 << bit,
998+
"Unexpected Intel PT config noretcomp format"
999+
);
1000+
}
1001+
1002+
#[test]
1003+
#[cfg(target_os = "linux")]
1004+
fn intel_pt_pt_config_psb_period_format() {
1005+
let ptconfig_psb_period = PtConfig::DEFAULT.with_psb_period(u4::MAX).raw_value;
1006+
let path = format!("{PT_EVENT_PATH}/format/psb_period");
1007+
let s =
1008+
fs::read_to_string(&path).expect("Failed to read Intel PT config psb_period format");
1009+
assert!(
1010+
s.starts_with("config:"),
1011+
"Unexpected Intel PT config psb_period format"
1012+
);
1013+
let from = s["config:".len().."config:".len() + 2]
1014+
.parse::<u32>()
1015+
.expect("Failed to parse Intel PT config psb_period format");
1016+
let to = s["config:".len() + 3..]
1017+
.trim()
1018+
.parse::<u32>()
1019+
.expect("Failed to parse Intel PT config psb_period format");
1020+
let mut format = 0;
1021+
for bit in from..=to {
1022+
format |= 0b1 << bit;
1023+
}
1024+
assert_eq!(
1025+
ptconfig_psb_period, format,
1026+
"Unexpected Intel PT config psb_period format"
1027+
);
1028+
}
10301029
}

libafl_targets/src/cmps/mod.rs

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -442,7 +442,7 @@ pub static mut libafl_cmplog_map: CmpLogMap = CmpLogMap {
442442
#[cfg(feature = "cmplog_extended_instrumentation")]
443443
#[allow(clippy::large_stack_arrays)]
444444
pub static mut libafl_cmplog_map_extended: AFLppCmpLogMap = AFLppCmpLogMap {
445-
headers: [AFLppCmpLogHeader { data: [0; 2] }; CMPLOG_MAP_W],
445+
headers: [AFLppCmpLogHeader::new_with_raw_value(0); CMPLOG_MAP_W],
446446
vals: AFLppCmpLogVals {
447447
operands: [[AFLppCmpLogOperands {
448448
v0: 0,
@@ -463,7 +463,7 @@ pub use libafl_cmplog_map as CMPLOG_MAP;
463463
pub use libafl_cmplog_map_extended as CMPLOG_MAP_EXTENDED;
464464

465465
#[derive(Debug, Clone)]
466-
#[repr(C, packed)]
466+
#[repr(C)]
467467
/// Comparison map compatible with AFL++ cmplog instrumentation
468468
pub struct AFLppCmpLogMap {
469469
headers: [AFLppCmpLogHeader; CMPLOG_MAP_W],
@@ -478,6 +478,7 @@ impl HasLen for AFLppCmpLogMap {
478478

479479
impl AFLppCmpLogMap {
480480
#[must_use]
481+
#[allow(clippy::cast_ptr_alignment)]
481482
/// Instantiate a new boxed zeroed `AFLppCmpLogMap`. This should be used to create a new
482483
/// map, because it is so large it cannot be allocated on the stack with default
483484
/// runtime configuration.
@@ -524,6 +525,7 @@ impl Serialize for AFLppCmpLogMap {
524525
}
525526

526527
impl<'de> Deserialize<'de> for AFLppCmpLogMap {
528+
#[allow(clippy::cast_ptr_alignment)]
527529
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
528530
where
529531
D: Deserializer<'de>,
@@ -540,11 +542,11 @@ impl CmpMap for AFLppCmpLogMap {
540542
}
541543

542544
fn executions_for(&self, idx: usize) -> usize {
543-
self.headers[idx].hits() as usize
545+
self.headers[idx].hits().value() as usize
544546
}
545547

546548
fn usable_executions_for(&self, idx: usize) -> usize {
547-
if self.headers[idx]._type() == CMPLOG_KIND_INS {
549+
if self.headers[idx].type_().value() == CMPLOG_KIND_INS {
548550
if self.executions_for(idx) < CMPLOG_MAP_H {
549551
self.executions_for(idx)
550552
} else {
@@ -558,9 +560,10 @@ impl CmpMap for AFLppCmpLogMap {
558560
}
559561

560562
fn values_of(&self, idx: usize, execution: usize) -> Option<CmpValues> {
561-
if self.headers[idx]._type() == CMPLOG_KIND_INS {
563+
let header = self.headers[idx];
564+
if header.type_().value() == CMPLOG_KIND_INS {
562565
unsafe {
563-
match self.headers[idx].shape() {
566+
match self.headers[idx].shape().value() {
564567
0 => Some(CmpValues::U8((
565568
self.vals.operands[idx][execution].v0 as u8,
566569
self.vals.operands[idx][execution].v1 as u8,
@@ -600,7 +603,7 @@ impl CmpMap for AFLppCmpLogMap {
600603

601604
fn reset(&mut self) -> Result<(), Error> {
602605
// For performance, we reset just the headers
603-
self.headers.fill(AFLppCmpLogHeader { data: [0; 2] });
606+
self.headers.fill(AFLppCmpLogHeader::new_with_raw_value(0));
604607

605608
Ok(())
606609
}

0 commit comments

Comments
 (0)