crashrustler/unwind/
frame_pointer.rs1use super::registers::RegisterContext;
7use super::{MemoryReader, UnwindError};
8
9pub fn step_frame_pointer(
20 reader: &dyn MemoryReader,
21 regs: &mut RegisterContext,
22 is_64_bit: bool,
23) -> Result<bool, UnwindError> {
24 let fp = regs.fp().ok_or(UnwindError::BrokenFrameChain)?;
25
26 if fp == 0 {
27 return Ok(false);
28 }
29
30 if is_64_bit && fp % 8 != 0 {
32 return Err(UnwindError::BrokenFrameChain);
33 }
34
35 let ptr_size = if is_64_bit { 8 } else { 4 };
36
37 let prev_fp = reader
39 .read_pointer(fp, is_64_bit)
40 .ok_or(UnwindError::MemoryReadFailed(fp))?;
41 let return_addr = reader
42 .read_pointer(fp + ptr_size, is_64_bit)
43 .ok_or(UnwindError::MemoryReadFailed(fp + ptr_size))?;
44
45 if prev_fp != 0 && prev_fp <= fp {
48 return Err(UnwindError::BrokenFrameChain);
49 }
50
51 if return_addr == 0 {
52 return Ok(false);
53 }
54
55 regs.clear_volatile();
57
58 regs.set_fp(prev_fp);
60 regs.set_sp(fp + 2 * ptr_size); regs.set_pc(return_addr);
62
63 Ok(true)
64}
65
66#[cfg(test)]
67mod tests {
68 use super::*;
69 use crate::types::CpuType;
70 use crate::unwind::SliceMemoryReader;
71
72 fn build_arm64_stack() -> (SliceMemoryReader, RegisterContext) {
75 let base = 0x7000_0000u64;
76 let mut data = vec![0u8; 0x1000];
77
78 let frame0_off = 0x800usize;
82 data[frame0_off..frame0_off + 8].copy_from_slice(&(base + 0x900).to_le_bytes());
83 data[frame0_off + 8..frame0_off + 16].copy_from_slice(&0xDEAD_0001u64.to_le_bytes());
84
85 let frame1_off = 0x900usize;
89 data[frame1_off..frame1_off + 8].copy_from_slice(&(base + 0xA00).to_le_bytes());
90 data[frame1_off + 8..frame1_off + 16].copy_from_slice(&0xDEAD_0002u64.to_le_bytes());
91
92 let frame2_off = 0xA00usize;
96 data[frame2_off..frame2_off + 8].copy_from_slice(&0u64.to_le_bytes());
97 data[frame2_off + 8..frame2_off + 16].copy_from_slice(&0xDEAD_0003u64.to_le_bytes());
98
99 let reader = SliceMemoryReader {
100 data,
101 base_address: base,
102 };
103
104 let mut regs = RegisterContext::new(CpuType::ARM64);
105 regs.set_fp(base + frame0_off as u64);
106 regs.set_sp(base + frame0_off as u64 - 16);
107 regs.set_pc(0xCAFE_0000);
108
109 (reader, regs)
110 }
111
112 #[test]
113 fn walk_three_frames() {
114 let (reader, mut regs) = build_arm64_stack();
115 let base = 0x7000_0000u64;
116
117 assert!(step_frame_pointer(&reader, &mut regs, true).unwrap());
119 assert_eq!(regs.fp(), Some(base + 0x900));
120 assert_eq!(regs.pc(), Some(0xDEAD_0001));
121
122 assert!(step_frame_pointer(&reader, &mut regs, true).unwrap());
124 assert_eq!(regs.fp(), Some(base + 0xA00));
125 assert_eq!(regs.pc(), Some(0xDEAD_0002));
126
127 assert!(step_frame_pointer(&reader, &mut regs, true).unwrap());
129 assert_eq!(regs.pc(), Some(0xDEAD_0003));
130 assert_eq!(regs.fp(), Some(0));
131
132 assert!(!step_frame_pointer(&reader, &mut regs, true).unwrap());
134 }
135
136 #[test]
137 fn zero_fp_returns_false() {
138 let reader = SliceMemoryReader {
139 data: vec![0u8; 64],
140 base_address: 0x1000,
141 };
142 let mut regs = RegisterContext::new(CpuType::ARM64);
143 regs.set_fp(0);
144 regs.set_pc(0x1234);
145
146 assert!(!step_frame_pointer(&reader, &mut regs, true).unwrap());
147 }
148
149 #[test]
150 fn misaligned_fp_returns_error() {
151 let reader = SliceMemoryReader {
152 data: vec![0u8; 64],
153 base_address: 0x1000,
154 };
155 let mut regs = RegisterContext::new(CpuType::ARM64);
156 regs.set_fp(0x1003); assert_eq!(
159 step_frame_pointer(&reader, &mut regs, true),
160 Err(UnwindError::BrokenFrameChain)
161 );
162 }
163
164 #[test]
165 fn fp_not_growing_returns_error() {
166 let base = 0x2000u64;
167 let mut data = vec![0u8; 0x100];
168 data[0x80..0x88].copy_from_slice(&(base + 0x40).to_le_bytes());
170 data[0x88..0x90].copy_from_slice(&0xAAAAu64.to_le_bytes());
171
172 let reader = SliceMemoryReader {
173 data,
174 base_address: base,
175 };
176 let mut regs = RegisterContext::new(CpuType::ARM64);
177 regs.set_fp(base + 0x80);
178
179 assert_eq!(
180 step_frame_pointer(&reader, &mut regs, true),
181 Err(UnwindError::BrokenFrameChain)
182 );
183 }
184
185 #[test]
186 fn volatile_regs_cleared_after_step() {
187 let (reader, mut regs) = build_arm64_stack();
188 regs.set(0, 0x1111); assert!(step_frame_pointer(&reader, &mut regs, true).unwrap());
191 assert!(regs.get(0).is_none()); }
193}