Skip to content
Open
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
106 changes: 106 additions & 0 deletions crates/libafl/src/mutators/token_mutations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -577,6 +577,35 @@ where
}
}
}
CmpValues::U128((v1, v2, v1_is_const)) => {
if len >= size_of::<u128>() {
for i in off..=len - size_of::<u128>() {
let val =
u128::from_ne_bytes(bytes[i..i + size_of::<u128>()].try_into().unwrap());
if !v1_is_const && val == *v1 {
let new_bytes = v2.to_ne_bytes();
bytes[i..i + size_of::<u128>()].copy_from_slice(&new_bytes);
result = MutationResult::Mutated;
break;
} else if !v1_is_const && val.swap_bytes() == *v1 {
let new_bytes = v2.swap_bytes().to_ne_bytes();
bytes[i..i + size_of::<u128>()].copy_from_slice(&new_bytes);
result = MutationResult::Mutated;
break;
} else if val == *v2 {
let new_bytes = v1.to_ne_bytes();
bytes[i..i + size_of::<u128>()].copy_from_slice(&new_bytes);
result = MutationResult::Mutated;
break;
} else if val.swap_bytes() == *v2 {
let new_bytes = v1.swap_bytes().to_ne_bytes();
bytes[i..i + size_of::<u128>()].copy_from_slice(&new_bytes);
result = MutationResult::Mutated;
break;
}
}
}
}
CmpValues::Bytes(v) => {
'outer: for i in off..len {
let mut size = core::cmp::min(v.0.len(), len - i);
Expand Down Expand Up @@ -789,6 +818,39 @@ where
}
}
}
CmpValues::U128(v) => {
let cmp_size = random_slice_size::<{ size_of::<u128>() }, S>(state);

if len >= cmp_size {
for i in off..(len - (cmp_size - 1)) {
let mut val_bytes = [0; size_of::<u128>()];
val_bytes[..cmp_size].copy_from_slice(&bytes[i..i + cmp_size]);
let val = u128::from_ne_bytes(val_bytes);

if val == v.0 {
let new_bytes = &v.1.to_ne_bytes()[..cmp_size];
bytes[i..i + cmp_size].copy_from_slice(new_bytes);
result = MutationResult::Mutated;
break;
} else if val == v.1 {
let new_bytes = &v.0.to_ne_bytes()[..cmp_size];
bytes[i..i + cmp_size].copy_from_slice(new_bytes);
result = MutationResult::Mutated;
break;
} else if val.swap_bytes() == v.0 {
let new_bytes = v.1.swap_bytes().to_ne_bytes();
bytes[i..i + cmp_size].copy_from_slice(&new_bytes[..cmp_size]);
result = MutationResult::Mutated;
break;
} else if val.swap_bytes() == v.1 {
let new_bytes = v.0.swap_bytes().to_ne_bytes();
bytes[i..i + cmp_size].copy_from_slice(&new_bytes[..cmp_size]);
result = MutationResult::Mutated;
break;
}
}
}
}
CmpValues::Bytes(v) => {
'outer: for i in off..len {
let mut size = core::cmp::min(v.0.len(), len - i);
Expand Down Expand Up @@ -1798,6 +1860,50 @@ where
}
}
}
// U128 comparisons: pass only the low 64 bits to cmp_extend_encoding
// (the existing API is u64-based; 128-bit magic values are still useful
// as autotoken candidates even without full extend-encoding support)
(CmpValues::U128(orig), CmpValues::U128(new)) => {
let orig_v0 = orig.0 as u64;
let orig_v1 = orig.1 as u64;
let new_v0 = new.0 as u64;
let new_v1 = new.1 as u64;
let attribute = header.attribute().value();

if new_v0 != orig_v0 && orig_v0 != orig_v1 {
self.cmp_extend_encoding(
orig_v0,
orig_v1,
new_v0,
new_v1,
attribute,
new_bytes,
orig_bytes,
cmp_buf_idx,
taint_len,
input_len,
hshape,
&mut ret,
)?;
}

if new_v1 != orig_v1 && orig_v0 != orig_v1 {
self.cmp_extend_encoding(
orig_v1,
orig_v0,
new_v1,
new_v0,
Self::swapa(attribute),
new_bytes,
orig_bytes,
cmp_buf_idx,
taint_len,
input_len,
hshape,
&mut ret,
)?;
}
}
(CmpValues::Bytes(orig), CmpValues::Bytes(new)) => {
let (orig_v0, orig_v1, new_v0, new_v1) =
(&orig.0, &orig.1, &new.0, &new.1);
Expand Down
74 changes: 60 additions & 14 deletions crates/libafl/src/observers/cmp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,19 @@ use serde::{Deserialize, Serialize};

use crate::{Error, HasMetadata, executors::ExitKind, observers::Observer};

/// A bytes string for cmplog with up to 32 elements.
#[derive(Debug, Copy, Clone, Serialize, Deserialize, Eq, PartialEq)]
/// A bytes string for cmplog with up to 64 elements.
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct CmplogBytes {
buf: [u8; 32],
buf: [u8; 64],
len: u8,
}

impl CmplogBytes {
/// Creates a new [`CmplogBytes`] object from the provided buf and length.
/// Lengths above 32 are illegal but will be ignored.
/// Lengths above 64 are illegal but will be ignored.
#[must_use]
pub fn from_buf_and_len(buf: [u8; 32], len: u8) -> Self {
debug_assert!(len <= 32, "Len too big: {len}, max: 32");
pub fn from_buf_and_len(buf: [u8; 64], len: u8) -> Self {
debug_assert!(len <= 64, "Len too big: {len}, max: 64");
CmplogBytes { buf, len }
}
}
Expand All @@ -46,6 +46,49 @@ impl HasLen for CmplogBytes {
}
}

impl Serialize for CmplogBytes {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
use serde::ser::SerializeStruct;
let mut s = serializer.serialize_struct("CmplogBytes", 2)?;
s.serialize_field("buf", &self.buf.as_slice())?;
s.serialize_field("len", &self.len)?;
s.end()
}
}

impl<'de> Deserialize<'de> for CmplogBytes {
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
use serde::de::{self, MapAccess, Visitor};
use core::fmt;
struct CmplogBytesVisitor;
impl<'de> Visitor<'de> for CmplogBytesVisitor {
type Value = CmplogBytes;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("struct CmplogBytes")
}
fn visit_map<V: MapAccess<'de>>(self, mut map: V) -> Result<CmplogBytes, V::Error> {
let mut buf_vec: Option<Vec<u8>> = None;
let mut len: Option<u8> = None;
while let Some(key) = map.next_key::<alloc::string::String>()? {
match key.as_str() {
"buf" => { buf_vec = Some(map.next_value()?); }
"len" => { len = Some(map.next_value()?); }
_ => { let _ = map.next_value::<de::IgnoredAny>()?; }
}
}
let buf_vec = buf_vec.ok_or_else(|| de::Error::missing_field("buf"))?;
let len = len.ok_or_else(|| de::Error::missing_field("len"))?;
let mut buf = [0u8; 64];
let copy_len = buf_vec.len().min(64);
buf[..copy_len].copy_from_slice(&buf_vec[..copy_len]);
Ok(CmplogBytes { buf, len })
}
}
const FIELDS: &[&str] = &["buf", "len"];
deserializer.deserialize_struct("CmplogBytes", FIELDS, CmplogBytesVisitor)
}
}

/// Compare values collected during a run
#[derive(Eq, PartialEq, Debug, Serialize, Deserialize, Clone)]
pub enum CmpValues {
Expand All @@ -57,6 +100,8 @@ pub enum CmpValues {
U32((u32, u32, bool)),
/// (side 1 of comparison, side 2 of comparison, side 1 value is const)
U64((u64, u64, bool)),
/// (side 1 of comparison, side 2 of comparison, side 1 value is const)
U128((u128, u128, bool)),
/// Two vecs of u8 values/byte
Bytes((CmplogBytes, CmplogBytes)),
}
Expand All @@ -67,18 +112,19 @@ impl CmpValues {
pub fn is_numeric(&self) -> bool {
matches!(
self,
CmpValues::U8(_) | CmpValues::U16(_) | CmpValues::U32(_) | CmpValues::U64(_)
CmpValues::U8(_) | CmpValues::U16(_) | CmpValues::U32(_) | CmpValues::U64(_) | CmpValues::U128(_)
)
}

/// Converts the value to a u64 tuple
#[must_use]
pub fn to_u64_tuple(&self) -> Option<(u64, u64, bool)> {
pub fn to_u128_tuple(&self) -> Option<(u128, u128, bool)> {
match self {
CmpValues::U8(t) => Some((u64::from(t.0), u64::from(t.1), t.2)),
CmpValues::U16(t) => Some((u64::from(t.0), u64::from(t.1), t.2)),
CmpValues::U32(t) => Some((u64::from(t.0), u64::from(t.1), t.2)),
CmpValues::U64(t) => Some(*t),
CmpValues::U8(t) => Some((u128::from(t.0), u128::from(t.1), t.2)),
CmpValues::U16(t) => Some((u128::from(t.0), u128::from(t.1), t.2)),
CmpValues::U32(t) => Some((u128::from(t.0), u128::from(t.1), t.2)),
CmpValues::U64(t) => Some((u128::from(t.0), u128::from(t.1), t.2)),
CmpValues::U128(t) => Some(*t),
CmpValues::Bytes(_) => None,
}
}
Expand Down Expand Up @@ -140,8 +186,8 @@ impl CmpValuesMetadata {
let mut last: Option<CmpValues> = None;
for j in 0..execs {
if let Some(val) = cmp_map.values_of(i, j) {
if let Some(l) = last.and_then(|x| x.to_u64_tuple())
&& let Some(v) = val.to_u64_tuple()
if let Some(l) = last.and_then(|x| x.to_u128_tuple())
&& let Some(v) = val.to_u128_tuple()
{
if l.0.wrapping_add(1) == v.0 {
increasing_v0 += 1;
Expand Down
63 changes: 45 additions & 18 deletions crates/libafl_targets/src/cmps/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -422,23 +422,34 @@ impl CmpMap for CmpLogMap {
self.vals.operands[idx][execution].1,
self.vals.operands[idx][execution].2 == 1,
))),
// TODO handle 128 bits & 256 bits & 512 bits cmps
15 | 31 | 63 => None,
// 128-bit: reconstruct u128 from lo and hi 64-bit halves
15 => {
let v0_lo = self.vals.operands[idx][execution].0;
let v0_hi = self.vals.operands[idx][execution].1;
let v0 = (v0_hi as u128) << 64 | v0_lo as u128;
let v1_lo = self.vals.operands[idx][execution].1;
let v1_hi = self.vals.operands[idx][execution].0;
let v1 = (v1_hi as u128) << 64 | v1_lo as u128;
let is_const = self.vals.operands[idx][execution].2 == 1;
Some(CmpValues::U128((v0, v1, is_const)))
}
// TODO handle 256 bits & 512 bits cmps
31 | 63 => None,
_ => panic!("Invalid CmpLog shape {shape}"),
}
}
} else {
unsafe {
Some(CmpValues::Bytes((
CmplogBytes::from_buf_and_len(
self.vals.routines[idx][execution].0,
CMPLOG_RTN_LEN as u8,
),
CmplogBytes::from_buf_and_len(
self.vals.routines[idx][execution].1,
CMPLOG_RTN_LEN as u8,
),
)))
Some(CmpValues::Bytes({
let mut buf0 = [0u8; 64];
let mut buf1 = [0u8; 64];
buf0[..CMPLOG_RTN_LEN].copy_from_slice(&self.vals.routines[idx][execution].0);
buf1[..CMPLOG_RTN_LEN].copy_from_slice(&self.vals.routines[idx][execution].1);
(
CmplogBytes::from_buf_and_len(buf0, CMPLOG_RTN_LEN as u8),
CmplogBytes::from_buf_and_len(buf1, CMPLOG_RTN_LEN as u8),
)
}))
}
}
}
Expand Down Expand Up @@ -628,19 +639,35 @@ impl CmpMap for AflppCmpLogMap {
self.vals.operands[idx][execution].v1,
false,
))),
// TODO handle 128 bits & 256 bits & 512 bits cmps
15 | 31 | 63 => None,
// 128-bit: reconstruct u128 from v0/v0_128 and v1/v1_128 fields
15 => {
let v0_lo = self.vals.operands[idx][execution].v0;
let v0_hi = self.vals.operands[idx][execution].v0_128;
let v0 = (v0_hi as u128) << 64 | v0_lo as u128;
let v1_lo = self.vals.operands[idx][execution].v1;
let v1_hi = self.vals.operands[idx][execution].v1_128;
let v1 = (v1_hi as u128) << 64 | v1_lo as u128;
Some(CmpValues::U128((v0, v1, false)))
}
// TODO handle 256 bits & 512 bits cmps
31 | 63 => None,
_ => panic!("Invalid CmpLog shape {shape}"),
}
}
} else {
unsafe {
let v0_len = self.vals.fn_operands[idx][execution].v0_len & (0x80 - 1);
let v1_len = self.vals.fn_operands[idx][execution].v1_len & (0x80 - 1);
Some(CmpValues::Bytes((
CmplogBytes::from_buf_and_len(self.vals.fn_operands[idx][execution].v0, v0_len),
CmplogBytes::from_buf_and_len(self.vals.fn_operands[idx][execution].v1, v1_len),
)))
Some(CmpValues::Bytes({
let mut buf0 = [0u8; 64];
let mut buf1 = [0u8; 64];
buf0[..32].copy_from_slice(&self.vals.fn_operands[idx][execution].v0);
buf1[..32].copy_from_slice(&self.vals.fn_operands[idx][execution].v1);
(
CmplogBytes::from_buf_and_len(buf0, v0_len),
CmplogBytes::from_buf_and_len(buf1, v1_len),
)
}))
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions crates/libafl_targets/src/cmps/observers/aflpp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -229,8 +229,8 @@ pub fn add_to_aflpp_cmp_metadata(
let mut last: Option<CmpValues> = None;
for j in 0..execs {
if let Some(val) = cmp_map.values_of(i, j) {
if let Some(l) = last.and_then(|x| x.to_u64_tuple())
&& let Some(v) = val.to_u64_tuple()
if let Some(l) = last.and_then(|x| x.to_u128_tuple())
&& let Some(v) = val.to_u128_tuple()
{
if l.0.wrapping_add(1) == v.0 {
increasing_v0 += 1;
Expand Down
Loading