diff --git a/libafl/src/mutators/token_mutations.rs b/libafl/src/mutators/token_mutations.rs index c39219a36c..fa39fda243 100644 --- a/libafl/src/mutators/token_mutations.rs +++ b/libafl/src/mutators/token_mutations.rs @@ -457,41 +457,41 @@ 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::() { for i in off..=len - size_of::() { let val = u16::from_ne_bytes(bytes[i..i + size_of::()].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::()].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::()].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::()].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::()].copy_from_slice(&new_bytes); result = MutationResult::Mutated; break; @@ -499,28 +499,28 @@ where } } } - CmpValues::U32(v) => { + CmpValues::U32((v1, v2, v1_is_const)) => { if len >= size_of::() { for i in off..=len - size_of::() { let val = u32::from_ne_bytes(bytes[i..i + size_of::()].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::()].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::()].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::()].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::()].copy_from_slice(&new_bytes); result = MutationResult::Mutated; break; @@ -528,28 +528,28 @@ where } } } - CmpValues::U64(v) => { + CmpValues::U64((v1, v2, v1_is_const)) => { if len >= size_of::() { for i in off..=len - size_of::() { let val = u64::from_ne_bytes(bytes[i..i + size_of::()].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::()].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::()].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::()].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::()].copy_from_slice(&new_bytes); result = MutationResult::Mutated; break; diff --git a/libafl/src/observers/cmp.rs b/libafl/src/observers/cmp.rs index 2d5b0298a2..ffd08ddf23 100644 --- a/libafl/src/observers/cmp.rs +++ b/libafl/src/observers/cmp.rs @@ -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)), } @@ -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, } diff --git a/libafl_targets/src/cmplog.c b/libafl_targets/src/cmplog.c index ffaec50f3b..db4d51b667 100644 --- a/libafl_targets/src/cmplog.c +++ b/libafl_targets/src/cmplog.c @@ -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 @@ -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) { @@ -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) { @@ -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) { @@ -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__) @@ -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, @@ -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 /* diff --git a/libafl_targets/src/cmplog.h b/libafl_targets/src/cmplog.h index 27a11e4955..2750a85fde 100644 --- a/libafl_targets/src/cmplog.h +++ b/libafl_targets/src/cmplog.h @@ -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 { @@ -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; @@ -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; } diff --git a/libafl_targets/src/cmps/mod.rs b/libafl_targets/src/cmps/mod.rs index c11bf6ee01..a729ff6620 100644 --- a/libafl_targets/src/cmps/mod.rs +++ b/libafl_targets/src/cmps/mod.rs @@ -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)] @@ -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, @@ -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], }, }; @@ -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), diff --git a/libafl_targets/src/sancov_cmp.c b/libafl_targets/src/sancov_cmp.c index 42eb6aa3ee..4720894a1a 100644 --- a/libafl_targets/src/sancov_cmp.c +++ b/libafl_targets/src/sancov_cmp.c @@ -8,60 +8,46 @@ #include "cmplog.h" #endif -void __sanitizer_cov_trace_cmp1(uint8_t arg1, uint8_t arg2) { - uintptr_t k = RETADDR; - k = (k >> 4) ^ (k << 8); - +// Note: for RETADDR to give us the fuzz target caller address we need +// to guarantee that this code is inlined. `inline` keyword provides +// no such guarantees, but a macro does. #ifdef SANCOV_VALUE_PROFILE - k &= CMP_MAP_SIZE - 1; - __libafl_targets_value_profile1(k, arg1, arg2); + #define SANCOV_VALUE_PROFILE_CALL(k, arg_size, arg1, arg2, arg1_is_const) \ + k &= CMP_MAP_SIZE - 1; \ + __libafl_targets_value_profile1(k, arg1, arg2); +#else + #define SANCOV_VALUE_PROFILE_CALL(k, arg_size, arg1, arg2, arg1_is_const) #endif + #ifdef SANCOV_CMPLOG - k &= CMPLOG_MAP_W - 1; - cmplog_instructions_checked(k, 1, (uint64_t)arg1, (uint64_t)arg2); + #define SANCOV_CMPLOG_CALL(k, arg_size, arg1, arg2, arg1_is_const) \ + k &= CMPLOG_MAP_W - 1; \ + cmplog_instructions_checked(k, arg_size, (uint64_t)arg1, (uint64_t)arg2, arg1_is_const); +#else + #define SANCOV_CMPLOG_CALL(k, arg_size, arg1, arg2, arg1_is_const) #endif + +#define HANDLE_SANCOV_TRACE_CMP(arg_size, arg1, arg2, arg1_is_const) { \ + uintptr_t k = RETADDR; \ + k = (k >> 4) ^ (k << 8); \ + SANCOV_VALUE_PROFILE_CALL(k, arg_size, arg1, arg2, arg1_is_const) \ + SANCOV_CMPLOG_CALL(k, arg_size, arg1, arg2, arg1_is_const) \ } -void __sanitizer_cov_trace_cmp2(uint16_t arg1, uint16_t arg2) { - uintptr_t k = RETADDR; - k = (k >> 4) ^ (k << 8); +void __sanitizer_cov_trace_cmp1(uint8_t arg1, uint8_t arg2) { + HANDLE_SANCOV_TRACE_CMP(1, arg1, arg2, 0); +} -#ifdef SANCOV_VALUE_PROFILE - k &= CMP_MAP_SIZE - 1; - __libafl_targets_value_profile2(k, arg1, arg2); -#endif -#ifdef SANCOV_CMPLOG - k &= CMPLOG_MAP_W - 1; - cmplog_instructions_checked(k, 2, (uint64_t)arg1, (uint64_t)arg2); -#endif +void __sanitizer_cov_trace_cmp2(uint16_t arg1, uint16_t arg2) { + HANDLE_SANCOV_TRACE_CMP(2, arg1, arg2, 0); } void __sanitizer_cov_trace_cmp4(uint32_t arg1, uint32_t arg2) { - uintptr_t k = RETADDR; - k = (k >> 4) ^ (k << 8); - -#ifdef SANCOV_VALUE_PROFILE - k &= CMP_MAP_SIZE - 1; - __libafl_targets_value_profile4(k, arg1, arg2); -#endif -#ifdef SANCOV_CMPLOG - k &= CMPLOG_MAP_W - 1; - cmplog_instructions_checked(k, 4, (uint64_t)arg1, (uint64_t)arg2); -#endif + HANDLE_SANCOV_TRACE_CMP(4, arg1, arg2, 0); } void __sanitizer_cov_trace_cmp8(uint64_t arg1, uint64_t arg2) { - uintptr_t k = RETADDR; - k = (k >> 4) ^ (k << 8); - -#ifdef SANCOV_VALUE_PROFILE - k &= CMP_MAP_SIZE - 1; - __libafl_targets_value_profile8(k, arg1, arg2); -#endif -#ifdef SANCOV_CMPLOG - k &= CMPLOG_MAP_W - 1; - cmplog_instructions_checked(k, 8, (uint64_t)arg1, (uint64_t)arg2); -#endif + HANDLE_SANCOV_TRACE_CMP(8, arg1, arg2, 0); } void __sanitizer_cov_trace_switch(uint64_t val, uint64_t *cases) { @@ -94,25 +80,26 @@ void __sanitizer_cov_trace_switch(uint64_t val, uint64_t *cases) { #endif #ifdef SANCOV_CMPLOG k &= CMPLOG_MAP_W - 1; - cmplog_instructions_checked(k, cases[1] / 8, val, cases[i + 2]); + // Note: cases[i + 2] are the constant values, so keep them in arg1 and indicate that it's const + cmplog_instructions_checked(k, cases[1] / 8, cases[i + 2], val, 1); #endif } } void __sanitizer_cov_trace_const_cmp1(uint8_t arg1, uint8_t arg2) { - __sanitizer_cov_trace_cmp1(arg1, arg2); + HANDLE_SANCOV_TRACE_CMP(1, arg1, arg2, 1); } void __sanitizer_cov_trace_const_cmp2(uint16_t arg1, uint16_t arg2) { - __sanitizer_cov_trace_cmp2(arg1, arg2); + HANDLE_SANCOV_TRACE_CMP(2, arg1, arg2, 1); } void __sanitizer_cov_trace_const_cmp4(uint32_t arg1, uint32_t arg2) { - __sanitizer_cov_trace_cmp4(arg1, arg2); + HANDLE_SANCOV_TRACE_CMP(4, arg1, arg2, 1); } void __sanitizer_cov_trace_const_cmp8(uint64_t arg1, uint64_t arg2) { - __sanitizer_cov_trace_cmp8(arg1, arg2); + HANDLE_SANCOV_TRACE_CMP(8, arg1, arg2, 1); } #pragma GCC diagnostic push