tawasuyu

tawasuyu/brahman

Rust 326TOML 123JSON 17Markdown 144606 Definiciones

⎇ d3649bfd1a80153652c2a028315369bb4b184024 crates/shared/ente-incarnate/src/pre_exec.rs Rust · 103 líneas · 3.2 KB Historia Quién tocó qué crudo

feat(shipote): data plane + DAG fan-in/out + stats + lifecycle (fases F-I) 36dac00c8 · sergio ·

1//! Hook declarativo pre-execve para el hijo.
2//!
3//! Las ops corren EN EL HIJO, post-fork/clone, pre-execve. Reglas:
4//! - sólo syscalls async-signal-safe.
5//! - sin allocator (los CStrings ya están construidos por el padre).
6//! - sin Drop con efectos.
7
8use std::ffi::CString;
9
10/// Operaciones declarativas aplicables pre-execve.
11#[derive(Debug, Clone)]
12pub enum ChildPreExec {
13    /// `PR_SET_NO_NEW_PRIVS = 1` — bloquea escaladas futuras
14    /// (suid bits, file caps, AT_SECURE). Recomendado en sandboxes.
15    NoNewPrivs,
16    /// `PR_SET_PDEATHSIG = sig` — el child recibe esta señal cuando su
17    /// padre (PID 1 del namespace, o el que sea) muere. Útil para
18    /// auto-cleanup de procesos huérfanos.
19    ParentDeathSig(i32),
20    /// `PR_SET_DUMPABLE` — controla si el proceso permite core dump.
21    Dumpable(bool),
22    /// `setsid()` — nuevo session/group leader (desconecta del controlling tty).
23    NewSession,
24    /// `chdir(path)` — cambiar working dir. Path pre-allocado.
25    Chdir(CString),
26    /// `umask(mode)` — fijar umask (octal, e.g. 0o022).
27    Umask(libc::mode_t),
28}
29
30/// Setup completo del hijo. Default = sin ops.
31#[derive(Debug, Clone, Default)]
32pub struct ChildSetup {
33    pub ops: Vec<ChildPreExec>,
34}
35
36impl ChildSetup {
37    pub fn new() -> Self {
38        Self::default()
39    }
40
41    pub fn push(&mut self, op: ChildPreExec) -> &mut Self {
42        self.ops.push(op);
43        self
44    }
45
46    pub fn with(mut self, op: ChildPreExec) -> Self {
47        self.ops.push(op);
48        self
49    }
50
51    pub fn is_empty(&self) -> bool {
52        self.ops.is_empty()
53    }
54}
55
56/// Aplica las ops en orden. SAFETY: ejecuta en el hijo, post-fork,
57/// pre-execve. Sólo libc, sin allocator, sin Drop.
58///
59/// En caso de error, retorna el código de exit que el caller usará para
60/// abortar el child (igual semántica que el resto de la closure de clone).
61/// 0 = todo OK.
62pub unsafe fn apply_unchecked(ops: &[ChildPreExec]) -> i32 {
63    for op in ops {
64        match op {
65            ChildPreExec::NoNewPrivs => {
66                // PR_SET_NO_NEW_PRIVS = 38 en Linux.
67                let r = unsafe { libc::prctl(libc::PR_SET_NO_NEW_PRIVS, 1u64, 0u64, 0u64, 0u64) };
68                if r != 0 {
69                    return 110;
70                }
71            }
72            ChildPreExec::ParentDeathSig(sig) => {
73                let r = unsafe { libc::prctl(libc::PR_SET_PDEATHSIG, *sig as u64, 0u64, 0u64, 0u64) };
74                if r != 0 {
75                    return 111;
76                }
77            }
78            ChildPreExec::Dumpable(yes) => {
79                let v: u64 = if *yes { 1 } else { 0 };
80                let r = unsafe { libc::prctl(libc::PR_SET_DUMPABLE, v, 0u64, 0u64, 0u64) };
81                if r != 0 {
82                    return 112;
83                }
84            }
85            ChildPreExec::NewSession => {
86                let r = unsafe { libc::setsid() };
87                if r < 0 {
88                    return 113;
89                }
90            }
91            ChildPreExec::Chdir(path) => {
92                let r = unsafe { libc::chdir(path.as_ptr()) };
93                if r != 0 {
94                    return 114;
95                }
96            }
97            ChildPreExec::Umask(mode) => {
98                unsafe { libc::umask(*mode) };
99            }
100        }
101    }
102    0
103}