Various fixes for frida-asan on aarch64-linux-android (#64)
This commit is contained in:
parent
f3b4305dac
commit
e62f4de6b5
@ -317,6 +317,8 @@ unsafe fn fuzz(
|
|||||||
let scheduler = IndexesLenTimeMinimizerCorpusScheduler::new(QueueCorpusScheduler::new());
|
let scheduler = IndexesLenTimeMinimizerCorpusScheduler::new(QueueCorpusScheduler::new());
|
||||||
let mut fuzzer = StdFuzzer::new(tuple_list!(stage));
|
let mut fuzzer = StdFuzzer::new(tuple_list!(stage));
|
||||||
|
|
||||||
|
frida_helper.register_thread();
|
||||||
|
|
||||||
// Create the executor for an in-process function with just one observer for edge coverage
|
// Create the executor for an in-process function with just one observer for edge coverage
|
||||||
let mut executor = FridaInProcessExecutor::new(
|
let mut executor = FridaInProcessExecutor::new(
|
||||||
&gum,
|
&gum,
|
||||||
@ -351,7 +353,6 @@ unsafe fn fuzz(
|
|||||||
println!("We imported {} inputs from disk.", state.corpus().count());
|
println!("We imported {} inputs from disk.", state.corpus().count());
|
||||||
}
|
}
|
||||||
|
|
||||||
//executor.helper.register_thread();
|
|
||||||
fuzzer.fuzz_loop(&mut state, &mut executor, &mut restarting_mgr, &scheduler)?;
|
fuzzer.fuzz_loop(&mut state, &mut executor, &mut restarting_mgr, &scheduler)?;
|
||||||
|
|
||||||
// Never reached
|
// Never reached
|
||||||
|
@ -24,7 +24,7 @@ use color_backtrace::{default_output_stream, BacktracePrinter, Verbosity};
|
|||||||
use dynasmrt::{dynasm, DynasmApi, DynasmLabelApi};
|
use dynasmrt::{dynasm, DynasmApi, DynasmLabelApi};
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
use gothook::GotHookLibrary;
|
use gothook::GotHookLibrary;
|
||||||
use libc::{sysconf, _SC_PAGESIZE};
|
use libc::{_SC_PAGESIZE, getrlimit64, rlimit64, sysconf};
|
||||||
use rangemap::RangeSet;
|
use rangemap::RangeSet;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::{
|
use std::{
|
||||||
@ -52,6 +52,7 @@ struct Allocator {
|
|||||||
allocations: HashMap<usize, AllocationMetadata>,
|
allocations: HashMap<usize, AllocationMetadata>,
|
||||||
shadow_pages: RangeSet<usize>,
|
shadow_pages: RangeSet<usize>,
|
||||||
allocation_queue: HashMap<usize, Vec<AllocationMetadata>>,
|
allocation_queue: HashMap<usize, Vec<AllocationMetadata>>,
|
||||||
|
largest_allocation: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! map_to_shadow {
|
macro_rules! map_to_shadow {
|
||||||
@ -88,7 +89,7 @@ impl Allocator {
|
|||||||
addr as *mut c_void,
|
addr as *mut c_void,
|
||||||
page_size,
|
page_size,
|
||||||
ProtFlags::PROT_READ | ProtFlags::PROT_WRITE,
|
ProtFlags::PROT_READ | ProtFlags::PROT_WRITE,
|
||||||
MapFlags::MAP_PRIVATE | MapFlags::MAP_ANONYMOUS | MapFlags::MAP_FIXED,
|
MapFlags::MAP_PRIVATE | MapFlags::MAP_ANONYMOUS | MapFlags::MAP_FIXED | MapFlags::MAP_NORESERVE,
|
||||||
-1,
|
-1,
|
||||||
0,
|
0,
|
||||||
)
|
)
|
||||||
@ -108,7 +109,7 @@ impl Allocator {
|
|||||||
addr as *mut c_void,
|
addr as *mut c_void,
|
||||||
addr + addr,
|
addr + addr,
|
||||||
ProtFlags::PROT_READ | ProtFlags::PROT_WRITE,
|
ProtFlags::PROT_READ | ProtFlags::PROT_WRITE,
|
||||||
MapFlags::MAP_ANONYMOUS | MapFlags::MAP_FIXED,
|
MapFlags::MAP_ANONYMOUS | MapFlags::MAP_FIXED | MapFlags::MAP_PRIVATE | MapFlags::MAP_NORESERVE,
|
||||||
-1,
|
-1,
|
||||||
0,
|
0,
|
||||||
)
|
)
|
||||||
@ -124,6 +125,7 @@ impl Allocator {
|
|||||||
allocations: HashMap::new(),
|
allocations: HashMap::new(),
|
||||||
shadow_pages: RangeSet::new(),
|
shadow_pages: RangeSet::new(),
|
||||||
allocation_queue: HashMap::new(),
|
allocation_queue: HashMap::new(),
|
||||||
|
largest_allocation: 0,
|
||||||
};
|
};
|
||||||
unsafe {
|
unsafe {
|
||||||
ALLOCATOR_SINGLETON = Some(RefCell::new(allocator));
|
ALLOCATOR_SINGLETON = Some(RefCell::new(allocator));
|
||||||
@ -154,6 +156,19 @@ impl Allocator {
|
|||||||
(value / self.page_size) * self.page_size
|
(value / self.page_size) * self.page_size
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn find_smallest_fit(&mut self, size: usize) -> Option<AllocationMetadata> {
|
||||||
|
let mut current_size = size;
|
||||||
|
while current_size <= self.largest_allocation {
|
||||||
|
if self.allocation_queue.contains_key(¤t_size) {
|
||||||
|
if let Some(metadata) = self.allocation_queue.entry(current_size).or_default().pop() {
|
||||||
|
return Some(metadata);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
current_size *= 2;
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
pub unsafe fn alloc(&mut self, size: usize, _alignment: usize) -> *mut c_void {
|
pub unsafe fn alloc(&mut self, size: usize, _alignment: usize) -> *mut c_void {
|
||||||
let mut is_malloc_zero = false;
|
let mut is_malloc_zero = false;
|
||||||
let size = if size == 0 {
|
let size = if size == 0 {
|
||||||
@ -168,11 +183,7 @@ impl Allocator {
|
|||||||
}
|
}
|
||||||
let rounded_up_size = self.round_up_to_page(size);
|
let rounded_up_size = self.round_up_to_page(size);
|
||||||
|
|
||||||
let metadata = if let Some(mut metadata) = self
|
let metadata = if let Some(mut metadata) = self.find_smallest_fit(rounded_up_size)
|
||||||
.allocation_queue
|
|
||||||
.entry(rounded_up_size)
|
|
||||||
.or_default()
|
|
||||||
.pop()
|
|
||||||
{
|
{
|
||||||
//println!("reusing allocation at {:x}, (actual mapping starts at {:x}) size {:x}", metadata.address, metadata.address - self.page_size, size);
|
//println!("reusing allocation at {:x}, (actual mapping starts at {:x}) size {:x}", metadata.address, metadata.address - self.page_size, size);
|
||||||
metadata.is_malloc_zero = is_malloc_zero;
|
metadata.is_malloc_zero = is_malloc_zero;
|
||||||
@ -189,7 +200,7 @@ impl Allocator {
|
|||||||
} else {
|
} else {
|
||||||
let mapping = match mmap(
|
let mapping = match mmap(
|
||||||
std::ptr::null_mut(),
|
std::ptr::null_mut(),
|
||||||
rounded_up_size + 2 * self.page_size,
|
rounded_up_size,
|
||||||
ProtFlags::PROT_READ | ProtFlags::PROT_WRITE,
|
ProtFlags::PROT_READ | ProtFlags::PROT_WRITE,
|
||||||
MapFlags::MAP_ANONYMOUS | MapFlags::MAP_PRIVATE,
|
MapFlags::MAP_ANONYMOUS | MapFlags::MAP_PRIVATE,
|
||||||
-1,
|
-1,
|
||||||
@ -204,12 +215,12 @@ impl Allocator {
|
|||||||
|
|
||||||
self.map_shadow_for_region(
|
self.map_shadow_for_region(
|
||||||
mapping,
|
mapping,
|
||||||
mapping + rounded_up_size + 2 * self.page_size,
|
mapping + rounded_up_size,
|
||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut metadata = AllocationMetadata {
|
let mut metadata = AllocationMetadata {
|
||||||
address: mapping + self.page_size,
|
address: mapping,
|
||||||
size,
|
size,
|
||||||
actual_size: rounded_up_size,
|
actual_size: rounded_up_size,
|
||||||
..AllocationMetadata::default()
|
..AllocationMetadata::default()
|
||||||
@ -227,6 +238,7 @@ impl Allocator {
|
|||||||
metadata
|
metadata
|
||||||
};
|
};
|
||||||
|
|
||||||
|
self.largest_allocation = std::cmp::max(self.largest_allocation, metadata.actual_size);
|
||||||
// unpoison the shadow memory for the allocation itself
|
// unpoison the shadow memory for the allocation itself
|
||||||
Self::unpoison(map_to_shadow!(self, metadata.address), size);
|
Self::unpoison(map_to_shadow!(self, metadata.address), size);
|
||||||
let address = metadata.address as *mut c_void;
|
let address = metadata.address as *mut c_void;
|
||||||
@ -774,23 +786,44 @@ impl AsanRuntime {
|
|||||||
pub fn register_thread(&self) {
|
pub fn register_thread(&self) {
|
||||||
let mut allocator = Allocator::get();
|
let mut allocator = Allocator::get();
|
||||||
let (stack_start, stack_end) = Self::current_stack();
|
let (stack_start, stack_end) = Self::current_stack();
|
||||||
|
println!("current stack: {:#016x}-{:#016x}", stack_start, stack_end);
|
||||||
allocator.map_shadow_for_region(stack_start, stack_end, true);
|
allocator.map_shadow_for_region(stack_start, stack_end, true);
|
||||||
|
|
||||||
let (tls_start, tls_end) = Self::current_tls();
|
//let (tls_start, tls_end) = Self::current_tls();
|
||||||
allocator.map_shadow_for_region(tls_start, tls_end, true);
|
//allocator.map_shadow_for_region(tls_start, tls_end, true);
|
||||||
println!(
|
//println!(
|
||||||
"registering thread with stack {:x}:{:x} and tls {:x}:{:x}",
|
//"registering thread with stack {:x}:{:x} and tls {:x}:{:x}",
|
||||||
stack_start as usize, stack_end as usize, tls_start as usize, tls_end as usize
|
//stack_start as usize, stack_end as usize, tls_start as usize, tls_end as usize
|
||||||
);
|
//);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Determine the stack start, end for the currently running thread
|
/// Determine the stack start, end for the currently running thread
|
||||||
pub fn current_stack() -> (usize, usize) {
|
pub fn current_stack() -> (usize, usize) {
|
||||||
let stack_var = 0xeadbeef;
|
let stack_var = 0xeadbeef;
|
||||||
let stack_address = &stack_var as *const _ as *const c_void as usize;
|
let stack_address = &stack_var as *const _ as *const c_void as usize;
|
||||||
|
|
||||||
let (start, end, _, _) = find_mapping_for_address(stack_address).unwrap();
|
let (start, end, _, _) = find_mapping_for_address(stack_address).unwrap();
|
||||||
(start, end)
|
|
||||||
|
let mut stack_rlimit = rlimit64 { rlim_cur: 0, rlim_max: 0 };
|
||||||
|
assert!(unsafe { getrlimit64(3, &mut stack_rlimit as *mut rlimit64 ) } == 0);
|
||||||
|
|
||||||
|
println!("stack_rlimit: {:?}", stack_rlimit);
|
||||||
|
|
||||||
|
let max_start = end - stack_rlimit.rlim_cur as usize;
|
||||||
|
|
||||||
|
if start != max_start {
|
||||||
|
let mapping = unsafe {
|
||||||
|
mmap(
|
||||||
|
max_start as *mut c_void,
|
||||||
|
start - max_start,
|
||||||
|
ProtFlags::PROT_READ | ProtFlags::PROT_WRITE,
|
||||||
|
MapFlags::MAP_ANONYMOUS | MapFlags::MAP_FIXED | MapFlags::MAP_PRIVATE | MapFlags::MAP_STACK,
|
||||||
|
-1,
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
assert!(mapping.unwrap() as usize == max_start);
|
||||||
|
}
|
||||||
|
(max_start, end)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Determine the tls start, end for the currently running thread
|
/// Determine the tls start, end for the currently running thread
|
||||||
@ -1269,12 +1302,12 @@ impl AsanRuntime {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
AsanError::UnallocatedFree((ptr, backtrace)) => {
|
AsanError::UnallocatedFree((ptr, backtrace)) => {
|
||||||
writeln!(output, " of {:?}", ptr).unwrap();
|
writeln!(output, " of {:#016x}", ptr).unwrap();
|
||||||
output.reset().unwrap();
|
output.reset().unwrap();
|
||||||
backtrace_printer.print_trace(&backtrace, output).unwrap();
|
backtrace_printer.print_trace(&backtrace, output).unwrap();
|
||||||
}
|
}
|
||||||
AsanError::Leak((ptr, mut metadata)) => {
|
AsanError::Leak((ptr, mut metadata)) => {
|
||||||
writeln!(output, " of {:?}", ptr).unwrap();
|
writeln!(output, " of {:#016x}", ptr).unwrap();
|
||||||
output.reset().unwrap();
|
output.reset().unwrap();
|
||||||
|
|
||||||
#[allow(clippy::non_ascii_literal)]
|
#[allow(clippy::non_ascii_literal)]
|
||||||
@ -1388,6 +1421,7 @@ impl AsanRuntime {
|
|||||||
; b >skip_report
|
; b >skip_report
|
||||||
|
|
||||||
; report:
|
; report:
|
||||||
|
; brk 0x11
|
||||||
; stp x29, x30, [sp, #-0x10]!
|
; stp x29, x30, [sp, #-0x10]!
|
||||||
; mov x29, sp
|
; mov x29, sp
|
||||||
|
|
||||||
@ -1487,7 +1521,7 @@ impl AsanRuntime {
|
|||||||
; mov x1, #1
|
; mov x1, #1
|
||||||
; add x1, xzr, x1, lsl #shadow_bit
|
; add x1, xzr, x1, lsl #shadow_bit
|
||||||
; add x1, x1, x0, lsr #3
|
; add x1, x1, x0, lsr #3
|
||||||
; ubfx x1, x1, #0, #(shadow_bit + 1)
|
; ubfx x1, x1, #0, #(shadow_bit + 2)
|
||||||
; ldrh w1, [x1, #0]
|
; ldrh w1, [x1, #0]
|
||||||
; and x0, x0, #7
|
; and x0, x0, #7
|
||||||
; rev16 w1, w1
|
; rev16 w1, w1
|
||||||
@ -1507,6 +1541,7 @@ impl AsanRuntime {
|
|||||||
; b >skip_report
|
; b >skip_report
|
||||||
|
|
||||||
; report:
|
; report:
|
||||||
|
; brk 0x22
|
||||||
; stp x29, x30, [sp, #-0x10]!
|
; stp x29, x30, [sp, #-0x10]!
|
||||||
; mov x29, sp
|
; mov x29, sp
|
||||||
|
|
||||||
@ -1607,7 +1642,7 @@ impl AsanRuntime {
|
|||||||
; mov x1, #1
|
; mov x1, #1
|
||||||
; add x1, xzr, x1, lsl #shadow_bit
|
; add x1, xzr, x1, lsl #shadow_bit
|
||||||
; add x1, x1, x0, lsr #3
|
; add x1, x1, x0, lsr #3
|
||||||
; ubfx x1, x1, #0, #(shadow_bit + 1)
|
; ubfx x1, x1, #0, #(shadow_bit + 2)
|
||||||
; ldrh w1, [x1, #0]
|
; ldrh w1, [x1, #0]
|
||||||
; and x0, x0, #7
|
; and x0, x0, #7
|
||||||
; rev16 w1, w1
|
; rev16 w1, w1
|
||||||
|
@ -6,7 +6,7 @@ use libafl::utils::find_mapping_for_path;
|
|||||||
use libafl_targets::drcov::{DrCovBasicBlock, DrCovWriter};
|
use libafl_targets::drcov::{DrCovBasicBlock, DrCovWriter};
|
||||||
|
|
||||||
#[cfg(target_arch = "aarch64")]
|
#[cfg(target_arch = "aarch64")]
|
||||||
use capstone::arch::{arm64::Arm64OperandType, ArchOperand::Arm64Operand};
|
use capstone::arch::{arm64::{Arm64OperandType, Arm64Extender, Arm64Shift}, ArchOperand::Arm64Operand};
|
||||||
use capstone::{
|
use capstone::{
|
||||||
arch::{self, BuildsCapstone},
|
arch::{self, BuildsCapstone},
|
||||||
Capstone, Insn,
|
Capstone, Insn,
|
||||||
@ -215,7 +215,7 @@ impl<'a> FridaInstrumentationHelper<'a> {
|
|||||||
if options.stalker_enabled() {
|
if options.stalker_enabled() {
|
||||||
for (id, module_name) in modules_to_instrument.iter().enumerate() {
|
for (id, module_name) in modules_to_instrument.iter().enumerate() {
|
||||||
let (lib_start, lib_end) = find_mapping_for_path(module_name);
|
let (lib_start, lib_end) = find_mapping_for_path(module_name);
|
||||||
println!("including range {:x}-{:x}", lib_start, lib_end);
|
println!("including range {:x}-{:x} for {}", lib_start, lib_end, module_name);
|
||||||
helper
|
helper
|
||||||
.ranges
|
.ranges
|
||||||
.insert(lib_start..lib_end, (id as u16, module_name));
|
.insert(lib_start..lib_end, (id as u16, module_name));
|
||||||
@ -262,7 +262,7 @@ impl<'a> FridaInstrumentationHelper<'a> {
|
|||||||
#[cfg(not(target_arch = "aarch64"))]
|
#[cfg(not(target_arch = "aarch64"))]
|
||||||
todo!("Implement ASAN for non-aarch64 targets");
|
todo!("Implement ASAN for non-aarch64 targets");
|
||||||
#[cfg(target_arch = "aarch64")]
|
#[cfg(target_arch = "aarch64")]
|
||||||
if let Ok((basereg, indexreg, displacement, width)) =
|
if let Ok((basereg, indexreg, displacement, width, shift, extender)) =
|
||||||
helper.is_interesting_instruction(address, instr)
|
helper.is_interesting_instruction(address, instr)
|
||||||
{
|
{
|
||||||
helper.emit_shadow_check(
|
helper.emit_shadow_check(
|
||||||
@ -272,6 +272,8 @@ impl<'a> FridaInstrumentationHelper<'a> {
|
|||||||
indexreg,
|
indexreg,
|
||||||
displacement,
|
displacement,
|
||||||
width,
|
width,
|
||||||
|
shift,
|
||||||
|
extender,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -310,6 +312,8 @@ impl<'a> FridaInstrumentationHelper<'a> {
|
|||||||
indexreg: capstone::RegId,
|
indexreg: capstone::RegId,
|
||||||
displacement: i32,
|
displacement: i32,
|
||||||
width: u32,
|
width: u32,
|
||||||
|
shift: Arm64Shift,
|
||||||
|
extender: Arm64Extender,
|
||||||
) {
|
) {
|
||||||
let writer = output.writer();
|
let writer = output.writer();
|
||||||
|
|
||||||
@ -363,11 +367,43 @@ impl<'a> FridaInstrumentationHelper<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let (Arm64Extender::ARM64_EXT_INVALID, Arm64Shift::Invalid) = (extender, shift) {
|
||||||
writer.put_add_reg_reg_reg(
|
writer.put_add_reg_reg_reg(
|
||||||
Aarch64Register::X0,
|
Aarch64Register::X0,
|
||||||
Aarch64Register::X0,
|
Aarch64Register::X0,
|
||||||
Aarch64Register::X1,
|
Aarch64Register::X1,
|
||||||
);
|
);
|
||||||
|
} else {
|
||||||
|
let extender_encoding: i32 = match extender {
|
||||||
|
Arm64Extender::ARM64_EXT_UXTB => 0b000,
|
||||||
|
Arm64Extender::ARM64_EXT_UXTH => 0b001,
|
||||||
|
Arm64Extender::ARM64_EXT_UXTW => 0b010,
|
||||||
|
Arm64Extender::ARM64_EXT_UXTX => 0b011,
|
||||||
|
Arm64Extender::ARM64_EXT_SXTB => 0b100,
|
||||||
|
Arm64Extender::ARM64_EXT_SXTH => 0b101,
|
||||||
|
Arm64Extender::ARM64_EXT_SXTW => 0b110,
|
||||||
|
Arm64Extender::ARM64_EXT_SXTX => 0b111,
|
||||||
|
_ => -1,
|
||||||
|
};
|
||||||
|
let (shift_encoding, shift_amount): (i32, u32) = match shift {
|
||||||
|
Arm64Shift::Lsl(amount) => (0b00, amount),
|
||||||
|
Arm64Shift::Lsr(amount) => (0b01, amount),
|
||||||
|
Arm64Shift::Asr(amount) => (0b10, amount),
|
||||||
|
_ => (-1, 0),
|
||||||
|
};
|
||||||
|
|
||||||
|
if extender_encoding != -1 && shift_amount < 0b1000 {
|
||||||
|
// emit add extended register: https://developer.arm.com/documentation/ddi0602/latest/Base-Instructions/ADD--extended-register---Add--extended-register--
|
||||||
|
writer.put_bytes(&(0x8b210000 | ((extender_encoding as u32) << 13) | (shift_amount << 10)).to_le_bytes());
|
||||||
|
} else if shift_encoding != -1 {
|
||||||
|
writer.put_bytes(&(0x8b010000 | ((shift_encoding as u32) << 22) | (shift_amount << 10)).to_le_bytes());
|
||||||
|
} else {
|
||||||
|
panic!("extender: {:?}, shift: {:?}", extender, shift);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
let displacement = displacement
|
let displacement = displacement
|
||||||
@ -512,7 +548,7 @@ impl<'a> FridaInstrumentationHelper<'a> {
|
|||||||
&self,
|
&self,
|
||||||
_address: u64,
|
_address: u64,
|
||||||
instr: &Insn,
|
instr: &Insn,
|
||||||
) -> Result<(capstone::RegId, capstone::RegId, i32, u32), ()> {
|
) -> Result<(capstone::RegId, capstone::RegId, i32, u32, Arm64Shift, Arm64Extender), ()> {
|
||||||
// We have to ignore these instructions. Simulating them with their side effects is
|
// We have to ignore these instructions. Simulating them with their side effects is
|
||||||
// complex, to say the least.
|
// complex, to say the least.
|
||||||
match instr.mnemonic().unwrap() {
|
match instr.mnemonic().unwrap() {
|
||||||
@ -539,6 +575,8 @@ impl<'a> FridaInstrumentationHelper<'a> {
|
|||||||
opmem.index(),
|
opmem.index(),
|
||||||
opmem.disp(),
|
opmem.disp(),
|
||||||
self.get_instruction_width(instr, &operands),
|
self.get_instruction_width(instr, &operands),
|
||||||
|
arm64operand.shift,
|
||||||
|
arm64operand.ext,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user