kdmp_parser/
error.rs

1// Axel '0vercl0k' Souchet - March 19 2024
2//! This is the error type used across the codebase.
3use std::fmt::{self, Display};
4use std::io;
5use std::string::FromUtf16Error;
6
7use crate::gxa::{Gpa, Gva};
8use crate::structs::{DUMP_HEADER64_EXPECTED_SIGNATURE, DUMP_HEADER64_EXPECTED_VALID_DUMP};
9pub type Result<R> = std::result::Result<R, Error>;
10
11/// Identifies which page table entry level.
12#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13pub enum PxeKind {
14    Pml4e,
15    Pdpte,
16    Pde,
17    Pte,
18}
19
20impl Display for PxeKind {
21    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
22        match self {
23            PxeKind::Pml4e => write!(f, "PML4E"),
24            PxeKind::Pdpte => write!(f, "PDPTE"),
25            PxeKind::Pde => write!(f, "PDE"),
26            PxeKind::Pte => write!(f, "PTE"),
27        }
28    }
29}
30
31/// Represent the fundamental reason a single page read can fail.
32#[derive(Debug, Clone)]
33pub enum PageReadError {
34    /// Virtual address translation failed because a page table entry is not
35    /// present (it exists in the dump but is marked as not present).
36    NotPresent { gva: Gva, which_pxe: PxeKind },
37    /// A physical page is missing from the dump.
38    NotInDump {
39        gva: Option<(Gva, Option<PxeKind>)>,
40        gpa: Gpa,
41    },
42}
43
44impl std::error::Error for PageReadError {}
45
46/// Memory errors that can occur during memory reads.
47///
48/// There are several failure conditions that can happen while trying to read
49/// virtual (or physical) memory out of a crash-dump that might not be obvious.
50///
51/// For example, consider reading two 4K pages from the virtual address
52/// `0x1337_000`; it can fail because:
53/// - The virtual address (the first 4K page) isn't present in the address space
54///   at the [`PxeKind::Pde`] level: `PageReadError::NotPresent { gva:
55///   0x1337_000, which_pxe: PxeKind::Pde })`
56/// - The [`PxeKind::Pde`] that needs reading as part of the address translation
57///   (of the first page) isn't part of the crash-dump: PageReadError::NotInDump
58///   { gva: Some((0x1337_000, PxeKind::Pde)), gpa: .. })`
59/// - The physical page backing that virtual address isn't included in the
60///   crash-dump: `Error::PageRead(PageReadError::NotInDump { gva:
61///   Some((0x1337_000, None)), gpa: .. })`
62/// - Reading the second (and only the second) page failed because of any of the
63///   previous reasons: `PartialRead { expected_amount: 8_192, actual_amount:
64///   4_096, reason: PageReadError::.. }`
65///
66/// Similarly, for physical memory reads starting at `0x1337_000`:
67/// - The physical page isn't in the crash-dump:
68///   `MemoryError::PageRead(PageReadError::NotInDump { gpa: 0x1337_000 })`
69/// - Reading the second page failed: `PartialRead { expected_amount: 8_192,
70///   actual_amount: 4_096, reason: PageReadError::NotInDump { gva: None, gpa:
71///   0x1338_000 } }`
72impl Display for PageReadError {
73    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
74        match self {
75            PageReadError::NotPresent { gva, which_pxe } => {
76                write!(f, "{gva} isn't present at the {which_pxe} level")
77            }
78            PageReadError::NotInDump { gva, gpa } => match gva {
79                Some((gva, Some(which_pxe))) => write!(
80                    f,
81                    "{gpa} was needed while translating {gva} at the {which_pxe} level but is missing from the dump)"
82                ),
83                Some((gva, None)) => write!(f, "{gpa} backs {gva} but is missing from the dump)"),
84                None => {
85                    write!(f, "{gpa} is missing from the dump)")
86                }
87            },
88        }
89    }
90}
91
92#[derive(Debug)]
93pub enum Error {
94    InvalidUnicodeString,
95    Utf16(FromUtf16Error),
96    Overflow(&'static str),
97    Io(io::Error),
98    InvalidData(&'static str),
99    UnknownDumpType(u32),
100    DuplicateGpa(Gpa),
101    InvalidSignature(u32),
102    InvalidValidDump(u32),
103    PhysAddrOverflow(u32, u64),
104    PageOffsetOverflow(u32, u64),
105    BitmapPageOffsetOverflow(u64, usize),
106    PartialRead {
107        expected_amount: usize,
108        actual_amount: usize,
109        reason: PageReadError,
110    },
111    PageRead(PageReadError),
112}
113
114impl From<io::Error> for Error {
115    fn from(value: io::Error) -> Self {
116        Self::Io(value)
117    }
118}
119
120impl From<FromUtf16Error> for Error {
121    fn from(value: FromUtf16Error) -> Self {
122        Self::Utf16(value)
123    }
124}
125
126impl From<PageReadError> for Error {
127    fn from(value: PageReadError) -> Self {
128        Self::PageRead(value)
129    }
130}
131
132impl Display for Error {
133    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
134        match self {
135            Self::InvalidUnicodeString => write!(f, "invalid UNICODE_STRING"),
136            Self::Utf16(_) => write!(f, "utf16"),
137            Self::Overflow(o) => write!(f, "overflow: {o}"),
138            Self::Io(_) => write!(f, "io"),
139            Self::InvalidData(i) => write!(f, "invalid data: {i}"),
140            Self::UnknownDumpType(u) => write!(f, "unsupported dump type {u:#x}"),
141            Self::DuplicateGpa(gpa) => {
142                write!(f, "duplicate gpa found in physmem map for {gpa}")
143            }
144            Self::InvalidSignature(sig) => write!(
145                f,
146                "header's signature looks wrong: {sig:#x} vs {DUMP_HEADER64_EXPECTED_SIGNATURE:#x}"
147            ),
148            Self::InvalidValidDump(dump) => write!(
149                f,
150                "header's valid dump looks wrong: {dump:#x} vs {DUMP_HEADER64_EXPECTED_VALID_DUMP:#x}"
151            ),
152            Self::PhysAddrOverflow(run, page) => {
153                write!(f, "overflow for phys addr w/ run {run} page {page}")
154            }
155            Self::PageOffsetOverflow(run, page) => {
156                write!(f, "overflow for page offset w/ run {run} page {page}")
157            }
158            Self::BitmapPageOffsetOverflow(bitmap_idx, bit_idx) => write!(
159                f,
160                "overflow for page offset w/ bitmap_idx {bitmap_idx} bit_idx {bit_idx}"
161            ),
162            Self::PartialRead {
163                expected_amount,
164                actual_amount,
165                reason,
166            } => write!(
167                f,
168                "partially read {actual_amount} bytes out of {expected_amount} because of {reason}"
169            ),
170            Self::PageRead(p) => write!(f, "page read: {p}"),
171        }
172    }
173}
174
175impl std::error::Error for Error {
176    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
177        match self {
178            Self::Utf16(u) => Some(u),
179            Self::Io(e) => Some(e),
180            Self::PartialRead { reason, .. } => Some(reason),
181            _ => None,
182        }
183    }
184}