diff --git a/libafl_qemu/librasan/asan/src/patch/raw.rs b/libafl_qemu/librasan/asan/src/patch/raw.rs index 8add18e2de..d04bda6af5 100644 --- a/libafl_qemu/librasan/asan/src/patch/raw.rs +++ b/libafl_qemu/librasan/asan/src/patch/raw.rs @@ -19,7 +19,12 @@ impl Patch for RawPatch { if target == destination { Err(RawPatchError::IdentityPatch(target))?; } - let patch = Self::get_patch(destination)?; + let patch = Self::get_patch(target, destination)?; + + // Mask the thumb mode indicator bit + #[cfg(target_arch = "arm")] + let target = target & !1; + trace!("patch: {:02x?}", patch); let dest = unsafe { from_raw_parts_mut(target as *mut u8, patch.len()) }; dest.copy_from_slice(&patch); @@ -29,7 +34,7 @@ impl Patch for RawPatch { impl RawPatch { #[cfg(target_arch = "x86_64")] - fn get_patch(destination: GuestAddr) -> Result, RawPatchError> { + fn get_patch(_target: GuestAddr, destination: GuestAddr) -> Result, RawPatchError> { // mov rax, 0xdeadfacef00dd00d // jmp rax let insns = [ @@ -55,7 +60,7 @@ impl RawPatch { } #[cfg(target_arch = "x86")] - fn get_patch(destination: GuestAddr) -> Result, RawPatchError> { + fn get_patch(_target: GuestAddr, destination: GuestAddr) -> Result, RawPatchError> { // mov eax, 0xdeadface // jmp eax let insns = [ @@ -69,22 +74,37 @@ impl RawPatch { } #[cfg(target_arch = "arm")] - fn get_patch(destination: GuestAddr) -> Result, RawPatchError> { - // ldr ip, [pc] - // mov pc, ip - // .long 0xdeadface - let insns = [ - [0x00, 0xc0, 0x9f, 0xe5].to_vec(), - [0x0c, 0xf0, 0xa0, 0xe1].to_vec(), - [0xce, 0xfa, 0xad, 0xde].to_vec(), - ]; - let addr = destination.to_ne_bytes().to_vec(); - let insns_mod = [&insns[0], &insns[1], &addr]; - Ok(insns_mod.into_iter().flatten().cloned().collect()) + fn get_patch(target: GuestAddr, destination: GuestAddr) -> Result, RawPatchError> { + // If our target is in thumb mode + if target & 1 == 1 { + // ldr ip, [pc, #2] + // bx ip + // .long 0xdeadface + let insns = [ + [0xdf, 0xf8, 0x02, 0xc0].to_vec(), + [0x60, 0x47].to_vec(), + [0xce, 0xfa, 0xad, 0xde].to_vec(), + ]; + let addr = destination.to_ne_bytes().to_vec(); + let insns_mod = [&insns[0], &insns[1], &addr]; + Ok(insns_mod.into_iter().flatten().cloned().collect()) + } else { + // ldr ip, [pc] + // bx ip + // .long 0xdeadface + let insns = [ + [0x00, 0xc0, 0x9f, 0xe5].to_vec(), + [0x1c, 0xff, 0x2f, 0xe1].to_vec(), + [0xce, 0xfa, 0xad, 0xde].to_vec(), + ]; + let addr = destination.to_ne_bytes().to_vec(); + let insns_mod = [&insns[0], &insns[1], &addr]; + Ok(insns_mod.into_iter().flatten().cloned().collect()) + } } #[cfg(target_arch = "aarch64")] - fn get_patch(destination: GuestAddr) -> Result, RawPatchError> { + fn get_patch(_target: GuestAddr, destination: GuestAddr) -> Result, RawPatchError> { // ldr x16, #8 // br x16 // .quad 0xdeadfacef00dd00d @@ -100,7 +120,7 @@ impl RawPatch { } #[cfg(target_arch = "powerpc")] - fn get_patch(destination: GuestAddr) -> Result, RawPatchError> { + fn get_patch(_target: GuestAddr, destination: GuestAddr) -> Result, RawPatchError> { // lis 12, 0xdead // ori 12, 12, 0xface // mtctr 12 diff --git a/libafl_qemu/librasan/asan/tests/patch_raw.rs b/libafl_qemu/librasan/asan/tests/patch_raw.rs index 18a2a51f6a..df72c48737 100644 --- a/libafl_qemu/librasan/asan/tests/patch_raw.rs +++ b/libafl_qemu/librasan/asan/tests/patch_raw.rs @@ -1,11 +1,15 @@ +#![cfg_attr(target_arch = "arm", feature(arm_target_feature))] + #[cfg(test)] #[cfg(feature = "libc")] +#[cfg(not(target_arch = "arm"))] mod tests { use asan::{ GuestAddr, mmap::{Mmap, MmapProt, linux::LinuxMmap}, patch::{Patch, raw::RawPatch}, }; + use libc::{_SC_PAGESIZE, sysconf}; use log::info; #[unsafe(no_mangle)] @@ -42,10 +46,12 @@ mod tests { let ptest2 = test2 as *const () as GuestAddr; info!("pfn: {:#x}", ptest1); let aligned_pfn = ptest1 & !0xfff; + let page_size = unsafe { sysconf(_SC_PAGESIZE) as usize }; info!("aligned_pfn: {:#x}", aligned_pfn); + info!("page_size: {:#x}", page_size); LinuxMmap::protect( aligned_pfn, - 0x4096, + page_size * 2, MmapProt::READ | MmapProt::WRITE | MmapProt::EXEC, ) .unwrap(); @@ -53,5 +59,140 @@ mod tests { RawPatch::patch(ptest1, ptest2).unwrap(); let ret = test1(1, 2, 3, 4, 5, 6); assert_eq!(ret, 0xd00df00d); + LinuxMmap::protect(aligned_pfn, page_size * 2, MmapProt::READ | MmapProt::EXEC).unwrap(); } } + +#[cfg(test)] +#[cfg(feature = "libc")] +#[cfg(target_arch = "arm")] +mod tests { + use asan::{ + GuestAddr, + mmap::{Mmap, MmapProt, linux::LinuxMmap}, + patch::{Patch, raw::RawPatch}, + }; + use libc::{_SC_PAGESIZE, sysconf}; + use log::info; + + macro_rules! define_test_function { + ($fn_name:ident, $ret_val:expr) => { + define_test_function!([], $fn_name, $ret_val); + }; + + ($attr:meta, $fn_name:ident, $ret_val:expr) => { + define_test_function!([$attr], $fn_name, $ret_val); + }; + + ([$($attr:meta)*], $fn_name:ident, $ret_val:expr) => { + #[unsafe(no_mangle)] + $(#[$attr])* + extern "C" fn $fn_name( + a1: usize, + a2: usize, + a3: usize, + a4: usize, + a5: usize, + a6: usize, + ) -> usize { + assert_eq!(a1, 1); + assert_eq!(a2, 2); + assert_eq!(a3, 3); + assert_eq!(a4, 4); + assert_eq!(a5, 5); + assert_eq!(a6, 6); + return $ret_val; + } + }; + } + + macro_rules! define_test { + ( + $fn_name:ident, + $test_fn1:ident, + $test_fn2:ident, + $test_ret_val1:expr, + $test_ret_val2:expr + ) => { + #[test] + fn $fn_name() { + #[allow(unused_unsafe)] + unsafe { + let ret1 = $test_fn1(1, 2, 3, 4, 5, 6); + assert_eq!(ret1, $test_ret_val1); + + let ret2 = $test_fn2(1, 2, 3, 4, 5, 6); + assert_eq!(ret2, $test_ret_val2); + + let ptest1 = $test_fn1 as *const () as GuestAddr; + let ptest2 = $test_fn2 as *const () as GuestAddr; + info!("pfn: {:#x}", ptest1); + let aligned_pfn = ptest1 & !0xfff; + let page_size = unsafe { sysconf(_SC_PAGESIZE) as usize }; + info!("aligned_pfn: {:#x}", aligned_pfn); + info!("page_size: {:#x}", page_size); + LinuxMmap::protect( + aligned_pfn, + page_size * 2, + MmapProt::READ | MmapProt::WRITE | MmapProt::EXEC, + ) + .unwrap(); + + RawPatch::patch(ptest1, ptest2).unwrap(); + let ret = $test_fn1(1, 2, 3, 4, 5, 6); + assert_eq!(ret, $test_ret_val2); + LinuxMmap::protect(aligned_pfn, page_size * 2, MmapProt::READ | MmapProt::EXEC) + .unwrap(); + } + } + }; + } + + define_test_function!(arm_patch_target, 0xdeadface); + define_test_function!(patched_arm_to_arm, 0xd00df00d); + define_test_function!(patched_arm_to_thumb, 0xfeeddeaf); + define_test_function!( + target_feature(enable = "thumb-mode"), + thumb_patch_target, + 0xcafebabe + ); + define_test_function!( + target_feature(enable = "thumb-mode"), + patched_thumb_to_thumb, + 0xbeeffade + ); + define_test_function!( + target_feature(enable = "thumb-mode"), + patched_thumb_to_arm, + 0xdeedcede + ); + + define_test!( + test_patch_arm_to_arm, + patched_arm_to_arm, + arm_patch_target, + 0xd00df00d, + 0xdeadface + ); + define_test!( + test_patch_arm_to_thumb, + patched_arm_to_thumb, + thumb_patch_target, + 0xfeeddeaf, + 0xcafebabe + ); + define_test!( + test_patch_thumb_to_arm, + patched_thumb_to_arm, + arm_patch_target, + 0xdeedcede, + 0xdeadface + ); + define_test!( + test_patch_thumb_to_thumb, + patched_thumb_to_thumb, + thumb_patch_target, + 0xbeeffade, + 0xcafebabe + ); +}