Skip to main content

syd/
err.rs

1//
2// Syd: rock-solid application kernel
3// src/err.rs: Error types and error handling code
4//
5// Copyright (c) 2024, 2025, 2026 Ali Polatel <alip@chesswob.org>
6//
7// SPDX-License-Identifier: GPL-3.0
8
9// SAFETY: This module has been liberated from unsafe code!
10#![forbid(unsafe_code)]
11
12use std::{
13    array::TryFromSliceError,
14    env::VarError,
15    error::Error,
16    fmt, io,
17    net::AddrParseError,
18    num::{ParseIntError, TryFromIntError},
19    str::Utf8Error,
20    thread::JoinHandle,
21};
22
23use btoi::ParseIntegerError;
24#[cfg(feature = "oci")]
25use libcgroups::common::AnyManagerError;
26#[cfg(feature = "oci")]
27use libcgroups::common::CreateCgroupSetupError;
28#[cfg(feature = "oci")]
29use libcontainer::error::LibcontainerError;
30#[cfg(feature = "oci")]
31use libcontainer::signal::SignalError;
32#[cfg(feature = "oci")]
33use libcontainer::utils::PathBufExtError;
34use libseccomp::error::SeccompError;
35use nix::errno::Errno;
36use procfs_core::ProcError;
37use shellexpand::LookupError;
38#[cfg(feature = "oci")]
39use tracing::subscriber::SetGlobalDefaultError;
40
41use crate::{caps::errors::CapsError, elf::ElfError, landlock::RulesetError, sandbox::Capability};
42
43/// Convenience type to use for functions returning a SydError.
44pub type SydResult<T> = std::result::Result<T, SydError>;
45
46/// Convenience type to use for join handlers returning a SydError.
47pub type SydJoinHandle<T> = JoinHandle<SydResult<T>>;
48
49/// A macro to create a SydError from the last errno.
50#[macro_export]
51macro_rules! lasterrno {
52    () => {
53        SydError::Nix(nix::errno::Errno::last())
54    };
55}
56
57/// Enum representing different types of errors
58pub enum SydError {
59    /// This error represents a network address parse error.
60    Addr(AddrParseError),
61    /// This error type represents a lexopt error.
62    Args(lexopt::Error),
63    /// This error type represents a capability error.
64    Caps(CapsError),
65    /// This error type represents an ELF parse error.
66    Elf(ElfError),
67    /// This error type represents an error in environment variable lookup in configuration.
68    Env(LookupError<VarError>),
69    /// This error type represents an `Errno`.
70    Nix(Errno),
71    /// This error type represents JSON errors.
72    Json(serde_json::Error),
73    /// This error type represents integer parse errors.
74    ParseInt(ParseIntError),
75    /// This error type represents integer parse errors.
76    ParseInteger(ParseIntegerError),
77    /// This error type represents integer parse errors.
78    TryInt(TryFromIntError),
79    /// This error type represents a slice conversion error.
80    TrySlice(TryFromSliceError),
81    /// This error type represents size parse errors.
82    ParseSize(parse_size::Error),
83    /// This error type represents shell parse errors.
84    ParseShell(shell_words::ParseError),
85    /// This error type represents a /proc filesystem error.
86    Proc(ProcError),
87    /// This error type represents Seccomp errors.
88    Scmp(SeccompError),
89    /// This error type represents UTF-8 errors.
90    Utf8(Utf8Error),
91    /// This error type represents an environment variable error.
92    Var(VarError),
93    #[cfg(feature = "oci")]
94    /// This error type represents a Cgroup setup error.
95    CgSetup(CreateCgroupSetupError),
96    #[cfg(feature = "oci")]
97    /// This error type represents a miscellaneous Cgroup error.
98    CgMisc(AnyManagerError),
99    #[cfg(feature = "oci")]
100    /// This error type represents container errors.
101    Cont(LibcontainerError),
102    #[cfg(feature = "oci")]
103    /// This error type represents a path canonicalization error.
104    Pext(PathBufExtError),
105    #[cfg(feature = "oci")]
106    /// This error type represents a tracing subscriber error.
107    SetTracing(SetGlobalDefaultError),
108    #[cfg(feature = "oci")]
109    /// This error type represents a signal parsing error.
110    Signal(SignalError<String>),
111    #[cfg(feature = "oci")]
112    /// This error type represents OCI-Spec errors.
113    Spec(oci_spec::OciSpecError),
114}
115
116impl SydError {
117    /// Converts a SydError to an Errno if applicable.
118    pub fn errno(&self) -> Option<Errno> {
119        match self {
120            Self::Nix(errno) => Some(*errno),
121            Self::Proc(error) => proc_error_to_errno(error),
122            Self::Scmp(error) => scmp2no(error),
123            Self::ParseInt(_)
124            | Self::ParseInteger(_)
125            | Self::ParseSize(_)
126            | Self::ParseShell(_)
127            | Self::Var(_) => Some(Errno::EINVAL),
128            _ => None,
129        }
130    }
131}
132
133impl fmt::Debug for SydError {
134    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
135        match self {
136            Self::Addr(error) => write!(f, "AddrParseError: {error:?}"),
137            Self::Args(error) => write!(f, "ArgsParseError: {error:?}"),
138            Self::Caps(error) => write!(f, "CapsError: {error:?}"),
139            Self::Elf(error) => write!(f, "ElfError: {error:?}"),
140            Self::Env(error) => write!(f, "LookupError<VarError>: {error:?}"),
141            Self::Nix(errno) => write!(f, "LinuxError: {errno:?}"),
142            Self::Json(error) => write!(f, "JsonError: {error:?}"),
143            Self::ParseInt(error) => write!(f, "ParseIntError: {error:?}"),
144            Self::ParseInteger(error) => write!(f, "ParseIntegerError: {error:?}"),
145            Self::Scmp(error) => write!(f, "SeccompError: {error:?}"),
146            Self::TryInt(error) => write!(f, "TryFromIntError: {error:?}"),
147            Self::TrySlice(error) => write!(f, "TryFromSliceError: {error:?}"),
148            Self::ParseSize(error) => write!(f, "ParseSizeError: {error:?}"),
149            Self::ParseShell(error) => write!(f, "ParseShellError: {error:?}"),
150            Self::Proc(error) => write!(f, "ProcError: {error:?}"),
151            Self::Utf8(error) => write!(f, "Utf8Error: {error:?}"),
152            Self::Var(error) => write!(f, "VarError: {error:?}"),
153            #[cfg(feature = "oci")]
154            Self::CgSetup(error) => write!(f, "CgroupSetupError: {error:?}"),
155            #[cfg(feature = "oci")]
156            Self::CgMisc(error) => write!(f, "AnyManagerError: {error:?}"),
157            #[cfg(feature = "oci")]
158            Self::Cont(error) => write!(f, "ContainerError: {error:?}"),
159            #[cfg(feature = "oci")]
160            Self::Pext(error) => write!(f, "PathBufExtError: {error:?}"),
161            #[cfg(feature = "oci")]
162            Self::SetTracing(error) => write!(f, "SetGlobalDefaultError: {error:?}"),
163            #[cfg(feature = "oci")]
164            Self::Signal(error) => write!(f, "SignalError: {error:?}"),
165            #[cfg(feature = "oci")]
166            Self::Spec(error) => write!(f, "OciSpecError: {error:?}"),
167        }
168    }
169}
170
171impl fmt::Display for SydError {
172    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
173        match self {
174            Self::Addr(error) => write!(f, "AddrParseError: {error}"),
175            Self::Args(error) => write!(f, "ArgsParseError: {error}"),
176            Self::Caps(error) => write!(f, "CapsError: {error}"),
177            Self::Elf(error) => write!(f, "ElfError: {error}"),
178            Self::Env(error) => write!(f, "LookupError<VarError>: {error}"),
179            Self::Nix(errno) => write!(f, "LinuxError: {errno}"),
180            Self::Json(error) => write!(f, "JsonError: {error}"),
181            Self::ParseInt(error) => write!(f, "ParseIntError: {error}"),
182            Self::ParseInteger(error) => write!(f, "ParseIntegerError: {error}"),
183            Self::Scmp(error) => write!(f, "SeccompError: {error}"),
184            Self::TryInt(error) => write!(f, "TryFromIntError: {error}"),
185            Self::TrySlice(error) => write!(f, "TryFromSliceError: {error}"),
186            Self::ParseSize(error) => write!(f, "ParseSizeError: {error}"),
187            Self::ParseShell(error) => write!(f, "ParseShellError: {error}"),
188            Self::Proc(error) => write!(f, "ProcError: {error}"),
189            Self::Utf8(error) => write!(f, "Utf8Error: {error}"),
190            Self::Var(error) => write!(f, "VarError: {error}"),
191            #[cfg(feature = "oci")]
192            Self::CgSetup(error) => write!(f, "CgroupSetupError: {error}"),
193            #[cfg(feature = "oci")]
194            Self::CgMisc(error) => write!(f, "AnyManagerError: {error}"),
195            #[cfg(feature = "oci")]
196            Self::Cont(error) => write!(f, "ContainerError: {error}"),
197            #[cfg(feature = "oci")]
198            Self::Pext(error) => write!(f, "PathBufExtError: {error}"),
199            #[cfg(feature = "oci")]
200            Self::SetTracing(error) => write!(f, "SetGlobalDefaultError: {error}"),
201            #[cfg(feature = "oci")]
202            Self::Signal(error) => write!(f, "SignalError: {error}"),
203            #[cfg(feature = "oci")]
204            Self::Spec(error) => write!(f, "OciSpecError: {error}"),
205        }
206    }
207}
208
209impl std::error::Error for SydError {
210    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
211        match self {
212            Self::Addr(error) => Some(error),
213            Self::Args(error) => Some(error),
214            Self::Nix(errno) => Some(errno),
215            Self::ParseInt(error) => Some(error),
216            Self::ParseInteger(error) => Some(error),
217            Self::TryInt(error) => Some(error),
218            Self::TrySlice(error) => Some(error),
219            Self::ParseSize(error) => Some(error),
220            Self::ParseShell(error) => Some(error),
221            Self::Proc(error) => Some(error),
222            Self::Utf8(error) => Some(error),
223            Self::Var(error) => Some(error),
224            #[cfg(feature = "oci")]
225            Self::CgSetup(error) => Some(error),
226            #[cfg(feature = "oci")]
227            Self::CgMisc(error) => Some(error),
228            #[cfg(feature = "oci")]
229            Self::Cont(error) => Some(error),
230            #[cfg(feature = "oci")]
231            Self::Pext(error) => Some(error),
232            #[cfg(feature = "oci")]
233            Self::SetTracing(error) => Some(error),
234            #[cfg(feature = "oci")]
235            Self::Signal(error) => Some(error),
236            #[cfg(feature = "oci")]
237            Self::Spec(error) => Some(error),
238            _ => None,
239        }
240    }
241}
242
243// Conversions from std::io::Error to SydError.
244impl From<io::Error> for SydError {
245    fn from(err: io::Error) -> SydError {
246        SydError::Nix(err2no(&err))
247    }
248}
249
250// Conversions from SydError to std::io::Error.
251impl From<SydError> for io::Error {
252    fn from(err: SydError) -> io::Error {
253        match err.errno() {
254            Some(errno) => io::Error::from_raw_os_error(errno as i32),
255            None => io::Error::other(err),
256        }
257    }
258}
259
260// Conversions from AddrParseError to SydError.
261impl From<AddrParseError> for SydError {
262    fn from(err: AddrParseError) -> SydError {
263        SydError::Addr(err)
264    }
265}
266
267// Conversions from Utf8Error to SydError.
268impl From<Utf8Error> for SydError {
269    fn from(err: Utf8Error) -> SydError {
270        Self::Utf8(err)
271    }
272}
273
274// Conversions from ProcError to SydError.
275impl From<ProcError> for SydError {
276    fn from(err: ProcError) -> SydError {
277        Self::Proc(err)
278    }
279}
280
281// Conversions from lexopt::Error to SydError.
282impl From<lexopt::Error> for SydError {
283    fn from(err: lexopt::Error) -> SydError {
284        Self::Args(err)
285    }
286}
287
288// Conversions from CapsError to SydError.
289impl From<CapsError> for SydError {
290    fn from(err: CapsError) -> SydError {
291        Self::Caps(err)
292    }
293}
294
295// Conversions from ElfError to SydError.
296impl From<ElfError> for SydError {
297    fn from(err: ElfError) -> SydError {
298        Self::Elf(err)
299    }
300}
301
302// Conversions from LookupError<VarError> to SydError.
303impl From<LookupError<VarError>> for SydError {
304    fn from(err: LookupError<VarError>) -> SydError {
305        Self::Env(err)
306    }
307}
308
309// Conversions from VarError to SydError.
310impl From<VarError> for SydError {
311    fn from(err: VarError) -> SydError {
312        Self::Var(err)
313    }
314}
315
316// Conversions from nix::errno::Errno to SydError.
317impl From<Errno> for SydError {
318    fn from(err: Errno) -> SydError {
319        Self::Nix(err)
320    }
321}
322
323// Conversions from serde_json::Error to SydError.
324impl From<serde_json::Error> for SydError {
325    fn from(err: serde_json::Error) -> SydError {
326        Self::Json(err)
327    }
328}
329
330// Conversions from AnyManagerError to SydError.
331#[cfg(feature = "oci")]
332impl From<AnyManagerError> for SydError {
333    fn from(err: AnyManagerError) -> SydError {
334        Self::CgMisc(err)
335    }
336}
337
338// Conversions from CreateCgroupSetupError to SydError.
339#[cfg(feature = "oci")]
340impl From<CreateCgroupSetupError> for SydError {
341    fn from(err: CreateCgroupSetupError) -> SydError {
342        Self::CgSetup(err)
343    }
344}
345
346// Conversions from LibcontainerError to SydError.
347#[cfg(feature = "oci")]
348impl From<LibcontainerError> for SydError {
349    fn from(err: LibcontainerError) -> SydError {
350        Self::Cont(err)
351    }
352}
353
354// Conversions from PathBufExtError to SydError.
355#[cfg(feature = "oci")]
356impl From<PathBufExtError> for SydError {
357    fn from(err: PathBufExtError) -> SydError {
358        Self::Pext(err)
359    }
360}
361
362// Conversions from SetGlobalDefaultError to SydError.
363#[cfg(feature = "oci")]
364impl From<SetGlobalDefaultError> for SydError {
365    fn from(err: SetGlobalDefaultError) -> SydError {
366        Self::SetTracing(err)
367    }
368}
369
370// Conversions from SignalError<String> to SydError.
371#[cfg(feature = "oci")]
372impl From<SignalError<String>> for SydError {
373    fn from(err: SignalError<String>) -> SydError {
374        Self::Signal(err)
375    }
376}
377
378// Conversions from OciSpecError to SydError.
379#[cfg(feature = "oci")]
380impl From<oci_spec::OciSpecError> for SydError {
381    fn from(err: oci_spec::OciSpecError) -> SydError {
382        Self::Spec(err)
383    }
384}
385
386// Conversions from ParseIntError to SydError.
387impl From<ParseIntError> for SydError {
388    fn from(err: ParseIntError) -> SydError {
389        Self::ParseInt(err)
390    }
391}
392
393// Conversions from ParseIntegerError to SydError.
394impl From<ParseIntegerError> for SydError {
395    fn from(err: ParseIntegerError) -> SydError {
396        Self::ParseInteger(err)
397    }
398}
399
400// Conversions from TryFromIntError to SydError.
401impl From<TryFromIntError> for SydError {
402    fn from(err: TryFromIntError) -> SydError {
403        Self::TryInt(err)
404    }
405}
406
407// Conversions from TryFromSliceError to SydError.
408impl From<TryFromSliceError> for SydError {
409    fn from(err: TryFromSliceError) -> SydError {
410        Self::TrySlice(err)
411    }
412}
413
414// Conversions from parse_size::Error to SydError.
415impl From<parse_size::Error> for SydError {
416    fn from(err: parse_size::Error) -> SydError {
417        Self::ParseSize(err)
418    }
419}
420
421// Conversions from shell_words::ParseError to SydError.
422impl From<shell_words::ParseError> for SydError {
423    fn from(err: shell_words::ParseError) -> SydError {
424        Self::ParseShell(err)
425    }
426}
427
428// Conversions from SeccompError to SydError.
429impl From<SeccompError> for SydError {
430    fn from(err: SeccompError) -> SydError {
431        Self::Scmp(err)
432    }
433}
434
435/// Convert a std::io::Error into a nix::Errno.
436pub fn err2no(err: &std::io::Error) -> Errno {
437    err.raw_os_error()
438        .map(Errno::from_raw)
439        .unwrap_or(Errno::ENOSYS)
440}
441
442/// Convert a RulesetError into a nix::Errno.
443pub fn err2set(err: &RulesetError) -> Errno {
444    err.source()
445        .and_then(|s| s.downcast_ref::<std::io::Error>())
446        .map(err2no)
447        .unwrap_or(Errno::EINVAL)
448}
449
450/// Convert SeccompError to a nix::Errno.
451pub fn scmp2no(err: &SeccompError) -> Option<Errno> {
452    err.sysrawrc().map(|errno| errno.abs()).map(Errno::from_raw)
453}
454
455/// Convert proc errno to nix errno.
456pub fn proc_error_to_errno(error: &ProcError) -> Option<Errno> {
457    match error {
458        ProcError::PermissionDenied(_) => Some(Errno::EACCES),
459        ProcError::NotFound(_) => Some(Errno::ESRCH),
460        ProcError::Io(error, _) => Some(err2no(error)),
461        ProcError::Other(_) => None,
462        ProcError::Incomplete(_) => None,
463        ProcError::InternalError(_) => None,
464    }
465}
466
467/// Convert sandbox Capability to nix::Errno.
468pub fn cap2no(cap: Capability) -> Errno {
469    if cap.intersects(Capability::CAP_WALK | Capability::CAP_STAT) {
470        Errno::ENOENT
471    } else {
472        Errno::EACCES
473    }
474}