Skip to main content

process_reader/linux/
error.rs

1use core::{
2    error::Error as StdError,
3    ffi::c_int,
4    fmt::{Display, Formatter, Result as FmtResult},
5};
6
7/// An error returned while reading memory from a target process.
8///
9/// `ReadError` is intentionally a small public wrapper around private strategy
10/// errors. Its [`Display`] implementation describes the high-level failure. Its
11/// [`core::error::Error::source`] implementation returns a lower-level source
12/// when there is exactly one failed strategy, including failures from forced
13/// strategies and failures from a strategy selected by an automatic reader.
14///
15/// Short successful reads are not represented by `ReadError`; they are returned
16/// from [`crate::ProcessReader::read_at`] as `Ok(n)` where `n` is smaller than
17/// the requested buffer length.
18///
19/// When [`crate::ProcessReader::new`] tries every strategy and all of them fail,
20/// there is no single source error. In that case
21/// [`core::error::Error::source`] returns `None`, and callers can inspect each
22/// branch with
23/// [`ReadError::virtual_mem_error`], [`ReadError::file_error`], and
24/// [`ReadError::ptrace_error`].
25///
26/// If the `serde` feature is enabled, this type implements `Serialize` and
27/// `Deserialize`.
28#[derive(Debug)]
29#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
30pub struct ReadError(pub(crate) ReadErrorInner);
31
32impl ReadError {
33    /// Returns the error from the `process_vm_readv(2)` strategy, if present.
34    ///
35    /// This returns `Some` when a reader forced to or cached on
36    /// [`crate::ProcessReader::for_virtual_mem`] fails, and when automatic
37    /// strategy selection fails after attempting `process_vm_readv`.
38    ///
39    /// It returns `None` when the error does not contain a `process_vm_readv`
40    /// failure.
41    pub fn virtual_mem_error(&self) -> Option<&(dyn StdError + 'static)> {
42        match &self.0 {
43            ReadErrorInner::AllStrategies { vmem_err, .. } => Some(vmem_err),
44            ReadErrorInner::VirtualMemStrategy(e) => Some(e),
45            _ => None,
46        }
47    }
48
49    /// Returns the error from the `/proc/<pid>/mem` strategy, if present.
50    ///
51    /// This returns `Some` when a reader forced to or cached on
52    /// [`crate::ProcessReader::for_file`] fails, and when automatic strategy
53    /// selection fails after attempting to use `/proc/<pid>/mem`.
54    ///
55    /// It returns `None` when the error does not contain a `/proc/<pid>/mem`
56    /// failure.
57    pub fn file_error(&self) -> Option<&(dyn StdError + 'static)> {
58        match &self.0 {
59            ReadErrorInner::AllStrategies { file_err, .. } => Some(file_err),
60            ReadErrorInner::FileStrategy(e) => Some(e),
61            _ => None,
62        }
63    }
64
65    /// Returns the error from the `ptrace(PTRACE_PEEKDATA)` strategy, if present.
66    ///
67    /// This returns `Some` when a reader forced to or cached on
68    /// [`crate::ProcessReader::for_ptrace`] fails, and when automatic strategy
69    /// selection fails after attempting `ptrace(PTRACE_PEEKDATA)`.
70    ///
71    /// It returns `None` when the error does not contain a ptrace failure.
72    pub fn ptrace_error(&self) -> Option<&(dyn StdError + 'static)> {
73        match &self.0 {
74            ReadErrorInner::AllStrategies { ptrace_err, .. } => Some(ptrace_err),
75            ReadErrorInner::PtraceStrategy(e) => Some(e),
76            _ => None,
77        }
78    }
79}
80
81impl Display for ReadError {
82    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
83        use ReadErrorInner as E;
84        match &self.0 {
85            E::AllStrategies { .. } => write!(f, "all process reading strategies failed"),
86            E::VirtualMemStrategy(_) => write!(f, "virtual memory strategy failed"),
87            E::FileStrategy(_) => write!(f, "file strategy failed"),
88            E::PtraceStrategy(_) => write!(f, "ptrace strategy failed"),
89        }
90    }
91}
92
93impl StdError for ReadError {
94    fn source(&self) -> Option<&(dyn StdError + 'static)> {
95        use ReadErrorInner as E;
96        match &self.0 {
97            E::AllStrategies { .. } => None,
98            E::VirtualMemStrategy(e) => Some(e),
99            E::FileStrategy(e) => Some(e),
100            E::PtraceStrategy(e) => Some(e),
101        }
102    }
103}
104
105#[derive(Debug)]
106#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
107pub(crate) enum ReadErrorInner {
108    AllStrategies {
109        vmem_err: ProcessVmReadvFailed,
110        file_err: FileStrategyError,
111        ptrace_err: PtraceError,
112    },
113    VirtualMemStrategy(ProcessVmReadvFailed),
114    FileStrategy(FileStrategyError),
115    PtraceStrategy(PtraceError),
116}
117
118/// Error returned by [`ProcessReader::read_exact_at`].
119///
120/// [`ProcessReader::read_exact_at`] is built on top of
121/// [`ProcessReader::read_at`]. It repeatedly performs partial reads until the
122/// caller's buffer is full. This error reports why that exact read could not be
123/// completed.
124#[derive(Debug)]
125#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
126pub enum ReadExactError {
127    /// An underlying call to [`ProcessReader::read_at`] failed before the buffer
128    /// was completely filled.
129    ///
130    /// The wrapped [`ReadError`] describes the strategy that failed, or the set
131    /// of strategies that failed if automatic strategy selection had not yet
132    /// chosen a strategy.
133    Read(ReadError),
134    /// The reader stopped making progress before the buffer was completely
135    /// filled.
136    ///
137    /// This occurs when [`ProcessReader::read_at`] returns `Ok(0)` while
138    /// `read_exact_at` still has bytes left to read.
139    UnexpectedEof,
140}
141
142impl Display for ReadExactError {
143    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
144        match self {
145            Self::Read(_) => write!(f, "an error occurred before filling entire buffer"),
146            Self::UnexpectedEof => write!(f, "unexpected end-of-file before filling entire buffer"),
147        }
148    }
149}
150
151impl StdError for ReadExactError {
152    fn source(&self) -> Option<&(dyn StdError + 'static)> {
153        match self {
154            Self::Read(e) => Some(e),
155            Self::UnexpectedEof => None,
156        }
157    }
158}
159
160#[derive(Debug)]
161#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
162pub(crate) struct ProcessVmReadvFailed(pub(crate) c_int);
163
164impl Display for ProcessVmReadvFailed {
165    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
166        write!(f, "process_vm_readv() returned an error code: {}", self.0)
167    }
168}
169
170impl StdError for ProcessVmReadvFailed {}
171
172#[derive(Debug)]
173#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
174pub(crate) enum FileStrategyError {
175    Open(OpenFailed),
176    Read(ReadExactAtError),
177}
178
179impl Display for FileStrategyError {
180    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
181        match self {
182            Self::Open(_) => write!(f, "failed to open /proc/<pid>/mem file"),
183            Self::Read(_) => write!(f, "failed to read /proc/<pid>/mem file"),
184        }
185    }
186}
187
188impl StdError for FileStrategyError {
189    fn source(&self) -> Option<&(dyn StdError + 'static)> {
190        match self {
191            Self::Open(e) => Some(e),
192            Self::Read(e) => Some(e),
193        }
194    }
195}
196
197#[derive(Debug)]
198#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
199pub(crate) struct OpenFailed(pub(crate) c_int);
200
201impl Display for OpenFailed {
202    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
203        write!(f, "open64() returned an error code: {}", self.0)
204    }
205}
206
207impl StdError for OpenFailed {}
208
209#[derive(Debug)]
210#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
211pub(crate) enum ReadExactAtError {
212    ReadAt(ReadAtFailed),
213    AddressOverflow,
214    UnexpectedEof { position: usize },
215}
216
217impl Display for ReadExactAtError {
218    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
219        match self {
220            Self::ReadAt(_) => write!(f, "I/O error reading file"),
221            Self::AddressOverflow => write!(
222                f,
223                "the given address/length pair overflowed the machine's memory range"
224            ),
225            Self::UnexpectedEof { position } => write!(
226                f,
227                "unexpected end-of-file encountered at position: {position}"
228            ),
229        }
230    }
231}
232
233impl StdError for ReadExactAtError {
234    fn source(&self) -> Option<&(dyn StdError + 'static)> {
235        match self {
236            Self::ReadAt(e) => Some(e),
237            Self::AddressOverflow => None,
238            Self::UnexpectedEof { .. } => None,
239        }
240    }
241}
242
243#[derive(Debug)]
244#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
245pub(crate) enum ReadAtFailed {
246    Syscall(c_int),
247    AddressOutOfBounds,
248}
249
250impl Display for ReadAtFailed {
251    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
252        match self {
253            Self::Syscall(errno) => write!(f, "pread64() returned an error code: {errno}"),
254            Self::AddressOutOfBounds => {
255                write!(f, "requested address was out-of-bounds for pread64()")
256            }
257        }
258    }
259}
260
261impl StdError for ReadAtFailed {}
262
263#[derive(Debug)]
264#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
265pub(crate) enum PtraceError {
266    Syscall { errno: c_int, position: usize },
267    AddressOverflow,
268}
269
270impl Display for PtraceError {
271    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
272        match self {
273            Self::Syscall { errno, position } => write!(
274                f,
275                "ptrace(PTRACE_PEEKDATA) returned an error code at position {position}: {errno}",
276            ),
277            Self::AddressOverflow => write!(
278                f,
279                "the given address/length pair overflowed the machine's memory range"
280            ),
281        }
282    }
283}
284
285impl StdError for PtraceError {}