Skip to main content

starnix_core/task/
seccomp.rs

1// Copyright 2023 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use crate::mm::MemoryAccessorExt;
6use crate::signals::{SignalDetail, SignalInfo, send_standard_signal};
7use crate::task::{
8    CurrentTask, EventHandler, ExitStatus, Kernel, Task, TaskFlags, WaitCanceler, WaitQueue, Waiter,
9};
10use crate::vfs::buffers::{InputBuffer, OutputBuffer};
11use crate::vfs::{
12    Anon, FdFlags, FdNumber, FileObject, FileObjectState, FileOps, fileops_impl_nonseekable,
13    fileops_impl_noop_sync,
14};
15use bstr::ByteSlice;
16use ebpf::{
17    BPF_ABS, BPF_IND, BPF_LD, BPF_ST, BPF_W, BpfProgramContext, CbpfConfig, EbpfProgram, MemoryId,
18    NoMap, ProgramArgument, Type, bpf_addressing_mode, bpf_class, bpf_size, convert_and_link_cbpf,
19};
20use ebpf_api::SECCOMP_CBPF_CONFIG;
21use linux_uapi::AUDIT_SECCOMP;
22use starnix_logging::{log_warn, track_stub};
23use starnix_sync::{LockDepMutex, SeccompNotifierLock};
24use starnix_syscalls::decls::Syscall;
25use starnix_syscalls::{SyscallArg, SyscallResult};
26use starnix_uapi::errors::Errno;
27use starnix_uapi::open_flags::OpenFlags;
28use starnix_uapi::signals::{SIGKILL, SIGSYS};
29#[cfg(target_arch = "aarch64")]
30use starnix_uapi::user_address::ArchSpecific;
31use starnix_uapi::user_address::{UserAddress, UserRef};
32use starnix_uapi::vfs::FdEvents;
33use starnix_uapi::{
34    __NR_exit, __NR_read, __NR_write, SECCOMP_IOCTL_NOTIF_ADDFD, SECCOMP_IOCTL_NOTIF_ID_VALID,
35    SECCOMP_IOCTL_NOTIF_RECV, SECCOMP_IOCTL_NOTIF_SEND, SECCOMP_MODE_DISABLED, SECCOMP_MODE_FILTER,
36    SECCOMP_MODE_STRICT, SECCOMP_RET_ACTION_FULL, SECCOMP_RET_DATA,
37    SECCOMP_USER_NOTIF_FLAG_CONTINUE, SYS_SECCOMP, errno, errno_from_code, error, seccomp_data,
38    seccomp_notif, seccomp_notif_resp, sock_filter,
39};
40use std::collections::HashMap;
41use std::sync::atomic::{AtomicU8, Ordering};
42use std::sync::{Arc, LazyLock};
43use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout};
44
45#[cfg(target_arch = "aarch64")]
46use starnix_uapi::__NR_clock_getres;
47#[cfg(target_arch = "aarch64")]
48use starnix_uapi::__NR_clock_gettime;
49#[cfg(target_arch = "aarch64")]
50use starnix_uapi::__NR_gettimeofday;
51#[cfg(target_arch = "aarch64")]
52use starnix_uapi::{AUDIT_ARCH_AARCH64, AUDIT_ARCH_ARM};
53
54#[cfg(target_arch = "x86_64")]
55use starnix_uapi::__NR_clock_gettime;
56#[cfg(target_arch = "x86_64")]
57use starnix_uapi::__NR_getcpu;
58#[cfg(target_arch = "x86_64")]
59use starnix_uapi::__NR_gettimeofday;
60#[cfg(target_arch = "x86_64")]
61use starnix_uapi::__NR_time;
62#[cfg(target_arch = "x86_64")]
63use starnix_uapi::AUDIT_ARCH_X86_64;
64
65#[cfg(target_arch = "riscv64")]
66use starnix_uapi::AUDIT_ARCH_RISCV64;
67
68pub struct SeccompFilter {
69    /// The BPF program associated with this filter.
70    program: EbpfProgram<SeccompFilter>,
71
72    /// The unique-to-this-process id of this filter.  SECCOMP_FILTER_FLAG_TSYNC only works if all
73    /// threads in this process have filters that are a prefix of the filters of the thread
74    /// attempting to do the TSYNC. Identical filters attached in separate seccomp calls are treated
75    /// as different from each other for this purpose, so we need a way of distinguishing them.
76    unique_id: u64,
77
78    // Whether to log the results of this filter
79    log: bool,
80
81    /// The notifier associated with this filter, if it was created with SECCOMP_FILTER_FLAG_NEW_LISTENER
82    pub notifier: Option<SeccompNotifierHandle>,
83}
84
85/// The result of running a set of seccomp filters.
86pub struct SeccompFilterResult {
87    /// The action indicated by the seccomp filter with the highest priority result.
88    action: SeccompAction,
89
90    /// The filter that returned the highest priority result, as used by SECCOMP_RET_USER_NOTIF,
91    /// which has to have access to its cookie value
92    filter: Option<Arc<SeccompFilter>>,
93}
94
95impl SeccompFilter {
96    /// Creates a SeccompFilter object from the given sock_filter.  Associates the user-provided
97    /// id with it, which is intended to be unique to this process.
98    pub fn from_cbpf(
99        code: &Vec<sock_filter>,
100        maybe_unique_id: u64,
101        should_log: bool,
102        notifier: Option<SeccompNotifierHandle>,
103    ) -> Result<Self, Errno> {
104        for insn in code {
105            // If an instruction loads from / stores to an absolute address, that address has to be
106            // 32-bit aligned and inside the struct seccomp_data passed in.
107            if (bpf_class(insn) == BPF_LD || bpf_class(insn) == BPF_ST)
108                && (bpf_addressing_mode(insn) == BPF_ABS)
109                && (insn.k & 0x3 != 0 || insn.k as usize >= std::mem::size_of::<seccomp_data>())
110            {
111                return error!(EINVAL);
112            }
113            // Indirect loads (BPF_IND) are strictly forbidden.
114            if (bpf_class(insn) == BPF_LD || bpf_class(insn) == BPF_ST)
115                && bpf_addressing_mode(insn) == BPF_IND
116            {
117                return error!(EINVAL);
118            }
119            // 8 and 16 bits read and write are strictly forbidden.
120            if (bpf_class(insn) == BPF_LD || bpf_class(insn) == BPF_ST) && bpf_size(insn) != BPF_W {
121                return error!(EINVAL);
122            }
123        }
124
125        let program = convert_and_link_cbpf::<SeccompFilter>(code).map_err(|errmsg| {
126            log_warn!("{}", errmsg);
127            errno!(EINVAL)
128        })?;
129
130        Ok(SeccompFilter { program, unique_id: maybe_unique_id, log: should_log, notifier })
131    }
132
133    pub fn run(&self, data: &seccomp_data) -> u32 {
134        self.program.run(&mut (), &SeccompData(*data)) as u32
135    }
136}
137
138// Wrapper for `seccomp_data`. Required in order to implement the `ProgramArgument` trait below.
139#[repr(C)]
140#[derive(Debug, Default, Clone, IntoBytes, FromBytes, KnownLayout, Immutable)]
141pub struct SeccompData(seccomp_data);
142
143impl BpfProgramContext for SeccompFilter {
144    type RunContext<'a> = ();
145    type Packet<'a> = &'a SeccompData;
146    type Map = NoMap;
147    const CBPF_CONFIG: &'static CbpfConfig = &SECCOMP_CBPF_CONFIG;
148}
149
150ebpf::empty_static_helper_set!(SeccompFilter);
151
152static SECCOMP_DATA_TYPE: LazyLock<Type> =
153    LazyLock::new(|| Type::PtrToMemory { id: MemoryId::new(), offset: 0.into(), buffer_size: 0 });
154
155impl ProgramArgument for &'_ SeccompData {
156    fn get_type() -> &'static Type {
157        &*SECCOMP_DATA_TYPE
158    }
159}
160
161const SECCOMP_MAX_INSNS_PER_PATH: u16 = 32768;
162
163/// A list of seccomp filters, intended to be associated with a specific process.
164#[derive(Default)]
165pub struct SeccompFilterContainer {
166    /// List of currently installed seccomp_filters; most recently added is last.
167    pub filters: Vec<Arc<SeccompFilter>>,
168
169    // The total length of the provided seccomp filters, which cannot
170    // exceed SECCOMP_MAX_INSNS_PER_PATH - 4 * the number of filters.  This is stored
171    // instead of computed because we store seccomp filters in an
172    // expanded form, and it is impossible to get the original length.
173    pub provided_instructions: u16,
174}
175
176impl Clone for SeccompFilterContainer {
177    fn clone(&self) -> Self {
178        for filter in &self.filters {
179            if let Some(n) = &filter.notifier {
180                n.lock().add_thread();
181            }
182        }
183        SeccompFilterContainer {
184            filters: self.filters.clone(),
185            provided_instructions: self.provided_instructions,
186        }
187    }
188}
189
190impl Drop for SeccompFilterContainer {
191    fn drop(&mut self) {
192        for filter in &self.filters {
193            if let Some(n) = &filter.notifier {
194                // Notifier needs to send threads a HUP when there is no one left
195                // referencing it.
196                n.lock().remove_thread();
197            }
198        }
199    }
200}
201
202fn make_seccomp_data(
203    #[allow(unused_variables)] current_task: &CurrentTask,
204    syscall: &Syscall,
205    ip: u64,
206) -> seccomp_data {
207    #[cfg(target_arch = "x86_64")]
208    let arch_val = AUDIT_ARCH_X86_64;
209    #[cfg(target_arch = "aarch64")]
210    let arch_val = if current_task.is_arch32() { AUDIT_ARCH_ARM } else { AUDIT_ARCH_AARCH64 };
211    #[cfg(target_arch = "riscv64")]
212    let arch_val = AUDIT_ARCH_RISCV64;
213    seccomp_data {
214        nr: syscall.decl.number as i32,
215        arch: arch_val,
216        instruction_pointer: ip,
217        args: [
218            syscall.arg0.raw(),
219            syscall.arg1.raw(),
220            syscall.arg2.raw(),
221            syscall.arg3.raw(),
222            syscall.arg4.raw(),
223            syscall.arg5.raw(),
224        ],
225    }
226}
227
228impl SeccompFilterContainer {
229    /// Ensures that this set of seccomp filters can be "synced to" the given set.
230    /// This means that our filters are a prefix of the given set of filters.
231    pub fn can_sync_to(&self, source: &SeccompFilterContainer) -> bool {
232        if source.filters.len() < self.filters.len() {
233            return false;
234        }
235        for (filter, other_filter) in self.filters.iter().zip(source.filters.iter()) {
236            if other_filter.unique_id != filter.unique_id {
237                return false;
238            }
239        }
240        true
241    }
242
243    /// Adds the given filter to this list.  The original_length parameter is the length of
244    /// the originally provided BPF (i.e., the number of sock_filter instructions), used
245    /// to ensure the total length does not exceed SECCOMP_MAX_INSNS_PER_PATH
246    pub fn add_filter(
247        &mut self,
248        filter: Arc<SeccompFilter>,
249        original_length: u16,
250    ) -> Result<(), Errno> {
251        let maybe_new_length = self.provided_instructions + original_length + 4;
252        if maybe_new_length > SECCOMP_MAX_INSNS_PER_PATH {
253            return error!(ENOMEM);
254        }
255
256        self.provided_instructions = maybe_new_length;
257        self.filters.push(filter);
258        Ok(())
259    }
260
261    /// Runs all of the seccomp filters in this container, most-to-least recent.  Returns the
262    /// highest priority result (which contains a reference to the filter that generated it)
263    pub fn run_all(&self, current_task: &CurrentTask, syscall: &Syscall) -> SeccompFilterResult {
264        let mut r = SeccompFilterResult { action: SeccompAction::Allow, filter: None };
265
266        // VDSO calls can't be caught by seccomp, so most seccomp filters forget to declare them.
267        // But our VDSO implementation is incomplete, and most of the calls forward to the actual
268        // syscalls. So seccomp should ignore them until they're implemented correctly in the VDSO.
269        #[cfg(target_arch = "x86_64")] // The set of VDSO calls is arch dependent.
270        #[allow(non_upper_case_globals)]
271        if let __NR_clock_gettime | __NR_getcpu | __NR_gettimeofday | __NR_time =
272            syscall.decl.number as u32
273        {
274            return r;
275        }
276        #[cfg(target_arch = "aarch64")]
277        #[allow(non_upper_case_globals)]
278        if let __NR_clock_gettime | __NR_clock_getres | __NR_gettimeofday =
279            syscall.decl.number as u32
280        {
281            return r;
282        }
283
284        let data = make_seccomp_data(
285            current_task,
286            syscall,
287            current_task.thread_state.registers.instruction_pointer_register(),
288        );
289
290        // Filters are executed in reverse order of addition
291        for filter in self.filters.iter().rev() {
292            let new_result = filter.run(&data);
293
294            let action = SeccompAction::from_u32(new_result).unwrap_or(SeccompAction::KillProcess);
295
296            if SeccompAction::has_prio(&action, &r.action) == std::cmp::Ordering::Less {
297                r = SeccompFilterResult { action, filter: Some(filter.clone()) };
298            }
299        }
300        r
301    }
302
303    pub fn create_notifier() -> SeccompNotifierHandle {
304        SeccompNotifier::new()
305    }
306
307    pub fn register_listener(
308        current_task: &CurrentTask,
309        notifier: SeccompNotifierHandle,
310    ) -> Result<FdNumber, Errno> {
311        // Create the `Anon` handle file before taking the write lock on the task, because
312        // `Anon::new_file()` needs to read the `current_task` SID to label the file object.
313        let handle = Anon::new_file(
314            current_task,
315            Box::new(SeccompNotifierFileObject { notifier: notifier.clone() }),
316            OpenFlags::RDWR,
317            "seccomp notify",
318        )?;
319
320        // Take the write lock to check for an existing notifier.
321        let filters = &mut current_task.write().seccomp_filters;
322        if filters.filters.iter().any(|f| f.notifier.is_some()) {
323            return error!(EBUSY);
324        }
325        let fd = current_task.add_file(handle, FdFlags::CLOEXEC)?;
326        {
327            let mut state = notifier.lock();
328            state.add_thread();
329        }
330        Ok(fd)
331    }
332}
333
334/// Possible values for the current status of the seccomp filters for
335/// this process.
336#[repr(u8)]
337#[derive(Clone, Copy, PartialEq)]
338pub enum SeccompStateValue {
339    None = SECCOMP_MODE_DISABLED as u8,
340    Strict = SECCOMP_MODE_STRICT as u8,
341    UserDefined = SECCOMP_MODE_FILTER as u8,
342}
343
344/// Per-process state that cannot be stored in the container (e.g., whether there is a container).
345#[derive(Default)]
346pub struct SeccompState {
347    // This AtomicU8 corresponds to a SeccompStateValue.
348    filter_state: AtomicU8,
349}
350
351impl SeccompState {
352    pub fn from(state: &SeccompState) -> SeccompState {
353        SeccompState { filter_state: AtomicU8::new(state.filter_state.load(Ordering::Acquire)) }
354    }
355
356    fn from_u8(value: u8) -> SeccompStateValue {
357        match value {
358            v if v == SECCOMP_MODE_DISABLED as u8 => SeccompStateValue::None,
359            v if v == SECCOMP_MODE_STRICT as u8 => SeccompStateValue::Strict,
360            v if v == SECCOMP_MODE_FILTER as u8 => SeccompStateValue::UserDefined,
361            _ => unreachable!(),
362        }
363    }
364
365    pub fn get(&self) -> SeccompStateValue {
366        Self::from_u8(self.filter_state.load(Ordering::Acquire))
367    }
368
369    pub fn set(&self, state: &SeccompStateValue) -> Result<(), Errno> {
370        loop {
371            let seccomp_filter_status = self.get();
372            if seccomp_filter_status == *state {
373                return Ok(());
374            }
375            if seccomp_filter_status != SeccompStateValue::None {
376                return error!(EINVAL);
377            }
378
379            if self
380                .filter_state
381                .compare_exchange(
382                    seccomp_filter_status as u8,
383                    *state as u8,
384                    Ordering::Release,
385                    Ordering::Acquire,
386                )
387                .is_ok()
388            {
389                return Ok(());
390            }
391        }
392    }
393
394    /// Check to see if this syscall is allowed in STRICT mode, and, if not,
395    /// send the current task a SIGKILL.
396    pub fn do_strict(task: &Task, syscall: &Syscall) -> Option<Result<SyscallResult, Errno>> {
397        if syscall.decl.number as u32 != __NR_exit
398            && syscall.decl.number as u32 != __NR_read
399            && syscall.decl.number as u32 != __NR_write
400        {
401            send_standard_signal(task, SignalInfo::kernel(SIGKILL));
402            return Some(Err(errno_from_code!(0)));
403        }
404        None
405    }
406
407    // This is supposed to be put in the audit log, but starnix does not yet have an
408    // audit log.  Also, it does not match the Linux format.  Still, the machinery
409    // is in place for when we have to support it for real.
410    fn log_action(task: &CurrentTask, syscall: &Syscall) {
411        let creds = task.current_creds();
412        let (uid, gid) = (creds.uid, creds.gid);
413        let arch = if cfg!(target_arch = "x86_64") {
414            "x86_64"
415        } else if cfg!(target_arch = "aarch64") {
416            "aarch64"
417        } else {
418            "unknown"
419        };
420        task.kernel().audit_logger().audit_log(AUDIT_SECCOMP as u16, || {
421            format!(
422                "uid={} gid={} pid={} comm={} syscall={} ip={} ARCH={} SYSCALL={}",
423                uid,
424                gid,
425                task.thread_group().leader,
426                task.command(),
427                syscall.decl.number,
428                task.thread_state.registers.instruction_pointer_register(),
429                arch,
430                syscall.decl.name(),
431            )
432        });
433    }
434
435    /// Take the given |action| on the given |task|.  The action is one of the SECCOMP_RET values
436    /// (ALLOW, LOG, KILL, KILL_PROCESS, TRAP, ERRNO, USER_NOTIF, TRACE).  |task| is the thread that
437    /// invoked the syscall, and |syscall| is the syscall that was invoked.
438    /// Returns the result that the syscall will be forced to return by this
439    /// filter, or None, if the syscall should return its actual return value.
440    // NB: Allow warning below so that it is clear what we are doing on KILL_PROCESS
441    #[allow(clippy::wildcard_in_or_patterns)]
442    pub fn do_user_defined(
443        result: SeccompFilterResult,
444        current_task: &mut CurrentTask,
445        syscall: &Syscall,
446    ) -> Option<Result<SyscallResult, Errno>> {
447        let action = result.action;
448        if let Some(filter) = result.filter.as_ref() {
449            if action.is_logged(current_task.kernel(), filter.log) {
450                Self::log_action(current_task, syscall);
451            }
452        }
453        match action {
454            SeccompAction::Allow => None,
455            SeccompAction::Errno(code) => Some(Err(errno_from_code!(code as i16))),
456            SeccompAction::KillThread => {
457                let siginfo = SignalInfo::kernel(SIGSYS);
458
459                let is_last_thread = current_task.thread_group().read().tasks_count() == 1;
460                let mut task_state = current_task.write();
461
462                if is_last_thread {
463                    task_state.set_flags(TaskFlags::DUMP_ON_EXIT, true);
464                    task_state.set_exit_status_if_not_already(ExitStatus::CoreDump(siginfo));
465                } else {
466                    task_state.set_exit_status_if_not_already(ExitStatus::Kill(siginfo));
467                }
468                Some(Err(errno_from_code!(0)))
469            }
470            SeccompAction::KillProcess => {
471                current_task.kill_thread_group(ExitStatus::CoreDump(SignalInfo::kernel(SIGSYS)));
472                Some(Err(errno_from_code!(0)))
473            }
474            SeccompAction::Log => {
475                Self::log_action(current_task, syscall);
476                None
477            }
478            SeccompAction::Trace => {
479                track_stub!(TODO("https://fxbug.dev/297311898"), "ptrace seccomp support");
480                Some(error!(ENOSYS))
481            }
482            SeccompAction::Trap(errno) => {
483                #[cfg(target_arch = "x86_64")]
484                let arch_val = AUDIT_ARCH_X86_64;
485                #[cfg(target_arch = "aarch64")]
486                let arch_val =
487                    if current_task.is_arch32() { AUDIT_ARCH_ARM } else { AUDIT_ARCH_AARCH64 };
488                #[cfg(target_arch = "riscv64")]
489                let arch_val = AUDIT_ARCH_RISCV64;
490
491                let siginfo = SignalInfo::new(
492                    SIGSYS,
493                    errno as i32,
494                    SYS_SECCOMP as i32,
495                    SignalDetail::SIGSYS {
496                        call_addr: current_task
497                            .thread_state
498                            .registers
499                            .instruction_pointer_register()
500                            .into(),
501                        syscall: syscall.decl.number as i32,
502                        arch: arch_val,
503                    },
504                    true,
505                    None,
506                );
507
508                send_standard_signal(current_task, siginfo);
509                Some(Err(errno_from_code!(-(syscall.decl.number as i16))))
510            }
511            SeccompAction::UserNotif => {
512                if let Some(notifier) = result.filter.as_ref().and_then(|f| f.notifier.clone()) {
513                    let waiter = Waiter::new();
514                    let cookie;
515                    {
516                        let mut notifier = notifier.lock();
517                        if notifier.is_closed {
518                            return Some(error!(ENOSYS));
519                        }
520                        cookie = notifier.next_cookie();
521                        let msg = seccomp_notif {
522                            id: cookie,
523                            pid: current_task.tid as u32,
524                            flags: 0,
525                            data: make_seccomp_data(
526                                current_task,
527                                syscall,
528                                current_task.thread_state.registers.instruction_pointer_register(),
529                            ),
530                        };
531                        notifier.create_notification(cookie, msg);
532                        notifier.waiters.wait_async_value(&waiter, cookie);
533                    }
534
535                    // Next, wait for a response from the supervisor
536                    if let Err(e) = waiter.wait(current_task) {
537                        return Some(Err(e));
538                    }
539
540                    // Fetch the response.
541                    let resp: Option<seccomp_notif_resp>;
542                    {
543                        let mut notifier = notifier.lock();
544                        resp = notifier.get_response(cookie);
545                        notifier.delete_notification(cookie);
546                    }
547
548                    // The response indicates what you are supposed to do with this syscall.
549                    if let Some(response) = resp {
550                        if response.val != 0 {
551                            return Some(Ok(response.val.into()));
552                        }
553                        if response.error != 0 {
554                            if response.error > 0 {
555                                return Some(Ok(response.error.into()));
556                            } else {
557                                return Some(Err(errno_from_code!(-response.error as i16)));
558                            }
559                        }
560                        if response.flags & SECCOMP_USER_NOTIF_FLAG_CONTINUE != 0 {
561                            return None;
562                        }
563                    }
564                    Some(Ok(0.into()))
565                } else {
566                    Some(error!(ENOSYS))
567                }
568            }
569        }
570    }
571}
572
573#[derive(Clone, Copy, PartialEq)]
574pub enum SeccompAction {
575    Allow,
576    Errno(u32),
577    KillProcess,
578    KillThread,
579    Log,
580    Trap(u32),
581    Trace,
582    UserNotif,
583}
584
585impl SeccompAction {
586    pub fn is_action_available(action: u32) -> Result<SyscallResult, Errno> {
587        if SeccompAction::from_u32(action).is_none() {
588            return error!(EOPNOTSUPP);
589        }
590        Ok(0.into())
591    }
592
593    pub fn from_u32(action: u32) -> Option<SeccompAction> {
594        match action & !SECCOMP_RET_DATA {
595            linux_uapi::SECCOMP_RET_ALLOW => Some(Self::Allow),
596            linux_uapi::SECCOMP_RET_ERRNO => {
597                let mut action = action & SECCOMP_RET_DATA;
598                // Linux kernel compatibility: if errno exceeds 0xfff, it is capped at 0xfff.
599                action = std::cmp::min(action & 0xffff, 0xfff);
600                Some(Self::Errno(action))
601            }
602            linux_uapi::SECCOMP_RET_KILL_PROCESS => Some(Self::KillProcess),
603            linux_uapi::SECCOMP_RET_KILL_THREAD => Some(Self::KillThread),
604            linux_uapi::SECCOMP_RET_LOG => Some(Self::Log),
605            linux_uapi::SECCOMP_RET_TRACE => Some(Self::Trace),
606            linux_uapi::SECCOMP_RET_TRAP => Some(Self::Trap(action & SECCOMP_RET_DATA)),
607
608            linux_uapi::SECCOMP_RET_USER_NOTIF => Some(Self::UserNotif),
609            _ => None,
610        }
611    }
612
613    pub fn to_isize(self) -> isize {
614        match self {
615            Self::Allow => linux_uapi::SECCOMP_RET_ALLOW as isize,
616            Self::Errno(x) => (linux_uapi::SECCOMP_RET_ERRNO | x) as isize,
617            Self::KillProcess => linux_uapi::SECCOMP_RET_KILL_PROCESS as isize,
618            Self::KillThread => linux_uapi::SECCOMP_RET_KILL_THREAD as isize,
619            Self::Log => linux_uapi::SECCOMP_RET_LOG as isize,
620            Self::Trace => linux_uapi::SECCOMP_RET_TRACE as isize,
621            Self::Trap(x) => (linux_uapi::SECCOMP_RET_TRAP | x) as isize,
622            Self::UserNotif => linux_uapi::SECCOMP_RET_USER_NOTIF as isize,
623        }
624    }
625
626    pub fn canonical_name(self) -> &'static str {
627        match self {
628            Self::Allow => &"allow",
629            Self::Errno(_) => &"errno",
630            Self::KillProcess => &"kill_process",
631            Self::KillThread => &"kill_thread",
632            Self::Log => &"log",
633            Self::Trace => &"trace",
634            Self::Trap(_) => &"trap",
635            Self::UserNotif => &"user_notif",
636        }
637    }
638
639    pub fn has_prio(a: &SeccompAction, b: &SeccompAction) -> std::cmp::Ordering {
640        let anum = a.to_isize() as i32;
641        let bnum = b.to_isize() as i32;
642        let fullnum = SECCOMP_RET_ACTION_FULL as i32;
643        let aval = anum & fullnum;
644        let bval = bnum & fullnum;
645        aval.cmp(&bval)
646    }
647
648    /// Returns a vector of all available actions, sorted by priority.
649    pub fn all_actions() -> Vec<SeccompAction> {
650        let mut result = vec![
651            Self::Allow,
652            Self::Errno(0),
653            Self::KillProcess,
654            Self::KillThread,
655            Self::Log,
656            Self::Trace,
657            Self::Trap(0),
658            Self::UserNotif,
659        ];
660
661        result.sort_by(Self::has_prio);
662        result
663    }
664
665    /// Gets the contents of /proc/sys/kernel/seccomp/actions_avail
666    pub fn get_actions_avail_file() -> Vec<u8> {
667        let all_actions = Self::all_actions();
668        if all_actions.len() == 0 {
669            return vec![];
670        }
671        let mut result = String::from(all_actions[0].canonical_name());
672        for i in 1..all_actions.len() {
673            result.push_str(" ");
674            result.push_str(all_actions[i].canonical_name());
675        }
676        result.push('\n');
677        result.into_bytes()
678    }
679
680    fn logged_bit_offset(&self) -> u32 {
681        match self {
682            Self::Allow => 1,
683            Self::Errno(_) => 2,
684            Self::KillProcess => 3,
685            Self::KillThread => 4,
686            Self::Log => 5,
687            Self::Trace => 6,
688            Self::Trap(_) => 7,
689            Self::UserNotif => 8,
690        }
691    }
692
693    fn set_logged_bit(&self, dst: &mut u16) {
694        *dst |= 1 << self.logged_bit_offset();
695    }
696
697    pub fn is_logged(&self, kernel: &Kernel, filter_flag: bool) -> bool {
698        if kernel.actions_logged.load(Ordering::Relaxed) & (1 << self.logged_bit_offset()) != 0 {
699            match self {
700                // Per the documentation on audit logging of seccomp actions in
701                // seccomp(2), just because it is listed as logged, that doesn't
702                // mean we actually log it.
703
704                // If it is KILL_PROCESS or KILL_THREAD, return true
705                Self::KillProcess | Self::KillThread => true,
706                // If it is one of these and the filter flag was set, return true.
707                Self::Errno(_) | Self::Log | Self::Trap(_) | Self::UserNotif => filter_flag,
708                // Never log ALLOW
709                _ => false,
710            }
711        } else {
712            false
713        }
714    }
715
716    pub fn set_actions_logged(kernel: &Kernel, data: &[u8]) -> Result<(), Errno> {
717        let mut new_actions_logged: u16 = 0;
718        for action_res in data.fields_with(|c| c.is_ascii_whitespace()) {
719            if let Ok(action) = action_res.to_str() {
720                match action {
721                    "errno" => Self::Errno(0).set_logged_bit(&mut new_actions_logged),
722                    "kill_process" => Self::KillProcess.set_logged_bit(&mut new_actions_logged),
723                    "kill_thread" => Self::KillThread.set_logged_bit(&mut new_actions_logged),
724                    "log" => Self::Log.set_logged_bit(&mut new_actions_logged),
725                    "trace" => Self::Trace.set_logged_bit(&mut new_actions_logged),
726                    "trap" => Self::Trap(0).set_logged_bit(&mut new_actions_logged),
727                    "user_notif" => Self::UserNotif.set_logged_bit(&mut new_actions_logged),
728                    // Not allowed to write anything other than the approved actions to that list.
729                    _ => return error!(EINVAL),
730                }
731            } else {
732                return error!(EINVAL);
733            }
734        }
735        kernel.actions_logged.store(new_actions_logged, Ordering::Relaxed);
736        Ok(())
737    }
738
739    pub fn get_actions_logged(kernel: &Kernel) -> Vec<u8> {
740        let al = kernel.actions_logged.load(Ordering::Relaxed);
741        let mut result: String = "".to_string();
742        for action in Self::all_actions() {
743            if (al & (1 << action.logged_bit_offset())) != 0 {
744                result.push_str(action.canonical_name());
745                result.push(' ');
746            }
747        }
748        if !result.is_empty() {
749            // remove trailing whitespace.
750            result.pop();
751        }
752
753        result.into_bytes()
754    }
755}
756
757/// This struct contains data that needs to be shuttled back and forth between the thread doing
758/// a USER_NOTIF and the supervisor thread responding to it.
759#[derive(Default)]
760struct SeccompNotification {
761    /// notif is the notification set by the filter.  When this is set, the associated fd will
762    /// be set to POLLIN.
763    notif: seccomp_notif,
764
765    /// Consumed indicates whether a supervisor process has read this notification (and so it
766    /// can no longer be consumed by any other SECCOMP_IOCTL_NOTIF_RECV ioctl).  When the notif
767    /// is consumed, the associated fd will be set to POLLOUT, indicating that it is ready to
768    /// receive a response.
769    consumed: bool,
770
771    /// resp is the response that the supervisor sends.  When this is set, an event will be sent
772    /// to SeccompNotifiers::waiters corresponding to the unique id of the notification.  This
773    /// will wake up the filter that is waiting for this particular response.
774    resp: Option<seccomp_notif_resp>,
775}
776
777impl SeccompNotification {
778    fn new(data: seccomp_notif) -> SeccompNotification {
779        SeccompNotification { notif: data, resp: None, consumed: false }
780    }
781}
782
783/// The underlying implementation of the file descriptor that connects a process that triggers a
784/// SECCOMP_RET_USER_NOTIF with the monitoring process. This support seccomp's ability to notify a
785/// user-space process on specific syscall triggers. See seccomp_unotify(2) for the semantics.
786pub struct SeccompNotifier {
787    waiters: WaitQueue,
788
789    pending_notifications: HashMap<u64, SeccompNotification>,
790
791    // This keeps track of the number of threads using this notifier as a filter.  If that hits
792    // zero, the listeners need to receive a HUP.
793    num_active_threads: u64,
794
795    // notifiers are referenced both by fds and in SeccompFilterContainer. If the file no longer
796    // has fds referring to it, it will be closed, and the SeccompFilterContainers should stop
797    // using it.
798    pub is_closed: bool,
799
800    next_cookie: u64,
801}
802
803pub type SeccompNotifierHandle = Arc<LockDepMutex<SeccompNotifier, SeccompNotifierLock>>;
804
805impl SeccompNotifier {
806    pub fn new() -> SeccompNotifierHandle {
807        Arc::new(
808            SeccompNotifier {
809                waiters: WaitQueue::default(),
810                pending_notifications: HashMap::default(),
811                num_active_threads: 0,
812                is_closed: false,
813                next_cookie: 0,
814            }
815            .into(),
816        )
817    }
818
819    fn next_cookie(&mut self) -> u64 {
820        let cookie = self.next_cookie;
821        self.next_cookie += 1;
822        cookie
823    }
824
825    fn add_thread(&mut self) {
826        self.num_active_threads += 1;
827    }
828
829    fn remove_thread(&mut self) {
830        self.num_active_threads -= 1;
831        if self.num_active_threads == 0 {
832            self.waiters.notify_fd_events(FdEvents::POLLHUP);
833        }
834    }
835
836    // Creates a pending notification for communication between the
837    // target thread and a supervisor, and notifies readers there is
838    // an opportunity to read.
839    fn create_notification(&mut self, cookie: u64, notif: seccomp_notif) {
840        self.pending_notifications.insert(cookie, SeccompNotification::new(notif));
841        self.waiters.notify_fd_events(FdEvents::POLLIN | FdEvents::POLLRDNORM);
842    }
843
844    // Gets a notification that needs to be handled by a supervisor,
845    // and notifies waiters that there is an opportunity to write.
846    fn consume_some_notification(&mut self) -> Option<seccomp_notif> {
847        for (_, notif) in self.pending_notifications.iter_mut() {
848            if !notif.consumed {
849                notif.consumed = true;
850                self.waiters.notify_fd_events(FdEvents::POLLOUT | FdEvents::POLLWRNORM);
851                return Some(notif.notif);
852            }
853        }
854        None
855    }
856
857    // In case something goes wrong after we consume the notification.
858    fn unconsume(&mut self, cookie: u64) {
859        if let Some(n) = self.pending_notifications.get_mut(&cookie).as_mut() {
860            n.consumed = false;
861        }
862    }
863
864    // Returns the appropriate notifications if someone is waiting with poll/epoll/select.
865    fn get_fd_notifications(&self) -> FdEvents {
866        let mut events = FdEvents::empty();
867
868        for (_, notification) in self.pending_notifications.iter() {
869            if !notification.consumed {
870                events |= FdEvents::POLLIN | FdEvents::POLLRDNORM;
871            } else if notification.resp.is_none() {
872                events |= FdEvents::POLLOUT | FdEvents::POLLWRNORM;
873            }
874        }
875
876        if self.num_active_threads == 0 {
877            events |= FdEvents::POLLHUP;
878        }
879        events
880    }
881
882    // Sets the value read by the target in response to this notification.  Intended for use by the
883    // supervisor.  Notifies the filter there is a response to this request.
884    fn set_response(&mut self, cookie: u64, resp: seccomp_notif_resp) -> Option<Errno> {
885        if let Some(entry) = self.pending_notifications.get_mut(&cookie) {
886            if entry.resp.is_some() {
887                return Some(errno!(EINPROGRESS));
888            }
889            entry.resp = Some(resp);
890            self.waiters.notify_value(resp.id);
891            None
892        } else {
893            Some(errno!(EINVAL))
894        }
895    }
896
897    // Gets the value set by the supervisor for the target to read.
898    fn get_response(&self, cookie: u64) -> Option<seccomp_notif_resp> {
899        if let Some(value) = self.pending_notifications.get(&cookie) {
900            return value.resp;
901        }
902        None
903    }
904
905    // Returns whether the cookie represents an active notification.
906    fn notification_pending(&self, cookie: u64) -> bool {
907        self.pending_notifications.contains_key(&cookie)
908    }
909
910    // Deletes the notification, when the target is done processing it.
911    fn delete_notification(&mut self, cookie: u64) {
912        let _ = self.pending_notifications.remove(&cookie);
913    }
914}
915
916struct SeccompNotifierFileObject {
917    notifier: SeccompNotifierHandle,
918}
919
920impl FileOps for SeccompNotifierFileObject {
921    fileops_impl_nonseekable!();
922    fileops_impl_noop_sync!();
923
924    fn close(self: Box<Self>, _file: &FileObjectState, _current_task: &CurrentTask) {
925        let mut state = self.notifier.lock();
926
927        for (cookie, notification) in state.pending_notifications.iter() {
928            if !notification.consumed {
929                state.waiters.notify_value(*cookie);
930                state.waiters.notify_fd_events(FdEvents::POLLIN | FdEvents::POLLRDNORM);
931            } else if notification.resp.is_none() {
932                state.waiters.notify_fd_events(FdEvents::POLLOUT | FdEvents::POLLWRNORM);
933            }
934        }
935        state.waiters.notify_fd_events(FdEvents::POLLHUP);
936
937        state.pending_notifications.clear();
938
939        state.is_closed = true;
940    }
941
942    fn read(
943        &self,
944        _file: &FileObject,
945        _current_task: &CurrentTask,
946        _offset: usize,
947        _usize: &mut dyn OutputBuffer,
948    ) -> Result<usize, Errno> {
949        error!(EINVAL)
950    }
951
952    fn write(
953        &self,
954        _file: &FileObject,
955        _current_task: &CurrentTask,
956        _offset: usize,
957        _buffer: &mut dyn InputBuffer,
958    ) -> Result<usize, Errno> {
959        error!(EINVAL)
960    }
961
962    fn ioctl(
963        &self,
964        _file: &FileObject,
965        current_task: &CurrentTask,
966        request: u32,
967        arg: SyscallArg,
968    ) -> Result<SyscallResult, Errno> {
969        let user_addr = UserAddress::from(arg);
970        match request {
971            SECCOMP_IOCTL_NOTIF_RECV => {
972                if let Ok(notif) =
973                    current_task.read_memory_to_vec(user_addr, std::mem::size_of::<seccomp_notif>())
974                {
975                    for value in notif.iter() {
976                        if *value != 0 {
977                            return error!(EINVAL);
978                        }
979                    }
980                }
981                // A RECV reads a notification, optionally waiting for one to become available.
982                let mut notif: Option<seccomp_notif>;
983                loop {
984                    // Grab a notification or wait for one to become readable.
985                    let waiter = Waiter::new();
986                    {
987                        let mut notifier = self.notifier.lock();
988                        notif = notifier.consume_some_notification();
989                        if notif.is_some() {
990                            break;
991                        }
992                        notifier.waiters.wait_async_fd_events(
993                            &waiter,
994                            FdEvents::POLLIN | FdEvents::POLLHUP,
995                            EventHandler::None,
996                        );
997                    }
998                    waiter.wait(current_task)?;
999                }
1000                if let Some(notif) = notif {
1001                    if let Err(e) =
1002                        current_task.write_object(UserRef::<seccomp_notif>::new(user_addr), &notif)
1003                    {
1004                        self.notifier.lock().unconsume(notif.id);
1005                        return Err(e);
1006                    }
1007                }
1008
1009                Ok(0.into())
1010            }
1011            SECCOMP_IOCTL_NOTIF_SEND => {
1012                // A SEND sends a response to a previously received notification.
1013                let resp: seccomp_notif_resp = current_task.read_object(UserRef::new(user_addr))?;
1014                if resp.flags & !SECCOMP_USER_NOTIF_FLAG_CONTINUE != 0 {
1015                    return error!(EINVAL);
1016                }
1017                if resp.flags & SECCOMP_USER_NOTIF_FLAG_CONTINUE != 0
1018                    && (resp.error != 0 || resp.val != 0)
1019                {
1020                    return error!(EINVAL);
1021                }
1022                {
1023                    let mut notifier = self.notifier.lock();
1024                    if let Some(err) = notifier.set_response(resp.id, resp) {
1025                        return Err(err);
1026                    }
1027                }
1028                Ok(0.into())
1029            }
1030            SECCOMP_IOCTL_NOTIF_ID_VALID => {
1031                // An ID_VALID indicates that the notification is still in progress.
1032                let cookie: u64 = current_task.read_object(UserRef::new(user_addr))?;
1033                {
1034                    let notifier = self.notifier.lock();
1035                    if notifier.notification_pending(cookie) {
1036                        Ok(0.into())
1037                    } else {
1038                        error!(ENOENT)
1039                    }
1040                }
1041            }
1042            SECCOMP_IOCTL_NOTIF_ADDFD => error!(EINVAL),
1043            _ => error!(EINVAL),
1044        }
1045    }
1046
1047    fn wait_async(
1048        &self,
1049        _file: &FileObject,
1050        _current_task: &CurrentTask,
1051        waiter: &Waiter,
1052        events: FdEvents,
1053        handler: EventHandler,
1054    ) -> Option<WaitCanceler> {
1055        let notifier = self.notifier.lock();
1056        Some(notifier.waiters.wait_async_fd_events(waiter, events, handler))
1057    }
1058
1059    fn query_events(
1060        &self,
1061        _file: &FileObject,
1062        _current_task: &CurrentTask,
1063    ) -> Result<FdEvents, Errno> {
1064        Ok(self.notifier.lock().get_fd_notifications())
1065    }
1066}
1067
1068#[cfg(test)]
1069mod test {
1070    use crate::task::SeccompAction;
1071    use crate::testing::spawn_kernel_and_run;
1072
1073    #[::fuchsia::test]
1074    async fn test_actions_logged_accepts_legal_string() {
1075        spawn_kernel_and_run(async |current_task| {
1076            let kernel = current_task.kernel();
1077            let mut actions = SeccompAction::get_actions_avail_file();
1078            // This is a test in Rust instead of a syscall test because we don't want to change the
1079            // global config in a test.
1080            assert!(
1081                SeccompAction::set_actions_logged(&kernel, &actions[..]).is_err(),
1082                "Should not be able to write allow to actions_logged file"
1083            );
1084            let action_string = std::string::String::from_utf8(actions.clone()).unwrap();
1085            if let Some(action_index) = action_string.find("allow") {
1086                actions.drain(action_index..action_index + "allow".len());
1087            }
1088            let write_result = SeccompAction::set_actions_logged(&kernel, &actions[..]);
1089            assert!(
1090                write_result.is_ok(),
1091                "Could not write legal string \"{}\" to actions_logged file: error {}",
1092                std::string::String::from_utf8(actions.clone()).unwrap(),
1093                write_result.unwrap_err()
1094            );
1095        })
1096        .await;
1097    }
1098}