Skip to content

Commit 81946e1

Browse files
committed
librasan: Support patching Thumb functions
1 parent 225f77d commit 81946e1

File tree

2 files changed

+140
-1
lines changed

2 files changed

+140
-1
lines changed

libafl_qemu/librasan/asan/src/patch/raw.rs

+38-1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ pub struct RawPatch;
1414

1515
impl Patch for RawPatch {
1616
type Error = RawPatchError;
17+
18+
#[cfg(not(target_arch = "arm"))]
1719
fn patch(target: GuestAddr, destination: GuestAddr) -> Result<(), Self::Error> {
1820
debug!("patch - addr: {:#x}, target: {:#x}", target, destination);
1921
if target == destination {
@@ -25,6 +27,26 @@ impl Patch for RawPatch {
2527
dest.copy_from_slice(&patch);
2628
Ok(())
2729
}
30+
31+
#[cfg(target_arch = "arm")]
32+
fn patch(target: GuestAddr, destination: GuestAddr) -> Result<(), Self::Error> {
33+
debug!("patch - addr: {:#x}, target: {:#x}", target, destination);
34+
if target == destination {
35+
Err(RawPatchError::IdentityPatch(target))?;
36+
}
37+
38+
let patch = if target & 1 == 1 {
39+
Self::get_patch_thumb(destination)?
40+
} else {
41+
Self::get_patch_arm(destination)?
42+
};
43+
44+
trace!("patch: {:02x?}", patch);
45+
let target = target & !1;
46+
let dest = unsafe { from_raw_parts_mut(target as *mut u8, patch.len()) };
47+
dest.copy_from_slice(&patch);
48+
Ok(())
49+
}
2850
}
2951

3052
impl RawPatch {
@@ -69,7 +91,7 @@ impl RawPatch {
6991
}
7092

7193
#[cfg(target_arch = "arm")]
72-
fn get_patch(destination: GuestAddr) -> Result<Vec<u8>, RawPatchError> {
94+
fn get_patch_arm(destination: GuestAddr) -> Result<Vec<u8>, RawPatchError> {
7395
// ldr ip, [pc]
7496
// bx ip
7597
// .long 0xdeadface
@@ -83,6 +105,21 @@ impl RawPatch {
83105
Ok(insns_mod.into_iter().flatten().cloned().collect())
84106
}
85107

108+
#[cfg(target_arch = "arm")]
109+
fn get_patch_thumb(destination: GuestAddr) -> Result<Vec<u8>, RawPatchError> {
110+
// ldr ip, [pc, #2]
111+
// bx ip
112+
// .long 0xdeadface
113+
let insns = [
114+
[0xdf, 0xf8, 0x02, 0xc0].to_vec(),
115+
[0x60, 0x47].to_vec(),
116+
[0xce, 0xfa, 0xad, 0xde].to_vec(),
117+
];
118+
let addr = destination.to_ne_bytes().to_vec();
119+
let insns_mod = [&insns[0], &insns[1], &addr];
120+
Ok(insns_mod.into_iter().flatten().cloned().collect())
121+
}
122+
86123
#[cfg(target_arch = "aarch64")]
87124
fn get_patch(destination: GuestAddr) -> Result<Vec<u8>, RawPatchError> {
88125
// ldr x16, #8

libafl_qemu/librasan/asan/tests/patch_raw.rs

+102
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
1+
#![cfg_attr(target_arch = "arm", feature(arm_target_feature))]
2+
13
#[cfg(test)]
24
#[cfg(feature = "libc")]
5+
#[cfg(not(target_arch = "arm"))]
36
mod tests {
47
use asan::{
58
GuestAddr,
@@ -55,3 +58,102 @@ mod tests {
5558
assert_eq!(ret, 0xd00df00d);
5659
}
5760
}
61+
62+
#[cfg(test)]
63+
#[cfg(feature = "libc")]
64+
#[cfg(target_arch = "arm")]
65+
mod tests {
66+
use asan::{
67+
GuestAddr,
68+
mmap::{Mmap, MmapProt, linux::LinuxMmap},
69+
patch::{Patch, raw::RawPatch},
70+
};
71+
use log::info;
72+
73+
macro_rules! define_test_function {
74+
($fn_name:ident, $ret_val:expr) => {
75+
define_test_function!([], $fn_name, $ret_val);
76+
};
77+
78+
($attr:meta, $fn_name:ident, $ret_val:expr) => {
79+
define_test_function!([$attr], $fn_name, $ret_val);
80+
};
81+
82+
([$($attr:meta)*], $fn_name:ident, $ret_val:expr) => {
83+
#[unsafe(no_mangle)]
84+
$(#[$attr])*
85+
extern "C" fn $fn_name(
86+
a1: usize,
87+
a2: usize,
88+
a3: usize,
89+
a4: usize,
90+
a5: usize,
91+
a6: usize,
92+
) -> usize {
93+
assert_eq!(a1, 1);
94+
assert_eq!(a2, 2);
95+
assert_eq!(a3, 3);
96+
assert_eq!(a4, 4);
97+
assert_eq!(a5, 5);
98+
assert_eq!(a6, 6);
99+
return $ret_val;
100+
}
101+
};
102+
}
103+
104+
macro_rules! define_test {
105+
($fn_name:ident, $test_fn1:ident, $test_fn2:ident, $test_ret_val1:expr, $test_ret_val2:expr) => {
106+
#[test]
107+
fn $fn_name() {
108+
#[allow(unused_unsafe)]
109+
unsafe {
110+
let ret1 = $test_fn1(1, 2, 3, 4, 5, 6);
111+
assert_eq!(ret1, $test_ret_val1);
112+
113+
let ret2 = $test_fn2(1, 2, 3, 4, 5, 6);
114+
assert_eq!(ret2, $test_ret_val2);
115+
116+
let ptest1 = $test_fn1 as *const () as GuestAddr;
117+
let ptest2 = $test_fn2 as *const () as GuestAddr;
118+
info!("pfn: {:#x}", ptest1);
119+
let aligned_pfn = ptest1 & !0xfff;
120+
info!("aligned_pfn: {:#x}", aligned_pfn);
121+
LinuxMmap::protect(
122+
aligned_pfn,
123+
0x4096,
124+
MmapProt::READ | MmapProt::WRITE | MmapProt::EXEC,
125+
)
126+
.unwrap();
127+
128+
RawPatch::patch(ptest1, ptest2).unwrap();
129+
let ret = $test_fn1(1, 2, 3, 4, 5, 6);
130+
assert_eq!(ret, $test_ret_val2);
131+
}
132+
}
133+
};
134+
}
135+
136+
define_test_function!(test_arm1, 0xdeadface);
137+
define_test_function!(test_arm2, 0xd00df00d);
138+
define_test_function!(test_arm3, 0xfeeddeaf);
139+
define_test_function!(
140+
target_feature(enable = "thumb-mode"),
141+
test_thumb1,
142+
0xcafebabe
143+
);
144+
define_test_function!(
145+
target_feature(enable = "thumb-mode"),
146+
test_thumb2,
147+
0xbeeffade
148+
);
149+
define_test_function!(
150+
target_feature(enable = "thumb-mode"),
151+
test_thumb3,
152+
0xdeedcede
153+
);
154+
155+
define_test!(test_patch_arm_to_arm, test_arm2, test_arm1, 0xd00df00d, 0xdeadface);
156+
define_test!(test_patch_arm_to_thumb, test_arm3, test_thumb1, 0xfeeddeaf, 0xcafebabe);
157+
define_test!(test_patch_thumb_to_arm, test_thumb3, test_arm1, 0xdeedcede, 0xdeadface);
158+
define_test!(test_patch_thumb_to_thumb, test_thumb2, test_thumb1, 0xbeeffade, 0xcafebabe);
159+
}

0 commit comments

Comments
 (0)