diff --git a/Cargo.toml b/Cargo.toml
index 9f3e528..029976f 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "kdmp-parser"
-version = "0.7.0"
+version = "0.8.0"
edition = "2024"
authors = ["Axel '0vercl0k' Souchet"]
categories = ["parser-implementations"]
@@ -11,16 +11,14 @@ license = "MIT"
repository = "https://github.com/0vercl0k/kdmp-parser-rs"
rust-version = "1.85"
-# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
-[dependencies]
-bitflags = "2.9.2"
-thiserror = "2"
-
[dev-dependencies]
anyhow = "1.0"
clap = { version = "4.5.45", features = ["derive"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
+[lints.clippy]
+pedantic = "warn"
+
[[example]]
name = "parser"
diff --git a/README.md b/README.md
index b8e1ede..9575d15 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,7 @@
kdmp-parser
- A KISS Rust crate to parse Windows kernel crash-dumps created by Windows & its debugger.
+ A KISS, dependency free Rust crate to parse Windows kernel crash-dumps created by Windows & its debugger.
diff --git a/examples/parser.rs b/examples/parser.rs
index 3871ad0..6bbd558 100644
--- a/examples/parser.rs
+++ b/examples/parser.rs
@@ -183,10 +183,10 @@ fn main() -> Result<()> {
.unwrap_or(Gpa::new(parser.headers().directory_table_base)),
)
} else {
- parser.phys_read(Gpa::new(addr), &mut buffer)
- };
+ parser.phys_read(Gpa::new(addr), &mut buffer).map(Some)
+ }?;
- if let Ok(amount) = amount {
+ if let Some(amount) = amount {
hexdump(addr, &buffer[..amount], args.len);
} else {
println!(
diff --git a/src/bits.rs b/src/bits.rs
index ac8fcc3..f2bf58f 100644
--- a/src/bits.rs
+++ b/src/bits.rs
@@ -16,9 +16,11 @@ use std::ops::RangeInclusive;
/// Utility trait to make it easier to extract ranges of bits.
pub trait Bits: Sized {
/// Get a range of bits.
+ #[must_use]
fn bits(&self, r: RangeInclusive) -> Self;
/// Get a bit.
+ #[must_use]
fn bit(&self, n: usize) -> Self {
self.bits(n..=n)
}
diff --git a/src/error.rs b/src/error.rs
index 2fdacd6..d26b626 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -1,71 +1,211 @@
// Axel '0vercl0k' Souchet - March 19 2024
//! This is the error type used across the codebase.
-use std::fmt::Display;
-use std::{io, string};
-
-use thiserror::Error;
+use std::error::Error;
+use std::fmt::{self, Display};
+use std::io;
+use std::string::FromUtf16Error;
use crate::structs::{DUMP_HEADER64_EXPECTED_SIGNATURE, DUMP_HEADER64_EXPECTED_VALID_DUMP};
use crate::{Gpa, Gva};
pub type Result = std::result::Result;
-#[derive(Debug)]
-pub enum PxeNotPresent {
+/// Identifies which page table entry level.
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum PxeKind {
Pml4e,
Pdpte,
Pde,
Pte,
}
-#[derive(Debug, Error)]
-pub enum AddrTranslationError {
- Virt(Gva, PxeNotPresent),
- Phys(Gpa),
+/// Represent the fundamental reason a single page read can fail.
+#[derive(Debug, Clone)]
+pub enum PageReadError {
+ /// Virtual address translation failed because a page table entry is not
+ /// present (it exists in the dump but is marked as not present).
+ NotPresent { gva: Gva, which_pxe: PxeKind },
+ /// A physical page is missing from the dump.
+ NotInDump {
+ gva: Option<(Gva, Option)>,
+ gpa: Gpa,
+ },
+}
+
+impl Error for PageReadError {}
+
+/// Recoverable memory errors that can occur during memory reads.
+///
+/// There are several failure conditions that can happen while trying to read
+/// virtual (or physical) memory out of a crash-dump that might not be obvious.
+///
+/// For example, consider reading two 4K pages from the virtual address
+/// `0x1337_000`; it can fail because:
+/// - The virtual address (the first 4K page) isn't present in the address space
+/// at the `Pde` level: `MemoryReadError::PageRead(PageReadError::NotPresent {
+/// gva: 0x1337_000, which_pxe: PxeKind::Pde })`
+/// - The `Pde` that needs reading as part of the address translation (of the
+/// first page) isn't part of the crash-dump:
+/// `MemoryReadError::PageRead(PageReadError::NotInDump { gva:
+/// Some((0x1337_000, PxeKind::Pde)), gpa: .. })`
+/// - The physical page backing that virtual address isn't included in the
+/// crash-dump: `MemoryReadError::PageRead(PageReadError::NotInDump { gva:
+/// Some((0x1337_000, None)), gpa: .. })`
+/// - Reading the second (and only the second) page failed because of any of the
+/// previous reasons: `MemoryReadError::PartialRead { expected_amount: 8_192,
+/// actual_amount: 4_096, reason: PageReadError::.. }`
+///
+/// Similarly, for physical memory reads starting at `0x1337_000`:
+/// - A direct physical page isn't in the crash-dump:
+/// `MemoryError::PageRead(PageReadError::NotInDump { gpa: 0x1337_000 })`
+/// - Reading the second page failed: `MemoryError::PartialRead {
+/// expected_amount: 8_192, actual_amount: 4_096, reason:
+/// PageReadError::NotInDump { gva: None, gpa: 0x1338_000 } }`
+///
+/// We consider any of those errors 'recoverable' which means that we won't even
+/// bubble those up to the callers with the regular APIs. Only the `strict`
+/// versions will.
+#[derive(Debug, Clone)]
+pub enum MemoryReadError {
+ /// A single page/read failed.
+ PageRead(PageReadError),
+ /// A read request was only partially fulfilled.
+ PartialRead {
+ expected_amount: usize,
+ actual_amount: usize,
+ reason: PageReadError,
+ },
}
-impl Display for AddrTranslationError {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+impl Display for PageReadError {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
- AddrTranslationError::Virt(gva, not_pres) => f.write_fmt(format_args!(
- "virt to phys translation of {gva}: {not_pres:?}"
- )),
- AddrTranslationError::Phys(gpa) => {
- f.write_fmt(format_args!("phys to offset translation of {gpa}"))
+ PageReadError::NotPresent { gva, which_pxe } => {
+ write!(f, "{gva} isn't present at the {which_pxe:?} level")
}
+ PageReadError::NotInDump { gva, gpa } => match gva {
+ Some((gva, Some(which_pxe))) => write!(
+ f,
+ "{gpa} was needed while translating {gva} at the {which_pxe:?} level but is missing from the dump)"
+ ),
+ Some((gva, None)) => write!(f, "{gpa} backs {gva} but is missing from the dump)"),
+ None => {
+ write!(f, "{gpa} is missing from the dump)")
+ }
+ },
}
}
}
-#[derive(Error, Debug)]
+impl Error for MemoryReadError {
+ fn source(&self) -> Option<&(dyn Error + 'static)> {
+ match self {
+ MemoryReadError::PageRead(e) => Some(e),
+ MemoryReadError::PartialRead { reason, .. } => Some(reason),
+ }
+ }
+}
+
+impl Display for MemoryReadError {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match self {
+ MemoryReadError::PageRead(_) => write!(f, "page read"),
+ MemoryReadError::PartialRead {
+ expected_amount,
+ actual_amount,
+ ..
+ } => {
+ write!(
+ f,
+ "partially read {actual_amount} off {expected_amount} wanted bytes"
+ )
+ }
+ }
+ }
+}
+
+#[derive(Debug)]
pub enum KdmpParserError {
- #[error("invalid UNICODE_STRING")]
InvalidUnicodeString,
- #[error("utf16: {0}")]
- Utf16(#[from] string::FromUtf16Error),
- #[error("overflow: {0}")]
+ Utf16(FromUtf16Error),
Overflow(&'static str),
- #[error("io: {0}")]
- Io(#[from] io::Error),
- #[error("invalid data: {0}")]
+ Io(io::Error),
InvalidData(&'static str),
- #[error("unsupported dump type {0:#x}")]
UnknownDumpType(u32),
- #[error("duplicate gpa found in physmem map for {0}")]
DuplicateGpa(Gpa),
- #[error("header's signature looks wrong: {0:#x} vs {DUMP_HEADER64_EXPECTED_SIGNATURE:#x}")]
InvalidSignature(u32),
- #[error("header's valid dump looks wrong: {0:#x} vs {DUMP_HEADER64_EXPECTED_VALID_DUMP:#x}")]
InvalidValidDump(u32),
- #[error("overflow for phys addr w/ run {0} page {1}")]
PhysAddrOverflow(u32, u64),
- #[error("overflow for page offset w/ run {0} page {1}")]
PageOffsetOverflow(u32, u64),
- #[error("overflow for page offset w/ bitmap_idx {0} bit_idx {1}")]
BitmapPageOffsetOverflow(u64, usize),
- #[error("partial physical memory read")]
- PartialPhysRead,
- #[error("partial virtual memory read")]
- PartialVirtRead,
- #[error("memory translation: {0}")]
- AddrTranslation(#[from] AddrTranslationError),
+ MemoryRead(MemoryReadError),
+}
+
+impl From for KdmpParserError {
+ fn from(value: io::Error) -> Self {
+ KdmpParserError::Io(value)
+ }
+}
+
+impl From for KdmpParserError {
+ fn from(value: FromUtf16Error) -> Self {
+ KdmpParserError::Utf16(value)
+ }
+}
+
+impl From for KdmpParserError {
+ fn from(value: MemoryReadError) -> Self {
+ KdmpParserError::MemoryRead(value)
+ }
+}
+
+impl From for KdmpParserError {
+ fn from(value: PageReadError) -> Self {
+ Self::MemoryRead(MemoryReadError::PageRead(value))
+ }
+}
+
+impl Display for KdmpParserError {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match self {
+ KdmpParserError::InvalidUnicodeString => write!(f, "invalid UNICODE_STRING"),
+ KdmpParserError::Utf16(_) => write!(f, "utf16"),
+ KdmpParserError::Overflow(o) => write!(f, "overflow: {o}"),
+ KdmpParserError::Io(_) => write!(f, "io"),
+ KdmpParserError::InvalidData(i) => write!(f, "invalid data: {i}"),
+ KdmpParserError::UnknownDumpType(u) => write!(f, "unsupported dump type {u:#x}"),
+ KdmpParserError::DuplicateGpa(gpa) => {
+ write!(f, "duplicate gpa found in physmem map for {gpa}")
+ }
+ KdmpParserError::InvalidSignature(sig) => write!(
+ f,
+ "header's signature looks wrong: {sig:#x} vs {DUMP_HEADER64_EXPECTED_SIGNATURE:#x}"
+ ),
+ KdmpParserError::InvalidValidDump(dump) => write!(
+ f,
+ "header's valid dump looks wrong: {dump:#x} vs {DUMP_HEADER64_EXPECTED_VALID_DUMP:#x}"
+ ),
+ KdmpParserError::PhysAddrOverflow(run, page) => {
+ write!(f, "overflow for phys addr w/ run {run} page {page}")
+ }
+ KdmpParserError::PageOffsetOverflow(run, page) => {
+ write!(f, "overflow for page offset w/ run {run} page {page}")
+ }
+ KdmpParserError::BitmapPageOffsetOverflow(bitmap_idx, bit_idx) => write!(
+ f,
+ "overflow for page offset w/ bitmap_idx {bitmap_idx} bit_idx {bit_idx}"
+ ),
+ KdmpParserError::MemoryRead(_) => write!(f, "memory read"),
+ }
+ }
+}
+
+impl Error for KdmpParserError {
+ fn source(&self) -> Option<&(dyn Error + 'static)> {
+ match self {
+ KdmpParserError::Utf16(u) => Some(u),
+ KdmpParserError::Io(e) => Some(e),
+ KdmpParserError::MemoryRead(m) => Some(m),
+ _ => None,
+ }
+ }
}
diff --git a/src/gxa.rs b/src/gxa.rs
index 5bd0797..58a7ccc 100644
--- a/src/gxa.rs
+++ b/src/gxa.rs
@@ -13,7 +13,7 @@
//! let page_aligned_gva = gva.page_align();
//! let page_offset = gva.offset();
//! ```
-use std::fmt::Display;
+use std::fmt::{self, Debug, Display};
use std::num::ParseIntError;
use std::ops::AddAssign;
use std::str::FromStr;
@@ -33,16 +33,19 @@ pub trait Gxa: Sized + Default + Copy + From {
}
/// Is it page aligned?
+ #[must_use]
fn page_aligned(&self) -> bool {
self.offset() == 0
}
/// Page-align it.
+ #[must_use]
fn page_align(&self) -> Self {
Self::from(self.u64() & !0xf_ff)
}
/// Get the next aligned page.
+ #[must_use]
fn next_aligned_page(self) -> Self {
Self::from(
self.page_align()
@@ -84,6 +87,7 @@ impl Gpa {
/// let gpa = Gpa::new(1337);
/// # }
/// ```
+ #[must_use]
pub const fn new(addr: u64) -> Self {
Self(addr)
}
@@ -99,6 +103,7 @@ impl Gpa {
/// assert_eq!(gpa.u64(), 0x1337_000);
/// # }
/// ```
+ #[must_use]
pub const fn from_pfn(pfn: Pfn) -> Self {
Self(pfn.u64() << (4 * 3))
}
@@ -115,6 +120,7 @@ impl Gpa {
/// assert_eq!(gpa.u64(), 0x1337_011);
/// # }
/// ```
+ #[must_use]
pub const fn from_pfn_with_offset(pfn: Pfn, offset: u64) -> Self {
let base = pfn.u64() << (4 * 3);
@@ -132,6 +138,7 @@ impl Gpa {
/// assert_eq!(gpa.pfn(), 0x1337);
/// # }
/// ```
+ #[must_use]
pub const fn pfn(&self) -> u64 {
self.0 >> (4 * 3)
}
@@ -140,7 +147,7 @@ impl Gpa {
/// Operator += for [`Gpa`].
impl AddAssign for Gpa {
fn add_assign(&mut self, rhs: Self) {
- self.0 += rhs.0
+ self.0 += rhs.0;
}
}
@@ -222,7 +229,7 @@ impl From<&Gpa> for u64 {
/// Format a [`Gpa`] as a string.
impl Display for Gpa {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "GPA:{:#x}", self.0)
}
}
@@ -272,6 +279,7 @@ impl Gva {
/// let gva = Gva::new(0xdeadbeef);
/// # }
/// ```
+ #[must_use]
pub const fn new(addr: u64) -> Self {
Self(addr)
}
@@ -290,6 +298,7 @@ impl Gva {
/// # }
/// ```
#[allow(clippy::erasing_op, clippy::identity_op)]
+ #[must_use]
pub const fn pte_idx(&self) -> u64 {
(self.0 >> (12 + (9 * 0))) & 0b1_1111_1111
}
@@ -308,6 +317,7 @@ impl Gva {
/// # }
/// ```
#[allow(clippy::identity_op)]
+ #[must_use]
pub const fn pde_idx(&self) -> u64 {
(self.0 >> (12 + (9 * 1))) & 0b1_1111_1111
}
@@ -325,6 +335,7 @@ impl Gva {
/// assert_eq!(second.pdpe_idx(), 0x88);
/// # }
/// ```
+ #[must_use]
pub const fn pdpe_idx(&self) -> u64 {
(self.0 >> (12 + (9 * 2))) & 0b1_1111_1111
}
@@ -342,6 +353,7 @@ impl Gva {
/// assert_eq!(second.pml4e_idx(), 0x22);
/// # }
/// ```
+ #[must_use]
pub fn pml4e_idx(&self) -> u64 {
(self.0 >> (12 + (9 * 3))) & 0b1_1111_1111
}
@@ -350,7 +362,7 @@ impl Gva {
/// Operator += for [`Gva`].
impl AddAssign for Gva {
fn add_assign(&mut self, rhs: Self) {
- self.0 += rhs.0
+ self.0 += rhs.0;
}
}
diff --git a/src/lib.rs b/src/lib.rs
index 0c8dd89..f9eaee0 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,4 +1,5 @@
// Axel '0vercl0k' Souchet - February 25 2024
+#![allow(clippy::doc_markdown)]
#![doc = include_str!("../README.md")]
mod bits;
mod error;
@@ -9,7 +10,7 @@ mod pxe;
mod structs;
pub use bits::Bits;
-pub use error::{AddrTranslationError, KdmpParserError, PxeNotPresent, Result};
+pub use error::{KdmpParserError, MemoryReadError, PageReadError, PxeKind, Result};
pub use gxa::{Gpa, Gva, Gxa};
pub use map::{MappedFileReader, Reader};
pub use parse::{KernelDumpParser, VirtTranslationDetails};
diff --git a/src/map.rs b/src/map.rs
index f25176c..f14d86a 100644
--- a/src/map.rs
+++ b/src/map.rs
@@ -1,10 +1,10 @@
// Axel '0vercl0k' Souchet - July 18 2023
//! This implements logic that allows to memory map a file on both
//! Unix and Windows (cf [`memory_map_file`] / [`unmap_memory_mapped_file`]).
-use std::fmt::Debug;
-use std::io::{Read, Seek};
+use std::fmt::{self, Debug};
+use std::fs::File;
+use std::io::{self, Cursor, Read, Seek};
use std::path::Path;
-use std::{fs, io, ptr, slice};
pub trait Reader: Read + Seek {}
@@ -14,27 +14,31 @@ impl Reader for T where T: Read + Seek {}
/// mapping and a cursor to be able to access the region.
pub struct MappedFileReader<'map> {
mapped_file: &'map [u8],
- cursor: io::Cursor<&'map [u8]>,
+ cursor: Cursor<&'map [u8]>,
}
impl Debug for MappedFileReader<'_> {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("MappedFileReader").finish()
}
}
impl MappedFileReader<'_> {
/// Create a new [`MappedFileReader`] from a path using a memory map.
+ ///
+ /// # Errors
+ ///
+ /// Returns an error if the file cannot be opened or memory mapped.
pub fn new(path: impl AsRef) -> io::Result {
// Open the file..
- let file = fs::File::open(path)?;
+ let file = File::open(path)?;
// ..and memory map it using the underlying OS-provided APIs.
- let mapped_file = memory_map_file(file)?;
+ let mapped_file = memory_map_file(&file)?;
Ok(Self {
mapped_file,
- cursor: io::Cursor::new(mapped_file),
+ cursor: Cursor::new(mapped_file),
})
}
}
@@ -55,19 +59,19 @@ impl Seek for MappedFileReader<'_> {
/// need to drop the mapping using OS-provided APIs.
impl Drop for MappedFileReader<'_> {
fn drop(&mut self) {
- unmap_memory_mapped_file(self.mapped_file).expect("failed to unmap")
+ unmap_memory_mapped_file(self.mapped_file).expect("failed to unmap");
}
}
#[cfg(windows)]
#[allow(non_camel_case_types, clippy::upper_case_acronyms)]
-/// Module that implements memory mapping on Windows using CreateFileMappingA /
-/// MapViewOfFile.
+/// Module that implements memory mapping on Windows using `CreateFileMappingA`
+/// / `MapViewOfFile`.
mod windows {
+ use std::fs::File;
use std::os::windows::prelude::AsRawHandle;
use std::os::windows::raw::HANDLE;
-
- use super::*;
+ use std::{io, ptr, slice};
const PAGE_READONLY: DWORD = 2;
const FILE_MAP_READ: DWORD = 4;
@@ -117,7 +121,7 @@ mod windows {
}
/// Memory map a file into memory.
- pub fn memory_map_file<'map>(file: fs::File) -> Result<&'map [u8], io::Error> {
+ pub fn memory_map_file<'map>(file: &File) -> Result<&'map [u8], io::Error> {
// Grab the underlying HANDLE.
let file_handle = file.as_raw_handle();
@@ -161,9 +165,7 @@ mod windows {
}
// Make sure the size is not bigger than what [`slice::from_raw_parts`] wants.
- if size > isize::MAX.try_into().unwrap() {
- panic!("slice is too large");
- }
+ assert!(size <= isize::MAX.try_into().unwrap(), "slice is too large");
// Create the slice over the mapping.
// SAFETY: This is safe because:
@@ -191,14 +193,14 @@ mod windows {
}
#[cfg(windows)]
-use windows::*;
+use windows::{memory_map_file, unmap_memory_mapped_file};
#[cfg(unix)]
/// Module that implements memory mapping on Unix using the mmap syscall.
mod unix {
+ use std::fs::File;
use std::os::fd::AsRawFd;
-
- use super::*;
+ use std::{io, ptr, slice};
const PROT_READ: i32 = 1;
const MAP_SHARED: i32 = 1;
@@ -217,7 +219,7 @@ mod unix {
fn munmap(addr: *const u8, length: usize) -> i32;
}
- pub fn memory_map_file<'map>(file: fs::File) -> Result<&'map [u8], io::Error> {
+ pub fn memory_map_file<'map>(file: &File) -> Result<&'map [u8], io::Error> {
// Grab the underlying file descriptor.
let file_fd = file.as_raw_fd();
@@ -234,9 +236,7 @@ mod unix {
}
// Make sure the size is not bigger than what [`slice::from_raw_parts`] wants.
- if size > isize::MAX.try_into().unwrap() {
- panic!("slice is too large");
- }
+ assert!(size <= isize::MAX.try_into().unwrap(), "slice is too large");
// Create the slice over the mapping.
// SAFETY: This is safe because:
@@ -264,7 +264,7 @@ mod unix {
}
#[cfg(unix)]
-use unix::*;
+use unix::{memory_map_file, unmap_memory_mapped_file};
#[cfg(not(any(windows, unix)))]
/// Your system hasn't been implemented; if you do it, send a PR!
diff --git a/src/parse.rs b/src/parse.rs
index 1f42d06..198eea8 100644
--- a/src/parse.rs
+++ b/src/parse.rs
@@ -4,14 +4,16 @@ use core::slice;
use std::cell::RefCell;
use std::cmp::min;
use std::collections::HashMap;
-use std::fmt::Debug;
+use std::fmt::{self, Debug};
use std::fs::File;
+use std::io::SeekFrom;
+use std::mem::MaybeUninit;
use std::ops::Range;
use std::path::Path;
use std::{io, mem};
use crate::bits::Bits;
-use crate::error::{PxeNotPresent, Result};
+use crate::error::{MemoryReadError, PageReadError, PxeKind, Result};
use crate::gxa::Gxa;
use crate::map::{MappedFileReader, Reader};
use crate::structs::{
@@ -20,7 +22,7 @@ use crate::structs::{
LdrDataTableEntry, ListEntry, PageKind, PfnRange, PhysmemDesc, PhysmemMap, PhysmemRun,
UnicodeString, read_struct,
};
-use crate::{AddrTranslationError, Gpa, Gva, KdmpParserError, Pfn, Pxe};
+use crate::{Gpa, Gva, KdmpParserError, Pfn, Pxe};
/// The details related to a virtual to physical address translation.
///
@@ -45,6 +47,11 @@ pub struct VirtTranslationDetails {
}
impl VirtTranslationDetails {
+ /// Create a new instance from a slice of PXEs and the original GVA.
+ ///
+ /// # Panics
+ ///
+ /// Panics if `pxes` is malformed (i.e. not between 2 and 4 entries).
pub fn new(pxes: &[Pxe], gva: Gva) -> Self {
let writable = pxes.iter().all(Pxe::writable);
let executable = pxes.iter().all(Pxe::executable);
@@ -68,6 +75,7 @@ impl VirtTranslationDetails {
}
}
+ #[must_use]
pub fn gpa(&self) -> Gpa {
self.pfn.gpa_with_offset(self.offset)
}
@@ -107,14 +115,14 @@ macro_rules! impl_checked_add {
impl_checked_add!(u32, u64);
-/// Walk a LIST_ENTRY of LdrDataTableEntry. It is used to dump both the user &
-/// driver / module lists.
+/// Walk a `LIST_ENTRY` of `LdrDataTableEntry`. It is used to dump both the user
+/// & driver / module lists.
fn try_read_module_map
(parser: &mut KernelDumpParser, head: Gva) -> Result