Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  amd64_unittest.rs   Sprache: unbekannt

 
// Copyright 2015 Ted Mielczarek. See the COPYRIGHT
// file at the top-level directory of this distribution.

use crate::*;
use minidump::format::CONTEXT_AMD64;
use minidump::system_info::{Cpu, Os};
use std::collections::HashMap;
use test_assembler::*;

struct TestFixture {
    pub raw: CONTEXT_AMD64,
    pub modules: MinidumpModuleList,
    pub system_info: SystemInfo,
    pub symbols: HashMap<String, String>,
}

impl TestFixture {
    pub fn new() -> TestFixture {
        TestFixture {
            raw: CONTEXT_AMD64::default(),
            // Give the two modules reasonable standard locations and names
            // for tests to play with.
            modules: MinidumpModuleList::from_modules(vec![
                MinidumpModule::new(0x00007400c0000000, 0x10000, "module1"),
                MinidumpModule::new(0x00007500b0000000, 0x10000, "module2"),
            ]),
            system_info: SystemInfo {
                os: Os::Linux,
                os_version: None,
                os_build: None,
                cpu: Cpu::X86_64,
                cpu_info: None,
                cpu_microcode_version: None,
                cpu_count: 1,
            },
            symbols: HashMap::new(),
        }
    }

    pub async fn walk_stack(&self, stack: Section) -> CallStack {
        let context = MinidumpContext {
            raw: MinidumpRawContext::Amd64(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 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,
            &self.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 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]
async fn 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.
    let mut f = TestFixture::new();
    let mut 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);

    f.raw.rip = 0x00007400c0000200;
    f.raw.rbp = frame0_rbp.value().unwrap();
    f.raw.rsp = stack.start().value().unwrap();

    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);
        if let MinidumpRawContext::Amd64(ctx) = &f0.context.raw {
            assert_eq!(ctx.rbp, frame0_rbp.value().unwrap());
        } else {
            unreachable!();
        }
    }

    {
        // To avoid reusing locals by mistake
        let f1 = &s.frames[1];
        assert_eq!(f1.trust, FrameTrust::FramePointer);
        if let MinidumpContextValidity::Some(ref which) = f1.context.valid {
            assert!(which.contains("rip"));
            assert!(which.contains("rsp"));
            assert!(which.contains("rbp"));
        } else {
            unreachable!();
        }
        if let MinidumpRawContext::Amd64(ctx) = &f1.context.raw {
            assert_eq!(ctx.rip, return_address);
            assert_eq!(ctx.rsp, frame1_sp.value().unwrap());
            assert_eq!(ctx.rbp, frame1_rbp.value().unwrap());
        } else {
            unreachable!();
        }
    }
}

#[tokio::test]
async fn test_windows_rbp_scan() {
    let mut f = TestFixture::new();
    f.system_info.os = Os::Windows;

    let mut 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);

    f.raw.rip = 0x00007400c0000200;
    f.raw.rbp = frame0_rbp.value().unwrap();
    f.raw.rsp = stack.start().value().unwrap();

    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);
        if let MinidumpRawContext::Amd64(ctx) = &f0.context.raw {
            assert_eq!(ctx.rbp, frame0_rbp.value().unwrap());
        } else {
            unreachable!();
        }
    }

    {
        // To avoid reusing locals by mistake
        let f1 = &s.frames[1];
        assert_eq!(f1.trust, FrameTrust::Scan);
        if let MinidumpContextValidity::Some(ref which) = f1.context.valid {
            assert!(which.contains("rip"));
            assert!(which.contains("rsp"));
        } else {
            unreachable!();
        }
        if let MinidumpRawContext::Amd64(ctx) = &f1.context.raw {
            assert_eq!(ctx.rip, return_address);
            assert_eq!(ctx.rsp, frame1_sp.value().unwrap());
        } else {
            unreachable!();
        }
    }
}

#[tokio::test]
async fn 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.
    let mut f = TestFixture::new();
    let mut 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

    f.raw.rip = 0x00007400c0000200;
    f.raw.rbp = frame1_rbp.value().unwrap();
    f.raw.rsp = stack.start().value().unwrap();

    let s = f.walk_stack(stack).await;
    assert_eq!(s.frames.len(), 3);

    {
        // 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);
        if let MinidumpContextValidity::Some(ref which) = f1.context.valid {
            assert!(which.contains("rip"));
            assert!(which.contains("rsp"));
            assert!(which.contains("rbp"));
        } else {
            unreachable!();
        }

        if let MinidumpRawContext::Amd64(ctx) = &f1.context.raw {
            assert_eq!(ctx.rip, return_address1);
            assert_eq!(ctx.rsp, frame1_sp.value().unwrap());
            assert_eq!(ctx.rbp, frame1_rbp.value().unwrap());
        } else {
            unreachable!();
        }
    }

    {
        // To avoid reusing locals by mistake
        let f2 = &s.frames[2];
        assert_eq!(f2.trust, FrameTrust::Scan);
        if let MinidumpContextValidity::Some(ref which) = f2.context.valid {
            assert!(which.contains("rip"));
            assert!(which.contains("rsp"));
        } else {
            unreachable!();
        }

        if let MinidumpRawContext::Amd64(ctx) = &f2.context.raw {
            assert_eq!(ctx.rip, return_address2);
            assert_eq!(ctx.rsp, frame2_sp.value().unwrap());
        } else {
            unreachable!();
        }
    }
}

#[tokio::test]
async fn 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.
    let mut f = TestFixture::new();
    let mut 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

    f.raw.rip = 0x00007400c0000200;
    f.raw.rbp = frame1_rbp.value().unwrap();
    f.raw.rsp = stack.start().value().unwrap();

    f.add_symbols(
        String::from("module1"),
        // The youngest frame's function.
        String::from("FUNC 100 400 10 monotreme\n"),
    );
    f.add_symbols(
        String::from("module2"),
        // The calling frame's function.
        String::from("FUNC 100 400 10 marsupial\n"),
    );

    let s = f.walk_stack(stack).await;
    assert_eq!(s.frames.len(), 2);

    {
        // Frame 0
        let frame = &s.frames[0];
        assert_eq!(frame.trust, FrameTrust::Context);
        assert_eq!(frame.context.valid, MinidumpContextValidity::All);
    }

    {
        // Frame 1
        let frame = &s.frames[1];
        let valid = &frame.context.valid;
        assert_eq!(frame.trust, FrameTrust::Scan);
        if let MinidumpContextValidity::Some(ref which) = valid {
            assert_eq!(which.len(), 3);
        } else {
            unreachable!();
        }

        if let MinidumpRawContext::Amd64(ctx) = &frame.context.raw {
            assert_eq!(ctx.get_register("rip", valid).unwrap(), return_address);
            assert_eq!(
                ctx.get_register("rsp", valid).unwrap(),
                frame1_rsp.value().unwrap()
            );
            assert_eq!(
                ctx.get_register("rbp", valid).unwrap(),
                frame1_rbp.value().unwrap()
            );
        } else {
            unreachable!();
        }
    }
}

const CALLEE_SAVE_REGS: &[&str] = &["rip", "rbx", "rbp", "rsp", "r12", "r13", "r14", "r15"];

fn init_cfi_state() -> (TestFixture, Section, CONTEXT_AMD64, MinidumpContextValidity) {
    let mut f = TestFixture::new();
    let symbols = [
        // The youngest frame's function.
        "FUNC 4000 1000 10 enchiridion\n",
        // Initially, just a return address.
        "STACK CFI INIT 4000 100 .cfa: $rsp 8 + .ra: .cfa 8 - ^\n",
        // Push %rbx.
        "STACK CFI 4001 .cfa: $rsp 16 + $rbx: .cfa 16 - ^\n",
        // Save %r12 in %rbx.  Weird, but permitted.
        "STACK CFI 4002 $r12: $rbx\n",
        // Allocate frame space, and save %r13.
        "STACK CFI 4003 .cfa: $rsp 40 + $r13: .cfa 32 - ^\n",
        // Put the return address in %r13.
        "STACK CFI 4005 .ra: $r13\n",
        // Save %rbp, and use it as a frame pointer.
        "STACK CFI 4006 .cfa: $rbp 16 + $rbp: .cfa 24 - ^\n",
        // The calling function.
        "FUNC 5000 1000 10 epictetus\n",
        // Mark it as end of stack.
        "STACK CFI INIT 5000 1000 .cfa: $rsp .ra 0\n",
    ];
    f.add_symbols(String::from("module1"), symbols.concat());

    f.raw.set_register("rsp", 0x8000000080000000);
    f.raw.set_register("rip", 0x00007400c0005510);
    f.raw.set_register("rbp", 0x68995b1de4700266);
    f.raw.set_register("rbx", 0x5a5beeb38de23be8);
    f.raw.set_register("r12", 0xed1b02e8cc0fc79c);
    f.raw.set_register("r13", 0x1d20ad8acacbe930);
    f.raw.set_register("r14", 0xe94cffc2f7adaa28);
    f.raw.set_register("r15", 0xb638d17d8da413b5);

    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("rsp", &raw_valid).unwrap());

    (f, stack, expected, expected_valid)
}

async fn 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);

    {
        // Frame 0
        let frame = &s.frames[0];
        assert_eq!(frame.trust, FrameTrust::Context);
        assert_eq!(frame.context.valid, MinidumpContextValidity::All);
    }

    {
        // Frame 1
        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::Amd64(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
        .D64(0x00007400c0005510)
        .mark(&frame1_rsp)
        .append_repeated(0, 1000);

    expected.set_register("rsp", frame1_rsp.value().unwrap());
    f.raw.set_register("rip", 0x00007400c0004000);

    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
        .D64(0x5a5beeb38de23be8) // saved %rbx
        .D64(0x00007400c0005510) // return address
        .mark(&frame1_rsp)
        .append_repeated(0, 1000);

    expected.set_register("rsp", frame1_rsp.value().unwrap());
    f.raw.set_register("rip", 0x00007400c0004001);
    f.raw.set_register("rbx", 0xbe0487d2f9eafe29);

    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
        .D64(0x5a5beeb38de23be8) // saved %rbx
        .D64(0x00007400c0005510) // return address
        .mark(&frame1_rsp)
        .append_repeated(0, 1000);

    expected.set_register("rsp", frame1_rsp.value().unwrap());
    f.raw.set_register("rip", 0x00007400c0004002);
    f.raw.set_register("rbx", 0xed1b02e8cc0fc79c); // saved %r12
    f.raw.set_register("r12", 0xb0118de918a4bcea); // callee's (distinct) %r12 value

    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
        .D64(0x0e023828dffd4d81) // garbage
        .D64(0x1d20ad8acacbe930) // saved %r13
        .D64(0x319e68b49e3ace0f) // garbage
        .D64(0x5a5beeb38de23be8) // saved %rbx
        .D64(0x00007400c0005510) // return address
        .mark(&frame1_rsp)
        .append_repeated(0, 1000);

    expected.set_register("rsp", frame1_rsp.value().unwrap());
    f.raw.set_register("rip", 0x00007400c0004003);
    f.raw.set_register("rbx", 0xed1b02e8cc0fc79c); // saved %r12
    f.raw.set_register("r12", 0x89d04fa804c87a43); // callee's (distinct) %r12
    f.raw.set_register("r13", 0x5118e02cbdb24b03); // callee's (distinct) %r13

    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
        .D64(0x0e023828dffd4d81) // garbage
        .D64(0x1d20ad8acacbe930) // saved %r13
        .D64(0x319e68b49e3ace0f) // garbage
        .D64(0x5a5beeb38de23be8) // saved %rbx
        .D64(0x00007400c0005510) // return address
        .mark(&frame1_rsp)
        .append_repeated(0, 1000);

    expected.set_register("rsp", frame1_rsp.value().unwrap());
    f.raw.set_register("rip", 0x00007400c0004004);
    f.raw.set_register("rbx", 0xed1b02e8cc0fc79c); // saved %r12
    f.raw.set_register("r12", 0x46b1b8868891b34a); // callee's (distinct) %r12
    f.raw.set_register("r13", 0x5118e02cbdb24b03); // callee's (distinct) %r13

    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
        .D64(0x4b516dd035745953) // garbage
        .D64(0x1d20ad8acacbe930) // saved %r13
        .D64(0xa6d445e16ae3d872) // garbage
        .D64(0x5a5beeb38de23be8) // saved %rbx
        .D64(0xaa95fa054aedfbae) // garbage
        .mark(&frame1_rsp)
        .append_repeated(0, 1000);

    expected.set_register("rsp", frame1_rsp.value().unwrap());
    f.raw.set_register("rip", 0x00007400c0004005);
    f.raw.set_register("rbx", 0xed1b02e8cc0fc79c); // saved %r12
    f.raw.set_register("r12", 0x46b1b8868891b34a); // callee's %r12
    f.raw.set_register("r13", 0x00007400c0005510); // return address

    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_rbp = Label::new();
    let frame1_rsp = Label::new();
    stack = stack
        .D64(0x043c6dfceb91aa34) // garbage
        .D64(0x1d20ad8acacbe930) // saved %r13
        .D64(0x68995b1de4700266) // saved %rbp
        .mark(&frame0_rbp) // frame pointer points here
        .D64(0x5a5beeb38de23be8) // saved %rbx
        .D64(0xf015ee516ad89eab) // garbage
        .mark(&frame1_rsp)
        .append_repeated(0, 1000);

    expected.set_register("rsp", frame1_rsp.value().unwrap());
    f.raw.set_register("rip", 0x00007400c0004006);
    f.raw.set_register("rbp", frame0_rbp.value().unwrap());
    f.raw.set_register("rbx", 0xed1b02e8cc0fc79c); // saved %r12
    f.raw.set_register("r12", 0x26e007b341acfebd); // callee's %r12
    f.raw.set_register("r13", 0x00007400c0005510); // return address

    check_cfi(f, stack, expected, expected_valid).await;
}

#[tokio::test]
async fn 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;

    let mut f = TestFixture::new();
    let mut 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

    f.raw.rip = 0x00007400c0000200;
    f.raw.rbp = bad_frame_ptr;
    f.raw.rsp = stack.start().value().unwrap() as Pointer;

    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]
async fn 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.

    let mut f = TestFixture::new();
    let mut 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);

    f.raw.rip = 0x00007400c0000200;
    f.raw.rbp = frame0_fp.value().unwrap() as Pointer;
    f.raw.rsp = stack.start().value().unwrap();

    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);
        if let 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);
        if let MinidumpContextValidity::Some(ref which) = f1.context.valid {
            assert!(which.contains("rip"));
            assert!(which.contains("rsp"));
            assert!(which.contains("rbp"));
        } else {
            unreachable!();
        }
        if let 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]
async fn 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.
    let mut f = TestFixture::new();
    let mut 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);

    f.raw.rip = 0x00007400c0000200;
    f.raw.rbp = frame1_rbp.value().unwrap();
    f.raw.rsp = stack.start().value().unwrap();

    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);
        if let MinidumpContextValidity::Some(ref which) = f1.context.valid {
            assert!(which.contains("rip"));
            assert!(which.contains("rsp"));
        } else {
            unreachable!();
        }

        if let 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!();
        }
    }
}

[ Dauer der Verarbeitung: 0.30 Sekunden  (vorverarbeitet)  ]

                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge