Skip to content
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

Use const to Inform CmpLog Replacements #2528

Merged
merged 5 commits into from
Sep 24, 2024
Merged
Show file tree
Hide file tree
Changes from 2 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
64 changes: 32 additions & 32 deletions libafl/src/mutators/token_mutations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -457,99 +457,99 @@ where

let mut result = MutationResult::Skipped;
match cmp_values {
CmpValues::U8(v) => {
CmpValues::U8((v1, v2, v1_is_const)) => {
for byte in bytes.iter_mut().take(len).skip(off) {
if *byte == v.0 {
*byte = v.1;
if !v1_is_const && *byte == *v1 {
*byte = *v2;
result = MutationResult::Mutated;
break;
} else if *byte == v.1 {
*byte = v.0;
} else if *byte == *v2 {
*byte = *v1;
result = MutationResult::Mutated;
break;
}
}
}
CmpValues::U16(v) => {
CmpValues::U16((v1, v2, v1_is_const)) => {
if len >= size_of::<u16>() {
for i in off..=len - size_of::<u16>() {
let val =
u16::from_ne_bytes(bytes[i..i + size_of::<u16>()].try_into().unwrap());
if val == v.0 {
let new_bytes = v.1.to_ne_bytes();
if !v1_is_const && val == *v1 {
let new_bytes = v2.to_ne_bytes();
bytes[i..i + size_of::<u16>()].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();
} else if !v1_is_const && val.swap_bytes() == *v1 {
let new_bytes = v2.swap_bytes().to_ne_bytes();
bytes[i..i + size_of::<u16>()].copy_from_slice(&new_bytes);
result = MutationResult::Mutated;
break;
} else if val == v.1 {
let new_bytes = v.0.to_ne_bytes();
} else if val == *v2 {
let new_bytes = v1.to_ne_bytes();
bytes[i..i + size_of::<u16>()].copy_from_slice(&new_bytes);
result = MutationResult::Mutated;
break;
} else if val.swap_bytes() == v.1 {
let new_bytes = v.0.swap_bytes().to_ne_bytes();
} else if val.swap_bytes() == *v2 {
let new_bytes = v1.swap_bytes().to_ne_bytes();
bytes[i..i + size_of::<u16>()].copy_from_slice(&new_bytes);
result = MutationResult::Mutated;
break;
}
}
}
}
CmpValues::U32(v) => {
CmpValues::U32((v1, v2, v1_is_const)) => {
if len >= size_of::<u32>() {
for i in off..=len - size_of::<u32>() {
let val =
u32::from_ne_bytes(bytes[i..i + size_of::<u32>()].try_into().unwrap());
if val == v.0 {
let new_bytes = v.1.to_ne_bytes();
if !v1_is_const && val == *v1 {
let new_bytes = v2.to_ne_bytes();
bytes[i..i + size_of::<u32>()].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();
} else if !v1_is_const && val.swap_bytes() == *v1 {
let new_bytes = v2.swap_bytes().to_ne_bytes();
bytes[i..i + size_of::<u32>()].copy_from_slice(&new_bytes);
result = MutationResult::Mutated;
break;
} else if val == v.1 {
let new_bytes = v.0.to_ne_bytes();
} else if val == *v2 {
let new_bytes = v1.to_ne_bytes();
bytes[i..i + size_of::<u32>()].copy_from_slice(&new_bytes);
result = MutationResult::Mutated;
break;
} else if val.swap_bytes() == v.1 {
let new_bytes = v.0.swap_bytes().to_ne_bytes();
} else if val.swap_bytes() == *v2 {
let new_bytes = v1.swap_bytes().to_ne_bytes();
bytes[i..i + size_of::<u32>()].copy_from_slice(&new_bytes);
result = MutationResult::Mutated;
break;
}
}
}
}
CmpValues::U64(v) => {
CmpValues::U64((v1, v2, v1_is_const)) => {
if len >= size_of::<u64>() {
for i in off..=len - size_of::<u64>() {
let val =
u64::from_ne_bytes(bytes[i..i + size_of::<u64>()].try_into().unwrap());
if val == v.0 {
let new_bytes = v.1.to_ne_bytes();
if !v1_is_const && val == *v1 {
let new_bytes = v2.to_ne_bytes();
bytes[i..i + size_of::<u64>()].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();
} else if !v1_is_const && val.swap_bytes() == *v1 {
let new_bytes = v2.swap_bytes().to_ne_bytes();
bytes[i..i + size_of::<u64>()].copy_from_slice(&new_bytes);
result = MutationResult::Mutated;
break;
} else if val == v.1 {
let new_bytes = v.0.to_ne_bytes();
} else if val == *v2 {
let new_bytes = v1.to_ne_bytes();
bytes[i..i + size_of::<u64>()].copy_from_slice(&new_bytes);
result = MutationResult::Mutated;
break;
} else if val.swap_bytes() == v.1 {
let new_bytes = v.0.swap_bytes().to_ne_bytes();
} else if val.swap_bytes() == *v2 {
let new_bytes = v1.swap_bytes().to_ne_bytes();
bytes[i..i + size_of::<u64>()].copy_from_slice(&new_bytes);
result = MutationResult::Mutated;
break;
Expand Down
24 changes: 12 additions & 12 deletions libafl/src/observers/cmp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,14 +71,14 @@ impl HasLen for CmplogBytes {
/// Compare values collected during a run
#[derive(Eq, PartialEq, Debug, Serialize, Deserialize, Clone)]
pub enum CmpValues {
/// Two u8 values
U8((u8, u8)),
/// Two u16 values
U16((u16, u16)),
/// Two u32 values
U32((u32, u32)),
/// Two u64 values
U64((u64, u64)),
/// (side 1 of comparison, side 2 of comparison, side 1 value is const)
U8((u8, u8, bool)),
/// (side 1 of comparison, side 2 of comparison, side 1 value is const)
U16((u16, u16, bool)),
/// (side 1 of comparison, side 2 of comparison, side 1 value is const)
U32((u32, u32, bool)),
/// (side 1 of comparison, side 2 of comparison, side 1 value is const)
U64((u64, u64, bool)),
/// Two vecs of u8 values/byte
Bytes((CmplogBytes, CmplogBytes)),
}
Expand All @@ -95,11 +95,11 @@ impl CmpValues {

/// Converts the value to a u64 tuple
#[must_use]
pub fn to_u64_tuple(&self) -> Option<(u64, u64)> {
pub fn to_u64_tuple(&self) -> Option<(u64, u64, bool)> {
match self {
CmpValues::U8(t) => Some((u64::from(t.0), u64::from(t.1))),
CmpValues::U16(t) => Some((u64::from(t.0), u64::from(t.1))),
CmpValues::U32(t) => Some((u64::from(t.0), u64::from(t.1))),
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::Bytes(_) => None,
}
Expand Down
14 changes: 7 additions & 7 deletions libafl_targets/src/cmplog.c
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ static inline long area_is_valid(const void *ptr, size_t len) {
// Very generic cmplog instructions callback
void __libafl_targets_cmplog_instructions(uintptr_t k, uint8_t shape,
uint64_t arg1, uint64_t arg2) {
cmplog_instructions_checked(k, shape, arg1, arg2);
cmplog_instructions_checked(k, shape, arg1, arg2, 0);
}

// Very generic cmplog routines callback
Expand Down Expand Up @@ -145,7 +145,7 @@ void __cmplog_ins_hook1(uint8_t arg1, uint8_t arg2) {
k = (k >> 4) ^ (k << 8);
k &= CMPLOG_MAP_W - 1;

cmplog_instructions_checked(k, 1, arg1, arg2);
cmplog_instructions_checked(k, 1, arg1, arg2, 0);
}

void __cmplog_ins_hook2_extended(uint16_t arg1, uint16_t arg2, uint8_t attr) {
Expand All @@ -160,7 +160,7 @@ void __cmplog_ins_hook2(uint16_t arg1, uint16_t arg2) {
k = (k >> 4) ^ (k << 8);
k &= CMPLOG_MAP_W - 1;

cmplog_instructions_checked(k, 2, arg1, arg2);
cmplog_instructions_checked(k, 2, arg1, arg2, 0);
}

void __cmplog_ins_hook4_extended(uint32_t arg1, uint32_t arg2, uint8_t attr) {
Expand All @@ -175,7 +175,7 @@ void __cmplog_ins_hook4(uint32_t arg1, uint32_t arg2) {
k = (k >> 4) ^ (k << 8);
k &= CMPLOG_MAP_W - 1;

cmplog_instructions_checked(k, 4, arg1, arg2);
cmplog_instructions_checked(k, 4, arg1, arg2, 0);
}

void __cmplog_ins_hook8_extended(uint64_t arg1, uint64_t arg2, uint8_t attr) {
Expand All @@ -190,7 +190,7 @@ void __cmplog_ins_hook8(uint64_t arg1, uint64_t arg2) {
k = (k >> 4) ^ (k << 8);
k &= CMPLOG_MAP_W - 1;

cmplog_instructions_checked(k, 8, arg1, arg2);
cmplog_instructions_checked(k, 8, arg1, arg2, 0);
}

#if !defined(_WIN32) && defined(__SIZEOF_INT128__)
Expand All @@ -207,7 +207,7 @@ void __cmplog_ins_hook16(uint128_t arg1, uint128_t arg2) {
k = (k >> 4) ^ (k << 8);
k &= CMPLOG_MAP_W - 1;

cmplog_instructions_checked(k, 16, arg1, arg2);
cmplog_instructions_checked(k, 16, arg1, arg2, 0);
}

void __cmplog_ins_hookN_extended(uint128_t arg1, uint128_t arg2, uint8_t attr,
Expand All @@ -223,7 +223,7 @@ void __cmplog_ins_hookN(uint128_t arg1, uint128_t arg2, uint8_t size) {
k = (k >> 4) ^ (k << 8);
k &= CMPLOG_MAP_W - 1;

cmplog_instructions_checked(k, size, arg1, arg2);
cmplog_instructions_checked(k, size, arg1, arg2, 0);
}
#endif
/*
Expand Down
8 changes: 7 additions & 1 deletion libafl_targets/src/cmplog.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ typedef PACKED(struct CmpLogHeaderExtended {
typedef struct CmpLogInstruction {
uint64_t v0;
uint64_t v1;
uint8_t v0_is_const;
} CmpLogInstruction;

typedef PACKED(struct CmpLogInstructionExtended {
Expand Down Expand Up @@ -106,7 +107,8 @@ extern uint8_t libafl_cmplog_enabled;
// cmplog_routines_checked_extended

static inline void cmplog_instructions_checked(uintptr_t k, uint8_t shape,
uint64_t arg1, uint64_t arg2) {
uint64_t arg1, uint64_t arg2,
uint8_t arg1_is_const) {
if (!libafl_cmplog_enabled) { return; }
libafl_cmplog_enabled = false;

Expand All @@ -126,6 +128,7 @@ static inline void cmplog_instructions_checked(uintptr_t k, uint8_t shape,
hits &= CMPLOG_MAP_H - 1;
libafl_cmplog_map_ptr->vals.operands[k][hits].v0 = arg1;
libafl_cmplog_map_ptr->vals.operands[k][hits].v1 = arg2;
libafl_cmplog_map_ptr->vals.operands[k][hits].v0_is_const = arg1_is_const;
libafl_cmplog_enabled = true;
}

Expand All @@ -152,6 +155,7 @@ static inline void cmplog_instructions_extended_checked(
hits &= CMPLOG_MAP_H - 1;
libafl_cmplog_map_extended_ptr->vals.operands[k][hits].v0 = arg1;
libafl_cmplog_map_extended_ptr->vals.operands[k][hits].v1 = arg2;
libafl_cmplog_map_extended_ptr->vals.operands[k][hits].v0_is_const = 0;
Copy link
Member

@tokatoka tokatoka Sep 18, 2024

Choose a reason for hiding this comment

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

this "extended" one is only used for llvm pass instrumentation (which is not using sancov stuff so you don't add it for this.
(but it's doable to add the const checking logic to the llvm pass

Copy link
Member

Choose a reason for hiding this comment

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

line 222, too

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, hence it is set to 0 for now (unless you wanted to create a separate CmpLog struct without the v0_is_const member). I think that for longer comparisons (such as u64 or memcmps), the chance of conincidentally matching a const value is lower (apart from values like 0); so it's probably not worth the extra effort.

Copy link
Member

Choose a reason for hiding this comment

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

what i mean is struct CmpLogInstructionExtended doesn't have v0_is_const member

Copy link
Contributor Author

Choose a reason for hiding this comment

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

True

libafl_cmplog_map_extended_ptr->headers[k].attribute = attr;
libafl_cmplog_enabled = true;
#else
Expand Down Expand Up @@ -186,6 +190,7 @@ static inline void cmplog_routines_checked(uintptr_t k, const uint8_t *ptr1,
hits &= CMPLOG_MAP_RTN_H - 1;
MEMCPY(libafl_cmplog_map_ptr->vals.routines[k][hits].v0, ptr1, len);
MEMCPY(libafl_cmplog_map_ptr->vals.routines[k][hits].v1, ptr2, len);
libafl_cmplog_map_ptr->vals.operands[k][hits].v0_is_const = 0;
Copy link
Member

Choose a reason for hiding this comment

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

this callback is for routines. you don't need it here

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Again, this is basically an unused struct member - I still think it's worth making sure that it's initialised, even if it is always 0.

Copy link
Member

Choose a reason for hiding this comment

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

hmm but this is a union. so you shouldn't be touching operand side of this struct.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good spot!

libafl_cmplog_enabled = true;
}

Expand Down Expand Up @@ -214,6 +219,7 @@ static inline void cmplog_routines_checked_extended(uintptr_t k,
hits &= CMPLOG_MAP_RTN_H - 1;
libafl_cmplog_map_extended_ptr->vals.routines[k][hits].v0_len = len;
libafl_cmplog_map_extended_ptr->vals.routines[k][hits].v1_len = len;
libafl_cmplog_map_extended_ptr->vals.operands[k][hits].v0_is_const = 0;
MEMCPY(libafl_cmplog_map_extended_ptr->vals.routines[k][hits].v0, ptr1, len);
MEMCPY(libafl_cmplog_map_extended_ptr->vals.routines[k][hits].v1, ptr2, len);
libafl_cmplog_enabled = true;
Expand Down
12 changes: 10 additions & 2 deletions libafl_targets/src/cmps/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,7 @@ impl AFLppCmpLogFnOperands {
/// The operands logged during `CmpLog`.
#[repr(C)]
#[derive(Default, Debug, Clone, Copy)]
pub struct CmpLogInstruction(u64, u64);
pub struct CmpLogInstruction(u64, u64, u8);

/// The routine arguments logged during `CmpLog`.
#[repr(C)]
Expand Down Expand Up @@ -371,18 +371,22 @@ impl CmpMap for CmpLogMap {
1 => Some(CmpValues::U8((
self.vals.operands[idx][execution].0 as u8,
self.vals.operands[idx][execution].1 as u8,
self.vals.operands[idx][execution].2 == 1,
))),
2 => Some(CmpValues::U16((
self.vals.operands[idx][execution].0 as u16,
self.vals.operands[idx][execution].1 as u16,
self.vals.operands[idx][execution].2 == 1,
))),
4 => Some(CmpValues::U32((
self.vals.operands[idx][execution].0 as u32,
self.vals.operands[idx][execution].1 as u32,
self.vals.operands[idx][execution].2 == 1,
))),
8 => Some(CmpValues::U64((
self.vals.operands[idx][execution].0,
self.vals.operands[idx][execution].1,
self.vals.operands[idx][execution].2 == 1,
))),
// other => panic!("Invalid CmpLog shape {}", other),
_ => None,
Expand Down Expand Up @@ -426,7 +430,7 @@ pub static mut libafl_cmplog_map: CmpLogMap = CmpLogMap {
kind: 0,
}; CMPLOG_MAP_W],
vals: CmpLogVals {
operands: [[CmpLogInstruction(0, 0); CMPLOG_MAP_H]; CMPLOG_MAP_W],
operands: [[CmpLogInstruction(0, 0, 0); CMPLOG_MAP_H]; CMPLOG_MAP_W],
},
};

Expand Down Expand Up @@ -551,18 +555,22 @@ impl CmpMap for AFLppCmpLogMap {
0 => Some(CmpValues::U8((
self.vals.operands[idx][execution].v0 as u8,
self.vals.operands[idx][execution].v1 as u8,
false,
))),
1 => Some(CmpValues::U16((
self.vals.operands[idx][execution].v0 as u16,
self.vals.operands[idx][execution].v1 as u16,
false,
))),
3 => Some(CmpValues::U32((
self.vals.operands[idx][execution].v0 as u32,
self.vals.operands[idx][execution].v1 as u32,
false,
))),
7 => Some(CmpValues::U64((
self.vals.operands[idx][execution].v0,
self.vals.operands[idx][execution].v1,
false,
))),
// TODO handle 128 bits & 256 bits cmps
// other => panic!("Invalid CmpLog shape {}", other),
Expand Down
Loading
Loading