1use 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 program: EbpfProgram<SeccompFilter>,
71
72 unique_id: u64,
77
78 log: bool,
80
81 pub notifier: Option<SeccompNotifierHandle>,
83}
84
85pub struct SeccompFilterResult {
87 action: SeccompAction,
89
90 filter: Option<Arc<SeccompFilter>>,
93}
94
95impl SeccompFilter {
96 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 (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 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 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#[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#[derive(Default)]
165pub struct SeccompFilterContainer {
166 pub filters: Vec<Arc<SeccompFilter>>,
168
169 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 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 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 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 pub fn run_all(&self, current_task: &CurrentTask, syscall: &Syscall) -> SeccompFilterResult {
264 let mut r = SeccompFilterResult { action: SeccompAction::Allow, filter: None };
265
266 #[cfg(target_arch = "x86_64")] #[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 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 let handle = Anon::new_file(
314 current_task,
315 Box::new(SeccompNotifierFileObject { notifier: notifier.clone() }),
316 OpenFlags::RDWR,
317 "seccomp notify",
318 )?;
319
320 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#[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#[derive(Default)]
346pub struct SeccompState {
347 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 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 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 #[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 if let Err(e) = waiter.wait(current_task) {
537 return Some(Err(e));
538 }
539
540 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 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 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 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 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 Self::KillProcess | Self::KillThread => true,
706 Self::Errno(_) | Self::Log | Self::Trap(_) | Self::UserNotif => filter_flag,
708 _ => 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 _ => 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 result.pop();
751 }
752
753 result.into_bytes()
754 }
755}
756
757#[derive(Default)]
760struct SeccompNotification {
761 notif: seccomp_notif,
764
765 consumed: bool,
770
771 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
783pub struct SeccompNotifier {
787 waiters: WaitQueue,
788
789 pending_notifications: HashMap<u64, SeccompNotification>,
790
791 num_active_threads: u64,
794
795 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 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 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 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 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 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 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 fn notification_pending(&self, cookie: u64) -> bool {
907 self.pending_notifications.contains_key(&cookie)
908 }
909
910 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 let mut notif: Option<seccomp_notif>;
983 loop {
984 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), ¬if)
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 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 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 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}