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}