Fix UB in frida fuzzers (#1385)
* WIP: fix ub issue in frida fuzzers * refactor frida helper: remove unused fields * revert frida-gum bump. Current frida-gum doesn't build on iOS :/ * libafl_frida: silence must_use_candidate lint this lint is very noisy, and adding #[must_use] to _all_ (even pure )functions seems very excessive to me * fix clippy
This commit is contained in:
parent
37bfead4e5
commit
fc9caa8314
@ -2365,7 +2365,7 @@ impl AsanRuntime {
|
|||||||
| addr | rip |
|
| addr | rip |
|
||||||
| Rcx | Rax |
|
| Rcx | Rax |
|
||||||
| Rsi | Rdx |
|
| Rsi | Rdx |
|
||||||
Old Rsp - (redsone_size) -> | flags | Rdi |
|
Old Rsp - (redzone_size) -> | flags | Rdi |
|
||||||
| | |
|
| | |
|
||||||
Old Rsp -> | | |
|
Old Rsp -> | | |
|
||||||
*/
|
*/
|
||||||
|
@ -1,11 +1,10 @@
|
|||||||
use core::fmt::{self, Debug, Formatter};
|
use core::fmt::{self, Debug, Formatter};
|
||||||
|
use std::{
|
||||||
#[cfg(target_arch = "aarch64")]
|
cell::{Ref, RefCell, RefMut},
|
||||||
use capstone::{
|
rc::Rc,
|
||||||
arch::{self, BuildsCapstone},
|
|
||||||
Capstone,
|
|
||||||
};
|
};
|
||||||
#[cfg(all(target_arch = "x86_64", unix))]
|
|
||||||
|
#[cfg(any(target_arch = "aarch64", all(target_arch = "x86_64", unix)))]
|
||||||
use capstone::{
|
use capstone::{
|
||||||
arch::{self, BuildsCapstone},
|
arch::{self, BuildsCapstone},
|
||||||
Capstone,
|
Capstone,
|
||||||
@ -114,14 +113,11 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// An helper that feeds `FridaInProcessExecutor` with edge-coverage instrumentation
|
/// An helper that feeds `FridaInProcessExecutor` with edge-coverage instrumentation
|
||||||
pub struct FridaInstrumentationHelper<'a, RT> {
|
pub struct FridaInstrumentationHelper<'a, RT: 'a> {
|
||||||
#[cfg(unix)]
|
|
||||||
capstone: Capstone,
|
|
||||||
ranges: RangeMap<usize, (u16, String)>,
|
|
||||||
module_map: ModuleMap,
|
|
||||||
options: &'a FuzzerOptions,
|
options: &'a FuzzerOptions,
|
||||||
transformer: Option<Transformer<'a>>,
|
transformer: Transformer<'a>,
|
||||||
runtimes: RT,
|
ranges: Rc<RefCell<RangeMap<usize, (u16, String)>>>,
|
||||||
|
runtimes: Rc<RefCell<RT>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<RT> Debug for FridaInstrumentationHelper<'_, RT> {
|
impl<RT> Debug for FridaInstrumentationHelper<'_, RT> {
|
||||||
@ -166,7 +162,7 @@ where
|
|||||||
/// Constructor function to create a new [`FridaInstrumentationHelper`], given a `module_name`.
|
/// Constructor function to create a new [`FridaInstrumentationHelper`], given a `module_name`.
|
||||||
#[allow(clippy::too_many_lines)]
|
#[allow(clippy::too_many_lines)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new(gum: &'a Gum, options: &'a FuzzerOptions, runtimes: RT) -> Self {
|
pub fn new(gum: &'a Gum, options: &'a FuzzerOptions, mut runtimes: RT) -> Self {
|
||||||
// workaround frida's frida-gum-allocate-near bug:
|
// workaround frida's frida-gum-allocate-near bug:
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
unsafe {
|
unsafe {
|
||||||
@ -202,182 +198,185 @@ where
|
|||||||
let modules_to_instrument: Vec<&str> =
|
let modules_to_instrument: Vec<&str> =
|
||||||
modules_to_instrument.iter().map(AsRef::as_ref).collect();
|
modules_to_instrument.iter().map(AsRef::as_ref).collect();
|
||||||
|
|
||||||
let mut helper = Self {
|
let module_map = ModuleMap::new_from_names(gum, &modules_to_instrument);
|
||||||
#[cfg(target_arch = "aarch64")]
|
let mut ranges = RangeMap::new();
|
||||||
capstone: Capstone::new()
|
|
||||||
.arm64()
|
|
||||||
.mode(arch::arm64::ArchMode::Arm)
|
|
||||||
.detail(true)
|
|
||||||
.build()
|
|
||||||
.expect("Failed to create Capstone object"),
|
|
||||||
#[cfg(all(target_arch = "x86_64", unix))]
|
|
||||||
capstone: Capstone::new()
|
|
||||||
.x86()
|
|
||||||
.mode(arch::x86::ArchMode::Mode64)
|
|
||||||
.detail(true)
|
|
||||||
.build()
|
|
||||||
.expect("Failed to create Capstone object"),
|
|
||||||
ranges: RangeMap::new(),
|
|
||||||
module_map: ModuleMap::new_from_names(gum, &modules_to_instrument),
|
|
||||||
options,
|
|
||||||
runtimes,
|
|
||||||
transformer: None,
|
|
||||||
};
|
|
||||||
|
|
||||||
if options.cmplog || options.asan || !options.disable_coverage {
|
if options.cmplog || options.asan || !options.disable_coverage {
|
||||||
for (i, module) in helper.module_map.values().iter().enumerate() {
|
for (i, module) in module_map.values().iter().enumerate() {
|
||||||
let range = module.range();
|
let range = module.range();
|
||||||
let start = range.base_address().0 as usize;
|
let start = range.base_address().0 as usize;
|
||||||
// log::trace!("start: {:x}", start);
|
// log::trace!("start: {:x}", start);
|
||||||
helper
|
ranges.insert(start..(start + range.size()), (i as u16, module.path()));
|
||||||
.ranges
|
|
||||||
.insert(start..(start + range.size()), (i as u16, module.path()));
|
|
||||||
}
|
}
|
||||||
if !options.dont_instrument.is_empty() {
|
if !options.dont_instrument.is_empty() {
|
||||||
for (module_name, offset) in options.dont_instrument.clone() {
|
for (module_name, offset) in options.dont_instrument.clone() {
|
||||||
let module_details = ModuleDetails::with_name(module_name).unwrap();
|
let module_details = ModuleDetails::with_name(module_name).unwrap();
|
||||||
let lib_start = module_details.range().base_address().0 as usize;
|
let lib_start = module_details.range().base_address().0 as usize;
|
||||||
// log::info!("removing address: {:#x}", lib_start + offset);
|
// log::info!("removing address: {:#x}", lib_start + offset);
|
||||||
helper
|
ranges.remove((lib_start + offset)..(lib_start + offset + 4));
|
||||||
.ranges
|
|
||||||
.remove((lib_start + offset)..(lib_start + offset + 4));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// make sure we aren't in the instrumented list, as it would cause recursions
|
// make sure we aren't in the instrumented list, as it would cause recursions
|
||||||
assert!(
|
assert!(
|
||||||
!helper.ranges.contains_key(&(Self::new as usize)),
|
!ranges.contains_key(&(Self::new as usize)),
|
||||||
"instrumented libraries must not include the fuzzer"
|
"instrumented libraries must not include the fuzzer"
|
||||||
);
|
);
|
||||||
|
|
||||||
helper
|
runtimes.init_all(gum, &ranges, &modules_to_instrument);
|
||||||
.runtimes
|
|
||||||
.init_all(gum, &helper.ranges, &modules_to_instrument);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let transformer = Transformer::from_callback(gum, |basic_block, output| {
|
#[cfg(target_arch = "aarch64")]
|
||||||
let mut first = true;
|
let capstone = Capstone::new()
|
||||||
for instruction in basic_block {
|
.arm64()
|
||||||
let instr = instruction.instr();
|
.mode(arch::arm64::ArchMode::Arm)
|
||||||
#[cfg(unix)]
|
.detail(true)
|
||||||
let instr_size = instr.bytes().len();
|
.build()
|
||||||
let address = instr.address();
|
.expect("Failed to create Capstone object");
|
||||||
//log::trace!("block @ {:x} transformed to {:x}", address, output.writer().pc());
|
#[cfg(all(target_arch = "x86_64", unix))]
|
||||||
|
let capstone = Capstone::new()
|
||||||
|
.x86()
|
||||||
|
.mode(arch::x86::ArchMode::Mode64)
|
||||||
|
.detail(true)
|
||||||
|
.build()
|
||||||
|
.expect("Failed to create Capstone object");
|
||||||
|
|
||||||
//log::trace!(
|
// Wrap ranges and runtimes in reference-counted refcells in order to move
|
||||||
//"address: {:x} contains: {:?}",
|
// these references both into the struct that we return and the transformer callback
|
||||||
//address,
|
// that we pass to frida-gum.
|
||||||
//self.ranges().contains_key(&(address as usize))
|
let ranges = Rc::new(RefCell::new(ranges));
|
||||||
//);
|
let runtimes = Rc::new(RefCell::new(runtimes));
|
||||||
|
|
||||||
//log::info!("Ranges: {:#?}", self.ranges());
|
let transformer = {
|
||||||
if helper.ranges().contains_key(&(address as usize)) {
|
let ranges = Rc::clone(&ranges);
|
||||||
if first {
|
let runtimes = Rc::clone(&runtimes);
|
||||||
first = false;
|
Transformer::from_callback(gum, move |basic_block, output| {
|
||||||
// log::info!(
|
let mut first = true;
|
||||||
// "block @ {:x} transformed to {:x}",
|
for instruction in basic_block {
|
||||||
// address,
|
let instr = instruction.instr();
|
||||||
// output.writer().pc()
|
#[cfg(unix)]
|
||||||
// );
|
let instr_size = instr.bytes().len();
|
||||||
if let Some(rt) = helper.runtime_mut::<CoverageRuntime>() {
|
let address = instr.address();
|
||||||
rt.emit_coverage_mapping(address, &output);
|
//log::trace!("block @ {:x} transformed to {:x}", address, output.writer().pc());
|
||||||
|
|
||||||
|
if ranges.borrow().contains_key(&(address as usize)) {
|
||||||
|
let mut runtimes = (*runtimes).borrow_mut();
|
||||||
|
if first {
|
||||||
|
first = false;
|
||||||
|
// log::info!(
|
||||||
|
// "block @ {:x} transformed to {:x}",
|
||||||
|
// address,
|
||||||
|
// output.writer().pc()
|
||||||
|
// );
|
||||||
|
if let Some(rt) = runtimes.match_first_type_mut::<CoverageRuntime>() {
|
||||||
|
rt.emit_coverage_mapping(address, &output);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
if let Some(rt) = runtimes.match_first_type_mut::<DrCovRuntime>() {
|
||||||
|
instruction.put_callout(|context| {
|
||||||
|
let real_address = rt.real_address_for_stalked(pc(&context));
|
||||||
|
//let (range, (id, name)) = helper.ranges.get_key_value(&real_address).unwrap();
|
||||||
|
//log::trace!("{}:0x{:016x}", name, real_address - range.start);
|
||||||
|
rt.drcov_basic_blocks.push(DrCovBasicBlock::new(
|
||||||
|
real_address,
|
||||||
|
real_address + instr_size,
|
||||||
|
));
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
if let Some(rt) = helper.runtime_mut::<DrCovRuntime>() {
|
let res = if let Some(_rt) = runtimes.match_first_type_mut::<AsanRuntime>()
|
||||||
instruction.put_callout(|context| {
|
|
||||||
let real_address = rt.real_address_for_stalked(pc(&context));
|
|
||||||
//let (range, (id, name)) = helper.ranges.get_key_value(&real_address).unwrap();
|
|
||||||
//log::trace!("{}:0x{:016x}", name, real_address - range.start);
|
|
||||||
rt.drcov_basic_blocks.push(DrCovBasicBlock::new(
|
|
||||||
real_address,
|
|
||||||
real_address + instr_size,
|
|
||||||
));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(unix)]
|
|
||||||
let res = if let Some(_rt) = helper.runtime::<AsanRuntime>() {
|
|
||||||
AsanRuntime::asan_is_interesting_instruction(
|
|
||||||
&helper.capstone,
|
|
||||||
address,
|
|
||||||
instr,
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
#[cfg(all(target_arch = "x86_64", unix))]
|
|
||||||
if let Some((segment, width, basereg, indexreg, scale, disp)) = res {
|
|
||||||
if let Some(rt) = helper.runtime_mut::<AsanRuntime>() {
|
|
||||||
rt.emit_shadow_check(
|
|
||||||
address, &output, segment, width, basereg, indexreg, scale, disp,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(target_arch = "aarch64")]
|
|
||||||
if let Some((basereg, indexreg, displacement, width, shift, extender)) = res {
|
|
||||||
if let Some(rt) = helper.runtime_mut::<AsanRuntime>() {
|
|
||||||
rt.emit_shadow_check(
|
|
||||||
address,
|
|
||||||
&output,
|
|
||||||
basereg,
|
|
||||||
indexreg,
|
|
||||||
displacement,
|
|
||||||
width,
|
|
||||||
shift,
|
|
||||||
extender,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(all(feature = "cmplog", target_arch = "aarch64"))]
|
|
||||||
if let Some(rt) = helper.runtime::<CmpLogRuntime>() {
|
|
||||||
if let Some((op1, op2, special_case)) =
|
|
||||||
CmpLogRuntime::cmplog_is_interesting_instruction(
|
|
||||||
&helper.capstone,
|
|
||||||
address,
|
|
||||||
instr,
|
|
||||||
)
|
|
||||||
{
|
{
|
||||||
//emit code that saves the relevant data in runtime(passes it to x0, x1)
|
AsanRuntime::asan_is_interesting_instruction(&capstone, address, instr)
|
||||||
rt.emit_comparison_handling(address, &output, &op1, &op2, special_case);
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
#[cfg(all(target_arch = "x86_64", unix))]
|
||||||
|
if let Some((segment, width, basereg, indexreg, scale, disp)) = res {
|
||||||
|
if let Some(rt) = runtimes.match_first_type_mut::<AsanRuntime>() {
|
||||||
|
rt.emit_shadow_check(
|
||||||
|
address, &output, segment, width, basereg, indexreg, scale,
|
||||||
|
disp,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_arch = "aarch64")]
|
||||||
|
if let Some((basereg, indexreg, displacement, width, shift, extender)) = res
|
||||||
|
{
|
||||||
|
if let Some(rt) = runtimes.match_first_type_mut::<AsanRuntime>() {
|
||||||
|
rt.emit_shadow_check(
|
||||||
|
address,
|
||||||
|
&output,
|
||||||
|
basereg,
|
||||||
|
indexreg,
|
||||||
|
displacement,
|
||||||
|
width,
|
||||||
|
shift,
|
||||||
|
extender,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(all(feature = "cmplog", target_arch = "aarch64"))]
|
||||||
|
if let Some(rt) = runtimes.match_first_type_mut::<CmpLogRuntime>() {
|
||||||
|
if let Some((op1, op2, special_case)) =
|
||||||
|
CmpLogRuntime::cmplog_is_interesting_instruction(
|
||||||
|
&helper.capstone,
|
||||||
|
address,
|
||||||
|
instr,
|
||||||
|
)
|
||||||
|
{
|
||||||
|
//emit code that saves the relevant data in runtime(passes it to x0, x1)
|
||||||
|
rt.emit_comparison_handling(
|
||||||
|
address,
|
||||||
|
&output,
|
||||||
|
&op1,
|
||||||
|
&op2,
|
||||||
|
special_case,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
if let Some(rt) = runtimes.match_first_type_mut::<AsanRuntime>() {
|
||||||
|
rt.add_stalked_address(
|
||||||
|
output.writer().pc() as usize - instr_size,
|
||||||
|
address as usize,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
if let Some(rt) = runtimes.match_first_type_mut::<DrCovRuntime>() {
|
||||||
|
rt.add_stalked_address(
|
||||||
|
output.writer().pc() as usize - instr_size,
|
||||||
|
address as usize,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
instruction.keep();
|
||||||
#[cfg(unix)]
|
|
||||||
if let Some(rt) = helper.runtime_mut::<AsanRuntime>() {
|
|
||||||
rt.add_stalked_address(
|
|
||||||
output.writer().pc() as usize - instr_size,
|
|
||||||
address as usize,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(unix)]
|
|
||||||
if let Some(rt) = helper.runtime_mut::<DrCovRuntime>() {
|
|
||||||
rt.add_stalked_address(
|
|
||||||
output.writer().pc() as usize - instr_size,
|
|
||||||
address as usize,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
instruction.keep();
|
})
|
||||||
}
|
};
|
||||||
});
|
|
||||||
|
|
||||||
helper.transformer = Some(transformer);
|
Self {
|
||||||
|
options,
|
||||||
helper
|
transformer,
|
||||||
|
ranges,
|
||||||
|
runtimes,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
/// Return the runtime
|
/// Return the runtime
|
||||||
pub fn runtime<R>(&self) -> Option<&R>
|
pub fn runtime<R>(&self) -> Option<&R>
|
||||||
where
|
where
|
||||||
R: FridaRuntime,
|
R: FridaRuntime,
|
||||||
{
|
{
|
||||||
self.runtimes.match_first_type::<R>()
|
self.runtimes.borrow().match_first_type::<R>()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the mutable runtime
|
/// Return the mutable runtime
|
||||||
@ -385,13 +384,13 @@ where
|
|||||||
where
|
where
|
||||||
R: FridaRuntime,
|
R: FridaRuntime,
|
||||||
{
|
{
|
||||||
self.runtimes.match_first_type_mut::<R>()
|
(*self.runtimes).borrow_mut().match_first_type_mut::<R>()
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
/// Returns ref to the Transformer
|
/// Returns ref to the Transformer
|
||||||
pub fn transformer(&mut self) -> &Transformer<'a> {
|
pub fn transformer(&self) -> &Transformer<'a> {
|
||||||
// the Transformer is always initialized on `new`. We can safely unwrap.
|
&self.transformer
|
||||||
self.transformer.as_ref().unwrap()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Initialize all
|
/// Initialize all
|
||||||
@ -401,17 +400,19 @@ where
|
|||||||
ranges: &RangeMap<usize, (u16, String)>,
|
ranges: &RangeMap<usize, (u16, String)>,
|
||||||
modules_to_instrument: &'a [&str],
|
modules_to_instrument: &'a [&str],
|
||||||
) {
|
) {
|
||||||
self.runtimes.init_all(gum, ranges, modules_to_instrument);
|
(*self.runtimes)
|
||||||
|
.borrow_mut()
|
||||||
|
.init_all(gum, ranges, modules_to_instrument);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Method called before execution
|
/// Method called before execution
|
||||||
pub fn pre_exec<I: Input + HasTargetBytes>(&mut self, input: &I) -> Result<(), Error> {
|
pub fn pre_exec<I: Input + HasTargetBytes>(&mut self, input: &I) -> Result<(), Error> {
|
||||||
self.runtimes.pre_exec_all(input)
|
(*self.runtimes).borrow_mut().pre_exec_all(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Method called after execution
|
/// Method called after execution
|
||||||
pub fn post_exec<I: Input + HasTargetBytes>(&mut self, input: &I) -> Result<(), Error> {
|
pub fn post_exec<I: Input + HasTargetBytes>(&mut self, input: &I) -> Result<(), Error> {
|
||||||
self.runtimes.post_exec_all(input)
|
(*self.runtimes).borrow_mut().post_exec_all(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// If stalker is enabled
|
/// If stalker is enabled
|
||||||
@ -421,18 +422,20 @@ where
|
|||||||
|
|
||||||
/// Pointer to coverage map
|
/// Pointer to coverage map
|
||||||
pub fn map_mut_ptr(&mut self) -> Option<*mut u8> {
|
pub fn map_mut_ptr(&mut self) -> Option<*mut u8> {
|
||||||
self.runtime_mut::<CoverageRuntime>()
|
(*self.runtimes)
|
||||||
|
.borrow_mut()
|
||||||
|
.match_first_type_mut::<CoverageRuntime>()
|
||||||
.map(CoverageRuntime::map_mut_ptr)
|
.map(CoverageRuntime::map_mut_ptr)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Ranges
|
/// Ranges
|
||||||
pub fn ranges(&self) -> &RangeMap<usize, (u16, String)> {
|
pub fn ranges(&self) -> Ref<RangeMap<usize, (u16, String)>> {
|
||||||
&self.ranges
|
self.ranges.borrow()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Mutable ranges
|
/// Mutable ranges
|
||||||
pub fn ranges_mut(&mut self) -> &mut RangeMap<usize, (u16, String)> {
|
pub fn ranges_mut(&mut self) -> RefMut<RangeMap<usize, (u16, String)>> {
|
||||||
&mut self.ranges
|
(*self.ranges).borrow_mut()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the ref to options
|
/// Return the ref to options
|
||||||
|
@ -19,7 +19,8 @@ Additional documentation is available in [the `LibAFL` book](https://aflplus.plu
|
|||||||
clippy::missing_docs_in_private_items,
|
clippy::missing_docs_in_private_items,
|
||||||
clippy::module_name_repetitions,
|
clippy::module_name_repetitions,
|
||||||
clippy::unreadable_literal,
|
clippy::unreadable_literal,
|
||||||
clippy::ptr_cast_constness
|
clippy::ptr_cast_constness,
|
||||||
|
clippy::must_use_candidate
|
||||||
)]
|
)]
|
||||||
#![cfg_attr(not(test), warn(
|
#![cfg_attr(not(test), warn(
|
||||||
missing_debug_implementations,
|
missing_debug_implementations,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user