Skip to main content

syd/unshare/
error.rs

1use std::{fmt, io};
2
3use nix::errno::Errno;
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq)]
6pub enum ErrorCode {
7    Exec = 1,
8    CapSet = 2,
9    ParentDeathSignal = 3,
10    PreExec = 4,
11    ProcessStop = 5,
12    ResetSignal = 6,
13    SetResourceLimits = 7,
14    LandlockFilterScopedSignals = 8,
15    Seccomp = 9,
16    SeccompFilterIoctl = 10,
17    SeccompFilterAppendOnly = 11,
18    SeccompFilterKptr = 12,
19    SeccompSendFd = 13,
20    SeccompWaitFd = 14,
21    SetDumpable = 15,
22    SetSid = 16,
23    SetPty = 17,
24    DupPty = 18,
25    SetPgrp = 19,
26    SetTSC = 20,
27}
28
29/// Error running process
30///
31/// This type has very large number of options and it's enum only to be
32/// compact. Probably you shouldn't match on the error cases but just format
33/// it for user into string.
34#[derive(Debug)]
35pub enum Error {
36    /// Unknown nix error
37    ///
38    /// Frankly, this error should not happen when running process. We just
39    /// keep it here in case `nix` returns this error, which should not happen.
40    NixError(i32), // Not sure it's possible, but it is here to convert from
41    // nix::Error safer
42    /// Some invalid error code received from child application
43    UnknownError,
44    /// Error when calling capset syscall
45    CapSet(i32),
46    /// Error when running execve() systemcall
47    Exec(i32),
48    /// Unable to set death signal (probably signal number invalid)
49    ParentDeathSignal(i32),
50    /// Before unfreeze callback error
51    BeforeUnfreeze(Box<dyn ::std::error::Error + Send + Sync + 'static>),
52    /// Before exec callback error
53    PreExec(i32),
54    /// Error stopping process
55    ProcessStop(i32),
56    /// Error resetting signals
57    ResetSignal(i32),
58    /// Error setting resource limits
59    SetResourceLimits(i32),
60    /// Error setting scoped signals using landlock(7)
61    LandlockFilterScopedSignals(i32),
62    /// Seccomp error (loading filter, getting notify fd)
63    Seccomp(i32),
64    /// Error filtering ioctl(2) requests with seccomp
65    SeccompFilterIoctl(i32),
66    /// Error filtering pwritev2(2) requests with seccomp
67    SeccompFilterAppendOnly(i32),
68    /// Error filtering kernel pointers in syscall arguments with seccomp
69    SeccompFilterKptr(i32),
70    /// Error sending notification fd through the seccomp sender channel
71    SeccompSendFd(i32),
72    /// Error waiting for parent to receive the seccomp fd
73    SeccompWaitFd(i32),
74    /// Error calling prctl(PR_SET_DUMPABLE)
75    SetDumpable(i32),
76    /// Error calling setsid(2)
77    SetSid(i32),
78    /// Error calling TIOCSCTTY ioctl(2)
79    SetPty(i32),
80    /// Error calling dup(2) on PTY fd
81    DupPty(i32),
82    /// Error calling tcsetpgrp(3)
83    SetPgrp(i32),
84    /// Error calling prctl PR_SET_TSC
85    SetTSC(i32),
86}
87
88impl std::error::Error for Error {}
89
90impl Error {
91    /// Similarly to `io::Error` returns bare error code
92    pub fn raw_os_error(&self) -> Option<i32> {
93        use self::Error::*;
94        match *self {
95            UnknownError => None,
96            NixError(x) => Some(x),
97            CapSet(x) => Some(x),
98            Exec(x) => Some(x),
99            ParentDeathSignal(x) => Some(x),
100            BeforeUnfreeze(..) => None,
101            PreExec(x) => Some(x),
102            ProcessStop(x) => Some(x),
103            ResetSignal(x) => Some(x),
104            SetResourceLimits(x) => Some(x),
105            LandlockFilterScopedSignals(x) => Some(x),
106            Seccomp(x) => Some(x),
107            SeccompFilterIoctl(x) => Some(x),
108            SeccompFilterAppendOnly(x) => Some(x),
109            SeccompFilterKptr(x) => Some(x),
110            SeccompSendFd(x) => Some(x),
111            SeccompWaitFd(x) => Some(x),
112            SetDumpable(x) => Some(x),
113            SetSid(x) => Some(x),
114            SetPty(x) => Some(x),
115            DupPty(x) => Some(x),
116            SetPgrp(x) => Some(x),
117            SetTSC(x) => Some(x),
118        }
119    }
120}
121
122impl Error {
123    fn title(&self) -> &'static str {
124        use self::Error::*;
125        match *self {
126            UnknownError => "unexpected value received via signal pipe",
127            NixError(_) => "some unknown nix error",
128            CapSet(_) => "error when setting capabilities",
129            Exec(_) => "error when executing",
130            ParentDeathSignal(_) => "error when death signal",
131            BeforeUnfreeze(_) => "error in before_unfreeze callback",
132            PreExec(_) => "error in pre_exec callback",
133            ProcessStop(_) => "error stopping process",
134            ResetSignal(_) => "error resetting signals",
135            SetResourceLimits(_) => "error setting resource limits",
136            LandlockFilterScopedSignals(_) => "error scoping signals with landlock",
137            Seccomp(_) => "error in seccomp filter load",
138            SeccompFilterIoctl(_) => "error filtering ioctl requests with seccomp",
139            SeccompFilterAppendOnly(_) => "error filtering pwritev2 requests with seccomp",
140            SeccompFilterKptr(_) => {
141                "error filtering kernel pointers in syscall arguments with seccomp"
142            }
143            SeccompSendFd(_) => "error sending seccomp file descriptor",
144            SeccompWaitFd(_) => "error waiting for parent to receive the seccomp file descriptor",
145            SetDumpable(_) => "error resetting process dumpable attribute",
146            SetSid(_) => "error calling setsid",
147            SetPty(_) => "error setting pty as controlling terminal",
148            DupPty(_) => "error duplicating pty onto stdio fds",
149            SetPgrp(_) => "error setting foreground process group",
150            SetTSC(_) => "error setting timestamp counter prctl",
151        }
152    }
153}
154
155impl fmt::Display for Error {
156    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
157        use crate::unshare::Error::*;
158        if let Some(code) = self.raw_os_error() {
159            let errno = Errno::from_raw(code);
160            if let nix::errno::Errno::UnknownErrno = errno {
161                // May be OS knows error name better
162                write!(
163                    fmt,
164                    "{}: {}",
165                    self.title(),
166                    io::Error::from_raw_os_error(code)
167                )
168            } else {
169                // Format similar to that of std::io::Error
170                write!(
171                    fmt,
172                    "{}: {} (os error {})",
173                    self.title(),
174                    errno.desc(),
175                    code
176                )
177            }
178        } else {
179            match self {
180                BeforeUnfreeze(err) => {
181                    write!(fmt, "{}: {}", self.title(), err)
182                }
183                _ => write!(fmt, "{}", self.title()),
184            }
185        }
186    }
187}