seccomp_tiny/buffer.rs
1use core::fmt;
2use core::convert::TryInto;
3use crate::abi::*;
4use crate::bpf::*;
5use crate::seccomp;
6
7/// Fixed size buffer for building seccomp BPF programs
8///
9/// Conceptually this is like a Vec<SockFilter>, but to keep compatibility with `no_std` and
10/// take advantage of the small maximum length of a BPF program, this type features a fixed
11/// size array that can hold the maximum (4096) instructions.
12///
13/// ```
14/// use sc::nr;
15/// use seccomp_tiny::{ProgramBuffer, abi, bpf::ret};
16///
17/// let mut p = ProgramBuffer::new();
18///
19/// p.if_any_eq(&[
20/// nr::ARCH_PRCTL,
21/// nr::PRCTL,
22/// nr::WAITID,
23/// nr::PTRACE,
24/// nr::KILL,
25/// ], &[
26/// ret(abi::SECCOMP_RET_ALLOW)
27/// ]);
28///
29/// p.inst(ret(abi::SECCOMP_RET_TRACE));
30///
31/// println!("{:?}", p);
32/// ```
33///
34#[derive(Clone, Eq, PartialEq)]
35pub struct ProgramBuffer {
36 len: u16,
37 array: [SockFilter; BPF_MAXINSNS],
38}
39
40impl ProgramBuffer {
41 /// Construct a new empty ProgramBuffer
42 pub fn new() -> Self {
43 const EMPTY: SockFilter = SockFilter {
44 code: 0, k: 0, jt: 0, jf: 0
45 };
46 ProgramBuffer {
47 len: 0,
48 array: [ EMPTY; BPF_MAXINSNS ]
49 }
50 }
51
52 /// Returns a slice referring to all SockFilter instructions added to the buffer
53 pub fn instructions(&self) -> &[SockFilter] {
54 &self.array[.. self.len as usize]
55 }
56
57 /// Activate the seccomp program, panic on error.
58 ///
59 /// This is equivalent to:
60 /// ```
61 /// # use seccomp_tiny::{ProgramBuffer, abi, bpf};
62 /// # let mut buffer = ProgramBuffer::new();
63 /// # buffer.inst(bpf::ret(abi::SECCOMP_RET_ALLOW));
64 ///
65 /// let prog = abi::SockFilterProg::new(buffer.instructions());
66 /// let result = seccomp_tiny::activate(&prog);
67 /// if let Err(code) = result {
68 /// panic!("... {}", code);
69 /// }
70 /// ```
71 pub fn activate(&self) {
72 let prog = SockFilterProg::new(self.instructions());
73 if let Err(result) = seccomp::activate(&prog) {
74 panic!("seccomp setup error ({})", result);
75 }
76 }
77
78 /// Copy a slice of SockFilter instructions to the end of the buffer
79 ///
80 /// Panics on buffer full.
81 pub fn block(&mut self, block: &[SockFilter]) {
82 for instruction in block {
83 self.inst(*instruction);
84 }
85 }
86
87 /// Copy a SockFilter instruction to the end of the buffer
88 ///
89 /// Panics on buffer full.
90 pub fn inst(&mut self, instruction: SockFilter) {
91 if self.len as usize == BPF_MAXINSNS {
92 panic!("filter program exceeding size limit");
93 }
94 self.array[self.len as usize] = instruction;
95 self.len += 1;
96 }
97
98 /// Build a conditional instruction block
99 ///
100 /// This copies a group of SockFilter instructions to the end of the buffer,
101 /// gated by a conditional jump such that the block runs if the accumulator
102 /// matches the value `k`.
103 ///
104 /// Panics if the buffer is full, or the block we are adding is larger
105 /// than the reach of a single jump (256 instructions).
106 pub fn if_eq(&mut self, k: usize, block: &[SockFilter]) {
107 let to_end_of_block: u8 = block.len().try_into().unwrap();
108 self.inst(jump( BPF_JMP+BPF_JEQ+BPF_K, k as u32, 0, to_end_of_block ));
109 self.block(block);
110 }
111
112 /// Build a conditional block that checks multiple values
113 ///
114 /// This is similar to making repeated calls to if_eq(), however the block
115 /// of code is only included once. This generates a series of conditional
116 /// jump instructions which test each value in `k_list`, and another jump
117 /// which skips the block if none of the values have matched.
118 ///
119 /// Panics if the buffer is full, or if either the list of values
120 /// or the instruction block are too large to jump past at once (256
121 /// instructions each).
122 pub fn if_any_eq(&mut self, k_list: &[usize], block: &[SockFilter]) {
123 let mut to_block: u8 = k_list.len().try_into().unwrap();
124 for k in k_list {
125 self.inst(jump( BPF_JMP+BPF_JEQ+BPF_K, *k as u32, to_block, 0 ));
126 to_block -= 1;
127 }
128 self.inst(jump_always(block.len().try_into().unwrap()));
129 self.block(block);
130 }
131}
132
133impl fmt::Debug for ProgramBuffer {
134 /// Format a ProgramBuffer as a list of instructions, one per line.
135 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
136 for index in 0 .. self.len {
137 write!(f, "{:04} {:?}\n",
138 index, self.array[index as usize])?;
139 }
140 Ok(())
141 }
142}