1use crate::abi::Abi;
17use crate::guest_stub::layout;
18use crate::sys::*;
19
20#[repr(C)]
22#[derive(Clone, Copy, Debug)]
23pub struct SockFilter {
24 pub code: u16,
25 pub jt: u8,
26 pub jf: u8,
27 pub k: u32,
28}
29
30#[repr(C)]
32pub struct SockFprog {
33 pub len: u16,
34 pub filter: *const SockFilter,
35}
36
37const BPF_LD_W_ABS: u16 = 0x20;
38const BPF_JEQ_K: u16 = 0x15;
39const BPF_JSET_K: u16 = 0x45;
40const BPF_JA: u16 = 0x05;
41const BPF_RET_K: u16 = 0x06;
42
43const SECCOMP_RET_TRAP: u32 = 0x0003_0000;
45
46fn arg_offset(i: usize) -> u32 {
48 16 + 8 * i as u32
49}
50
51pub struct Table<'a> {
53 pub abi: Abi,
54 pub notify: &'a [i64],
55 pub trace: &'a [i64],
56 pub ns_flags: &'a [i64],
59 pub refused: &'a [i64],
61 pub dumpable: &'a [i64],
64}
65
66fn stmt(code: u16, k: u32) -> SockFilter {
67 SockFilter {
68 code,
69 jt: 0,
70 jf: 0,
71 k,
72 }
73}
74fn jump(k: u32, jt: u8, jf: u8) -> SockFilter {
75 SockFilter {
76 code: BPF_JEQ_K,
77 jt,
78 jf,
79 k,
80 }
81}
82
83#[derive(Clone, Copy)]
85enum Sub {
86 OPath { arg: usize },
88 Socketcall,
90 NsFlags,
92 Dumpable,
94}
95
96impl Sub {
97 fn len(self) -> usize {
98 match self {
99 Sub::OPath { .. } => 3,
100 Sub::Socketcall => 4,
101 Sub::NsFlags => 3,
102 Sub::Dumpable => 4,
103 }
104 }
105}
106
107pub fn build(t64: &Table, t32: &Table, no_trace: bool) -> Vec<SockFilter> {
123 build_tiers(t64, t32, if no_trace { None } else { Some(SECCOMP_RET_TRACE) })
124}
125
126pub fn build_trapped(t64: &Table, t32: &Table) -> Vec<SockFilter> {
165 let ip = |gate: u64| gate + layout::SYSCALL_LEN as u64;
166 let notify = layout::GATE_NOTIFY_OFFSET as u64;
167 let hi64 = (ip(layout::GATE64) >> 32) as u32;
168 debug_assert_eq!(hi64, (ip(layout::GATE64 + notify) >> 32) as u32);
169 let mut f = vec![
170 stmt(BPF_LD_W_ABS, 4),
171 jump(t64.abi.audit(), 1, 0),
172 jump(t32.abi.audit(), 5, 12),
173 stmt(BPF_LD_W_ABS, 12),
174 jump(hi64, 0, 10),
175 stmt(BPF_LD_W_ABS, 8),
176 jump(ip(layout::GATE64) as u32, 6, 0),
177 jump(ip(layout::GATE64 + notify) as u32, 6, 7),
178 stmt(BPF_LD_W_ABS, 12),
179 jump(0, 0, 5),
180 stmt(BPF_LD_W_ABS, 8),
181 jump(ip(layout::GATE32) as u32, 1, 0),
182 jump(ip(layout::GATE32 + notify) as u32, 1, 2),
183 stmt(BPF_RET_K, SECCOMP_RET_ALLOW),
184 stmt(BPF_RET_K, SECCOMP_RET_USER_NOTIF),
185 stmt(BPF_LD_W_ABS, 0),
186 jump(crate::syscall::nr::CLONE3 as u32, 0, 1),
187 stmt(BPF_RET_K, SECCOMP_RET_ERRNO | libc::ENOSYS as u32),
188 ];
189 f.extend(build_tiers(t64, t32, Some(SECCOMP_RET_TRAP | layout::TAG)));
190 f
191}
192
193fn build_tiers(t64: &Table, t32: &Table, trace_ret: Option<u32>) -> Vec<SockFilter> {
204 let no_trace = trace_ret.is_none();
205 let subs_for = |t: &Table| -> Vec<(i64, Sub)> {
207 let mut v: Vec<(i64, Sub)> = t
208 .notify
209 .iter()
210 .filter_map(|nr| t.abi.open_flags_arg(*nr).map(|arg| (*nr, Sub::OPath { arg })))
211 .collect();
212 if let Some(nr) = t.abi.socketcall_nr() {
213 v.push((nr, Sub::Socketcall));
214 }
215 v.extend(t.ns_flags.iter().map(|nr| (*nr, Sub::NsFlags)));
216 v.extend(t.dumpable.iter().map(|nr| (*nr, Sub::Dumpable)));
217 v
218 };
219 let (subs64, subs32) = (subs_for(t64), subs_for(t32));
220 let plain64: Vec<i64> = t64
222 .notify
223 .iter()
224 .copied()
225 .filter(|nr| t64.abi.open_flags_arg(*nr).is_none())
226 .collect();
227 let plain32: Vec<i64> = t32
228 .notify
229 .iter()
230 .copied()
231 .filter(|nr| t32.abi.open_flags_arg(*nr).is_none())
232 .collect();
233 let n64 = plain64.len() + t64.trace.len() + t64.refused.len() + subs64.len();
234 let n32 = plain32.len() + t32.trace.len() + t32.refused.len() + subs32.len();
235 let sub_len: usize = subs64.iter().chain(subs32.iter()).map(|(_, s)| s.len()).sum();
236 let total = 4 + n64 + 2 + n32 + 1 + sub_len + 5;
237 assert!(total < 255, "filter too large for 8-bit jump offsets");
238 let a = 4 + n64;
255 let t32_idx = a + 1;
256 let a2 = t32_idx + 1 + n32;
257 let sub_base = a2 + 1;
258 let n_idx = sub_base + sub_len;
259 let t_idx = if no_trace { n_idx } else { n_idx + 1 };
260 let e_idx = n_idx + 2;
261 let p_idx = e_idx + 1;
262 let a3_idx = e_idx + 2;
263 let off = |from: usize, to: usize| (to - from - 1) as u8;
264 let sub_index = |k: usize| -> usize {
266 sub_base
267 + subs64
268 .iter()
269 .chain(subs32.iter())
270 .take(k)
271 .map(|(_, s)| s.len())
272 .sum::<usize>()
273 };
274 let mut f = Vec::with_capacity(total);
275 f.push(stmt(BPF_LD_W_ABS, 4));
276 f.push(jump(t64.abi.audit(), 1, 0));
277 if n32 == 0 {
278 f.push(jump(t32.abi.audit(), off(2, e_idx), off(2, e_idx)));
279 } else {
280 f.push(jump(t32.abi.audit(), off(2, t32_idx), off(2, e_idx)));
281 }
282 f.push(stmt(BPF_LD_W_ABS, 0));
283 let emit_table = |f: &mut Vec<SockFilter>,
284 start: usize,
285 plain: &[i64],
286 trace: &[i64],
287 refused: &[i64],
288 subs: &[(i64, Sub)],
289 k0: usize| {
290 let mut i = start;
291 for nr in plain {
292 f.push(jump(*nr as u32, off(i, n_idx), 0));
293 i += 1;
294 }
295 for nr in trace {
296 f.push(jump(*nr as u32, off(i, t_idx), 0));
297 i += 1;
298 }
299 for nr in refused {
300 f.push(jump(*nr as u32, off(i, p_idx), 0));
301 i += 1;
302 }
303 for (k, (nr, _)) in subs.iter().enumerate() {
304 f.push(jump(*nr as u32, off(i, sub_index(k0 + k)), 0));
305 i += 1;
306 }
307 };
308 emit_table(&mut f, 4, &plain64, t64.trace, t64.refused, &subs64, 0);
309 f.push(stmt(BPF_RET_K, SECCOMP_RET_ALLOW));
310 f.push(stmt(BPF_LD_W_ABS, 0));
311 emit_table(
312 &mut f,
313 t32_idx + 1,
314 &plain32,
315 t32.trace,
316 t32.refused,
317 &subs32,
318 subs64.len(),
319 );
320 f.push(stmt(BPF_RET_K, SECCOMP_RET_ALLOW));
321 for (k, (_, sub)) in subs64.iter().chain(subs32.iter()).enumerate() {
322 let here = sub_index(k);
323 match *sub {
324 Sub::OPath { arg } => {
325 f.push(stmt(BPF_LD_W_ABS, arg_offset(arg)));
326 f.push(SockFilter {
327 code: BPF_JSET_K,
328 jt: off(here + 1, t_idx),
329 jf: 0,
330 k: libc::O_PATH as u32,
331 });
332 f.push(SockFilter {
333 code: BPF_JA,
334 jt: 0,
335 jf: 0,
336 k: off(here + 2, n_idx) as u32,
337 });
338 }
339 Sub::Socketcall => {
340 f.push(stmt(BPF_LD_W_ABS, arg_offset(0)));
341 f.push(jump(2, off(here + 1, t_idx), 0)); f.push(jump(3, off(here + 2, t_idx), 0)); f.push(SockFilter {
346 code: BPF_JA,
347 jt: 0,
348 jf: 0,
349 k: off(here + 3, a3_idx) as u32,
350 });
351 }
352 Sub::NsFlags => {
353 f.push(stmt(BPF_LD_W_ABS, arg_offset(0)));
354 f.push(SockFilter {
355 code: BPF_JSET_K,
356 jt: off(here + 1, p_idx),
357 jf: 0,
358 k: crate::syscall::CLONE_NEWNS_FLAGS as u32,
359 });
360 f.push(SockFilter {
361 code: BPF_JA,
362 jt: 0,
363 jf: 0,
364 k: off(here + 2, a3_idx) as u32,
365 });
366 }
367 Sub::Dumpable => {
368 f.push(stmt(BPF_LD_W_ABS, arg_offset(0)));
369 f.push(jump(libc::PR_SET_DUMPABLE as u32, off(here + 1, n_idx), 0));
370 f.push(jump(libc::PR_GET_DUMPABLE as u32, off(here + 2, n_idx), 0));
371 f.push(SockFilter {
372 code: BPF_JA,
373 jt: 0,
374 jf: 0,
375 k: off(here + 3, a3_idx) as u32,
376 });
377 }
378 }
379 }
380 f.push(stmt(BPF_RET_K, SECCOMP_RET_USER_NOTIF));
381 f.push(stmt(BPF_RET_K, trace_ret.unwrap_or(SECCOMP_RET_TRACE)));
382 f.push(stmt(BPF_RET_K, SECCOMP_RET_ERRNO | libc::ENOSYS as u32));
383 f.push(stmt(BPF_RET_K, SECCOMP_RET_ERRNO | libc::EPERM as u32));
384 f.push(stmt(BPF_RET_K, SECCOMP_RET_ALLOW));
386 debug_assert_eq!(f.len(), a3_idx + 1);
387 f
388}
389
390pub fn install(program: &[SockFilter]) -> Result<i32> {
401 let prog = SockFprog {
402 len: program.len() as u16,
403 filter: program.as_ptr(),
404 };
405 let fd = unsafe {
406 libc::syscall(
407 libc::SYS_seccomp,
408 SECCOMP_SET_MODE_FILTER,
409 SECCOMP_FILTER_FLAG_NEW_LISTENER,
410 &prog as *const _,
411 )
412 };
413 check(fd).map(|fd| fd as i32)
414}
415
416pub fn from_bytes(bytes: &[u8]) -> std::result::Result<Vec<SockFilter>, String> {
424 if bytes.is_empty() || !bytes.len().is_multiple_of(8) {
425 return Err(format!(
426 "{} bytes is not a whole number of BPF instructions",
427 bytes.len()
428 ));
429 }
430 if bytes.len() / 8 > 4096 {
431 return Err("program longer than BPF_MAXINSNS".into());
432 }
433 Ok(bytes
434 .as_chunks::<8>()
435 .0
436 .iter()
437 .map(|c| SockFilter {
438 code: u16::from_ne_bytes([c[0], c[1]]),
439 jt: c[2],
440 jf: c[3],
441 k: u32::from_ne_bytes([c[4], c[5], c[6], c[7]]),
442 })
443 .collect())
444}
445
446pub fn install_plain(program: &[SockFilter]) -> Result<()> {
453 let prog = SockFprog {
454 len: program.len() as u16,
455 filter: program.as_ptr(),
456 };
457 check(unsafe {
458 libc::syscall(
459 libc::SYS_seccomp,
460 SECCOMP_SET_MODE_FILTER,
461 0u64,
462 &prog as *const _,
463 )
464 })
465 .map(|_| ())
466}
467
468#[cfg(test)]
469mod tests {
470 use super::*;
471
472 fn run_args(prog: &[SockFilter], arch: u32, nr: u32, args: [u64; 6]) -> u32 {
474 run_from(prog, arch, nr, args, 0x0040_1000)
475 }
476
477 fn run_from(prog: &[SockFilter], arch: u32, nr: u32, args: [u64; 6], ip: u64) -> u32 {
489 let mut pc = 0usize;
490 let mut acc = 0u32;
491 loop {
492 let i = prog[pc];
493 match i.code {
494 BPF_LD_W_ABS => {
495 acc = match i.k {
496 0 => nr,
497 4 => arch,
498 8 => ip as u32,
499 12 => (ip >> 32) as u32,
500 k => args[((k - 16) / 8) as usize] as u32,
501 }
502 }
503 BPF_JEQ_K => pc += if acc == i.k { i.jt as usize } else { i.jf as usize },
504 BPF_JSET_K => {
505 pc += if acc & i.k != 0 {
506 i.jt as usize
507 } else {
508 i.jf as usize
509 }
510 }
511 BPF_JA => pc += i.k as usize,
512 BPF_RET_K => return i.k,
513 _ => panic!("unknown op"),
514 }
515 pc += 1;
516 }
517 }
518
519 fn run(prog: &[SockFilter], arch: u32, nr: u32) -> u32 {
520 run_args(prog, arch, nr, [0; 6])
521 }
522
523 #[test]
529 fn trap_mode_routes_by_gate_and_traps_the_staged_calls() {
530 let p = build_trapped(
531 &Table {
532 abi: Abi::X86_64,
533 notify: &[56, 81],
534 trace: &[2, 59, 257],
535 ns_flags: &[272],
536 refused: &[308],
537 dumpable: &[],
538 },
539 &Table {
540 abi: Abi::I386,
541 notify: &[120, 133],
542 trace: &[5, 11, 174],
543 ns_flags: &[310],
544 refused: &[346],
545 dumpable: &[],
546 },
547 );
548 let trap = SECCOMP_RET_TRAP | layout::TAG;
549 let eperm = SECCOMP_RET_ERRNO | libc::EPERM as u32;
550 let enosys = SECCOMP_RET_ERRNO | libc::ENOSYS as u32;
551 let notify = layout::GATE_NOTIFY_OFFSET as u64;
552 let len = layout::SYSCALL_LEN as u64;
553 let (allow64, notify64) = (layout::GATE64 + len, layout::GATE64 + notify + len);
554 let (allow32, notify32) = (layout::GATE32 + len, layout::GATE32 + notify + len);
555 let mark = [layout::HELLO_MARK, 0, 0, 0, 0, 0];
556 assert_eq!(run(&p, AUDIT_ARCH_X86_64, 257), trap);
557 assert_eq!(run(&p, AUDIT_ARCH_X86_64, 59), trap);
558 assert_eq!(run(&p, AUDIT_ARCH_X86_64, 81), SECCOMP_RET_USER_NOTIF);
559 assert_eq!(run(&p, AUDIT_ARCH_X86_64, 56), SECCOMP_RET_USER_NOTIF);
560 assert_eq!(run(&p, AUDIT_ARCH_X86_64, 0), SECCOMP_RET_ALLOW);
561 assert_eq!(
562 run_from(&p, AUDIT_ARCH_X86_64, 257, [0; 6], allow64),
563 SECCOMP_RET_ALLOW
564 );
565 assert_eq!(
566 run_from(&p, AUDIT_ARCH_X86_64, 59, [0; 6], allow64),
567 SECCOMP_RET_ALLOW
568 );
569 assert_eq!(
570 run_from(&p, AUDIT_ARCH_X86_64, 257, [0; 6], notify64),
571 SECCOMP_RET_USER_NOTIF
572 );
573 assert_eq!(
574 run_from(&p, AUDIT_ARCH_X86_64, layout::HELLO64 as u32, mark, notify64),
575 SECCOMP_RET_USER_NOTIF
576 );
577 assert_eq!(run_from(&p, AUDIT_ARCH_X86_64, 257, [0; 6], allow64 + 1), trap);
578 assert_eq!(
579 run_from(&p, AUDIT_ARCH_X86_64, 257, [0; 6], notify64 + (1 << 32)),
580 trap
581 );
582 assert_eq!(run_from(&p, AUDIT_ARCH_X86_64, 257, [0; 6], allow32), trap);
583 assert_eq!(
584 run_args(&p, AUDIT_ARCH_X86_64, 257, [0, 0, 0, 0, 0, 0x4321]),
585 trap
586 );
587 assert_eq!(run(&p, AUDIT_ARCH_X86_64, 435), enosys);
588 let newuser = libc::CLONE_NEWUSER as u64;
589 assert_eq!(
590 run_args(&p, AUDIT_ARCH_X86_64, 272, [newuser, 0, 0, 0, 0, 0]),
591 eperm
592 );
593 assert_eq!(run(&p, AUDIT_ARCH_X86_64, 272), SECCOMP_RET_ALLOW);
594 assert_eq!(run(&p, AUDIT_ARCH_X86_64, 308), eperm);
595 assert_eq!(run(&p, AUDIT_ARCH_I386, 5), trap);
596 assert_eq!(run(&p, AUDIT_ARCH_I386, 11), trap);
597 assert_eq!(run(&p, AUDIT_ARCH_I386, 174), trap);
598 assert_eq!(run(&p, AUDIT_ARCH_I386, 120), SECCOMP_RET_USER_NOTIF);
599 assert_eq!(run(&p, AUDIT_ARCH_I386, 3), SECCOMP_RET_ALLOW);
600 assert_eq!(
601 run_from(&p, AUDIT_ARCH_I386, 5, [0; 6], allow32),
602 SECCOMP_RET_ALLOW
603 );
604 assert_eq!(
605 run_from(&p, AUDIT_ARCH_I386, 5, [0; 6], notify32),
606 SECCOMP_RET_USER_NOTIF
607 );
608 assert_eq!(
609 run_from(&p, AUDIT_ARCH_I386, layout::HELLO32 as u32, mark, notify32),
610 SECCOMP_RET_USER_NOTIF
611 );
612 assert_eq!(run_from(&p, AUDIT_ARCH_I386, 5, [0; 6], allow64), trap);
613 assert_eq!(run_from(&p, AUDIT_ARCH_I386, 5, [0; 6], allow32 + 2), trap);
614 assert_eq!(run_args(&p, AUDIT_ARCH_I386, 102, [3, 0, 0, 0, 0, 0]), trap);
615 assert_eq!(run_args(&p, AUDIT_ARCH_I386, 102, [2, 0, 0, 0, 0, 0]), trap);
616 assert_eq!(
617 run_args(&p, AUDIT_ARCH_I386, 102, [1, 0, 0, 0, 0, 0]),
618 SECCOMP_RET_ALLOW
619 );
620 assert_eq!(run(&p, AUDIT_ARCH_I386, 435), enosys);
621 assert_eq!(
622 run_args(&p, AUDIT_ARCH_I386, 310, [newuser, 0, 0, 0, 0, 0]),
623 eperm
624 );
625 assert_eq!(run(&p, AUDIT_ARCH_I386, 346), eperm);
626 }
627
628 #[test]
629 fn routes_each_tier() {
630 let p = build(
631 &Table {
632 abi: Abi::X86_64,
633 notify: &[2, 257],
634 trace: &[59, 80],
635 ns_flags: &[56, 272],
636 refused: &[308],
637 dumpable: &[157],
638 },
639 &Table {
640 abi: Abi::I386,
641 notify: &[5, 295],
642 trace: &[11, 12],
643 ns_flags: &[120, 310],
644 refused: &[346],
645 dumpable: &[172],
646 },
647 false,
648 );
649 let eperm = SECCOMP_RET_ERRNO | libc::EPERM as u32;
650 assert_eq!(
651 run_args(&p, AUDIT_ARCH_X86_64, 56, [0x1200011, 0, 0, 0, 0, 0]),
652 SECCOMP_RET_ALLOW
653 );
654 assert_eq!(
655 run_args(&p, AUDIT_ARCH_X86_64, 56, [0x10000011, 0, 0, 0, 0, 0]),
656 eperm
657 );
658 assert_eq!(
659 run_args(&p, AUDIT_ARCH_X86_64, 272, [0x20000, 0, 0, 0, 0, 0]),
660 eperm
661 );
662 assert_eq!(
663 run_args(&p, AUDIT_ARCH_X86_64, 272, [0x200, 0, 0, 0, 0, 0]),
664 SECCOMP_RET_ALLOW
665 );
666 assert_eq!(run_args(&p, AUDIT_ARCH_X86_64, 308, [0; 6]), eperm);
667 assert_eq!(
668 run_args(&p, AUDIT_ARCH_I386, 120, [0x10000011, 0, 0, 0, 0, 0]),
669 eperm
670 );
671 assert_eq!(
672 run_args(&p, AUDIT_ARCH_I386, 102, [1, 0, 0, 0, 0, 0]),
673 SECCOMP_RET_ALLOW
674 );
675 assert_eq!(
676 run_args(&p, AUDIT_ARCH_X86_64, 157, [4, 0, 0, 0, 0, 0]),
677 SECCOMP_RET_USER_NOTIF
678 );
679 assert_eq!(
680 run_args(&p, AUDIT_ARCH_X86_64, 157, [3, 0, 0, 0, 0, 0]),
681 SECCOMP_RET_USER_NOTIF
682 );
683 assert_eq!(
684 run_args(&p, AUDIT_ARCH_X86_64, 157, [15, 0, 0, 0, 0, 0]),
685 SECCOMP_RET_ALLOW
686 );
687 assert_eq!(
688 run_args(&p, AUDIT_ARCH_I386, 172, [4, 0, 0, 0, 0, 0]),
689 SECCOMP_RET_USER_NOTIF
690 );
691 assert_eq!(
692 run_args(&p, AUDIT_ARCH_I386, 172, [15, 0, 0, 0, 0, 0]),
693 SECCOMP_RET_ALLOW
694 );
695 assert_eq!(run(&p, AUDIT_ARCH_X86_64, 2), SECCOMP_RET_USER_NOTIF);
696 assert_eq!(run(&p, AUDIT_ARCH_X86_64, 257), SECCOMP_RET_USER_NOTIF);
697 assert_eq!(run(&p, AUDIT_ARCH_X86_64, 59), SECCOMP_RET_TRACE);
698 assert_eq!(run(&p, AUDIT_ARCH_X86_64, 80), SECCOMP_RET_TRACE);
699 assert_eq!(run(&p, AUDIT_ARCH_X86_64, 39), SECCOMP_RET_ALLOW);
700 assert_eq!(run(&p, AUDIT_ARCH_I386, 5), SECCOMP_RET_USER_NOTIF);
701 assert_eq!(run(&p, AUDIT_ARCH_I386, 295), SECCOMP_RET_USER_NOTIF);
702 assert_eq!(run(&p, AUDIT_ARCH_I386, 11), SECCOMP_RET_TRACE);
703 assert_eq!(run(&p, AUDIT_ARCH_I386, 12), SECCOMP_RET_TRACE);
704 assert_eq!(run(&p, AUDIT_ARCH_I386, 2), SECCOMP_RET_ALLOW);
705 assert_eq!(run(&p, 0xc000_00b7, 5), SECCOMP_RET_ERRNO | libc::ENOSYS as u32);
706 let opath = libc::O_PATH as u64;
708 assert_eq!(
709 run_args(&p, AUDIT_ARCH_X86_64, 257, [0, 0, opath | 2, 0, 0, 0]),
710 SECCOMP_RET_TRACE
711 );
712 assert_eq!(
713 run_args(&p, AUDIT_ARCH_X86_64, 257, [0, 0, 2, 0, 0, 0]),
714 SECCOMP_RET_USER_NOTIF
715 );
716 assert_eq!(
717 run_args(&p, AUDIT_ARCH_X86_64, 2, [0, opath, 0, 0, 0, 0]),
718 SECCOMP_RET_TRACE
719 );
720 assert_eq!(
721 run_args(&p, AUDIT_ARCH_I386, 295, [0, 0, opath, 0, 0, 0]),
722 SECCOMP_RET_TRACE
723 );
724 assert_eq!(
725 run_args(&p, AUDIT_ARCH_I386, 5, [0, 0, 0, 0, 0, 0]),
726 SECCOMP_RET_USER_NOTIF
727 );
728 let q = build(
730 &Table {
731 abi: Abi::Aarch64,
732 notify: &[56, 79],
733 trace: &[221],
734 ns_flags: &[],
735 refused: &[],
736 dumpable: &[],
737 },
738 &Table {
739 abi: Abi::Arm,
740 notify: &[],
741 trace: &[],
742 ns_flags: &[],
743 refused: &[],
744 dumpable: &[],
745 },
746 false,
747 );
748 assert_eq!(
749 run_args(&q, AUDIT_ARCH_AARCH64, 56, [0, 0, 0, 0, 0, 0]),
750 SECCOMP_RET_USER_NOTIF
751 );
752 assert_eq!(
753 run_args(&q, AUDIT_ARCH_AARCH64, 56, [0, 0, opath, 0, 0, 0]),
754 SECCOMP_RET_TRACE
755 );
756 assert_eq!(run_args(&q, AUDIT_ARCH_AARCH64, 221, [0; 6]), SECCOMP_RET_TRACE);
757 assert_eq!(run_args(&q, AUDIT_ARCH_AARCH64, 63, [0; 6]), SECCOMP_RET_ALLOW);
758 assert_eq!(
759 run(&q, AUDIT_ARCH_ARM, 5),
760 SECCOMP_RET_ERRNO | libc::ENOSYS as u32
761 );
762 }
763
764 #[test]
765 fn without_a_tracer_everything_is_notified() {
766 let p = build(
767 &Table {
768 abi: Abi::X86_64,
769 notify: &[2, 59, 257],
770 trace: &[],
771 ns_flags: &[272],
772 refused: &[],
773 dumpable: &[],
774 },
775 &Table {
776 abi: Abi::I386,
777 notify: &[5, 11, 295],
778 trace: &[],
779 ns_flags: &[],
780 refused: &[],
781 dumpable: &[],
782 },
783 true,
784 );
785 let opath = libc::O_PATH as u64;
786 assert_eq!(run(&p, AUDIT_ARCH_X86_64, 59), SECCOMP_RET_USER_NOTIF);
787 assert_eq!(
788 run_args(&p, AUDIT_ARCH_X86_64, 257, [0, 0, opath, 0, 0, 0]),
789 SECCOMP_RET_USER_NOTIF
790 );
791 assert_eq!(
792 run_args(&p, AUDIT_ARCH_I386, 102, [3, 0, 0, 0, 0, 0]),
793 SECCOMP_RET_USER_NOTIF
794 );
795 assert_eq!(
796 run_args(&p, AUDIT_ARCH_I386, 102, [1, 0, 0, 0, 0, 0]),
797 SECCOMP_RET_ALLOW
798 );
799 assert_eq!(run(&p, AUDIT_ARCH_I386, 11), SECCOMP_RET_USER_NOTIF);
800 }
801}