use crate::*;
use minidump::format::CONTEXT_X86;
use minidump::system_info::{Cpu, Os};
use std::collections::HashMap;
use test_assembler::*;
struct TestFixture {
pub raw: CONTEXT_X86,
pub modules: MinidumpModuleList,
pub symbols: HashMap<String, String>,
}
impl TestFixture {
pub fn new() -> TestFixture {
TestFixture {
raw: CONTEXT_X86::default(),
modules: MinidumpModuleList::from_modules(vec![
MinidumpModule::new(0x40000000, 0x10000, "module1"),
MinidumpModule::new(0x50000000, 0x10000, "module2"),
]),
symbols: HashMap::new(),
}
}
pub async fn walk_stack(&self, stack: Section) -> CallStack {
let context = MinidumpContext {
raw: MinidumpRawContext::X86(self.raw.clone()),
valid: MinidumpContextValidity::All,
};
let base = stack.start().value().unwrap();
let size = stack.size();
let stack = stack.get_contents().unwrap();
let stack_memory = MinidumpMemory {
desc: Default::default(),
base_address: base,
size,
bytes: &stack,
endian: scroll::LE,
};
let system_info = SystemInfo {
os: Os::Windows,
os_version: None,
os_build: None,
cpu: Cpu::X86,
cpu_info: None,
cpu_microcode_version: None,
cpu_count: 1,
};
let symbolizer = Symbolizer::new(string_symbol_supplier(self.symbols.clone()));
let mut stack = CallStack::with_context(context);
walk_stack(
0,
(),
&mut stack,
Some(UnifiedMemory::Memory(&stack_memory)),
&self.modules,
&system_info,
&symbolizer,
)
.await;
stack
}
pub fn add_symbols(&mut self, name: String, symbols: String) {
self.symbols.insert(name, symbols);
}
}
#[tokio::test]
async fn test_simple() {
let mut f = TestFixture::new();
let mut stack = Section::new();
stack.start().set_const(0x80000000);
stack = stack.D32(0).D32(0); f.raw.eip = 0x40000200;
f.raw.ebp = 0x80000000;
let s = f.walk_stack(stack).await;
assert_eq!(s.frames.len(), 1);
let f = &s.frames[0];
let m = f.module.as_ref().unwrap();
assert_eq!(m.code_file(), "module1");
}
#[tokio::test]
async fn test_traditional() {
let mut f = TestFixture::new();
let frame0_ebp = Label::new();
let frame1_ebp = Label::new();
let mut stack = Section::new();
stack.start().set_const(0x80000000);
stack = stack
.append_repeated(12, 0) .mark(&frame0_ebp) .D32(&frame1_ebp) .D32(0x40008679) .append_repeated(8, 0) .mark(&frame1_ebp) .D32(0) .D32(0); f.raw.eip = 0x4000c7a5;
f.raw.esp = stack.start().value().unwrap() as u32;
f.raw.ebp = frame0_ebp.value().unwrap() as u32;
let s = f.walk_stack(stack).await;
assert_eq!(s.frames.len(), 2);
{
let f0 = &s.frames[0];
assert_eq!(f0.trust, FrameTrust::Context);
assert_eq!(f0.context.valid, MinidumpContextValidity::All);
assert_eq!(f0.instruction, 0x4000c7a5);
assert_eq!(f0.resume_address, 0x4000c7a5);
}
{
let f1 = &s.frames[1];
assert_eq!(f1.trust, FrameTrust::FramePointer);
assert_eq!(f1.instruction, 0x40008678);
assert_eq!(f1.resume_address, 0x40008679);
}
}
#[tokio::test]
async fn test_traditional_scan() {
let mut f = TestFixture::new();
let frame1_esp = Label::new();
let frame1_ebp = Label::new();
let mut stack = Section::new();
let stack_start = 0x80000000;
stack.start().set_const(stack_start);
stack = stack
.D32(0xf065dc76) .D32(0x46ee2167) .D32(0xbab023ec) .D32(&frame1_ebp) .D32(0x4000129d) .mark(&frame1_esp)
.append_repeated(8, 0) .mark(&frame1_ebp) .D32(0) .D32(0);
f.raw.eip = 0x4000f49d;
f.raw.esp = stack.start().value().unwrap() as u32;
f.raw.ebp = 0xd43eed6e;
let s = f.walk_stack(stack).await;
assert_eq!(s.frames.len(), 2);
{
let f0 = &s.frames[0];
assert_eq!(f0.trust, FrameTrust::Context);
assert_eq!(f0.context.valid, MinidumpContextValidity::All);
assert_eq!(f0.instruction, 0x4000f49d);
assert_eq!(f0.resume_address, 0x4000f49d);
if let MinidumpRawContext::X86(ctx) = &f0.context.raw {
assert_eq!(ctx.eip, 0x4000f49d);
assert_eq!(ctx.esp, stack_start as u32);
assert_eq!(ctx.ebp, 0xd43eed6e);
} else {
unreachable!();
}
}
{
let f1 = &s.frames[1];
assert_eq!(f1.trust, FrameTrust::Scan);
if let MinidumpContextValidity::Some(ref which) = f1.context.valid {
assert!(which.contains("eip"));
assert!(which.contains("esp"));
assert!(which.contains("ebp"));
} else {
unreachable!();
}
assert_eq!(f1.instruction + 1, 0x4000129d);
assert_eq!(f1.resume_address, 0x4000129d);
if let MinidumpRawContext::X86(ctx) = &f1.context.raw {
assert_eq!(ctx.eip, 0x4000129d);
assert_eq!(ctx.esp, frame1_esp.value().unwrap() as u32);
assert_eq!(ctx.ebp, frame1_ebp.value().unwrap() as u32);
} else {
unreachable!();
}
}
}
#[tokio::test]
async fn test_traditional_scan_long_way() {
let mut f = TestFixture::new();
let frame1_esp = Label::new();
let frame1_ebp = Label::new();
let mut stack = Section::new();
let stack_start = 0x80000000;
stack.start().set_const(stack_start);
stack = stack
.D32(0xf065dc76) .D32(0x46ee2167) .D32(0xbab023ec) .append_repeated(20 * 4, 0) .D32(&frame1_ebp) .D32(0x4000129d) .mark(&frame1_esp)
.append_repeated(8, 0) .mark(&frame1_ebp) .D32(0) .D32(0);
f.raw.eip = 0x4000f49d;
f.raw.esp = stack.start().value().unwrap() as u32;
f.raw.ebp = 0xd43eed6e;
let s = f.walk_stack(stack).await;
assert_eq!(s.frames.len(), 2);
{
let f0 = &s.frames[0];
assert_eq!(f0.trust, FrameTrust::Context);
assert_eq!(f0.context.valid, MinidumpContextValidity::All);
assert_eq!(f0.instruction, 0x4000f49d);
if let MinidumpRawContext::X86(ctx) = &f0.context.raw {
assert_eq!(ctx.eip, 0x4000f49d);
assert_eq!(ctx.esp, stack_start as u32);
assert_eq!(ctx.ebp, 0xd43eed6e);
} else {
unreachable!();
}
}
{
let f1 = &s.frames[1];
assert_eq!(f1.trust, FrameTrust::Scan);
if let MinidumpContextValidity::Some(ref which) = f1.context.valid {
assert!(which.contains("eip"));
assert!(which.contains("esp"));
assert!(which.contains("ebp"));
} else {
unreachable!();
}
assert_eq!(f1.instruction + 1, 0x4000129d);
if let MinidumpRawContext::X86(ctx) = &f1.context.raw {
assert_eq!(ctx.eip, 0x4000129d);
assert_eq!(ctx.esp, frame1_esp.value().unwrap() as u32);
assert_eq!(ctx.ebp, frame1_ebp.value().unwrap() as u32);
} else {
unreachable!();
}
}
}
const CALLEE_SAVE_REGS: &[&str] = &["eip", "esp", "ebp", "ebx", "edi", "esi"];
fn init_cfi_state() -> (TestFixture, Section, CONTEXT_X86, MinidumpContextValidity) {
let mut f = TestFixture::new();
let symbols = [
"FUNC 4000 1000 10 enchiridion\n",
"STACK CFI INIT 4000 100 .cfa: $esp 4 + .ra: .cfa 4 - ^\n",
"STACK CFI 4001 .cfa: $esp 8 + $ebx: .cfa 8 - ^\n",
"STACK CFI 4002 $esi: $ebx\n",
"STACK CFI 4003 .cfa: $esp 20 + $edi: .cfa 16 - ^\n",
"STACK CFI 4005 .ra: $edi\n",
"STACK CFI 4006 .cfa: $ebp 8 + $ebp: .cfa 12 - ^\n",
"FUNC 5000 1000 10 epictetus\n",
"STACK CFI INIT 5000 1000 .cfa: $esp .ra 0\n",
];
f.add_symbols(String::from("module1"), symbols.concat());
f.raw.set_register("esp", 0x80000000);
f.raw.set_register("eip", 0x40005510);
f.raw.set_register("ebp", 0xc0d4aab9);
f.raw.set_register("ebx", 0x60f20ce6);
f.raw.set_register("esi", 0x53d1379d);
f.raw.set_register("edi", 0xafbae234);
let raw_valid = MinidumpContextValidity::All;
let expected = f.raw.clone();
let expected_regs = CALLEE_SAVE_REGS;
let expected_valid = MinidumpContextValidity::Some(expected_regs.iter().copied().collect());
let stack = Section::new();
stack
.start()
.set_const(f.raw.get_register("esp", &raw_valid).unwrap() as u64);
(f, stack, expected, expected_valid)
}
async fn check_cfi(
f: TestFixture,
stack: Section,
expected: CONTEXT_X86,
expected_valid: MinidumpContextValidity,
) {
let s = f.walk_stack(stack).await;
assert_eq!(s.frames.len(), 2);
{
let frame = &s.frames[0];
assert_eq!(frame.trust, FrameTrust::Context);
assert_eq!(frame.context.valid, MinidumpContextValidity::All);
}
{
if let MinidumpContextValidity::Some(ref expected_regs) = expected_valid {
let frame = &s.frames[1];
let valid = &frame.context.valid;
assert_eq!(frame.trust, FrameTrust::CallFrameInfo);
if let MinidumpContextValidity::Some(ref which) = valid {
assert_eq!(which.len(), expected_regs.len());
} else {
unreachable!();
}
if let MinidumpRawContext::X86(ctx) = &frame.context.raw {
for reg in expected_regs {
assert_eq!(
ctx.get_register(reg, valid),
expected.get_register(reg, &expected_valid),
"{reg} registers didn't match!"
);
}
return;
}
}
}
unreachable!();
}
#[tokio::test]
async fn test_cfi_at_4000() {
let (mut f, mut stack, mut expected, expected_valid) = init_cfi_state();
let frame1_rsp = Label::new();
stack = stack
.D32(0x40005510) .mark(&frame1_rsp)
.append_repeated(0, 1000);
expected.set_register("esp", frame1_rsp.value().unwrap() as u32);
f.raw.set_register("eip", 0x40004000);
check_cfi(f, stack, expected, expected_valid).await;
}
#[tokio::test]
async fn test_cfi_at_4001() {
let (mut f, mut stack, mut expected, expected_valid) = init_cfi_state();
let frame1_rsp = Label::new();
stack = stack
.D32(0x60f20ce6) .D32(0x40005510) .mark(&frame1_rsp)
.append_repeated(0, 1000);
expected.set_register("esp", frame1_rsp.value().unwrap() as u32);
f.raw.set_register("eip", 0x40004001);
f.raw.set_register("ebx", 0x91aa9a8b);
check_cfi(f, stack, expected, expected_valid).await;
}
#[tokio::test]
async fn test_cfi_at_4002() {
let (mut f, mut stack, mut expected, expected_valid) = init_cfi_state();
let frame1_rsp = Label::new();
stack = stack
.D32(0x60f20ce6) .D32(0x40005510) .mark(&frame1_rsp)
.append_repeated(0, 1000);
expected.set_register("esp", frame1_rsp.value().unwrap() as u32);
f.raw.set_register("eip", 0x40004002);
f.raw.set_register("ebx", 0x53d1379d);
f.raw.set_register("esi", 0xa5c790ed);
check_cfi(f, stack, expected, expected_valid).await;
}
#[tokio::test]
async fn test_cfi_at_4003() {
let (mut f, mut stack, mut expected, expected_valid) = init_cfi_state();
let frame1_rsp = Label::new();
stack = stack
.D32(0x56ec3db7) .D32(0xafbae234) .D32(0x53d67131) .D32(0x60f20ce6) .D32(0x40005510) .mark(&frame1_rsp)
.append_repeated(0, 1000);
expected.set_register("esp", frame1_rsp.value().unwrap() as u32);
f.raw.set_register("eip", 0x40004003);
f.raw.set_register("ebx", 0x53d1379d);
f.raw.set_register("esi", 0xa97f229d);
f.raw.set_register("edi", 0xb05cc997);
check_cfi(f, stack, expected, expected_valid).await;
}
#[tokio::test]
async fn test_cfi_at_4004() {
let (mut f, mut stack, mut expected, expected_valid) = init_cfi_state();
let frame1_rsp = Label::new();
stack = stack
.D32(0x56ec3db7) .D32(0xafbae234) .D32(0x53d67131) .D32(0x60f20ce6) .D32(0x40005510) .mark(&frame1_rsp)
.append_repeated(0, 1000);
expected.set_register("esp", frame1_rsp.value().unwrap() as u32);
f.raw.set_register("eip", 0x40004004);
f.raw.set_register("ebx", 0x53d1379d);
f.raw.set_register("esi", 0xa97f229d);
f.raw.set_register("edi", 0xb05cc997);
check_cfi(f, stack, expected, expected_valid).await;
}
#[tokio::test]
async fn test_cfi_at_4005() {
let (mut f, mut stack, mut expected, expected_valid) = init_cfi_state();
let frame1_rsp = Label::new();
stack = stack
.D32(0xe29782c2) .D32(0xafbae234) .D32(0x5ba29ce9) .D32(0x60f20ce6) .D32(0x8036cc02) .mark(&frame1_rsp)
.append_repeated(0, 1000);
expected.set_register("esp", frame1_rsp.value().unwrap() as u32);
f.raw.set_register("eip", 0x40004005);
f.raw.set_register("ebx", 0x53d1379d);
f.raw.set_register("esi", 0x0fb7dc4e);
f.raw.set_register("edi", 0x40005510);
check_cfi(f, stack, expected, expected_valid).await;
}
#[tokio::test]
async fn test_cfi_at_4006() {
let (mut f, mut stack, mut expected, expected_valid) = init_cfi_state();
let frame0_ebp = Label::new();
let frame1_rsp = Label::new();
stack = stack
.D32(0xdcdd25cd) .D32(0xafbae234) .D32(0xc0d4aab9) .mark(&frame0_ebp) .D32(0x60f20ce6) .D32(0x8036cc02) .mark(&frame1_rsp)
.append_repeated(0, 1000);
expected.set_register("esp", frame1_rsp.value().unwrap() as u32);
f.raw
.set_register("ebp", frame0_ebp.value().unwrap() as u32);
f.raw.set_register("eip", 0x40004006);
f.raw.set_register("ebx", 0x53d1379d);
f.raw.set_register("esi", 0x743833c9);
f.raw.set_register("edi", 0x40005510);
check_cfi(f, stack, expected, expected_valid).await;
}
#[tokio::test]
async fn test_stack_win_frame_data_basic() {
let mut f = TestFixture::new();
let symbols = [
"STACK WIN 4 aa85 176 0 0 4 10 4 0 1",
" $T2 $esp .cbSavedRegs + =",
" $T0 .raSearchStart =",
" $eip $T0 ^ =",
" $esp $T0 4 + =",
" $ebx $T2 4 - ^ =",
" $edi $T2 8 - ^ =",
" $esi $T2 12 - ^ =",
" $ebp $T2 16 - ^ =\n",
];
f.add_symbols(String::from("module1"), symbols.concat());
let frame1_esp = Label::new();
let frame1_ebp = Label::new();
let mut stack = Section::new();
let stack_start = 0x80000000;
stack.start().set_const(stack_start);
stack = stack
.D32(&frame1_ebp) .D32(0xa7120d1a) .D32(0x630891be) .D32(0x9068a878) .D32(0xa08ea45f) .D32(0x40001350) .mark(&frame1_esp)
.append_repeated(0, 12) .mark(&frame1_ebp)
.D32(0) .D32(0);
f.raw.set_register("eip", 0x4000aa85);
f.raw
.set_register("esp", stack.start().value().unwrap() as u32);
f.raw.set_register("ebp", 0xf052c1de);
let s = f.walk_stack(stack).await;
assert_eq!(s.frames.len(), 2);
{
let f0 = &s.frames[0];
assert_eq!(f0.trust, FrameTrust::Context);
assert_eq!(f0.context.valid, MinidumpContextValidity::All);
assert_eq!(f0.instruction, 0x4000aa85);
if let MinidumpRawContext::X86(ctx) = &f0.context.raw {
assert_eq!(ctx.eip, 0x4000aa85);
assert_eq!(ctx.esp, stack_start as u32);
assert_eq!(ctx.ebp, 0xf052c1de);
} else {
unreachable!();
}
}
{
let f1 = &s.frames[1];
assert_eq!(f1.trust, FrameTrust::CallFrameInfo);
if let MinidumpContextValidity::Some(ref which) = f1.context.valid {
assert!(which.contains("eip"));
assert!(which.contains("esp"));
assert!(which.contains("ebp"));
assert!(which.contains("ebx"));
assert!(which.contains("esi"));
assert!(which.contains("edi"));
} else {
unreachable!();
}
assert_eq!(f1.instruction + 1, 0x40001350);
if let MinidumpRawContext::X86(ctx) = &f1.context.raw {
assert_eq!(ctx.eip, 0x40001350);
assert_eq!(ctx.esp, frame1_esp.value().unwrap() as u32);
assert_eq!(ctx.ebp, frame1_ebp.value().unwrap() as u32);
assert_eq!(ctx.ebx, 0x9068a878);
assert_eq!(ctx.esi, 0xa7120d1a);
assert_eq!(ctx.edi, 0x630891be);
} else {
unreachable!();
}
}
}
#[tokio::test]
async fn test_stack_win_frame_data_overlapping() {
let mut f = TestFixture::new();
let symbols = [
"STACK WIN 4 aa80 181 0 0 4 10 4 0 1",
" $eip .raSearchStart =\n",
"STACK WIN 4 aa84 177 0 0 4 10 4 0 1",
" $eip .raSearchStart =\n",
"STACK WIN 4 aa85 176 0 0 4 10 4 0 1",
" $T2 $esp .cbSavedRegs + =",
" $T0 .raSearchStart =",
" $eip $T0 ^ =",
" $esp $T0 4 + =",
" $ebx $T2 4 - ^ =",
" $edi $T2 8 - ^ =",
" $esi $T2 12 - ^ =",
" $ebp $T2 16 - ^ =\n",
"STACK WIN 4 aa86 175 0 0 4 10 4 0 1",
" $eip .raSearchStart =\n",
];
f.add_symbols(String::from("module1"), symbols.concat());
let frame1_esp = Label::new();
let frame1_ebp = Label::new();
let mut stack = Section::new();
let stack_start = 0x80000000;
stack.start().set_const(stack_start);
stack = stack
.D32(&frame1_ebp) .D32(0xa7120d1a) .D32(0x630891be) .D32(0x9068a878) .D32(0xa08ea45f) .D32(0x40001350) .mark(&frame1_esp)
.append_repeated(0, 12) .mark(&frame1_ebp)
.D32(0) .D32(0);
f.raw.set_register("eip", 0x4000aa85);
f.raw
.set_register("esp", stack.start().value().unwrap() as u32);
f.raw.set_register("ebp", 0xf052c1de);
let s = f.walk_stack(stack).await;
assert_eq!(s.frames.len(), 2);
{
let f0 = &s.frames[0];
assert_eq!(f0.trust, FrameTrust::Context);
assert_eq!(f0.context.valid, MinidumpContextValidity::All);
assert_eq!(f0.instruction, 0x4000aa85);
if let MinidumpRawContext::X86(ctx) = &f0.context.raw {
assert_eq!(ctx.eip, 0x4000aa85);
assert_eq!(ctx.esp, stack_start as u32);
assert_eq!(ctx.ebp, 0xf052c1de);
} else {
unreachable!();
}
}
{
let f1 = &s.frames[1];
assert_eq!(f1.trust, FrameTrust::CallFrameInfo);
if let MinidumpContextValidity::Some(ref which) = f1.context.valid {
assert!(which.contains("eip"));
assert!(which.contains("esp"));
assert!(which.contains("ebp"));
assert!(which.contains("ebx"));
assert!(which.contains("esi"));
assert!(which.contains("edi"));
} else {
unreachable!();
}
assert_eq!(f1.instruction + 1, 0x40001350);
if let MinidumpRawContext::X86(ctx) = &f1.context.raw {
assert_eq!(ctx.eip, 0x40001350);
assert_eq!(ctx.esp, frame1_esp.value().unwrap() as u32);
assert_eq!(ctx.ebp, frame1_ebp.value().unwrap() as u32);
assert_eq!(ctx.ebx, 0x9068a878);
assert_eq!(ctx.esi, 0xa7120d1a);
assert_eq!(ctx.edi, 0x630891be);
} else {
unreachable!();
}
}
}
#[tokio::test]
async fn test_stack_win_frame_data_parameter_size() {
let mut f = TestFixture::new();
let module1_symbols = ["FUNC 1000 100 c module1::wheedle\n"];
let module2_symbols = [
"FUNC aa85 176 beef module2::whine\n",
"STACK WIN 4 aa85 176 0 0 4 10 4 0 1",
" $T2 $esp .cbLocals + .cbSavedRegs + =",
" $T0 .raSearchStart =",
" $eip $T0 ^ =",
" $esp $T0 4 + =",
" $ebp $T0 20 - ^ =",
" $ebx $T0 8 - ^ =\n",
];
f.add_symbols(String::from("module1"), module1_symbols.concat());
f.add_symbols(String::from("module2"), module2_symbols.concat());
let frame0_esp = Label::new();
let frame0_ebp = Label::new();
let frame1_esp = Label::new();
let frame2_esp = Label::new();
let frame2_ebp = Label::new();
let mut stack = Section::new();
let stack_start = 0x80000000;
stack.start().set_const(stack_start);
stack = stack
.mark(&frame0_esp)
.append_repeated(0, 16) .mark(&frame0_ebp)
.D32(0x6fa902e0) .D32(0x5000aa95) .mark(&frame1_esp)
.D32(0xbaa0cb7a) .D32(0xbdc92f9f) .D32(0x0b1d8442) .D32(&frame2_ebp) .D32(0xb1b90a15) .D32(0xf18e072d) .D32(0x2558c7f3) .D32(0x0365e25e) .D32(0x2a179e38) .mark(&frame2_esp)
.append_repeated(0, 12) .mark(&frame2_ebp)
.D32(0) .D32(0);
f.raw.set_register("eip", 0x40001004);
f.raw
.set_register("esp", stack.start().value().unwrap() as u32);
f.raw
.set_register("ebp", frame0_ebp.value().unwrap() as u32);
let s = f.walk_stack(stack).await;
assert_eq!(s.frames.len(), 3);
{
let f0 = &s.frames[0];
assert_eq!(f0.trust, FrameTrust::Context);
assert_eq!(f0.context.valid, MinidumpContextValidity::All);
assert_eq!(f0.instruction, 0x40001004);
if let MinidumpRawContext::X86(ctx) = &f0.context.raw {
assert_eq!(ctx.eip, 0x40001004);
assert_eq!(ctx.esp, frame0_esp.value().unwrap() as u32);
assert_eq!(ctx.ebp, frame0_ebp.value().unwrap() as u32);
} else {
unreachable!();
}
}
{
let f1 = &s.frames[1];
assert_eq!(f1.trust, FrameTrust::FramePointer);
if let MinidumpContextValidity::Some(ref which) = f1.context.valid {
assert!(which.contains("eip"));
assert!(which.contains("esp"));
assert!(which.contains("ebp"));
} else {
unreachable!();
}
assert_eq!(f1.instruction + 1, 0x5000aa95);
if let MinidumpRawContext::X86(ctx) = &f1.context.raw {
assert_eq!(ctx.eip, 0x5000aa95);
assert_eq!(ctx.esp, frame1_esp.value().unwrap() as u32);
assert_eq!(ctx.ebp, 0x6fa902e0);
} else {
unreachable!();
}
}
{
let f2 = &s.frames[2];
assert_eq!(f2.trust, FrameTrust::CallFrameInfo);
if let MinidumpContextValidity::Some(ref which) = f2.context.valid {
assert!(which.contains("eip"));
assert!(which.contains("esp"));
assert!(which.contains("ebp"));
assert!(which.contains("ebx"));
} else {
unreachable!();
}
assert_eq!(f2.instruction + 1, 0x2a179e38);
if let MinidumpRawContext::X86(ctx) = &f2.context.raw {
assert_eq!(ctx.eip, 0x2a179e38);
assert_eq!(ctx.esp, frame2_esp.value().unwrap() as u32);
assert_eq!(ctx.ebp, frame2_ebp.value().unwrap() as u32);
assert_eq!(ctx.ebx, 0x2558c7f3);
} else {
unreachable!();
}
}
}
#[tokio::test]
async fn test_frame_pointer_overflow() {
type Pointer = u32;
let stack_max: Pointer = Pointer::MAX;
let stack_size: Pointer = 1000;
let bad_frame_ptr: Pointer = stack_max;
let mut f = TestFixture::new();
let mut stack = Section::new();
let stack_start: Pointer = stack_max - stack_size;
stack.start().set_const(stack_start as u64);
stack = stack
.append_repeated(0, stack_size as usize);
f.raw.eip = 0x7a100000;
f.raw.ebp = bad_frame_ptr;
f.raw.esp = stack.start().value().unwrap() as Pointer;
let s = f.walk_stack(stack).await;
assert_eq!(s.frames.len(), 1);
}
#[tokio::test]
async fn test_frame_pointer_overflow_nonsense_32bit_stack() {
type Pointer = u32;
let pointer_size: u64 = std::mem::size_of::<Pointer>() as u64;
let stack_max: u64 = Pointer::MAX as u64 + pointer_size * 2;
let stack_size: u64 = 1000;
let bad_frame_ptr: u64 = Pointer::MAX as u64 - pointer_size;
let mut f = TestFixture::new();
let mut stack = Section::new();
let stack_start: u64 = stack_max - stack_size;
stack.start().set_const(stack_start);
stack = stack
.append_repeated(0, 1000);
f.raw.eip = 0x7a100000;
f.raw.ebp = bad_frame_ptr as u32;
f.raw.esp = stack.start().value().unwrap() as Pointer;
let s = f.walk_stack(stack).await;
assert_eq!(s.frames.len(), 1);
}
#[tokio::test]
async fn test_frame_pointer_barely_no_overflow() {
let mut f = TestFixture::new();
let mut stack = Section::new();
type Pointer = u32;
let pointer_size: Pointer = std::mem::size_of::<Pointer>() as Pointer;
let stack_max: Pointer = Pointer::MAX;
let stack_size: Pointer = pointer_size * 3;
let stack_start: Pointer = stack_max - stack_size;
let return_address: Pointer = 0x7b302000;
stack.start().set_const(stack_start as u64);
let frame0_fp = Label::new();
let frame1_sp = Label::new();
let frame1_fp = Label::new();
stack = stack
.mark(&frame0_fp)
.D32(&frame1_fp) .D32(return_address) .mark(&frame1_sp)
.mark(&frame1_fp) .D32(0);
f.raw.eip = 0x7a100000;
f.raw.ebp = frame0_fp.value().unwrap() as Pointer;
f.raw.esp = stack.start().value().unwrap() as Pointer;
let s = f.walk_stack(stack).await;
assert_eq!(s.frames.len(), 2);
{
let f0 = &s.frames[0];
assert_eq!(f0.trust, FrameTrust::Context);
assert_eq!(f0.context.valid, MinidumpContextValidity::All);
if let MinidumpRawContext::X86(ctx) = &f0.context.raw {
assert_eq!(ctx.ebp, frame0_fp.value().unwrap() as Pointer);
} else {
unreachable!();
}
}
{
let f1 = &s.frames[1];
assert_eq!(f1.trust, FrameTrust::FramePointer);
if let MinidumpContextValidity::Some(ref which) = f1.context.valid {
assert!(which.contains("eip"));
assert!(which.contains("esp"));
assert!(which.contains("ebp"));
} else {
unreachable!();
}
if let MinidumpRawContext::X86(ctx) = &f1.context.raw {
assert_eq!(ctx.eip, return_address);
assert_eq!(ctx.esp, frame1_sp.value().unwrap() as Pointer);
assert_eq!(ctx.ebp, frame1_fp.value().unwrap() as Pointer);
} else {
unreachable!();
}
}
}