extrasafe_multiarch/builtins/
danger_zone.rs

1//! Contains a [`RuleSet`] for allowing syscalls that may be dangerous.
2
3use std::collections::{HashMap, HashSet};
4
5use crate::syscalls::Sysno;
6
7use crate::{SeccompRule, RuleSet};
8
9use super::YesReally;
10
11// const CLONE_PARENT: u64 = libc::CLONE_PARENT as u64;
12// const CLONE_THREAD: u64 = libc::CLONE_THREAD as u64;
13
14/// Allows `clone` and `sleep` syscalls, which allow creating new threads and processes, and
15/// pausing them.
16///
17/// # Security
18/// This is in the danger zone not because it's dangerous but because it can be misused: Threads
19/// do not provide isolation from each other. You can still access other threads' memory and
20/// potentially get them to do operations that are not allowed in the current thread's seccomp
21/// context.
22#[must_use]
23pub struct Threads {
24    allowed: HashSet<Sysno>,
25}
26
27impl Threads {
28    /// Create a new [`Threads`] ruleset with nothing allowed by default.
29    pub fn nothing() -> Threads {
30        Threads {
31            allowed: HashSet::new(),
32        }
33    }
34
35    /// Allow creating new threads and processes.
36    pub fn allow_create(mut self) -> Threads {
37        self.allowed.extend([Sysno::clone, Sysno::clone3]);
38
39        self
40    }
41
42    /// Allow sleeping on the current thread
43    ///
44    /// # Security considerations
45    /// An attacker with arbitrary code execution and access to a high resolution timer can mount
46    /// timing attacks (e.g. spectre).
47    pub fn allow_sleep(mut self) -> YesReally<Threads> {
48        self.allowed
49            .extend([Sysno::clock_nanosleep, Sysno::nanosleep]);
50
51        YesReally::new(self)
52    }
53}
54
55impl RuleSet for Threads {
56    fn simple_rules(&self) -> Vec<Sysno> {
57        self.allowed.iter().copied().collect()
58    }
59
60    fn conditional_rules(&self) -> HashMap<Sysno, Vec<SeccompRule>> {
61        // let mut rules = HashMap::new();
62
63        // let clone = SeccompRule::new(Sysno::clone)
64        //     .and_condition(seccomp_arg_filter!(arg2 & CLONE_THREAD == CLONE_THREAD));
65        // rules.entry(Sysno::clone)
66        //     .or_insert_with(Vec::new)
67        //     .push(clone);
68
69        // let clone3 = SeccompRule::new(Sysno::clone3)
70        //     .and_condition(seccomp_arg_filter!(arg2 & CLONE_THREAD == CLONE_THREAD));
71        // rules.entry(Sysno::clone3)
72        //     .or_insert_with(Vec::new)
73        //     .push(clone3);
74
75        // rules
76        HashMap::new()
77    }
78
79    fn name(&self) -> &'static str {
80        "Threads"
81    }
82}
83
84/// [`ForkAndExec`] is in the danger zone because it can be used to start another process,
85/// including more privileged ones. That process will still be under seccomp's restrictions (see
86/// `tests/inherit_filters.rs`) but depending on your filter it could still do bad things.
87///
88/// Note that this also allows the `clone` syscall.
89#[must_use]
90pub struct ForkAndExec;
91impl RuleSet for ForkAndExec {
92    fn simple_rules(&self) -> Vec<Sysno> {
93        let mut rules = vec![
94            #[cfg(enabled_arch = "x86_64")]
95            Sysno::fork,
96            #[cfg(enabled_arch = "x86_64")]
97            Sysno::vfork,
98            Sysno::execve, Sysno::execveat,
99            Sysno::wait4, Sysno::waitid,
100            Sysno::clone, Sysno::clone3,
101        ];
102
103        // musl creates a pipe when it starts a new process, and fails the operation if it can't
104        // create the pipe
105        if cfg!(target_env = "musl") {
106            rules.extend([
107                #[cfg(enabled_arch = "x86_64")]
108                Sysno::pipe,
109                Sysno::pipe2,
110            ]);
111        }
112
113        rules
114    }
115
116    fn conditional_rules(&self) -> HashMap<Sysno, Vec<SeccompRule>> {
117        // TODO: figure out if there's something reasonable we can do with this. same as with
118        // Threads
119        // let mut custom = HashMap::new();
120        //
121        // let clone = SeccompRule::new(Sysno::clone)
122        //     .and_condition(seccomp_arg_filter!(arg2 & CLONE_PARENT == CLONE_PARENT));
123        // custom.entry(Sysno::clone)
124        //     .or_insert_with(Vec::new)
125        //     .push(clone);
126
127        // let clone3 = SeccompRule::new(Sysno::clone3)
128        //     .and_condition(seccomp_arg_filter!(arg2 & CLONE_PARENT == CLONE_PARENT));
129        // custom.entry(Sysno::clone3)
130        //     .or_insert_with(Vec::new)
131        //     .push(clone3);
132        //
133        // custom
134        HashMap::new()
135    }
136
137    fn name(&self) -> &'static str {
138        "ForkAndExec"
139    }
140}