[go: up one dir, main page]

rbpf/
lib.rs

1// SPDX-License-Identifier: (Apache-2.0 OR MIT)
2// Derived from uBPF <https://github.com/iovisor/ubpf>
3// Copyright 2016 6WIND S.A. <quentin.monnet@6wind.com>
4// Copyright 2023 Isovalent, Inc. <quentin@isovalent.com>
5
6//! Virtual machine and JIT compiler for eBPF programs.
7#![doc(
8    html_logo_url = "https://raw.githubusercontent.com/qmonnet/rbpf/main/misc/rbpf.png",
9    html_favicon_url = "https://raw.githubusercontent.com/qmonnet/rbpf/main/misc/rbpf.ico"
10)]
11// Test examples from README.md as part as doc tests.
12#![doc = include_str!("../README.md")]
13#![warn(missing_docs)]
14// There are unused mut warnings due to unsafe code.
15#![allow(unused_mut)]
16// Allows old-style clippy
17#![allow(renamed_and_removed_lints)]
18#![cfg_attr(
19    feature = "cargo-clippy",
20    allow(
21        redundant_field_names,
22        single_match,
23        cast_lossless,
24        doc_markdown,
25        match_same_arms,
26        unreadable_literal
27    )
28)]
29// Configures the crate to be `no_std` when `std` feature is disabled.
30#![cfg_attr(not(feature = "std"), no_std)]
31
32extern crate byteorder;
33extern crate combine;
34extern crate log;
35#[cfg(feature = "std")]
36extern crate time;
37
38#[cfg(not(feature = "std"))]
39extern crate alloc;
40
41#[cfg(feature = "cranelift")]
42extern crate cranelift_codegen;
43#[cfg(feature = "cranelift")]
44extern crate cranelift_frontend;
45#[cfg(feature = "cranelift")]
46extern crate cranelift_jit;
47#[cfg(feature = "cranelift")]
48extern crate cranelift_module;
49#[cfg(feature = "cranelift")]
50extern crate cranelift_native;
51
52use crate::lib::*;
53use byteorder::{ByteOrder, LittleEndian};
54
55mod asm_parser;
56pub mod assembler;
57#[cfg(feature = "cranelift")]
58mod cranelift;
59pub mod disassembler;
60pub mod ebpf;
61pub mod helpers;
62pub mod insn_builder;
63mod interpreter;
64#[cfg(all(not(windows), feature = "std"))]
65mod jit;
66#[cfg(not(feature = "std"))]
67mod no_std_error;
68mod verifier;
69
70/// Reexports all the types needed from the `std`, `core`, and `alloc`
71/// crates. This avoids elaborate import wrangling having to happen in every
72/// module. Inspired by the design used in `serde`.
73pub mod lib {
74    mod core {
75        #[cfg(not(feature = "std"))]
76        pub use core::*;
77        #[cfg(feature = "std")]
78        pub use std::*;
79    }
80
81    pub use self::core::convert::TryInto;
82    pub use self::core::mem;
83    pub use self::core::mem::ManuallyDrop;
84    pub use self::core::ptr;
85
86    pub use self::core::{f64, u32, u64};
87
88    #[cfg(feature = "std")]
89    pub use std::println;
90
91    #[cfg(not(feature = "std"))]
92    pub use alloc::vec;
93    #[cfg(not(feature = "std"))]
94    pub use alloc::vec::Vec;
95    #[cfg(feature = "std")]
96    pub use std::vec::Vec;
97
98    #[cfg(not(feature = "std"))]
99    pub use alloc::string::{String, ToString};
100    #[cfg(feature = "std")]
101    pub use std::string::{String, ToString};
102
103    // In no_std we cannot use randomness for hashing, thus we need to use
104    // BTree-based implementations of Maps and Sets. The cranelift module uses
105    // BTrees by default, hence we need to expose it twice here.
106    #[cfg(not(feature = "std"))]
107    pub use alloc::collections::{BTreeMap as HashMap, BTreeMap, BTreeSet as HashSet, BTreeSet};
108    #[cfg(feature = "std")]
109    pub use std::collections::{BTreeMap, HashMap, HashSet};
110
111    /// In no_std we use a custom implementation of the error which acts as a
112    /// replacement for the io Error.
113    #[cfg(not(feature = "std"))]
114    pub use crate::no_std_error::{Error, ErrorKind};
115    #[cfg(feature = "std")]
116    pub use std::io::{Error, ErrorKind};
117
118    #[cfg(not(feature = "std"))]
119    pub use alloc::format;
120}
121
122/// eBPF verification function that returns an error if the program does not meet its requirements.
123///
124/// Some examples of things the verifier may reject the program for:
125///
126///   - Program does not terminate.
127///   - Unknown instructions.
128///   - Bad formed instruction.
129///   - Unknown eBPF helper index.
130pub type Verifier = fn(prog: &[u8]) -> Result<(), Error>;
131
132/// eBPF helper function.
133pub type Helper = fn(u64, u64, u64, u64, u64) -> u64;
134
135// A metadata buffer with two offset indications. It can be used in one kind of eBPF VM to simulate
136// the use of a metadata buffer each time the program is executed, without the user having to
137// actually handle it. The offsets are used to tell the VM where in the buffer the pointers to
138// packet data start and end should be stored each time the program is run on a new packet.
139struct MetaBuff {
140    data_offset: usize,
141    data_end_offset: usize,
142    buffer: Vec<u8>,
143}
144
145/// A virtual machine to run eBPF program. This kind of VM is used for programs expecting to work
146/// on a metadata buffer containing pointers to packet data.
147///
148/// # Examples
149///
150/// ```
151/// let prog = &[
152///     0x79, 0x11, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, // Load mem from mbuff at offset 8 into R1.
153///     0x69, 0x10, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // ldhx r1[2], r0
154///     0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00  // exit
155/// ];
156/// let mem = &mut [
157///     0xaa, 0xbb, 0x11, 0x22, 0xcc, 0xdd
158/// ];
159///
160/// // Just for the example we create our metadata buffer from scratch, and we store the pointers
161/// // to packet data start and end in it.
162/// let mut mbuff = [0u8; 32];
163/// unsafe {
164///     let mut data     = mbuff.as_ptr().offset(8)  as *mut u64;
165///     let mut data_end = mbuff.as_ptr().offset(24) as *mut u64;
166///     *data     = mem.as_ptr() as u64;
167///     *data_end = mem.as_ptr() as u64 + mem.len() as u64;
168/// }
169///
170/// // Instantiate a VM.
171/// let mut vm = rbpf::EbpfVmMbuff::new(Some(prog)).unwrap();
172///
173/// // Provide both a reference to the packet data, and to the metadata buffer.
174/// let res = vm.execute_program(mem, &mut mbuff).unwrap();
175/// assert_eq!(res, 0x2211);
176/// ```
177pub struct EbpfVmMbuff<'a> {
178    prog: Option<&'a [u8]>,
179    verifier: Verifier,
180    #[cfg(all(not(windows), feature = "std"))]
181    jit: Option<jit::JitMemory<'a>>,
182    #[cfg(feature = "cranelift")]
183    cranelift_prog: Option<cranelift::CraneliftProgram>,
184    helpers: HashMap<u32, ebpf::Helper>,
185    allowed_memory: HashSet<u64>,
186}
187
188impl<'a> EbpfVmMbuff<'a> {
189    /// Create a new virtual machine instance, and load an eBPF program into that instance.
190    /// When attempting to load the program, it passes through a simple verifier.
191    ///
192    /// # Examples
193    ///
194    /// ```
195    /// let prog = &[
196    ///     0x79, 0x11, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, // Load mem from mbuff into R1.
197    ///     0x69, 0x10, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // ldhx r1[2], r0
198    ///     0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00  // exit
199    /// ];
200    ///
201    /// // Instantiate a VM.
202    /// let mut vm = rbpf::EbpfVmMbuff::new(Some(prog)).unwrap();
203    /// ```
204    pub fn new(prog: Option<&'a [u8]>) -> Result<EbpfVmMbuff<'a>, Error> {
205        if let Some(prog) = prog {
206            verifier::check(prog)?;
207        }
208
209        Ok(EbpfVmMbuff {
210            prog,
211            verifier: verifier::check,
212            #[cfg(all(not(windows), feature = "std"))]
213            jit: None,
214            #[cfg(feature = "cranelift")]
215            cranelift_prog: None,
216            helpers: HashMap::new(),
217            allowed_memory: HashSet::new(),
218        })
219    }
220
221    /// Load a new eBPF program into the virtual machine instance.
222    ///
223    /// # Examples
224    ///
225    /// ```
226    /// let prog1 = &[
227    ///     0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, 0
228    ///     0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00  // exit
229    /// ];
230    /// let prog2 = &[
231    ///     0x79, 0x11, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, // Load mem from mbuff into R1.
232    ///     0x69, 0x10, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // ldhx r1[2], r0
233    ///     0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00  // exit
234    /// ];
235    ///
236    /// // Instantiate a VM.
237    /// let mut vm = rbpf::EbpfVmMbuff::new(Some(prog1)).unwrap();
238    /// vm.set_program(prog2).unwrap();
239    /// ```
240    pub fn set_program(&mut self, prog: &'a [u8]) -> Result<(), Error> {
241        (self.verifier)(prog)?;
242        self.prog = Some(prog);
243        Ok(())
244    }
245
246    /// Set a new verifier function. The function should return an `Error` if the program should be
247    /// rejected by the virtual machine. If a program has been loaded to the VM already, the
248    /// verifier is immediately run.
249    ///
250    /// # Examples
251    ///
252    /// ```
253    /// use rbpf::lib::{Error, ErrorKind};
254    /// use rbpf::ebpf;
255    ///
256    /// // Define a simple verifier function.
257    /// fn verifier(prog: &[u8]) -> Result<(), Error> {
258    ///     let last_insn = ebpf::get_insn(prog, (prog.len() / ebpf::INSN_SIZE) - 1);
259    ///     if last_insn.opc != ebpf::EXIT {
260    ///         return Err(Error::new(ErrorKind::Other,
261    ///                    "[Verifier] Error: program does not end with “EXIT” instruction"));
262    ///     }
263    ///     Ok(())
264    /// }
265    ///
266    /// let prog1 = &[
267    ///     0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, 0
268    ///     0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00  // exit
269    /// ];
270    ///
271    /// // Instantiate a VM.
272    /// let mut vm = rbpf::EbpfVmMbuff::new(Some(prog1)).unwrap();
273    /// // Change the verifier.
274    /// vm.set_verifier(verifier).unwrap();
275    /// ```
276    pub fn set_verifier(&mut self, verifier: Verifier) -> Result<(), Error> {
277        if let Some(prog) = self.prog {
278            verifier(prog)?;
279        }
280        self.verifier = verifier;
281        Ok(())
282    }
283
284    /// Register a built-in or user-defined helper function in order to use it later from within
285    /// the eBPF program. The helper is registered into a hashmap, so the `key` can be any `u32`.
286    ///
287    /// If using JIT-compiled eBPF programs, be sure to register all helpers before compiling the
288    /// program. You should be able to change registered helpers after compiling, but not to add
289    /// new ones (i.e. with new keys).
290    ///
291    /// # Examples
292    ///
293    /// ```
294    /// use rbpf::helpers;
295    ///
296    /// // This program was compiled with clang, from a C program containing the following single
297    /// // instruction: `return bpf_trace_printk("foo %c %c %c\n", 10, 1, 2, 3);`
298    /// let prog = &[
299    ///     0x18, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // load 0 as u64 into r1 (That would be
300    ///     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // replaced by tc by the address of
301    ///                                                     // the format string, in the .map
302    ///                                                     // section of the ELF file).
303    ///     0xb7, 0x02, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, // mov r2, 10
304    ///     0xb7, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, // mov r3, 1
305    ///     0xb7, 0x04, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, // mov r4, 2
306    ///     0xb7, 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, // mov r5, 3
307    ///     0x85, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, // call helper with key 6
308    ///     0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00  // exit
309    /// ];
310    ///
311    /// // Instantiate a VM.
312    /// let mut vm = rbpf::EbpfVmMbuff::new(Some(prog)).unwrap();
313    ///
314    /// // Register a helper.
315    /// // On running the program this helper will print the content of registers r3, r4 and r5 to
316    /// // standard output.
317    /// # #[cfg(feature = "std")]
318    /// vm.register_helper(6, helpers::bpf_trace_printf).unwrap();
319    /// ```
320    pub fn register_helper(&mut self, key: u32, function: Helper) -> Result<(), Error> {
321        self.helpers.insert(key, function);
322        Ok(())
323    }
324
325    /// Register a set of addresses that the eBPF program is allowed to load and store.
326    ///
327    /// When using certain helpers, typically map lookups, the Linux kernel will return pointers
328    /// to structs that the eBPF program needs to interact with. By default rbpf only allows the
329    /// program to interact with its stack, the memory buffer and the program itself, making it
330    /// impossible to supply functional implementations of these helpers.
331    /// This option allows you to pass in a list of addresses that rbpf will allow the program
332    /// to load and store to. Given Rust's memory model you will always know these addresses up
333    /// front when implementing the helpers.
334    ///
335    /// Each invocation of this method will append to the set of allowed addresses.
336    ///
337    /// # Examples
338    ///
339    /// ```
340    /// use std::iter::FromIterator;
341    /// use std::ptr::addr_of;
342    ///
343    /// struct MapValue {
344    ///     data: u8
345    /// }
346    /// static VALUE: MapValue = MapValue { data: 1 };
347    ///
348    /// let prog = &[
349    ///     0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, 0
350    ///     0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00  // exit
351    /// ];
352    ///
353    /// // Instantiate a VM.
354    /// let mut vm = rbpf::EbpfVmMbuff::new(Some(prog)).unwrap();
355    /// let start = addr_of!(VALUE) as u64;
356    /// let addrs = Vec::from_iter(start..start+size_of::<MapValue>() as u64);
357    /// vm.register_allowed_memory(&addrs);
358    /// ```
359    pub fn register_allowed_memory(&mut self, addrs: &[u64]) -> () {
360        for i in addrs {
361            self.allowed_memory.insert(*i);
362        }
363    }
364
365    /// Execute the program loaded, with the given packet data and metadata buffer.
366    ///
367    /// If the program is made to be compatible with Linux kernel, it is expected to load the
368    /// address of the beginning and of the end of the memory area used for packet data from the
369    /// metadata buffer, at some appointed offsets. It is up to the user to ensure that these
370    /// pointers are correctly stored in the buffer.
371    ///
372    /// # Examples
373    ///
374    /// ```
375    /// let prog = &[
376    ///     0x79, 0x11, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, // Load mem from mbuff into R1.
377    ///     0x69, 0x10, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // ldhx r1[2], r0
378    ///     0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00  // exit
379    /// ];
380    /// let mem = &mut [
381    ///     0xaa, 0xbb, 0x11, 0x22, 0xcc, 0xdd
382    /// ];
383    ///
384    /// // Just for the example we create our metadata buffer from scratch, and we store the
385    /// // pointers to packet data start and end in it.
386    /// let mut mbuff = [0u8; 32];
387    /// unsafe {
388    ///     let mut data     = mbuff.as_ptr().offset(8)  as *mut u64;
389    ///     let mut data_end = mbuff.as_ptr().offset(24) as *mut u64;
390    ///     *data     = mem.as_ptr() as u64;
391    ///     *data_end = mem.as_ptr() as u64 + mem.len() as u64;
392    /// }
393    ///
394    /// // Instantiate a VM.
395    /// let mut vm = rbpf::EbpfVmMbuff::new(Some(prog)).unwrap();
396    ///
397    /// // Provide both a reference to the packet data, and to the metadata buffer.
398    /// let res = vm.execute_program(mem, &mut mbuff).unwrap();
399    /// assert_eq!(res, 0x2211);
400    /// ```
401    pub fn execute_program(&self, mem: &[u8], mbuff: &[u8]) -> Result<u64, Error> {
402        interpreter::execute_program(self.prog, mem, mbuff, &self.helpers, &self.allowed_memory)
403    }
404
405    /// JIT-compile the loaded program. No argument required for this.
406    ///
407    /// If using helper functions, be sure to register them into the VM before calling this
408    /// function.
409    ///
410    /// # Examples
411    ///
412    /// ```
413    /// let prog = &[
414    ///     0x79, 0x11, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, // Load mem from mbuff into R1.
415    ///     0x69, 0x10, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // ldhx r1[2], r0
416    ///     0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00  // exit
417    /// ];
418    ///
419    /// // Instantiate a VM.
420    /// let mut vm = rbpf::EbpfVmMbuff::new(Some(prog)).unwrap();
421    ///
422    /// vm.jit_compile();
423    /// ```
424    #[cfg(all(not(windows), feature = "std"))]
425    pub fn jit_compile(&mut self) -> Result<(), Error> {
426        let prog = match self.prog {
427            Some(prog) => prog,
428            None => Err(Error::new(
429                ErrorKind::Other,
430                "Error: No program set, call prog_set() to load one",
431            ))?,
432        };
433        self.jit = Some(jit::JitMemory::new(prog, &self.helpers, true, false)?);
434        Ok(())
435    }
436
437    /// Execute the previously JIT-compiled program, with the given packet data and metadata
438    /// buffer, in a manner very similar to `execute_program()`.
439    ///
440    /// If the program is made to be compatible with Linux kernel, it is expected to load the
441    /// address of the beginning and of the end of the memory area used for packet data from the
442    /// metadata buffer, at some appointed offsets. It is up to the user to ensure that these
443    /// pointers are correctly stored in the buffer.
444    ///
445    /// # Safety
446    ///
447    /// **WARNING:** JIT-compiled assembly code is not safe, in particular there is no runtime
448    /// check for memory access; so if the eBPF program attempts erroneous accesses, this may end
449    /// very bad (program may segfault). It may be wise to check that the program works with the
450    /// interpreter before running the JIT-compiled version of it.
451    ///
452    /// For this reason the function should be called from within an `unsafe` bloc.
453    ///
454    /// # Examples
455    ///
456    /// ```
457    /// let prog = &[
458    ///     0x79, 0x11, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, // Load mem from mbuff into r1.
459    ///     0x69, 0x10, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // ldhx r1[2], r0
460    ///     0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00  // exit
461    /// ];
462    /// let mem = &mut [
463    ///     0xaa, 0xbb, 0x11, 0x22, 0xcc, 0xdd
464    /// ];
465    ///
466    /// // Just for the example we create our metadata buffer from scratch, and we store the
467    /// // pointers to packet data start and end in it.
468    /// let mut mbuff = [0u8; 32];
469    /// unsafe {
470    ///     let mut data     = mbuff.as_ptr().offset(8)  as *mut u64;
471    ///     let mut data_end = mbuff.as_ptr().offset(24) as *mut u64;
472    ///     *data     = mem.as_ptr() as u64;
473    ///     *data_end = mem.as_ptr() as u64 + mem.len() as u64;
474    /// }
475    ///
476    /// // Instantiate a VM.
477    /// let mut vm = rbpf::EbpfVmMbuff::new(Some(prog)).unwrap();
478    ///
479    /// # #[cfg(all(not(windows), feature = "std"))]
480    /// vm.jit_compile();
481    ///
482    /// // Provide both a reference to the packet data, and to the metadata buffer.
483    /// # #[cfg(all(not(windows), feature = "std"))]
484    /// unsafe {
485    ///     let res = vm.execute_program_jit(mem, &mut mbuff).unwrap();
486    ///     assert_eq!(res, 0x2211);
487    /// }
488    /// ```
489    #[cfg(all(not(windows), feature = "std"))]
490    pub unsafe fn execute_program_jit(
491        &self,
492        mem: &mut [u8],
493        mbuff: &'a mut [u8],
494    ) -> Result<u64, Error> {
495        // If packet data is empty, do not send the address of an empty slice; send a null pointer
496        //  as first argument instead, as this is uBPF's behavior (empty packet should not happen
497        //  in the kernel; anyway the verifier would prevent the use of uninitialized registers).
498        //  See `mul_loop` test.
499        let mem_ptr = match mem.len() {
500            0 => std::ptr::null_mut(),
501            _ => mem.as_ptr() as *mut u8,
502        };
503        // The last two arguments are not used in this function. They would be used if there was a
504        // need to indicate to the JIT at which offset in the mbuff mem_ptr and mem_ptr + mem.len()
505        // should be stored; this is what happens with struct EbpfVmFixedMbuff.
506        match &self.jit {
507            Some(jit) => Ok(jit.get_prog()(
508                mbuff.as_ptr() as *mut u8,
509                mbuff.len(),
510                mem_ptr,
511                mem.len(),
512                0,
513                0,
514            )),
515            None => Err(Error::new(
516                ErrorKind::Other,
517                "Error: program has not been JIT-compiled",
518            )),
519        }
520    }
521
522    /// Compile the loaded program using the Cranelift JIT.
523    ///
524    /// If using helper functions, be sure to register them into the VM before calling this
525    /// function.
526    ///
527    /// # Examples
528    ///
529    /// ```
530    /// let prog = &[
531    ///     0x79, 0x11, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, // Load mem from mbuff into R1.
532    ///     0x69, 0x10, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // ldhx r1[2], r0
533    ///     0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00  // exit
534    /// ];
535    ///
536    /// // Instantiate a VM.
537    /// let mut vm = rbpf::EbpfVmMbuff::new(Some(prog)).unwrap();
538    ///
539    /// vm.cranelift_compile();
540    /// ```
541    #[cfg(feature = "cranelift")]
542    pub fn cranelift_compile(&mut self) -> Result<(), Error> {
543        use crate::cranelift::CraneliftCompiler;
544
545        let prog = match self.prog {
546            Some(prog) => prog,
547            None => Err(Error::new(
548                ErrorKind::Other,
549                "Error: No program set, call prog_set() to load one",
550            ))?,
551        };
552
553        let mut compiler = CraneliftCompiler::new(self.helpers.clone());
554        let program = compiler.compile_function(prog)?;
555
556        self.cranelift_prog = Some(program);
557        Ok(())
558    }
559
560    /// Execute the previously compiled program, with the given packet data and metadata
561    /// buffer, in a manner very similar to `execute_program()`.
562    ///
563    /// If the program is made to be compatible with Linux kernel, it is expected to load the
564    /// address of the beginning and of the end of the memory area used for packet data from the
565    /// metadata buffer, at some appointed offsets. It is up to the user to ensure that these
566    /// pointers are correctly stored in the buffer.
567    ///
568    ///
569    /// # Examples
570    ///
571    /// ```
572    /// let prog = &[
573    ///     0x79, 0x11, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, // Load mem from mbuff into r1.
574    ///     0x69, 0x10, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // ldhx r1[2], r0
575    ///     0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00  // exit
576    /// ];
577    /// let mem = &mut [
578    ///     0xaa, 0xbb, 0x11, 0x22, 0xcc, 0xdd
579    /// ];
580    ///
581    /// // Just for the example we create our metadata buffer from scratch, and we store the
582    /// // pointers to packet data start and end in it.
583    /// let mut mbuff = [0u8; 32];
584    /// unsafe {
585    ///     let mut data     = mbuff.as_ptr().offset(8)  as *mut u64;
586    ///     let mut data_end = mbuff.as_ptr().offset(24) as *mut u64;
587    ///     *data     = mem.as_ptr() as u64;
588    ///     *data_end = mem.as_ptr() as u64 + mem.len() as u64;
589    /// }
590    ///
591    /// // Instantiate a VM.
592    /// let mut vm = rbpf::EbpfVmMbuff::new(Some(prog)).unwrap();
593    ///
594    /// vm.cranelift_compile();
595    ///
596    /// // Provide both a reference to the packet data, and to the metadata buffer.
597    /// let res = vm.execute_program_cranelift(mem, &mut mbuff).unwrap();
598    /// assert_eq!(res, 0x2211);
599    /// ```
600    #[cfg(feature = "cranelift")]
601    pub fn execute_program_cranelift(
602        &self,
603        mem: &mut [u8],
604        mbuff: &'a mut [u8],
605    ) -> Result<u64, Error> {
606        // If packet data is empty, do not send the address of an empty slice; send a null pointer
607        //  as first argument instead, as this is uBPF's behavior (empty packet should not happen
608        //  in the kernel; anyway the verifier would prevent the use of uninitialized registers).
609        //  See `mul_loop` test.
610        let mem_ptr = match mem.len() {
611            0 => ptr::null_mut(),
612            _ => mem.as_ptr() as *mut u8,
613        };
614
615        // The last two arguments are not used in this function. They would be used if there was a
616        // need to indicate to the JIT at which offset in the mbuff mem_ptr and mem_ptr + mem.len()
617        // should be stored; this is what happens with struct EbpfVmFixedMbuff.
618        match &self.cranelift_prog {
619            Some(prog) => {
620                Ok(prog.execute(mem_ptr, mem.len(), mbuff.as_ptr() as *mut u8, mbuff.len()))
621            }
622            None => Err(Error::new(
623                ErrorKind::Other,
624                "Error: program has not been compiled with cranelift",
625            )),
626        }
627    }
628}
629
630/// A virtual machine to run eBPF program. This kind of VM is used for programs expecting to work
631/// on a metadata buffer containing pointers to packet data, but it internally handles the buffer
632/// so as to save the effort to manually handle the metadata buffer for the user.
633///
634/// This struct implements a static internal buffer that is passed to the program. The user has to
635/// indicate the offset values at which the eBPF program expects to find the start and the end of
636/// packet data in the buffer. On calling the `execute_program()` or `execute_program_jit()` functions, the
637/// struct automatically updates the addresses in this static buffer, at the appointed offsets, for
638/// the start and the end of the packet data the program is called upon.
639///
640/// # Examples
641///
642/// This was compiled with clang from the following program, in C:
643///
644/// ```c
645/// #include <linux/bpf.h>
646/// #include "path/to/linux/samples/bpf/bpf_helpers.h"
647///
648/// SEC(".classifier")
649/// int classifier(struct __sk_buff *skb)
650/// {
651///   void *data = (void *)(long)skb->data;
652///   void *data_end = (void *)(long)skb->data_end;
653///
654///   // Check program is long enough.
655///   if (data + 5 > data_end)
656///     return 0;
657///
658///   return *((char *)data + 5);
659/// }
660/// ```
661///
662/// Some small modifications have been brought to have it work, see comments.
663///
664/// ```
665/// let prog = &[
666///     0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, 0
667///     // Here opcode 0x61 had to be replace by 0x79 so as to load a 8-bytes long address.
668///     // Also, offset 0x4c had to be replace with e.g. 0x40 so as to prevent the two pointers
669///     // from overlapping in the buffer.
670///     0x79, 0x12, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, // load pointer to mem from r1[0x40] to r2
671///     0x07, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, // add r2, 5
672///     // Here opcode 0x61 had to be replace by 0x79 so as to load a 8-bytes long address.
673///     0x79, 0x11, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, // load ptr to mem_end from r1[0x50] to r1
674///     0x2d, 0x12, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, // if r2 > r1 skip 3 instructions
675///     0x71, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // load r2 (= *(mem + 5)) into r0
676///     0x67, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, // r0 >>= 56
677///     0xc7, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, // r0 <<= 56 (arsh) extend byte sign to u64
678///     0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00  // exit
679/// ];
680/// let mem1 = &mut [
681///     0xaa, 0xbb, 0x11, 0x22, 0xcc, 0xdd
682/// ];
683/// let mem2 = &mut [
684///     0xaa, 0xbb, 0x11, 0x22, 0xcc, 0x27
685/// ];
686///
687/// // Instantiate a VM. Note that we provide the start and end offsets for mem pointers.
688/// let mut vm = rbpf::EbpfVmFixedMbuff::new(Some(prog), 0x40, 0x50).unwrap();
689///
690/// // Provide only a reference to the packet data. We do not manage the metadata buffer.
691/// let res = vm.execute_program(mem1).unwrap();
692/// assert_eq!(res, 0xffffffffffffffdd);
693///
694/// let res = vm.execute_program(mem2).unwrap();
695/// assert_eq!(res, 0x27);
696/// ```
697pub struct EbpfVmFixedMbuff<'a> {
698    parent: EbpfVmMbuff<'a>,
699    mbuff: MetaBuff,
700}
701
702impl<'a> EbpfVmFixedMbuff<'a> {
703    /// Create a new virtual machine instance, and load an eBPF program into that instance.
704    /// When attempting to load the program, it passes through a simple verifier.
705    ///
706    /// # Examples
707    ///
708    /// ```
709    /// let prog = &[
710    ///     0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, 0
711    ///     0x79, 0x12, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, // load mem from r1[0x40] to r2
712    ///     0x07, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, // add r2, 5
713    ///     0x79, 0x11, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, // load mem_end from r1[0x50] to r1
714    ///     0x2d, 0x12, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, // if r2 > r1 skip 3 instructions
715    ///     0x71, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // load r2 (= *(mem + 5)) into r0
716    ///     0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00  // exit
717    /// ];
718    ///
719    /// // Instantiate a VM. Note that we provide the start and end offsets for mem pointers.
720    /// let mut vm = rbpf::EbpfVmFixedMbuff::new(Some(prog), 0x40, 0x50).unwrap();
721    /// ```
722    pub fn new(
723        prog: Option<&'a [u8]>,
724        data_offset: usize,
725        data_end_offset: usize,
726    ) -> Result<EbpfVmFixedMbuff<'a>, Error> {
727        let parent = EbpfVmMbuff::new(prog)?;
728        let get_buff_len = |x: usize, y: usize| if x >= y { x + 8 } else { y + 8 };
729        let buffer = vec![0u8; get_buff_len(data_offset, data_end_offset)];
730        let mbuff = MetaBuff {
731            data_offset,
732            data_end_offset,
733            buffer,
734        };
735        Ok(EbpfVmFixedMbuff { parent, mbuff })
736    }
737
738    /// Load a new eBPF program into the virtual machine instance.
739    ///
740    /// At the same time, load new offsets for storing pointers to start and end of packet data in
741    /// the internal metadata buffer.
742    ///
743    /// # Examples
744    ///
745    /// ```
746    /// let prog1 = &[
747    ///     0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, 0
748    ///     0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00  // exit
749    /// ];
750    /// let prog2 = &[
751    ///     0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, 0
752    ///     0x79, 0x12, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, // load mem from r1[0x40] to r2
753    ///     0x07, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, // add r2, 5
754    ///     0x79, 0x11, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, // load mem_end from r1[0x50] to r1
755    ///     0x2d, 0x12, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, // if r2 > r1 skip 3 instructions
756    ///     0x71, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // load r2 (= *(mem + 5)) into r0
757    ///     0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00  // exit
758    /// ];
759    ///
760    /// let mem = &mut [
761    ///     0xaa, 0xbb, 0x11, 0x22, 0xcc, 0x27,
762    /// ];
763    ///
764    /// let mut vm = rbpf::EbpfVmFixedMbuff::new(Some(prog1), 0, 0).unwrap();
765    /// vm.set_program(prog2, 0x40, 0x50);
766    ///
767    /// let res = vm.execute_program(mem).unwrap();
768    /// assert_eq!(res, 0x27);
769    /// ```
770    pub fn set_program(
771        &mut self,
772        prog: &'a [u8],
773        data_offset: usize,
774        data_end_offset: usize,
775    ) -> Result<(), Error> {
776        let get_buff_len = |x: usize, y: usize| if x >= y { x + 8 } else { y + 8 };
777        let buffer = vec![0u8; get_buff_len(data_offset, data_end_offset)];
778        self.mbuff.buffer = buffer;
779        self.mbuff.data_offset = data_offset;
780        self.mbuff.data_end_offset = data_end_offset;
781        self.parent.set_program(prog)?;
782        Ok(())
783    }
784
785    /// Set a new verifier function. The function should return an `Error` if the program should be
786    /// rejected by the virtual machine. If a program has been loaded to the VM already, the
787    /// verifier is immediately run.
788    ///
789    /// # Examples
790    ///
791    /// ```
792    /// use rbpf::lib::{Error, ErrorKind};
793    /// use rbpf::ebpf;
794    ///
795    /// // Define a simple verifier function.
796    /// fn verifier(prog: &[u8]) -> Result<(), Error> {
797    ///     let last_insn = ebpf::get_insn(prog, (prog.len() / ebpf::INSN_SIZE) - 1);
798    ///     if last_insn.opc != ebpf::EXIT {
799    ///         return Err(Error::new(ErrorKind::Other,
800    ///                    "[Verifier] Error: program does not end with “EXIT” instruction"));
801    ///     }
802    ///     Ok(())
803    /// }
804    ///
805    /// let prog1 = &[
806    ///     0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, 0
807    ///     0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00  // exit
808    /// ];
809    ///
810    /// // Instantiate a VM.
811    /// let mut vm = rbpf::EbpfVmMbuff::new(Some(prog1)).unwrap();
812    /// // Change the verifier.
813    /// vm.set_verifier(verifier).unwrap();
814    /// ```
815    pub fn set_verifier(&mut self, verifier: Verifier) -> Result<(), Error> {
816        self.parent.set_verifier(verifier)
817    }
818
819    /// Register a built-in or user-defined helper function in order to use it later from within
820    /// the eBPF program. The helper is registered into a hashmap, so the `key` can be any `u32`.
821    ///
822    /// If using JIT-compiled eBPF programs, be sure to register all helpers before compiling the
823    /// program. You should be able to change registered helpers after compiling, but not to add
824    /// new ones (i.e. with new keys).
825    ///
826    /// # Examples
827    ///
828    /// ```
829    /// #[cfg(feature = "std")] {
830    ///     use rbpf::helpers;
831    ///
832    ///     // This program was compiled with clang, from a C program containing the following single
833    ///     // instruction: `return bpf_trace_printk("foo %c %c %c\n", 10, 1, 2, 3);`
834    ///     let prog = &[
835    ///         0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, 0
836    ///         0x79, 0x12, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, // load mem from r1[0x40] to r2
837    ///         0x07, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, // add r2, 5
838    ///         0x79, 0x11, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, // load mem_end from r1[0x50] to r1
839    ///         0x2d, 0x12, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, // if r2 > r1 skip 6 instructions
840    ///         0x71, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // load r2 (= *(mem + 5)) into r1
841    ///         0xb7, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r2, 0
842    ///         0xb7, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r3, 0
843    ///         0xb7, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r4, 0
844    ///         0xb7, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r5, 0
845    ///         0x85, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, // call helper with key 1
846    ///         0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00  // exit
847    ///     ];
848    ///
849    ///     let mem = &mut [
850    ///         0xaa, 0xbb, 0x11, 0x22, 0xcc, 0x09,
851    ///     ];
852    ///
853    ///     // Instantiate a VM.
854    ///     let mut vm = rbpf::EbpfVmFixedMbuff::new(Some(prog), 0x40, 0x50).unwrap();
855    ///
856    ///     // Register a helper. This helper will store the result of the square root of r1 into r0.
857    ///     vm.register_helper(1, helpers::sqrti);
858    ///
859    ///     let res = vm.execute_program(mem).unwrap();
860    ///     assert_eq!(res, 3);
861    /// }
862    /// ```
863    pub fn register_helper(
864        &mut self,
865        key: u32,
866        function: fn(u64, u64, u64, u64, u64) -> u64,
867    ) -> Result<(), Error> {
868        self.parent.register_helper(key, function)
869    }
870
871    /// Register an object that the eBPF program is allowed to load and store.
872    ///
873    /// When using certain helpers, typically map lookups, the Linux kernel will return pointers
874    /// to structs that the eBPF program needs to interact with. By default rbpf only allows the
875    /// program to interact with its stack, the memory buffer and the program itself, making it
876    /// impossible to supply functional implementations of these helpers.
877    /// This option allows you to pass in a list of addresses that rbpf will allow the program
878    /// to load and store to. Given Rust's memory model you will always know these addresses up
879    /// front when implementing the helpers.
880    ///
881    /// Each invocation of this method will append to the set of allowed addresses.
882    ///
883    /// # Examples
884    ///
885    /// ```
886    /// use std::iter::FromIterator;
887    /// use std::ptr::addr_of;
888    ///
889    /// struct MapValue {
890    ///     data: u8
891    /// }
892    /// static VALUE: MapValue = MapValue { data: 1 };
893    ///
894    /// let prog = &[
895    ///     0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, 0
896    ///     0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00  // exit
897    /// ];
898    ///
899    /// // Instantiate a VM.
900    /// let mut vm = rbpf::EbpfVmFixedMbuff::new(Some(prog), 0x40, 0x50).unwrap();
901    /// let start = addr_of!(VALUE) as u64;
902    /// let addrs = Vec::from_iter(start..start+size_of::<MapValue>() as u64);
903    /// vm.register_allowed_memory(&addrs);
904    /// ```
905    pub fn register_allowed_memory(&mut self, allowed: &[u64]) -> () {
906        self.parent.register_allowed_memory(allowed)
907    }
908
909    /// Execute the program loaded, with the given packet data.
910    ///
911    /// If the program is made to be compatible with Linux kernel, it is expected to load the
912    /// address of the beginning and of the end of the memory area used for packet data from some
913    /// metadata buffer, which in the case of this VM is handled internally. The offsets at which
914    /// the addresses should be placed should have be set at the creation of the VM.
915    ///
916    /// # Examples
917    ///
918    /// ```
919    /// let prog = &[
920    ///     0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, 0
921    ///     0x79, 0x12, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, // load mem from r1[0x40] to r2
922    ///     0x07, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, // add r2, 5
923    ///     0x79, 0x11, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, // load mem_end from r1[0x50] to r1
924    ///     0x2d, 0x12, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, // if r2 > r1 skip 3 instructions
925    ///     0x71, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // load r2 (= *(mem + 5)) into r0
926    ///     0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00  // exit
927    /// ];
928    /// let mem = &mut [
929    ///     0xaa, 0xbb, 0x11, 0x22, 0xcc, 0xdd
930    /// ];
931    ///
932    /// // Instantiate a VM. Note that we provide the start and end offsets for mem pointers.
933    /// let mut vm = rbpf::EbpfVmFixedMbuff::new(Some(prog), 0x40, 0x50).unwrap();
934    ///
935    /// // Provide only a reference to the packet data. We do not manage the metadata buffer.
936    /// let res = vm.execute_program(mem).unwrap();
937    /// assert_eq!(res, 0xdd);
938    /// ```
939    pub fn execute_program(&mut self, mem: &'a mut [u8]) -> Result<u64, Error> {
940        let l = self.mbuff.buffer.len();
941        // Can this ever happen? Probably not, should be ensured at mbuff creation.
942        if self.mbuff.data_offset + 8 > l || self.mbuff.data_end_offset + 8 > l {
943            Err(Error::new(ErrorKind::Other, format!("Error: buffer too small ({:?}), cannot use data_offset {:?} and data_end_offset {:?}",
944            l, self.mbuff.data_offset, self.mbuff.data_end_offset)))?;
945        }
946        LittleEndian::write_u64(
947            &mut self.mbuff.buffer[(self.mbuff.data_offset)..],
948            mem.as_ptr() as u64,
949        );
950        LittleEndian::write_u64(
951            &mut self.mbuff.buffer[(self.mbuff.data_end_offset)..],
952            mem.as_ptr() as u64 + mem.len() as u64,
953        );
954        self.parent.execute_program(mem, &self.mbuff.buffer)
955    }
956
957    /// JIT-compile the loaded program. No argument required for this.
958    ///
959    /// If using helper functions, be sure to register them into the VM before calling this
960    /// function.
961    ///
962    /// # Examples
963    ///
964    /// ```
965    /// let prog = &[
966    ///     0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, 0
967    ///     0x79, 0x12, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, // load mem from r1[0x40] to r2
968    ///     0x07, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, // add r2, 5
969    ///     0x79, 0x11, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, // load mem_end from r1[0x50] to r1
970    ///     0x2d, 0x12, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, // if r2 > r1 skip 3 instructions
971    ///     0x71, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // load r2 (= *(mem + 5)) into r0
972    ///     0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00  // exit
973    /// ];
974    ///
975    /// // Instantiate a VM. Note that we provide the start and end offsets for mem pointers.
976    /// let mut vm = rbpf::EbpfVmFixedMbuff::new(Some(prog), 0x40, 0x50).unwrap();
977    ///
978    /// vm.jit_compile();
979    /// ```
980    #[cfg(all(not(windows), feature = "std"))]
981    pub fn jit_compile(&mut self) -> Result<(), Error> {
982        let prog = match self.parent.prog {
983            Some(prog) => prog,
984            None => Err(Error::new(
985                ErrorKind::Other,
986                "Error: No program set, call prog_set() to load one",
987            ))?,
988        };
989        self.parent.jit = Some(jit::JitMemory::new(prog, &self.parent.helpers, true, true)?);
990        Ok(())
991    }
992
993    /// Execute the previously JIT-compiled program, with the given packet data, in a manner very
994    /// similar to `execute_program()`.
995    ///
996    /// If the program is made to be compatible with Linux kernel, it is expected to load the
997    /// address of the beginning and of the end of the memory area used for packet data from some
998    /// metadata buffer, which in the case of this VM is handled internally. The offsets at which
999    /// the addresses should be placed should have be set at the creation of the VM.
1000    ///
1001    /// # Safety
1002    ///
1003    /// **WARNING:** JIT-compiled assembly code is not safe, in particular there is no runtime
1004    /// check for memory access; so if the eBPF program attempts erroneous accesses, this may end
1005    /// very bad (program may segfault). It may be wise to check that the program works with the
1006    /// interpreter before running the JIT-compiled version of it.
1007    ///
1008    /// For this reason the function should be called from within an `unsafe` bloc.
1009    ///
1010    /// # Examples
1011    ///
1012    /// ```
1013    /// let prog = &[
1014    ///     0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, 0
1015    ///     0x79, 0x12, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, // load mem from r1[0x40] to r2
1016    ///     0x07, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, // add r2, 5
1017    ///     0x79, 0x11, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, // load mem_end from r1[0x50] to r1
1018    ///     0x2d, 0x12, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, // if r2 > r1 skip 3 instructions
1019    ///     0x71, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // load r2 (= *(mem + 5)) into r0
1020    ///     0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00  // exit
1021    /// ];
1022    /// let mem = &mut [
1023    ///     0xaa, 0xbb, 0x11, 0x22, 0xcc, 0xdd
1024    /// ];
1025    ///
1026    /// // Instantiate a VM. Note that we provide the start and end offsets for mem pointers.
1027    /// let mut vm = rbpf::EbpfVmFixedMbuff::new(Some(prog), 0x40, 0x50).unwrap();
1028    ///
1029    /// # #[cfg(all(not(windows), feature = "std"))]
1030    /// vm.jit_compile();
1031    ///
1032    /// // Provide only a reference to the packet data. We do not manage the metadata buffer.
1033    /// # #[cfg(all(not(windows), feature = "std"))]
1034    /// unsafe {
1035    ///     let res = vm.execute_program_jit(mem).unwrap();
1036    ///     assert_eq!(res, 0xdd);
1037    /// }
1038    /// ```
1039    // This struct redefines the `execute_program_jit()` function, in order to pass the offsets
1040    // associated with the fixed mbuff.
1041    #[cfg(all(not(windows), feature = "std"))]
1042    pub unsafe fn execute_program_jit(&mut self, mem: &'a mut [u8]) -> Result<u64, Error> {
1043        // If packet data is empty, do not send the address of an empty slice; send a null pointer
1044        //  as first argument instead, as this is uBPF's behavior (empty packet should not happen
1045        //  in the kernel; anyway the verifier would prevent the use of uninitialized registers).
1046        //  See `mul_loop` test.
1047        let mem_ptr = match mem.len() {
1048            0 => ptr::null_mut(),
1049            _ => mem.as_ptr() as *mut u8,
1050        };
1051
1052        match &self.parent.jit {
1053            Some(jit) => Ok(jit.get_prog()(
1054                self.mbuff.buffer.as_ptr() as *mut u8,
1055                self.mbuff.buffer.len(),
1056                mem_ptr,
1057                mem.len(),
1058                self.mbuff.data_offset,
1059                self.mbuff.data_end_offset,
1060            )),
1061            None => Err(Error::new(
1062                ErrorKind::Other,
1063                "Error: program has not been JIT-compiled",
1064            )),
1065        }
1066    }
1067
1068    /// Compile the loaded program using the Cranelift JIT.
1069    ///
1070    /// If using helper functions, be sure to register them into the VM before calling this
1071    /// function.
1072    ///
1073    /// # Examples
1074    ///
1075    /// ```
1076    /// let prog = &[
1077    ///     0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, 0
1078    ///     0x79, 0x12, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, // load mem from r1[0x40] to r2
1079    ///     0x07, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, // add r2, 5
1080    ///     0x79, 0x11, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, // load mem_end from r1[0x50] to r1
1081    ///     0x2d, 0x12, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, // if r2 > r1 skip 3 instructions
1082    ///     0x71, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // load r2 (= *(mem + 5)) into r0
1083    ///     0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00  // exit
1084    /// ];
1085    ///
1086    /// // Instantiate a VM. Note that we provide the start and end offsets for mem pointers.
1087    /// let mut vm = rbpf::EbpfVmFixedMbuff::new(Some(prog), 0x40, 0x50).unwrap();
1088    ///
1089    /// vm.cranelift_compile();
1090    /// ```
1091    #[cfg(feature = "cranelift")]
1092    pub fn cranelift_compile(&mut self) -> Result<(), Error> {
1093        use crate::cranelift::CraneliftCompiler;
1094
1095        let prog = match self.parent.prog {
1096            Some(prog) => prog,
1097            None => Err(Error::new(
1098                ErrorKind::Other,
1099                "Error: No program set, call prog_set() to load one",
1100            ))?,
1101        };
1102
1103        let mut compiler = CraneliftCompiler::new(self.parent.helpers.clone());
1104        let program = compiler.compile_function(prog)?;
1105
1106        self.parent.cranelift_prog = Some(program);
1107        Ok(())
1108    }
1109
1110    /// Execute the previously compiled program, with the given packet data and metadata
1111    /// buffer, in a manner very similar to `execute_program()`.
1112    ///
1113    /// If the program is made to be compatible with Linux kernel, it is expected to load the
1114    /// address of the beginning and of the end of the memory area used for packet data from some
1115    /// metadata buffer, which in the case of this VM is handled internally. The offsets at which
1116    /// the addresses should be placed should have be set at the creation of the VM.
1117    ///
1118    /// # Examples
1119    ///
1120    /// ```
1121    /// let prog = &[
1122    ///     0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, 0
1123    ///     0x79, 0x12, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, // load mem from r1[0x40] to r2
1124    ///     0x07, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, // add r2, 5
1125    ///     0x79, 0x11, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, // load mem_end from r1[0x50] to r1
1126    ///     0x2d, 0x12, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, // if r2 > r1 skip 3 instructions
1127    ///     0x71, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // load r2 (= *(mem + 5)) into r0
1128    ///     0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00  // exit
1129    /// ];
1130    /// let mem = &mut [
1131    ///     0xaa, 0xbb, 0x11, 0x22, 0xcc, 0xdd
1132    /// ];
1133    ///
1134    /// // Instantiate a VM. Note that we provide the start and end offsets for mem pointers.
1135    /// let mut vm = rbpf::EbpfVmFixedMbuff::new(Some(prog), 0x40, 0x50).unwrap();
1136    ///
1137    /// vm.cranelift_compile();
1138    ///
1139    /// // Provide only a reference to the packet data. We do not manage the metadata buffer.
1140    /// let res = vm.execute_program_cranelift(mem).unwrap();
1141    /// assert_eq!(res, 0xdd);
1142    /// ```
1143    #[cfg(feature = "cranelift")]
1144    pub fn execute_program_cranelift(&mut self, mem: &'a mut [u8]) -> Result<u64, Error> {
1145        // If packet data is empty, do not send the address of an empty slice; send a null pointer
1146        //  as first argument instead, as this is uBPF's behavior (empty packet should not happen
1147        //  in the kernel; anyway the verifier would prevent the use of uninitialized registers).
1148        //  See `mul_loop` test.
1149        let mem_ptr = match mem.len() {
1150            0 => ptr::null_mut(),
1151            _ => mem.as_ptr() as *mut u8,
1152        };
1153
1154        let l = self.mbuff.buffer.len();
1155        // Can this ever happen? Probably not, should be ensured at mbuff creation.
1156        if self.mbuff.data_offset + 8 > l || self.mbuff.data_end_offset + 8 > l {
1157            Err(Error::new(ErrorKind::Other, format!("Error: buffer too small ({:?}), cannot use data_offset {:?} and data_end_offset {:?}",
1158            l, self.mbuff.data_offset, self.mbuff.data_end_offset)))?;
1159        }
1160        LittleEndian::write_u64(
1161            &mut self.mbuff.buffer[(self.mbuff.data_offset)..],
1162            mem.as_ptr() as u64,
1163        );
1164        LittleEndian::write_u64(
1165            &mut self.mbuff.buffer[(self.mbuff.data_end_offset)..],
1166            mem.as_ptr() as u64 + mem.len() as u64,
1167        );
1168
1169        match &self.parent.cranelift_prog {
1170            Some(prog) => Ok(prog.execute(
1171                mem_ptr,
1172                mem.len(),
1173                self.mbuff.buffer.as_ptr() as *mut u8,
1174                self.mbuff.buffer.len(),
1175            )),
1176            None => Err(Error::new(
1177                ErrorKind::Other,
1178                "Error: program has not been compiled with cranelift",
1179            )),
1180        }
1181    }
1182}
1183
1184/// A virtual machine to run eBPF program. This kind of VM is used for programs expecting to work
1185/// directly on the memory area representing packet data.
1186///
1187/// # Examples
1188///
1189/// ```
1190/// let prog = &[
1191///     0x71, 0x11, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, // ldxb r1[0x04], r1
1192///     0x07, 0x01, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, // add r1, 0x22
1193///     0xbf, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, r1
1194///     0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00  // exit
1195/// ];
1196/// let mem = &mut [
1197///     0xaa, 0xbb, 0x11, 0x22, 0xcc, 0xdd
1198/// ];
1199///
1200/// // Instantiate a VM.
1201/// let vm = rbpf::EbpfVmRaw::new(Some(prog)).unwrap();
1202///
1203/// // Provide only a reference to the packet data.
1204/// let res = vm.execute_program(mem).unwrap();
1205/// assert_eq!(res, 0x22cc);
1206/// ```
1207pub struct EbpfVmRaw<'a> {
1208    parent: EbpfVmMbuff<'a>,
1209}
1210
1211impl<'a> EbpfVmRaw<'a> {
1212    /// Create a new virtual machine instance, and load an eBPF program into that instance.
1213    /// When attempting to load the program, it passes through a simple verifier.
1214    ///
1215    /// # Examples
1216    ///
1217    /// ```
1218    /// let prog = &[
1219    ///     0x71, 0x11, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, // ldxb r1[0x04], r1
1220    ///     0x07, 0x01, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, // add r1, 0x22
1221    ///     0xbf, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, r1
1222    ///     0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00  // exit
1223    /// ];
1224    ///
1225    /// // Instantiate a VM.
1226    /// let vm = rbpf::EbpfVmRaw::new(Some(prog)).unwrap();
1227    /// ```
1228    pub fn new(prog: Option<&'a [u8]>) -> Result<EbpfVmRaw<'a>, Error> {
1229        let parent = EbpfVmMbuff::new(prog)?;
1230        Ok(EbpfVmRaw { parent })
1231    }
1232
1233    /// Load a new eBPF program into the virtual machine instance.
1234    ///
1235    /// # Examples
1236    ///
1237    /// ```
1238    /// let prog1 = &[
1239    ///     0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, 0
1240    ///     0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00  // exit
1241    /// ];
1242    /// let prog2 = &[
1243    ///     0x71, 0x11, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, // ldxb r1[0x04], r1
1244    ///     0x07, 0x01, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, // add r1, 0x22
1245    ///     0xbf, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, r1
1246    ///     0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00  // exit
1247    /// ];
1248    ///
1249    /// let mem = &mut [
1250    ///     0xaa, 0xbb, 0x11, 0x22, 0xcc, 0x27,
1251    /// ];
1252    ///
1253    /// let mut vm = rbpf::EbpfVmRaw::new(Some(prog1)).unwrap();
1254    /// vm.set_program(prog2);
1255    ///
1256    /// let res = vm.execute_program(mem).unwrap();
1257    /// assert_eq!(res, 0x22cc);
1258    /// ```
1259    pub fn set_program(&mut self, prog: &'a [u8]) -> Result<(), Error> {
1260        self.parent.set_program(prog)?;
1261        Ok(())
1262    }
1263
1264    /// Set a new verifier function. The function should return an `Error` if the program should be
1265    /// rejected by the virtual machine. If a program has been loaded to the VM already, the
1266    /// verifier is immediately run.
1267    ///
1268    /// # Examples
1269    ///
1270    /// ```
1271    /// use rbpf::lib::{Error, ErrorKind};
1272    /// use rbpf::ebpf;
1273    ///
1274    /// // Define a simple verifier function.
1275    /// fn verifier(prog: &[u8]) -> Result<(), Error> {
1276    ///     let last_insn = ebpf::get_insn(prog, (prog.len() / ebpf::INSN_SIZE) - 1);
1277    ///     if last_insn.opc != ebpf::EXIT {
1278    ///         return Err(Error::new(ErrorKind::Other,
1279    ///                    "[Verifier] Error: program does not end with “EXIT” instruction"));
1280    ///     }
1281    ///     Ok(())
1282    /// }
1283    ///
1284    /// let prog1 = &[
1285    ///     0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, 0
1286    ///     0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00  // exit
1287    /// ];
1288    ///
1289    /// // Instantiate a VM.
1290    /// let mut vm = rbpf::EbpfVmMbuff::new(Some(prog1)).unwrap();
1291    /// // Change the verifier.
1292    /// vm.set_verifier(verifier).unwrap();
1293    /// ```
1294    pub fn set_verifier(&mut self, verifier: Verifier) -> Result<(), Error> {
1295        self.parent.set_verifier(verifier)
1296    }
1297
1298    /// Register a built-in or user-defined helper function in order to use it later from within
1299    /// the eBPF program. The helper is registered into a hashmap, so the `key` can be any `u32`.
1300    ///
1301    /// If using JIT-compiled eBPF programs, be sure to register all helpers before compiling the
1302    /// program. You should be able to change registered helpers after compiling, but not to add
1303    /// new ones (i.e. with new keys).
1304    ///
1305    /// # Examples
1306    ///
1307    /// ```
1308    /// #[cfg(feature = "std")] {
1309    ///     use rbpf::helpers;
1310    ///
1311    ///     let prog = &[
1312    ///         0x79, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ldxdw r1, r1[0x00]
1313    ///         0xb7, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r2, 0
1314    ///         0xb7, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r3, 0
1315    ///         0xb7, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r4, 0
1316    ///         0xb7, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r5, 0
1317    ///         0x85, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, // call helper with key 1
1318    ///         0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00  // exit
1319    ///     ];
1320    ///
1321    ///     let mem = &mut [
1322    ///         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01
1323    ///     ];
1324    ///
1325    ///     // Instantiate a VM.
1326    ///     let mut vm = rbpf::EbpfVmRaw::new(Some(prog)).unwrap();
1327    ///
1328    ///     // Register a helper. This helper will store the result of the square root of r1 into r0.
1329    ///     vm.register_helper(1, helpers::sqrti);
1330    ///
1331    ///     let res = vm.execute_program(mem).unwrap();
1332    ///     assert_eq!(res, 0x10000000);
1333    /// }
1334    /// ```
1335    pub fn register_helper(
1336        &mut self,
1337        key: u32,
1338        function: fn(u64, u64, u64, u64, u64) -> u64,
1339    ) -> Result<(), Error> {
1340        self.parent.register_helper(key, function)
1341    }
1342
1343    /// Register an object that the eBPF program is allowed to load and store.
1344    ///
1345    /// When using certain helpers, typically map lookups, the Linux kernel will return pointers
1346    /// to structs that the eBPF program needs to interact with. By default rbpf only allows the
1347    /// program to interact with its stack, the memory buffer and the program itself, making it
1348    /// impossible to supply functional implementations of these helpers.
1349    /// This option allows you to pass in a list of addresses that rbpf will allow the program
1350    /// to load and store to. Given Rust's memory model you will always know these addresses up
1351    /// front when implementing the helpers.
1352    ///
1353    /// Each invocation of this method will append to the set of allowed addresses.
1354    ///
1355    /// # Examples
1356    ///
1357    /// ```
1358    /// use std::iter::FromIterator;
1359    /// use std::ptr::addr_of;
1360    ///
1361    /// struct MapValue {
1362    ///     data: u8
1363    /// }
1364    /// static VALUE: MapValue = MapValue { data: 1 };
1365    ///
1366    /// let prog = &[
1367    ///     0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, 0
1368    ///     0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00  // exit
1369    /// ];
1370    ///
1371    /// // Instantiate a VM.
1372    /// let mut vm = rbpf::EbpfVmRaw::new(Some(prog)).unwrap();
1373    /// let start = addr_of!(VALUE) as u64;
1374    /// let addrs = Vec::from_iter(start..start+size_of::<MapValue>() as u64);
1375    /// vm.register_allowed_memory(&addrs);
1376    /// ```
1377    pub fn register_allowed_memory(&mut self, allowed: &[u64]) -> () {
1378        self.parent.register_allowed_memory(allowed)
1379    }
1380
1381    /// Execute the program loaded, with the given packet data.
1382    ///
1383    /// # Examples
1384    ///
1385    /// ```
1386    /// let prog = &[
1387    ///     0x71, 0x11, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, // ldxb r1[0x04], r1
1388    ///     0x07, 0x01, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, // add r1, 0x22
1389    ///     0xbf, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, r1
1390    ///     0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00  // exit
1391    /// ];
1392    ///
1393    /// let mem = &mut [
1394    ///     0xaa, 0xbb, 0x11, 0x22, 0xcc, 0x27
1395    /// ];
1396    ///
1397    /// let mut vm = rbpf::EbpfVmRaw::new(Some(prog)).unwrap();
1398    ///
1399    /// let res = vm.execute_program(mem).unwrap();
1400    /// assert_eq!(res, 0x22cc);
1401    /// ```
1402    pub fn execute_program(&self, mem: &'a mut [u8]) -> Result<u64, Error> {
1403        self.parent.execute_program(mem, &[])
1404    }
1405
1406    /// JIT-compile the loaded program. No argument required for this.
1407    ///
1408    /// If using helper functions, be sure to register them into the VM before calling this
1409    /// function.
1410    ///
1411    /// # Examples
1412    ///
1413    /// ```
1414    /// let prog = &[
1415    ///     0x71, 0x11, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, // ldxb r1[0x04], r1
1416    ///     0x07, 0x01, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, // add r1, 0x22
1417    ///     0xbf, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, r1
1418    ///     0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00  // exit
1419    /// ];
1420    ///
1421    /// let mut vm = rbpf::EbpfVmRaw::new(Some(prog)).unwrap();
1422    ///
1423    /// vm.jit_compile();
1424    /// ```
1425    #[cfg(all(not(windows), feature = "std"))]
1426    pub fn jit_compile(&mut self) -> Result<(), Error> {
1427        let prog = match self.parent.prog {
1428            Some(prog) => prog,
1429            None => Err(Error::new(
1430                ErrorKind::Other,
1431                "Error: No program set, call prog_set() to load one",
1432            ))?,
1433        };
1434        self.parent.jit = Some(jit::JitMemory::new(
1435            prog,
1436            &self.parent.helpers,
1437            false,
1438            false,
1439        )?);
1440        Ok(())
1441    }
1442
1443    /// Execute the previously JIT-compiled program, with the given packet data, in a manner very
1444    /// similar to `execute_program()`.
1445    ///
1446    /// # Safety
1447    ///
1448    /// **WARNING:** JIT-compiled assembly code is not safe, in particular there is no runtime
1449    /// check for memory access; so if the eBPF program attempts erroneous accesses, this may end
1450    /// very bad (program may segfault). It may be wise to check that the program works with the
1451    /// interpreter before running the JIT-compiled version of it.
1452    ///
1453    /// For this reason the function should be called from within an `unsafe` bloc.
1454    ///
1455    /// # Examples
1456    ///
1457    /// ```
1458    /// let prog = &[
1459    ///     0x71, 0x11, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, // ldxb r1[0x04], r1
1460    ///     0x07, 0x01, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, // add r1, 0x22
1461    ///     0xbf, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, r1
1462    ///     0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00  // exit
1463    /// ];
1464    ///
1465    /// let mem = &mut [
1466    ///     0xaa, 0xbb, 0x11, 0x22, 0xcc, 0x27
1467    /// ];
1468    ///
1469    /// let mut vm = rbpf::EbpfVmRaw::new(Some(prog)).unwrap();
1470    ///
1471    /// # #[cfg(all(not(windows), feature = "std"))]
1472    /// vm.jit_compile();
1473    ///
1474    /// # #[cfg(all(not(windows), feature = "std"))]
1475    /// unsafe {
1476    ///     let res = vm.execute_program_jit(mem).unwrap();
1477    ///     assert_eq!(res, 0x22cc);
1478    /// }
1479    /// ```
1480    #[cfg(all(not(windows), feature = "std"))]
1481    pub unsafe fn execute_program_jit(&self, mem: &'a mut [u8]) -> Result<u64, Error> {
1482        let mut mbuff = vec![];
1483        self.parent.execute_program_jit(mem, &mut mbuff)
1484    }
1485
1486    /// Compile the loaded program using the Cranelift JIT.
1487    ///
1488    /// If using helper functions, be sure to register them into the VM before calling this
1489    /// function.
1490    ///
1491    /// # Examples
1492    ///
1493    /// ```
1494    /// let prog = &[
1495    ///     0x71, 0x11, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, // ldxb r1[0x04], r1
1496    ///     0x07, 0x01, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, // add r1, 0x22
1497    ///     0xbf, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, r1
1498    ///     0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00  // exit
1499    /// ];
1500    ///
1501    /// let mut vm = rbpf::EbpfVmRaw::new(Some(prog)).unwrap();
1502    ///
1503    /// vm.cranelift_compile();
1504    /// ```
1505    #[cfg(feature = "cranelift")]
1506    pub fn cranelift_compile(&mut self) -> Result<(), Error> {
1507        use crate::cranelift::CraneliftCompiler;
1508
1509        let prog = match self.parent.prog {
1510            Some(prog) => prog,
1511            None => Err(Error::new(
1512                ErrorKind::Other,
1513                "Error: No program set, call prog_set() to load one",
1514            ))?,
1515        };
1516
1517        let mut compiler = CraneliftCompiler::new(self.parent.helpers.clone());
1518        let program = compiler.compile_function(prog)?;
1519
1520        self.parent.cranelift_prog = Some(program);
1521        Ok(())
1522    }
1523
1524    /// Execute the previously compiled program, with the given packet data, in a manner very
1525    /// similar to `execute_program()`.
1526    ///
1527    /// # Examples
1528    ///
1529    /// ```
1530    /// let prog = &[
1531    ///     0x71, 0x11, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, // ldxb r1[0x04], r1
1532    ///     0x07, 0x01, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, // add r1, 0x22
1533    ///     0xbf, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, r1
1534    ///     0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00  // exit
1535    /// ];
1536    ///
1537    /// let mem = &mut [
1538    ///     0xaa, 0xbb, 0x11, 0x22, 0xcc, 0x27
1539    /// ];
1540    ///
1541    /// let mut vm = rbpf::EbpfVmRaw::new(Some(prog)).unwrap();
1542    ///
1543    /// vm.cranelift_compile();
1544    ///
1545    /// let res = vm.execute_program_cranelift(mem).unwrap();
1546    /// assert_eq!(res, 0x22cc);
1547    /// ```
1548    #[cfg(feature = "cranelift")]
1549    pub fn execute_program_cranelift(&self, mem: &'a mut [u8]) -> Result<u64, Error> {
1550        let mut mbuff = vec![];
1551        self.parent.execute_program_cranelift(mem, &mut mbuff)
1552    }
1553}
1554
1555/// A virtual machine to run eBPF program. This kind of VM is used for programs that do not work
1556/// with any memory area—no metadata buffer, no packet data either.
1557///
1558/// # Examples
1559///
1560/// ```
1561/// let prog = &[
1562///     0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, 0
1563///     0xb7, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, // mov r1, 1
1564///     0xb7, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, // mov r2, 2
1565///     0xb7, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, // mov r3, 3
1566///     0xb7, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, // mov r4, 4
1567///     0xb7, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, // mov r5, 5
1568///     0xb7, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, // mov r6, 6
1569///     0xb7, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, // mov r7, 7
1570///     0xb7, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, // mov r8, 8
1571///     0x4f, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // or r0, r5
1572///     0x47, 0x00, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, // or r0, 0xa0
1573///     0x57, 0x00, 0x00, 0x00, 0xa3, 0x00, 0x00, 0x00, // and r0, 0xa3
1574///     0xb7, 0x09, 0x00, 0x00, 0x91, 0x00, 0x00, 0x00, // mov r9, 0x91
1575///     0x5f, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // and r0, r9
1576///     0x67, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, // lsh r0, 32
1577///     0x67, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, // lsh r0, 22
1578///     0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // lsh r0, r8
1579///     0x77, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, // rsh r0, 32
1580///     0x77, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, // rsh r0, 19
1581///     0x7f, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // rsh r0, r7
1582///     0xa7, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, // xor r0, 0x03
1583///     0xaf, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // xor r0, r2
1584///     0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00  // exit
1585/// ];
1586///
1587/// // Instantiate a VM.
1588/// let vm = rbpf::EbpfVmNoData::new(Some(prog)).unwrap();
1589///
1590/// // Provide only a reference to the packet data.
1591/// let res = vm.execute_program().unwrap();
1592/// assert_eq!(res, 0x11);
1593/// ```
1594pub struct EbpfVmNoData<'a> {
1595    parent: EbpfVmRaw<'a>,
1596}
1597
1598impl<'a> EbpfVmNoData<'a> {
1599    /// Create a new virtual machine instance, and load an eBPF program into that instance.
1600    /// When attempting to load the program, it passes through a simple verifier.
1601    ///
1602    /// # Examples
1603    ///
1604    /// ```
1605    /// let prog = &[
1606    ///     0xb7, 0x00, 0x00, 0x00, 0x11, 0x22, 0x00, 0x00, // mov r0, 0x2211
1607    ///     0xdc, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, // be16 r0
1608    ///     0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00  // exit
1609    /// ];
1610    ///
1611    /// // Instantiate a VM.
1612    /// let vm = rbpf::EbpfVmNoData::new(Some(prog));
1613    /// ```
1614    pub fn new(prog: Option<&'a [u8]>) -> Result<EbpfVmNoData<'a>, Error> {
1615        let parent = EbpfVmRaw::new(prog)?;
1616        Ok(EbpfVmNoData { parent })
1617    }
1618
1619    /// Load a new eBPF program into the virtual machine instance.
1620    ///
1621    /// # Examples
1622    ///
1623    /// ```
1624    /// let prog1 = &[
1625    ///     0xb7, 0x00, 0x00, 0x00, 0x11, 0x22, 0x00, 0x00, // mov r0, 0x2211
1626    ///     0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00  // exit
1627    /// ];
1628    /// let prog2 = &[
1629    ///     0xb7, 0x00, 0x00, 0x00, 0x11, 0x22, 0x00, 0x00, // mov r0, 0x2211
1630    ///     0xdc, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, // be16 r0
1631    ///     0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00  // exit
1632    /// ];
1633    ///
1634    /// let mut vm = rbpf::EbpfVmNoData::new(Some(prog1)).unwrap();
1635    ///
1636    /// let res = vm.execute_program().unwrap();
1637    /// assert_eq!(res, 0x2211);
1638    ///
1639    /// vm.set_program(prog2);
1640    ///
1641    /// let res = vm.execute_program().unwrap();
1642    /// assert_eq!(res, 0x1122);
1643    /// ```
1644    pub fn set_program(&mut self, prog: &'a [u8]) -> Result<(), Error> {
1645        self.parent.set_program(prog)?;
1646        Ok(())
1647    }
1648
1649    /// Set a new verifier function. The function should return an `Error` if the program should be
1650    /// rejected by the virtual machine. If a program has been loaded to the VM already, the
1651    /// verifier is immediately run.
1652    ///
1653    /// # Examples
1654    ///
1655    /// ```
1656    /// use rbpf::lib::{Error, ErrorKind};
1657    /// use rbpf::ebpf;
1658    ///
1659    /// // Define a simple verifier function.
1660    /// fn verifier(prog: &[u8]) -> Result<(), Error> {
1661    ///     let last_insn = ebpf::get_insn(prog, (prog.len() / ebpf::INSN_SIZE) - 1);
1662    ///     if last_insn.opc != ebpf::EXIT {
1663    ///         return Err(Error::new(ErrorKind::Other,
1664    ///                    "[Verifier] Error: program does not end with “EXIT” instruction"));
1665    ///     }
1666    ///     Ok(())
1667    /// }
1668    ///
1669    /// let prog1 = &[
1670    ///     0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, 0
1671    ///     0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00  // exit
1672    /// ];
1673    ///
1674    /// // Instantiate a VM.
1675    /// let mut vm = rbpf::EbpfVmMbuff::new(Some(prog1)).unwrap();
1676    /// // Change the verifier.
1677    /// vm.set_verifier(verifier).unwrap();
1678    /// ```
1679    pub fn set_verifier(&mut self, verifier: Verifier) -> Result<(), Error> {
1680        self.parent.set_verifier(verifier)
1681    }
1682
1683    /// Register a built-in or user-defined helper function in order to use it later from within
1684    /// the eBPF program. The helper is registered into a hashmap, so the `key` can be any `u32`.
1685    ///
1686    /// If using JIT-compiled eBPF programs, be sure to register all helpers before compiling the
1687    /// program. You should be able to change registered helpers after compiling, but not to add
1688    /// new ones (i.e. with new keys).
1689    ///
1690    /// # Examples
1691    ///
1692    /// ```
1693    /// #[cfg(feature = "std")] {
1694    ///     use rbpf::helpers;
1695    ///
1696    ///     let prog = &[
1697    ///         0xb7, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, // mov r1, 0x010000000
1698    ///         0xb7, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r2, 0
1699    ///         0xb7, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r3, 0
1700    ///         0xb7, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r4, 0
1701    ///         0xb7, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r5, 0
1702    ///         0x85, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, // call helper with key 1
1703    ///         0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00  // exit
1704    ///     ];
1705    ///
1706    ///     let mut vm = rbpf::EbpfVmNoData::new(Some(prog)).unwrap();
1707    ///
1708    ///     // Register a helper. This helper will store the result of the square root of r1 into r0.
1709    ///     vm.register_helper(1, helpers::sqrti).unwrap();
1710    ///
1711    ///     let res = vm.execute_program().unwrap();
1712    ///     assert_eq!(res, 0x1000);
1713    /// }
1714    /// ```
1715    pub fn register_helper(
1716        &mut self,
1717        key: u32,
1718        function: fn(u64, u64, u64, u64, u64) -> u64,
1719    ) -> Result<(), Error> {
1720        self.parent.register_helper(key, function)
1721    }
1722
1723    /// Register an object that the eBPF program is allowed to load and store.
1724    ///
1725    /// When using certain helpers, typically map lookups, the Linux kernel will return pointers
1726    /// to structs that the eBPF program needs to interact with. By default rbpf only allows the
1727    /// program to interact with its stack, the memory buffer and the program itself, making it
1728    /// impossible to supply functional implementations of these helpers.
1729    /// This option allows you to pass in a list of addresses that rbpf will allow the program
1730    /// to load and store to. Given Rust's memory model you will always know these addresses up
1731    /// front when implementing the helpers.
1732    ///
1733    /// Each invocation of this method will append to the set of allowed addresses.
1734    ///
1735    /// # Examples
1736    ///
1737    /// ```
1738    /// use std::iter::FromIterator;
1739    /// use std::ptr::addr_of;
1740    ///
1741    /// struct MapValue {
1742    ///     data: u8
1743    /// }
1744    /// static VALUE: MapValue = MapValue { data: 1 };
1745    ///
1746    /// let prog = &[
1747    ///     0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, 0
1748    ///     0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00  // exit
1749    /// ];
1750    ///
1751    /// // Instantiate a VM.
1752    /// let mut vm = rbpf::EbpfVmNoData::new(Some(prog)).unwrap();
1753    /// let start = addr_of!(VALUE) as u64;
1754    /// let addrs = Vec::from_iter(start..start+size_of::<MapValue>() as u64);
1755    /// vm.register_allowed_memory(&addrs);
1756    /// ```
1757    pub fn register_allowed_memory(&mut self, allowed: &[u64]) -> () {
1758        self.parent.register_allowed_memory(allowed)
1759    }
1760
1761    /// JIT-compile the loaded program. No argument required for this.
1762    ///
1763    /// If using helper functions, be sure to register them into the VM before calling this
1764    /// function.
1765    ///
1766    /// # Examples
1767    ///
1768    /// ```
1769    /// let prog = &[
1770    ///     0xb7, 0x00, 0x00, 0x00, 0x11, 0x22, 0x00, 0x00, // mov r0, 0x2211
1771    ///     0xdc, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, // be16 r0
1772    ///     0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00  // exit
1773    /// ];
1774    ///
1775    /// let mut vm = rbpf::EbpfVmNoData::new(Some(prog)).unwrap();
1776    ///
1777    ///
1778    /// vm.jit_compile();
1779    /// ```
1780    #[cfg(all(not(windows), feature = "std"))]
1781    pub fn jit_compile(&mut self) -> Result<(), Error> {
1782        self.parent.jit_compile()
1783    }
1784
1785    /// Execute the program loaded, without providing pointers to any memory area whatsoever.
1786    ///
1787    /// # Examples
1788    ///
1789    /// ```
1790    /// let prog = &[
1791    ///     0xb7, 0x00, 0x00, 0x00, 0x11, 0x22, 0x00, 0x00, // mov r0, 0x2211
1792    ///     0xdc, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, // be16 r0
1793    ///     0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00  // exit
1794    /// ];
1795    ///
1796    /// let vm = rbpf::EbpfVmNoData::new(Some(prog)).unwrap();
1797    ///
1798    /// // For this kind of VM, the `execute_program()` function needs no argument.
1799    /// let res = vm.execute_program().unwrap();
1800    /// assert_eq!(res, 0x1122);
1801    /// ```
1802    pub fn execute_program(&self) -> Result<u64, Error> {
1803        self.parent.execute_program(&mut [])
1804    }
1805
1806    /// Execute the previously JIT-compiled program, without providing pointers to any memory area
1807    /// whatsoever, in a manner very similar to `execute_program()`.
1808    ///
1809    /// # Safety
1810    ///
1811    /// **WARNING:** JIT-compiled assembly code is not safe, in particular there is no runtime
1812    /// check for memory access; so if the eBPF program attempts erroneous accesses, this may end
1813    /// very bad (program may segfault). It may be wise to check that the program works with the
1814    /// interpreter before running the JIT-compiled version of it.
1815    ///
1816    /// For this reason the function should be called from within an `unsafe` bloc.
1817    ///
1818    /// # Examples
1819    ///
1820    /// ```
1821    /// let prog = &[
1822    ///     0xb7, 0x00, 0x00, 0x00, 0x11, 0x22, 0x00, 0x00, // mov r0, 0x2211
1823    ///     0xdc, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, // be16 r0
1824    ///     0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00  // exit
1825    /// ];
1826    ///
1827    /// let mut vm = rbpf::EbpfVmNoData::new(Some(prog)).unwrap();
1828    ///
1829    /// # #[cfg(all(not(windows), feature = "std"))]
1830    /// vm.jit_compile();
1831    ///
1832    /// # #[cfg(all(not(windows), feature = "std"))]
1833    /// unsafe {
1834    ///     let res = vm.execute_program_jit().unwrap();
1835    ///     assert_eq!(res, 0x1122);
1836    /// }
1837    /// ```
1838    #[cfg(all(not(windows), feature = "std"))]
1839    pub unsafe fn execute_program_jit(&self) -> Result<u64, Error> {
1840        self.parent.execute_program_jit(&mut [])
1841    }
1842
1843    /// Compile the loaded program using the Cranelift JIT.
1844    ///
1845    /// If using helper functions, be sure to register them into the VM before calling this
1846    /// function.
1847    ///
1848    /// # Examples
1849    ///
1850    /// ```
1851    /// let prog = &[
1852    ///     0xb7, 0x00, 0x00, 0x00, 0x11, 0x22, 0x00, 0x00, // mov r0, 0x2211
1853    ///     0xdc, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, // be16 r0
1854    ///     0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00  // exit
1855    /// ];
1856    ///
1857    /// let mut vm = rbpf::EbpfVmNoData::new(Some(prog)).unwrap();
1858    ///
1859    ///
1860    /// vm.cranelift_compile();
1861    /// ```
1862    #[cfg(feature = "cranelift")]
1863    pub fn cranelift_compile(&mut self) -> Result<(), Error> {
1864        self.parent.cranelift_compile()
1865    }
1866
1867    /// Execute the previously JIT-compiled program, without providing pointers to any memory area
1868    /// whatsoever, in a manner very similar to `execute_program()`.
1869    ///
1870    /// # Examples
1871    ///
1872    /// ```
1873    /// let prog = &[
1874    ///     0xb7, 0x00, 0x00, 0x00, 0x11, 0x22, 0x00, 0x00, // mov r0, 0x2211
1875    ///     0xdc, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, // be16 r0
1876    ///     0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00  // exit
1877    /// ];
1878    ///
1879    /// let mut vm = rbpf::EbpfVmNoData::new(Some(prog)).unwrap();
1880    ///
1881    /// vm.cranelift_compile();
1882    ///
1883    /// let res = vm.execute_program_cranelift().unwrap();
1884    /// assert_eq!(res, 0x1122);
1885    /// ```
1886    #[cfg(feature = "cranelift")]
1887    pub fn execute_program_cranelift(&self) -> Result<u64, Error> {
1888        self.parent.execute_program_cranelift(&mut [])
1889    }
1890}