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