#[tokio::test] asyncfn test_simple() { letmut f = TestFixture::new(); let stack = Section::new();
stack.start().set_const(0x80000000); // There should be no references to the stack in this walk: we don't // provide any call frame information, so trying to reconstruct the // context frame's caller should fail. So there's no need for us to // provide stack contents.
f.raw.rip = 0x00007400c0000200;
f.raw.rbp = 0x8000000080000000;
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] asyncfn test_caller_pushed_rbp() { // Functions typically push their %rbp upon entry and set %rbp pointing // there. If stackwalking finds a plausible address for the next frame's // %rbp directly below the return address, assume that it is indeed the // next frame's %rbp. letmut f = TestFixture::new(); letmut stack = Section::new(); let stack_start = 0x8000000080000000; let return_address = 0x00007500b0000110;
stack.start().set_const(stack_start);
let frame0_rbp = Label::new(); let frame1_sp = Label::new(); let frame1_rbp = Label::new();
stack = stack // frame 0
.append_repeated(0, 16) // space
.D64(0x00007400b0000000) // junk that's not
.D64(0x00007500b0000000) // a return address
.D64(0x00007400c0001000) // a couple of plausible addresses
.D64(0x00007500b000aaaa) // that are not within functions
.mark(&frame0_rbp)
.D64(&frame1_rbp) // caller-pushed %rbp
.D64(return_address) // actual return address // frame 1
.mark(&frame1_sp)
.append_repeated(0, 32) // body of frame1
.mark(&frame1_rbp) // end of stack
.D64(0);
letmut stack = Section::new(); let stack_start = 0x8000000080000000; let return_address = 0x00007500b0000110;
stack.start().set_const(stack_start);
let frame0_rbp = Label::new(); let frame1_sp = Label::new(); let frame1_rbp = Label::new();
stack = stack // frame 0
.append_repeated(0, 16) // space
.D64(0x00000000b0000000) // junk that's not
.D64(0x00000000b0000000) // a return address
.mark(&frame0_rbp) // the FP can point to the middle of the stack on Windows
.D64(0x00000000c0001000)
.D64(0x00000000b000aaaa)
.D64(&frame1_rbp) // caller-pushed %rbp
.D64(return_address) // actual return address // frame 1
.mark(&frame1_sp)
.append_repeated(0, 32) // body of frame1
.mark(&frame1_rbp) // end of stack
.D64(0);
#[tokio::test] asyncfn test_scan_without_symbols() { // When the stack walker resorts to scanning the stack, // only addresses located within loaded modules are // considered valid return addresses. // Force scanning through three frames to ensure that the // stack pointer is set properly in scan-recovered frames. letmut f = TestFixture::new(); letmut stack = Section::new(); let stack_start = 0x8000000080000000;
stack.start().set_const(stack_start);
let return_address1 = 0x00007500b0000100; let return_address2 = 0x00007500b0000900;
let frame1_sp = Label::new(); let frame2_sp = Label::new(); let frame1_rbp = Label::new();
stack = stack // frame 0
.append_repeated(0, 16) // space
.D64(0x00007400b0000000) // junk that's not
.D64(0x00007500d0000000) // a return address
.D64(return_address1) // actual return address // frame 1
.mark(&frame1_sp)
.append_repeated(0, 16) // space
.D64(0x00007400b0000000) // more junk
.D64(0x00007500d0000000)
.mark(&frame1_rbp)
.D64(stack_start) // This is in the right place to be // a saved rbp, but it's bogus, so // we shouldn't report it.
.D64(return_address2) // actual return address // frame 2
.mark(&frame2_sp)
.append_repeated(0, 32); // end of stack
#[tokio::test] asyncfn test_scan_with_symbols() { // Test that we can refine our scanning using symbols. Specifically we // should be able to reject pointers that are in modules but don't map to // any FUNC/PUBLIC record. letmut f = TestFixture::new(); letmut stack = Section::new(); let stack_start = 0x8000000080000000u64;
stack.start().set_const(stack_start);
let return_address = 0x00007500b0000110u64;
let frame1_rsp = Label::new(); let frame1_rbp = Label::new();
stack = stack // frame 0
.append_repeated(0, 16) // space
.D64(0x00007400b0000000u64) // junk that's not
.D64(0x00007500b0000000u64) // a return address
.D64(0x00007400c0001000u64) // a couple of plausible addresses
.D64(0x00007500b000aaaau64) // that are not within functions
.D64(return_address) // actual return address // frame 1
.mark(&frame1_rsp)
.append_repeated(0, 32)
.mark(&frame1_rbp); // end of stack
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("rsp", &raw_valid).unwrap());
(f, stack, expected, expected_valid)
}
asyncfn check_cfi(
f: TestFixture,
stack: Section,
expected: CONTEXT_AMD64,
expected_valid: MinidumpContextValidity,
) { let s = f.walk_stack(stack).await;
assert_eq!(s.frames.len(), 2);
#[tokio::test] asyncfn test_frame_pointer_overflow() { // Make sure we don't explode when trying frame pointer analysis on a value // that will overflow.
type Pointer = u64; let stack_max: Pointer = Pointer::MAX; let stack_size: Pointer = 1000; let bad_frame_ptr: Pointer = stack_max;
letmut f = TestFixture::new(); letmut stack = Section::new(); let stack_start: Pointer = stack_max - stack_size;
stack.start().set_const(stack_start);
stack = stack // frame 0
.append_repeated(0, stack_size as usize); // junk, not important to the test
let s = f.walk_stack(stack).await;
assert_eq!(s.frames.len(), 1);
// As long as we don't panic, we're good!
}
#[tokio::test] asyncfn test_frame_pointer_barely_no_overflow() { // This is test_caller_pushed_rbp but with the all the values pushed // as close to the upper memory boundary as possible, to confirm that // our code doesn't randomly overflow *AND* isn't overzealous in // its overflow guards.
letmut f = TestFixture::new(); letmut stack = Section::new();
type Pointer = u64; let stack_max: Pointer = Pointer::MAX; let pointer_size: Pointer = std::mem::size_of::<Pointer>() as Pointer; let stack_size: Pointer = pointer_size * 3;
let stack_start: Pointer = stack_max - stack_size; let return_address: Pointer = 0x00007500b0000110;
stack.start().set_const(stack_start);
let frame0_fp = Label::new(); let frame1_sp = Label::new(); let frame1_fp = Label::new();
stack = stack // frame 0
.mark(&frame0_fp)
.D64(&frame1_fp) // caller-pushed %rbp
.D64(return_address) // actual return address // frame 1
.mark(&frame1_sp)
.mark(&frame1_fp) // end of stack
.D64(0);
let s = f.walk_stack(stack).await;
assert_eq!(s.frames.len(), 2);
{ // To avoid reusing locals by mistake let f0 = &s.frames[0];
assert_eq!(f0.trust, FrameTrust::Context);
assert_eq!(f0.context.valid, MinidumpContextValidity::All); iflet MinidumpRawContext::Amd64(ctx) = &f0.context.raw {
assert_eq!(ctx.rbp, frame0_fp.value().unwrap() as Pointer);
} else {
unreachable!();
}
}
{ // To avoid reusing locals by mistake let f1 = &s.frames[1];
assert_eq!(f1.trust, FrameTrust::FramePointer); iflet MinidumpContextValidity::Some(ref which) = f1.context.valid {
assert!(which.contains("rip"));
assert!(which.contains("rsp"));
assert!(which.contains("rbp"));
} else {
unreachable!();
} iflet MinidumpRawContext::Amd64(ctx) = &f1.context.raw {
assert_eq!(ctx.rip, return_address);
assert_eq!(ctx.rsp, frame1_sp.value().unwrap() as Pointer);
assert_eq!(ctx.rbp, frame1_fp.value().unwrap() as Pointer);
} else {
unreachable!();
}
}
}
#[tokio::test] asyncfn test_scan_walk_overflow() { // There's a possible overflow when address_of_ip starts out at 0. // // To avoid this, we only try to recover rbp when we're scanning at least // 1 pointer width away from the start of the stack. letmut f = TestFixture::new(); letmut stack = Section::new(); let stack_start = 0;
stack.start().set_const(stack_start);
let return_address1 = 0x00007500b0000100_u64;
let frame1_sp = Label::new(); let frame1_rbp = Label::new();
stack = stack // frame 0
.D64(return_address1) // actual return address // frame 1
.mark(&frame1_sp)
.append_repeated(0, 16) // space
.D64(0x00007400b0000000) // more junk
.D64(0x00007500d0000000)
.mark(&frame1_rbp);
let s = f.walk_stack(stack).await;
assert_eq!(s.frames.len(), 2);
{ // To avoid reusing locals by mistake let f0 = &s.frames[0];
assert_eq!(f0.trust, FrameTrust::Context);
assert_eq!(f0.context.valid, MinidumpContextValidity::All);
}
{ // To avoid reusing locals by mistake let f1 = &s.frames[1];
assert_eq!(f1.trust, FrameTrust::Scan); iflet MinidumpContextValidity::Some(ref which) = f1.context.valid {
assert!(which.contains("rip"));
assert!(which.contains("rsp"));
} else {
unreachable!();
}
iflet MinidumpRawContext::Amd64(ctx) = &f1.context.raw {
assert_eq!(ctx.rip, return_address1);
assert_eq!(ctx.rsp, frame1_sp.value().unwrap()); // We were unable to recover rbp, so it defaulted to 0.
assert_eq!(ctx.rbp, 0);
} else {
unreachable!();
}
}
}
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.26 Sekunden
(vorverarbeitet am 2026-06-17)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.