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