secmem_proc/
error.rs

1//! Module containing the error structures used in the crate.
2
3/// Legacy error handling for macos, where we cannot (yet) use a `rustix` API.
4#[cfg(target_os = "macos")]
5mod sys_err {
6    #[cfg(not(feature = "std"))]
7    mod internal {
8        use core::fmt;
9
10        #[derive(Debug)]
11        pub(crate) struct SysErr;
12
13        impl fmt::Display for SysErr {
14            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
15                f.write_str("system error")
16            }
17        }
18
19        impl SysErr {
20            pub fn create() -> Self {
21                Self
22            }
23
24            pub fn create_anyhow() -> anyhow::Error {
25                anyhow::anyhow!(Self::create())
26            }
27        }
28    }
29
30    #[cfg(feature = "std")]
31    mod internal {
32        use core::fmt;
33
34        #[derive(Debug, thiserror::Error)]
35        pub(crate) struct SysErr(std::io::Error);
36
37        impl fmt::Display for SysErr {
38            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
39                write!(f, "system error: {}", self.0)
40            }
41        }
42
43        impl SysErr {
44            pub fn create() -> Self {
45                Self(std::io::Error::last_os_error())
46            }
47
48            pub fn create_anyhow() -> anyhow::Error {
49                anyhow::anyhow!(Self::create())
50            }
51        }
52    }
53
54    pub(crate) use internal::SysErr;
55}
56
57#[cfg(target_os = "macos")]
58pub(crate) use sys_err::SysErr;
59
60/// Private error types.
61pub(crate) mod private {
62    use core::fmt;
63
64    /// Error indicating that the global allocator returned a zero pointer,
65    /// possibly due to OOM.
66    #[derive(Debug, Clone)]
67    #[cfg_attr(feature = "std", derive(thiserror::Error))]
68    pub(crate) struct AllocError(core::alloc::Layout);
69
70    impl fmt::Display for AllocError {
71        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
72            f.write_str("allocation error, possibly OOM")
73        }
74    }
75
76    impl AllocError {
77        /// Create a new alloc error from a layout.
78        #[must_use]
79        pub(crate) fn new(layout: core::alloc::Layout) -> Self {
80            Self(layout)
81        }
82    }
83
84    pub(crate) fn alloc_err_from_size_align(size: usize, align: usize) -> anyhow::Error {
85        let layout = core::alloc::Layout::from_size_align(size, align);
86        match layout {
87            Ok(layout) => anyhow::anyhow!(AllocError::new(layout)),
88            Err(layout_err) => anyhow::anyhow!(layout_err),
89        }
90    }
91
92    pub(crate) trait ResultExt {
93        type T;
94        fn map_anyhow(self) -> anyhow::Result<Self::T>;
95    }
96
97    impl<T, E: Send + Sync + fmt::Debug + fmt::Display + 'static> ResultExt
98        for core::result::Result<T, E>
99    {
100        type T = T;
101
102        fn map_anyhow(self) -> anyhow::Result<Self::T> {
103            self.map_err(|e| anyhow::anyhow!(e))
104        }
105    }
106}
107
108// Public error types
109
110/// The result type used throughout the public API of this crate.
111pub type Result = core::result::Result<(), Error>;
112
113/// Error that occurred during hardening.
114///
115/// Either an internal error occurred (`Err` variant), or a debugger was
116/// detected (`BeingTraced` variant). This type implements
117/// [`Display`](core::fmt::Display) in a way that clearly distinguishes these
118/// cases, and prints more information about the detected debugger/tracer if
119/// available.
120#[must_use]
121#[cfg_attr(feature = "std", derive(thiserror::Error))]
122#[derive(Debug)]
123pub enum Error {
124    /// A debugger was detected. The [`Traced`] typed field might contain more
125    /// information about the debugger/tracer.
126    BeingTraced(Traced),
127    /// An internal error occurred. Contains an [`anyhow::Error`] with the
128    /// internal error.
129    Err(anyhow::Error),
130}
131
132/// A structure potentially containing more information about a detected
133/// debugger/tracer.
134#[derive(Debug, Clone)]
135pub struct Traced {
136    #[cfg(unix)]
137    pid: Option<rustix::process::Pid>,
138}
139
140#[cfg(unix)]
141impl Traced {
142    pub(crate) fn from_pid(pid: rustix::process::Pid) -> Self {
143        Self { pid: Some(pid) }
144    }
145}
146
147#[cfg(not(unix))]
148impl Traced {
149    pub(crate) const DEFAULT: Self = Self {};
150}
151
152impl core::fmt::Display for Traced {
153    #[cfg(unix)]
154    fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
155        match self.pid {
156            Some(pid) => write!(
157                formatter,
158                "program is being traced by the process with pid {}",
159                pid.as_raw_nonzero()
160            ),
161            None => formatter.write_str("program is being traced"),
162        }
163    }
164
165    #[cfg(not(unix))]
166    fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
167        formatter.write_str("program is being traced")
168    }
169}
170
171impl core::fmt::Display for Error {
172    fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
173        match self {
174            Self::BeingTraced(tr) => tr.fmt(formatter),
175            Self::Err(e) => e.fmt(formatter),
176        }
177    }
178}
179
180impl From<anyhow::Error> for Error {
181    fn from(err: anyhow::Error) -> Self {
182        Error::Err(err)
183    }
184}
185
186pub(crate) trait ResultExt {
187    fn create_ok() -> Self;
188    fn create_being_traced(traced: Traced) -> Self;
189    fn create_err(e: anyhow::Error) -> Self;
190}
191
192impl ResultExt for Result {
193    fn create_ok() -> Self {
194        Ok(())
195    }
196
197    fn create_being_traced(traced: Traced) -> Self {
198        Err(Error::BeingTraced(traced))
199    }
200
201    fn create_err(e: anyhow::Error) -> Self {
202        Err(Error::Err(e))
203    }
204}