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}