123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782 |
- // SPDX-License-Identifier: (Apache-2.0 OR MIT)
- // Derived from uBPF <https://github.com/iovisor/ubpf>
- // Copyright 2016 6WIND S.A. <quentin.monnet@6wind.com>
- // Copyright 2023 Isovalent, Inc. <quentin@isovalent.com>
- //! Virtual machine and JIT compiler for eBPF programs.
- #![doc(
- html_logo_url = "https://raw.githubusercontent.com/qmonnet/rbpf/main/misc/rbpf.png",
- html_favicon_url = "https://raw.githubusercontent.com/qmonnet/rbpf/main/misc/rbpf.ico"
- )]
- #![warn(missing_docs)]
- // There are unused mut warnings due to unsafe code.
- #![allow(unused_mut)]
- // Allows old-style clippy
- #![allow(renamed_and_removed_lints)]
- #![cfg_attr(
- clippy,
- allow(
- redundant_field_names,
- single_match,
- cast_lossless,
- doc_markdown,
- match_same_arms,
- unreadable_literal
- )
- )]
- // Configures the crate to be `no_std` when `std` feature is disabled.
- #![cfg_attr(not(feature = "std"), no_std)]
- extern crate alloc;
- use alloc::{collections::BTreeMap, format, vec, vec::Vec};
- use byteorder::{ByteOrder, LittleEndian};
- type HashMap<K, V> = BTreeMap<K, V>;
- #[cfg(feature = "cranelift")]
- type HashSet<T> = alloc::collections::BTreeSet<T>;
- mod asm_parser;
- pub mod assembler;
- #[cfg(feature = "cranelift")]
- mod cranelift;
- pub mod disassembler;
- pub mod ebpf;
- pub mod helpers;
- pub mod insn_builder;
- mod interpreter;
- #[cfg(all(not(windows), feature = "std"))]
- mod jit;
- #[cfg(not(feature = "std"))]
- mod no_std_error;
- mod stack;
- mod verifier;
- #[cfg(feature = "std")]
- pub use std::io::{Error, ErrorKind};
- /// In no_std we use a custom implementation of the error which acts as a
- /// replacement for the io Error.
- #[cfg(not(feature = "std"))]
- pub use crate::no_std_error::{Error, ErrorKind};
- /// eBPF verification function that returns an error if the program does not meet its requirements.
- ///
- /// Some examples of things the verifier may reject the program for:
- ///
- /// - Program does not terminate.
- /// - Unknown instructions.
- /// - Bad formed instruction.
- /// - Unknown eBPF helper index.
- pub type Verifier = fn(prog: &[u8]) -> Result<(), Error>;
- /// eBPF helper function.
- pub type Helper = fn(u64, u64, u64, u64, u64) -> u64;
- // A metadata buffer with two offset indications. It can be used in one kind of eBPF VM to simulate
- // the use of a metadata buffer each time the program is executed, without the user having to
- // actually handle it. The offsets are used to tell the VM where in the buffer the pointers to
- // packet data start and end should be stored each time the program is run on a new packet.
- struct MetaBuff {
- data_offset: usize,
- data_end_offset: usize,
- buffer: Vec<u8>,
- }
- /// A virtual machine to run eBPF program. This kind of VM is used for programs expecting to work
- /// on a metadata buffer containing pointers to packet data.
- ///
- /// # Examples
- ///
- /// ```
- /// let prog = &[
- /// 0x79, 0x11, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, // Load mem from mbuff at offset 8 into R1.
- /// 0x69, 0x10, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // ldhx r1[2], r0
- /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit
- /// ];
- /// let mem = &mut [
- /// 0xaa, 0xbb, 0x11, 0x22, 0xcc, 0xdd
- /// ];
- ///
- /// // Just for the example we create our metadata buffer from scratch, and we store the pointers
- /// // to packet data start and end in it.
- /// let mut mbuff = [0u8; 32];
- /// unsafe {
- /// let mut data = mbuff.as_ptr().offset(8) as *mut u64;
- /// let mut data_end = mbuff.as_ptr().offset(24) as *mut u64;
- /// *data = mem.as_ptr() as u64;
- /// *data_end = mem.as_ptr() as u64 + mem.len() as u64;
- /// }
- ///
- /// // Instantiate a VM.
- /// let mut vm = rbpf::EbpfVmMbuff::new(Some(prog)).unwrap();
- ///
- /// // Provide both a reference to the packet data, and to the metadata buffer.
- /// let res = vm.execute_program(mem, &mut mbuff).unwrap();
- /// assert_eq!(res, 0x2211);
- /// ```
- pub struct EbpfVmMbuff<'a> {
- prog: Option<&'a [u8]>,
- verifier: Verifier,
- #[cfg(all(not(windows), feature = "std"))]
- jit: Option<jit::JitMemory<'a>>,
- #[cfg(feature = "cranelift")]
- cranelift_prog: Option<cranelift::CraneliftProgram>,
- helpers: HashMap<u32, ebpf::Helper>,
- }
- impl<'a> EbpfVmMbuff<'a> {
- /// Create a new virtual machine instance, and load an eBPF program into that instance.
- /// When attempting to load the program, it passes through a simple verifier.
- ///
- /// # Examples
- ///
- /// ```
- /// let prog = &[
- /// 0x79, 0x11, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, // Load mem from mbuff into R1.
- /// 0x69, 0x10, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // ldhx r1[2], r0
- /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit
- /// ];
- ///
- /// // Instantiate a VM.
- /// let mut vm = rbpf::EbpfVmMbuff::new(Some(prog)).unwrap();
- /// ```
- pub fn new(prog: Option<&'a [u8]>) -> Result<EbpfVmMbuff<'a>, Error> {
- if let Some(prog) = prog {
- verifier::check(prog)?;
- }
- Ok(EbpfVmMbuff {
- prog,
- verifier: verifier::check,
- #[cfg(all(not(windows), feature = "std"))]
- jit: None,
- #[cfg(feature = "cranelift")]
- cranelift_prog: None,
- helpers: HashMap::new(),
- })
- }
- /// Load a new eBPF program into the virtual machine instance.
- ///
- /// # Examples
- ///
- /// ```
- /// let prog1 = &[
- /// 0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, 0
- /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit
- /// ];
- /// let prog2 = &[
- /// 0x79, 0x11, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, // Load mem from mbuff into R1.
- /// 0x69, 0x10, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // ldhx r1[2], r0
- /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit
- /// ];
- ///
- /// // Instantiate a VM.
- /// let mut vm = rbpf::EbpfVmMbuff::new(Some(prog1)).unwrap();
- /// vm.set_program(prog2).unwrap();
- /// ```
- pub fn set_program(&mut self, prog: &'a [u8]) -> Result<(), Error> {
- (self.verifier)(prog)?;
- self.prog = Some(prog);
- Ok(())
- }
- /// Set a new verifier function. The function should return an `Error` if the program should be
- /// rejected by the virtual machine. If a program has been loaded to the VM already, the
- /// verifier is immediately run.
- ///
- /// # Examples
- ///
- /// ```
- /// use rbpf::{Error, ErrorKind};
- /// use rbpf::ebpf;
- ///
- /// // Define a simple verifier function.
- /// fn verifier(prog: &[u8]) -> Result<(), Error> {
- /// let last_insn = ebpf::get_insn(prog, (prog.len() / ebpf::INSN_SIZE) - 1);
- /// if last_insn.opc != ebpf::EXIT {
- /// return Err(Error::new(ErrorKind::Other,
- /// "[Verifier] Error: program does not end with “EXIT” instruction"));
- /// }
- /// Ok(())
- /// }
- ///
- /// let prog1 = &[
- /// 0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, 0
- /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit
- /// ];
- ///
- /// // Instantiate a VM.
- /// let mut vm = rbpf::EbpfVmMbuff::new(Some(prog1)).unwrap();
- /// // Change the verifier.
- /// vm.set_verifier(verifier).unwrap();
- /// ```
- pub fn set_verifier(&mut self, verifier: Verifier) -> Result<(), Error> {
- if let Some(prog) = self.prog {
- verifier(prog)?;
- }
- self.verifier = verifier;
- Ok(())
- }
- /// Register a built-in or user-defined helper function in order to use it later from within
- /// the eBPF program. The helper is registered into a hashmap, so the `key` can be any `u32`.
- ///
- /// If using JIT-compiled eBPF programs, be sure to register all helpers before compiling the
- /// program. You should be able to change registered helpers after compiling, but not to add
- /// new ones (i.e. with new keys).
- ///
- /// # Examples
- ///
- /// ```
- /// use rbpf::helpers;
- ///
- /// // This program was compiled with clang, from a C program containing the following single
- /// // instruction: `return bpf_trace_printk("foo %c %c %c\n", 10, 1, 2, 3);`
- /// let prog = &[
- /// 0x18, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // load 0 as u64 into r1 (That would be
- /// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // replaced by tc by the address of
- /// // the format string, in the .map
- /// // section of the ELF file).
- /// 0xb7, 0x02, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, // mov r2, 10
- /// 0xb7, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, // mov r3, 1
- /// 0xb7, 0x04, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, // mov r4, 2
- /// 0xb7, 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, // mov r5, 3
- /// 0x85, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, // call helper with key 6
- /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit
- /// ];
- ///
- /// // Instantiate a VM.
- /// let mut vm = rbpf::EbpfVmMbuff::new(Some(prog)).unwrap();
- ///
- /// // Register a helper.
- /// // On running the program this helper will print the content of registers r3, r4 and r5 to
- /// // standard output.
- /// # #[cfg(feature = "std")]
- /// vm.register_helper(6, helpers::bpf_trace_printf).unwrap();
- /// ```
- pub fn register_helper(&mut self, key: u32, function: Helper) -> Result<(), Error> {
- self.helpers.insert(key, function);
- Ok(())
- }
- /// Execute the program loaded, with the given packet data and metadata buffer.
- ///
- /// If the program is made to be compatible with Linux kernel, it is expected to load the
- /// address of the beginning and of the end of the memory area used for packet data from the
- /// metadata buffer, at some appointed offsets. It is up to the user to ensure that these
- /// pointers are correctly stored in the buffer.
- ///
- /// # Examples
- ///
- /// ```
- /// let prog = &[
- /// 0x79, 0x11, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, // Load mem from mbuff into R1.
- /// 0x69, 0x10, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // ldhx r1[2], r0
- /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit
- /// ];
- /// let mem = &mut [
- /// 0xaa, 0xbb, 0x11, 0x22, 0xcc, 0xdd
- /// ];
- ///
- /// // Just for the example we create our metadata buffer from scratch, and we store the
- /// // pointers to packet data start and end in it.
- /// let mut mbuff = [0u8; 32];
- /// unsafe {
- /// let mut data = mbuff.as_ptr().offset(8) as *mut u64;
- /// let mut data_end = mbuff.as_ptr().offset(24) as *mut u64;
- /// *data = mem.as_ptr() as u64;
- /// *data_end = mem.as_ptr() as u64 + mem.len() as u64;
- /// }
- ///
- /// // Instantiate a VM.
- /// let mut vm = rbpf::EbpfVmMbuff::new(Some(prog)).unwrap();
- ///
- /// // Provide both a reference to the packet data, and to the metadata buffer.
- /// let res = vm.execute_program(mem, &mut mbuff).unwrap();
- /// assert_eq!(res, 0x2211);
- /// ```
- pub fn execute_program(&self, mem: &[u8], mbuff: &[u8]) -> Result<u64, Error> {
- interpreter::execute_program(self.prog, mem, mbuff, &self.helpers)
- }
- /// JIT-compile the loaded program. No argument required for this.
- ///
- /// If using helper functions, be sure to register them into the VM before calling this
- /// function.
- ///
- /// # Examples
- ///
- /// ```
- /// let prog = &[
- /// 0x79, 0x11, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, // Load mem from mbuff into R1.
- /// 0x69, 0x10, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // ldhx r1[2], r0
- /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit
- /// ];
- ///
- /// // Instantiate a VM.
- /// let mut vm = rbpf::EbpfVmMbuff::new(Some(prog)).unwrap();
- ///
- /// vm.jit_compile();
- /// ```
- #[cfg(all(not(windows), feature = "std"))]
- pub fn jit_compile(&mut self) -> Result<(), Error> {
- let prog = match self.prog {
- Some(prog) => prog,
- None => Err(Error::new(
- ErrorKind::Other,
- "Error: No program set, call prog_set() to load one",
- ))?,
- };
- self.jit = Some(jit::JitMemory::new(prog, &self.helpers, true, false)?);
- Ok(())
- }
- /// Execute the previously JIT-compiled program, with the given packet data and metadata
- /// buffer, in a manner very similar to `execute_program()`.
- ///
- /// If the program is made to be compatible with Linux kernel, it is expected to load the
- /// address of the beginning and of the end of the memory area used for packet data from the
- /// metadata buffer, at some appointed offsets. It is up to the user to ensure that these
- /// pointers are correctly stored in the buffer.
- ///
- /// # Safety
- ///
- /// **WARNING:** JIT-compiled assembly code is not safe, in particular there is no runtime
- /// check for memory access; so if the eBPF program attempts erroneous accesses, this may end
- /// very bad (program may segfault). It may be wise to check that the program works with the
- /// interpreter before running the JIT-compiled version of it.
- ///
- /// For this reason the function should be called from within an `unsafe` bloc.
- ///
- /// # Examples
- ///
- /// ```
- /// let prog = &[
- /// 0x79, 0x11, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, // Load mem from mbuff into r1.
- /// 0x69, 0x10, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // ldhx r1[2], r0
- /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit
- /// ];
- /// let mem = &mut [
- /// 0xaa, 0xbb, 0x11, 0x22, 0xcc, 0xdd
- /// ];
- ///
- /// // Just for the example we create our metadata buffer from scratch, and we store the
- /// // pointers to packet data start and end in it.
- /// let mut mbuff = [0u8; 32];
- /// unsafe {
- /// let mut data = mbuff.as_ptr().offset(8) as *mut u64;
- /// let mut data_end = mbuff.as_ptr().offset(24) as *mut u64;
- /// *data = mem.as_ptr() as u64;
- /// *data_end = mem.as_ptr() as u64 + mem.len() as u64;
- /// }
- ///
- /// // Instantiate a VM.
- /// let mut vm = rbpf::EbpfVmMbuff::new(Some(prog)).unwrap();
- ///
- /// # #[cfg(all(not(windows), feature = "std"))]
- /// vm.jit_compile();
- ///
- /// // Provide both a reference to the packet data, and to the metadata buffer.
- /// # #[cfg(all(not(windows), feature = "std"))]
- /// unsafe {
- /// let res = vm.execute_program_jit(mem, &mut mbuff).unwrap();
- /// assert_eq!(res, 0x2211);
- /// }
- /// ```
- #[cfg(all(not(windows), feature = "std"))]
- pub unsafe fn execute_program_jit(
- &self,
- mem: &mut [u8],
- mbuff: &'a mut [u8],
- ) -> Result<u64, Error> {
- // If packet data is empty, do not send the address of an empty slice; send a null pointer
- // as first argument instead, as this is uBPF's behavior (empty packet should not happen
- // in the kernel; anyway the verifier would prevent the use of uninitialized registers).
- // See `mul_loop` test.
- let mem_ptr = match mem.len() {
- 0 => std::ptr::null_mut(),
- _ => mem.as_ptr() as *mut u8,
- };
- // The last two arguments are not used in this function. They would be used if there was a
- // need to indicate to the JIT at which offset in the mbuff mem_ptr and mem_ptr + mem.len()
- // should be stored; this is what happens with struct EbpfVmFixedMbuff.
- match &self.jit {
- Some(jit) => Ok(jit.get_prog()(
- mbuff.as_ptr() as *mut u8,
- mbuff.len(),
- mem_ptr,
- mem.len(),
- 0,
- 0,
- )),
- None => Err(Error::new(
- ErrorKind::Other,
- "Error: program has not been JIT-compiled",
- )),
- }
- }
- /// Compile the loaded program using the Cranelift JIT.
- ///
- /// If using helper functions, be sure to register them into the VM before calling this
- /// function.
- ///
- /// # Examples
- ///
- /// ```
- /// let prog = &[
- /// 0x79, 0x11, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, // Load mem from mbuff into R1.
- /// 0x69, 0x10, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // ldhx r1[2], r0
- /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit
- /// ];
- ///
- /// // Instantiate a VM.
- /// let mut vm = rbpf::EbpfVmMbuff::new(Some(prog)).unwrap();
- ///
- /// vm.cranelift_compile();
- /// ```
- #[cfg(feature = "cranelift")]
- pub fn cranelift_compile(&mut self) -> Result<(), Error> {
- use crate::cranelift::CraneliftCompiler;
- let prog = match self.prog {
- Some(prog) => prog,
- None => Err(Error::new(
- ErrorKind::Other,
- "Error: No program set, call prog_set() to load one",
- ))?,
- };
- let mut compiler = CraneliftCompiler::new(self.helpers.clone());
- let program = compiler.compile_function(prog)?;
- self.cranelift_prog = Some(program);
- Ok(())
- }
- /// Execute the previously compiled program, with the given packet data and metadata
- /// buffer, in a manner very similar to `execute_program()`.
- ///
- /// If the program is made to be compatible with Linux kernel, it is expected to load the
- /// address of the beginning and of the end of the memory area used for packet data from the
- /// metadata buffer, at some appointed offsets. It is up to the user to ensure that these
- /// pointers are correctly stored in the buffer.
- ///
- ///
- /// # Examples
- ///
- /// ```
- /// let prog = &[
- /// 0x79, 0x11, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, // Load mem from mbuff into r1.
- /// 0x69, 0x10, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // ldhx r1[2], r0
- /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit
- /// ];
- /// let mem = &mut [
- /// 0xaa, 0xbb, 0x11, 0x22, 0xcc, 0xdd
- /// ];
- ///
- /// // Just for the example we create our metadata buffer from scratch, and we store the
- /// // pointers to packet data start and end in it.
- /// let mut mbuff = [0u8; 32];
- /// unsafe {
- /// let mut data = mbuff.as_ptr().offset(8) as *mut u64;
- /// let mut data_end = mbuff.as_ptr().offset(24) as *mut u64;
- /// *data = mem.as_ptr() as u64;
- /// *data_end = mem.as_ptr() as u64 + mem.len() as u64;
- /// }
- ///
- /// // Instantiate a VM.
- /// let mut vm = rbpf::EbpfVmMbuff::new(Some(prog)).unwrap();
- ///
- /// vm.cranelift_compile();
- ///
- /// // Provide both a reference to the packet data, and to the metadata buffer.
- /// let res = vm.execute_program_cranelift(mem, &mut mbuff).unwrap();
- /// assert_eq!(res, 0x2211);
- /// ```
- #[cfg(feature = "cranelift")]
- pub fn execute_program_cranelift(
- &self,
- mem: &mut [u8],
- mbuff: &'a mut [u8],
- ) -> Result<u64, Error> {
- // If packet data is empty, do not send the address of an empty slice; send a null pointer
- // as first argument instead, as this is uBPF's behavior (empty packet should not happen
- // in the kernel; anyway the verifier would prevent the use of uninitialized registers).
- // See `mul_loop` test.
- let mem_ptr = match mem.len() {
- 0 => core::ptr::null_mut(),
- _ => mem.as_ptr() as *mut u8,
- };
- // The last two arguments are not used in this function. They would be used if there was a
- // need to indicate to the JIT at which offset in the mbuff mem_ptr and mem_ptr + mem.len()
- // should be stored; this is what happens with struct EbpfVmFixedMbuff.
- match &self.cranelift_prog {
- Some(prog) => {
- Ok(prog.execute(mem_ptr, mem.len(), mbuff.as_ptr() as *mut u8, mbuff.len()))
- }
- None => Err(Error::new(
- ErrorKind::Other,
- "Error: program has not been compiled with cranelift",
- )),
- }
- }
- }
- /// A virtual machine to run eBPF program. This kind of VM is used for programs expecting to work
- /// on a metadata buffer containing pointers to packet data, but it internally handles the buffer
- /// so as to save the effort to manually handle the metadata buffer for the user.
- ///
- /// This struct implements a static internal buffer that is passed to the program. The user has to
- /// indicate the offset values at which the eBPF program expects to find the start and the end of
- /// packet data in the buffer. On calling the `execute_program()` or `execute_program_jit()` functions, the
- /// struct automatically updates the addresses in this static buffer, at the appointed offsets, for
- /// the start and the end of the packet data the program is called upon.
- ///
- /// # Examples
- ///
- /// This was compiled with clang from the following program, in C:
- ///
- /// ```c
- /// #include <linux/bpf.h>
- /// #include "path/to/linux/samples/bpf/bpf_helpers.h"
- ///
- /// SEC(".classifier")
- /// int classifier(struct __sk_buff *skb)
- /// {
- /// void *data = (void *)(long)skb->data;
- /// void *data_end = (void *)(long)skb->data_end;
- ///
- /// // Check program is long enough.
- /// if (data + 5 > data_end)
- /// return 0;
- ///
- /// return *((char *)data + 5);
- /// }
- /// ```
- ///
- /// Some small modifications have been brought to have it work, see comments.
- ///
- /// ```
- /// let prog = &[
- /// 0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, 0
- /// // Here opcode 0x61 had to be replace by 0x79 so as to load a 8-bytes long address.
- /// // Also, offset 0x4c had to be replace with e.g. 0x40 so as to prevent the two pointers
- /// // from overlapping in the buffer.
- /// 0x79, 0x12, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, // load pointer to mem from r1[0x40] to r2
- /// 0x07, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, // add r2, 5
- /// // Here opcode 0x61 had to be replace by 0x79 so as to load a 8-bytes long address.
- /// 0x79, 0x11, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, // load ptr to mem_end from r1[0x50] to r1
- /// 0x2d, 0x12, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, // if r2 > r1 skip 3 instructions
- /// 0x71, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // load r2 (= *(mem + 5)) into r0
- /// 0x67, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, // r0 >>= 56
- /// 0xc7, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, // r0 <<= 56 (arsh) extend byte sign to u64
- /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit
- /// ];
- /// let mem1 = &mut [
- /// 0xaa, 0xbb, 0x11, 0x22, 0xcc, 0xdd
- /// ];
- /// let mem2 = &mut [
- /// 0xaa, 0xbb, 0x11, 0x22, 0xcc, 0x27
- /// ];
- ///
- /// // Instantiate a VM. Note that we provide the start and end offsets for mem pointers.
- /// let mut vm = rbpf::EbpfVmFixedMbuff::new(Some(prog), 0x40, 0x50).unwrap();
- ///
- /// // Provide only a reference to the packet data. We do not manage the metadata buffer.
- /// let res = vm.execute_program(mem1).unwrap();
- /// assert_eq!(res, 0xffffffffffffffdd);
- ///
- /// let res = vm.execute_program(mem2).unwrap();
- /// assert_eq!(res, 0x27);
- /// ```
- pub struct EbpfVmFixedMbuff<'a> {
- parent: EbpfVmMbuff<'a>,
- mbuff: MetaBuff,
- }
- impl<'a> EbpfVmFixedMbuff<'a> {
- /// Create a new virtual machine instance, and load an eBPF program into that instance.
- /// When attempting to load the program, it passes through a simple verifier.
- ///
- /// # Examples
- ///
- /// ```
- /// let prog = &[
- /// 0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, 0
- /// 0x79, 0x12, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, // load mem from r1[0x40] to r2
- /// 0x07, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, // add r2, 5
- /// 0x79, 0x11, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, // load mem_end from r1[0x50] to r1
- /// 0x2d, 0x12, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, // if r2 > r1 skip 3 instructions
- /// 0x71, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // load r2 (= *(mem + 5)) into r0
- /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit
- /// ];
- ///
- /// // Instantiate a VM. Note that we provide the start and end offsets for mem pointers.
- /// let mut vm = rbpf::EbpfVmFixedMbuff::new(Some(prog), 0x40, 0x50).unwrap();
- /// ```
- pub fn new(
- prog: Option<&'a [u8]>,
- data_offset: usize,
- data_end_offset: usize,
- ) -> Result<EbpfVmFixedMbuff<'a>, Error> {
- let parent = EbpfVmMbuff::new(prog)?;
- let get_buff_len = |x: usize, y: usize| if x >= y { x + 8 } else { y + 8 };
- let buffer = vec![0u8; get_buff_len(data_offset, data_end_offset)];
- let mbuff = MetaBuff {
- data_offset,
- data_end_offset,
- buffer,
- };
- Ok(EbpfVmFixedMbuff { parent, mbuff })
- }
- /// Load a new eBPF program into the virtual machine instance.
- ///
- /// At the same time, load new offsets for storing pointers to start and end of packet data in
- /// the internal metadata buffer.
- ///
- /// # Examples
- ///
- /// ```
- /// let prog1 = &[
- /// 0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, 0
- /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit
- /// ];
- /// let prog2 = &[
- /// 0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, 0
- /// 0x79, 0x12, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, // load mem from r1[0x40] to r2
- /// 0x07, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, // add r2, 5
- /// 0x79, 0x11, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, // load mem_end from r1[0x50] to r1
- /// 0x2d, 0x12, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, // if r2 > r1 skip 3 instructions
- /// 0x71, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // load r2 (= *(mem + 5)) into r0
- /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit
- /// ];
- ///
- /// let mem = &mut [
- /// 0xaa, 0xbb, 0x11, 0x22, 0xcc, 0x27,
- /// ];
- ///
- /// let mut vm = rbpf::EbpfVmFixedMbuff::new(Some(prog1), 0, 0).unwrap();
- /// vm.set_program(prog2, 0x40, 0x50);
- ///
- /// let res = vm.execute_program(mem).unwrap();
- /// assert_eq!(res, 0x27);
- /// ```
- pub fn set_program(
- &mut self,
- prog: &'a [u8],
- data_offset: usize,
- data_end_offset: usize,
- ) -> Result<(), Error> {
- let get_buff_len = |x: usize, y: usize| if x >= y { x + 8 } else { y + 8 };
- let buffer = vec![0u8; get_buff_len(data_offset, data_end_offset)];
- self.mbuff.buffer = buffer;
- self.mbuff.data_offset = data_offset;
- self.mbuff.data_end_offset = data_end_offset;
- self.parent.set_program(prog)?;
- Ok(())
- }
- /// Set a new verifier function. The function should return an `Error` if the program should be
- /// rejected by the virtual machine. If a program has been loaded to the VM already, the
- /// verifier is immediately run.
- ///
- /// # Examples
- ///
- /// ```
- /// use rbpf::{Error, ErrorKind};
- /// use rbpf::ebpf;
- ///
- /// // Define a simple verifier function.
- /// fn verifier(prog: &[u8]) -> Result<(), Error> {
- /// let last_insn = ebpf::get_insn(prog, (prog.len() / ebpf::INSN_SIZE) - 1);
- /// if last_insn.opc != ebpf::EXIT {
- /// return Err(Error::new(ErrorKind::Other,
- /// "[Verifier] Error: program does not end with “EXIT” instruction"));
- /// }
- /// Ok(())
- /// }
- ///
- /// let prog1 = &[
- /// 0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, 0
- /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit
- /// ];
- ///
- /// // Instantiate a VM.
- /// let mut vm = rbpf::EbpfVmMbuff::new(Some(prog1)).unwrap();
- /// // Change the verifier.
- /// vm.set_verifier(verifier).unwrap();
- /// ```
- pub fn set_verifier(&mut self, verifier: Verifier) -> Result<(), Error> {
- self.parent.set_verifier(verifier)
- }
- /// Register a built-in or user-defined helper function in order to use it later from within
- /// the eBPF program. The helper is registered into a hashmap, so the `key` can be any `u32`.
- ///
- /// If using JIT-compiled eBPF programs, be sure to register all helpers before compiling the
- /// program. You should be able to change registered helpers after compiling, but not to add
- /// new ones (i.e. with new keys).
- ///
- /// # Examples
- ///
- /// ```
- /// #[cfg(feature = "std")] {
- /// use rbpf::helpers;
- ///
- /// // This program was compiled with clang, from a C program containing the following single
- /// // instruction: `return bpf_trace_printk("foo %c %c %c\n", 10, 1, 2, 3);`
- /// let prog = &[
- /// 0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, 0
- /// 0x79, 0x12, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, // load mem from r1[0x40] to r2
- /// 0x07, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, // add r2, 5
- /// 0x79, 0x11, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, // load mem_end from r1[0x50] to r1
- /// 0x2d, 0x12, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, // if r2 > r1 skip 6 instructions
- /// 0x71, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // load r2 (= *(mem + 5)) into r1
- /// 0xb7, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r2, 0
- /// 0xb7, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r3, 0
- /// 0xb7, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r4, 0
- /// 0xb7, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r5, 0
- /// 0x85, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, // call helper with key 1
- /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit
- /// ];
- ///
- /// let mem = &mut [
- /// 0xaa, 0xbb, 0x11, 0x22, 0xcc, 0x09,
- /// ];
- ///
- /// // Instantiate a VM.
- /// let mut vm = rbpf::EbpfVmFixedMbuff::new(Some(prog), 0x40, 0x50).unwrap();
- ///
- /// // Register a helper. This helper will store the result of the square root of r1 into r0.
- /// vm.register_helper(1, helpers::sqrti);
- ///
- /// let res = vm.execute_program(mem).unwrap();
- /// assert_eq!(res, 3);
- /// }
- /// ```
- pub fn register_helper(
- &mut self,
- key: u32,
- function: fn(u64, u64, u64, u64, u64) -> u64,
- ) -> Result<(), Error> {
- self.parent.register_helper(key, function)
- }
- /// Execute the program loaded, with the given packet data.
- ///
- /// If the program is made to be compatible with Linux kernel, it is expected to load the
- /// address of the beginning and of the end of the memory area used for packet data from some
- /// metadata buffer, which in the case of this VM is handled internally. The offsets at which
- /// the addresses should be placed should have be set at the creation of the VM.
- ///
- /// # Examples
- ///
- /// ```
- /// let prog = &[
- /// 0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, 0
- /// 0x79, 0x12, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, // load mem from r1[0x40] to r2
- /// 0x07, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, // add r2, 5
- /// 0x79, 0x11, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, // load mem_end from r1[0x50] to r1
- /// 0x2d, 0x12, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, // if r2 > r1 skip 3 instructions
- /// 0x71, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // load r2 (= *(mem + 5)) into r0
- /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit
- /// ];
- /// let mem = &mut [
- /// 0xaa, 0xbb, 0x11, 0x22, 0xcc, 0xdd
- /// ];
- ///
- /// // Instantiate a VM. Note that we provide the start and end offsets for mem pointers.
- /// let mut vm = rbpf::EbpfVmFixedMbuff::new(Some(prog), 0x40, 0x50).unwrap();
- ///
- /// // Provide only a reference to the packet data. We do not manage the metadata buffer.
- /// let res = vm.execute_program(mem).unwrap();
- /// assert_eq!(res, 0xdd);
- /// ```
- pub fn execute_program(&mut self, mem: &'a mut [u8]) -> Result<u64, Error> {
- let l = self.mbuff.buffer.len();
- // Can this ever happen? Probably not, should be ensured at mbuff creation.
- if self.mbuff.data_offset + 8 > l || self.mbuff.data_end_offset + 8 > l {
- Err(Error::new(ErrorKind::Other, format!("Error: buffer too small ({:?}), cannot use data_offset {:?} and data_end_offset {:?}",
- l, self.mbuff.data_offset, self.mbuff.data_end_offset)))?;
- }
- LittleEndian::write_u64(
- &mut self.mbuff.buffer[(self.mbuff.data_offset)..],
- mem.as_ptr() as u64,
- );
- LittleEndian::write_u64(
- &mut self.mbuff.buffer[(self.mbuff.data_end_offset)..],
- mem.as_ptr() as u64 + mem.len() as u64,
- );
- self.parent.execute_program(mem, &self.mbuff.buffer)
- }
- /// JIT-compile the loaded program. No argument required for this.
- ///
- /// If using helper functions, be sure to register them into the VM before calling this
- /// function.
- ///
- /// # Examples
- ///
- /// ```
- /// let prog = &[
- /// 0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, 0
- /// 0x79, 0x12, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, // load mem from r1[0x40] to r2
- /// 0x07, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, // add r2, 5
- /// 0x79, 0x11, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, // load mem_end from r1[0x50] to r1
- /// 0x2d, 0x12, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, // if r2 > r1 skip 3 instructions
- /// 0x71, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // load r2 (= *(mem + 5)) into r0
- /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit
- /// ];
- ///
- /// // Instantiate a VM. Note that we provide the start and end offsets for mem pointers.
- /// let mut vm = rbpf::EbpfVmFixedMbuff::new(Some(prog), 0x40, 0x50).unwrap();
- ///
- /// vm.jit_compile();
- /// ```
- #[cfg(all(not(windows), feature = "std"))]
- pub fn jit_compile(&mut self) -> Result<(), Error> {
- let prog = match self.parent.prog {
- Some(prog) => prog,
- None => Err(Error::new(
- ErrorKind::Other,
- "Error: No program set, call prog_set() to load one",
- ))?,
- };
- self.parent.jit = Some(jit::JitMemory::new(prog, &self.parent.helpers, true, true)?);
- Ok(())
- }
- /// Execute the previously JIT-compiled program, with the given packet data, in a manner very
- /// similar to `execute_program()`.
- ///
- /// If the program is made to be compatible with Linux kernel, it is expected to load the
- /// address of the beginning and of the end of the memory area used for packet data from some
- /// metadata buffer, which in the case of this VM is handled internally. The offsets at which
- /// the addresses should be placed should have be set at the creation of the VM.
- ///
- /// # Safety
- ///
- /// **WARNING:** JIT-compiled assembly code is not safe, in particular there is no runtime
- /// check for memory access; so if the eBPF program attempts erroneous accesses, this may end
- /// very bad (program may segfault). It may be wise to check that the program works with the
- /// interpreter before running the JIT-compiled version of it.
- ///
- /// For this reason the function should be called from within an `unsafe` bloc.
- ///
- /// # Examples
- ///
- /// ```
- /// let prog = &[
- /// 0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, 0
- /// 0x79, 0x12, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, // load mem from r1[0x40] to r2
- /// 0x07, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, // add r2, 5
- /// 0x79, 0x11, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, // load mem_end from r1[0x50] to r1
- /// 0x2d, 0x12, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, // if r2 > r1 skip 3 instructions
- /// 0x71, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // load r2 (= *(mem + 5)) into r0
- /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit
- /// ];
- /// let mem = &mut [
- /// 0xaa, 0xbb, 0x11, 0x22, 0xcc, 0xdd
- /// ];
- ///
- /// // Instantiate a VM. Note that we provide the start and end offsets for mem pointers.
- /// let mut vm = rbpf::EbpfVmFixedMbuff::new(Some(prog), 0x40, 0x50).unwrap();
- ///
- /// # #[cfg(all(not(windows), feature = "std"))]
- /// vm.jit_compile();
- ///
- /// // Provide only a reference to the packet data. We do not manage the metadata buffer.
- /// # #[cfg(all(not(windows), feature = "std"))]
- /// unsafe {
- /// let res = vm.execute_program_jit(mem).unwrap();
- /// assert_eq!(res, 0xdd);
- /// }
- /// ```
- // This struct redefines the `execute_program_jit()` function, in order to pass the offsets
- // associated with the fixed mbuff.
- #[cfg(all(not(windows), feature = "std"))]
- pub unsafe fn execute_program_jit(&mut self, mem: &'a mut [u8]) -> Result<u64, Error> {
- // If packet data is empty, do not send the address of an empty slice; send a null pointer
- // as first argument instead, as this is uBPF's behavior (empty packet should not happen
- // in the kernel; anyway the verifier would prevent the use of uninitialized registers).
- // See `mul_loop` test.
- let mem_ptr = match mem.len() {
- 0 => core::ptr::null_mut(),
- _ => mem.as_ptr() as *mut u8,
- };
- match &self.parent.jit {
- Some(jit) => Ok(jit.get_prog()(
- self.mbuff.buffer.as_ptr() as *mut u8,
- self.mbuff.buffer.len(),
- mem_ptr,
- mem.len(),
- self.mbuff.data_offset,
- self.mbuff.data_end_offset,
- )),
- None => Err(Error::new(
- ErrorKind::Other,
- "Error: program has not been JIT-compiled",
- )),
- }
- }
- /// Compile the loaded program using the Cranelift JIT.
- ///
- /// If using helper functions, be sure to register them into the VM before calling this
- /// function.
- ///
- /// # Examples
- ///
- /// ```
- /// let prog = &[
- /// 0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, 0
- /// 0x79, 0x12, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, // load mem from r1[0x40] to r2
- /// 0x07, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, // add r2, 5
- /// 0x79, 0x11, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, // load mem_end from r1[0x50] to r1
- /// 0x2d, 0x12, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, // if r2 > r1 skip 3 instructions
- /// 0x71, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // load r2 (= *(mem + 5)) into r0
- /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit
- /// ];
- ///
- /// // Instantiate a VM. Note that we provide the start and end offsets for mem pointers.
- /// let mut vm = rbpf::EbpfVmFixedMbuff::new(Some(prog), 0x40, 0x50).unwrap();
- ///
- /// vm.cranelift_compile();
- /// ```
- #[cfg(feature = "cranelift")]
- pub fn cranelift_compile(&mut self) -> Result<(), Error> {
- use crate::cranelift::CraneliftCompiler;
- let prog = match self.parent.prog {
- Some(prog) => prog,
- None => Err(Error::new(
- ErrorKind::Other,
- "Error: No program set, call prog_set() to load one",
- ))?,
- };
- let mut compiler = CraneliftCompiler::new(self.parent.helpers.clone());
- let program = compiler.compile_function(prog)?;
- self.parent.cranelift_prog = Some(program);
- Ok(())
- }
- /// Execute the previously compiled program, with the given packet data and metadata
- /// buffer, in a manner very similar to `execute_program()`.
- ///
- /// If the program is made to be compatible with Linux kernel, it is expected to load the
- /// address of the beginning and of the end of the memory area used for packet data from some
- /// metadata buffer, which in the case of this VM is handled internally. The offsets at which
- /// the addresses should be placed should have be set at the creation of the VM.
- ///
- /// # Examples
- ///
- /// ```
- /// let prog = &[
- /// 0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, 0
- /// 0x79, 0x12, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, // load mem from r1[0x40] to r2
- /// 0x07, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, // add r2, 5
- /// 0x79, 0x11, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, // load mem_end from r1[0x50] to r1
- /// 0x2d, 0x12, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, // if r2 > r1 skip 3 instructions
- /// 0x71, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // load r2 (= *(mem + 5)) into r0
- /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit
- /// ];
- /// let mem = &mut [
- /// 0xaa, 0xbb, 0x11, 0x22, 0xcc, 0xdd
- /// ];
- ///
- /// // Instantiate a VM. Note that we provide the start and end offsets for mem pointers.
- /// let mut vm = rbpf::EbpfVmFixedMbuff::new(Some(prog), 0x40, 0x50).unwrap();
- ///
- /// vm.cranelift_compile();
- ///
- /// // Provide only a reference to the packet data. We do not manage the metadata buffer.
- /// let res = vm.execute_program_cranelift(mem).unwrap();
- /// assert_eq!(res, 0xdd);
- /// ```
- #[cfg(feature = "cranelift")]
- pub fn execute_program_cranelift(&mut self, mem: &'a mut [u8]) -> Result<u64, Error> {
- // If packet data is empty, do not send the address of an empty slice; send a null pointer
- // as first argument instead, as this is uBPF's behavior (empty packet should not happen
- // in the kernel; anyway the verifier would prevent the use of uninitialized registers).
- // See `mul_loop` test.
- let mem_ptr = match mem.len() {
- 0 => core::ptr::null_mut(),
- _ => mem.as_ptr() as *mut u8,
- };
- let l = self.mbuff.buffer.len();
- // Can this ever happen? Probably not, should be ensured at mbuff creation.
- if self.mbuff.data_offset + 8 > l || self.mbuff.data_end_offset + 8 > l {
- Err(Error::new(ErrorKind::Other, format!("Error: buffer too small ({:?}), cannot use data_offset {:?} and data_end_offset {:?}",
- l, self.mbuff.data_offset, self.mbuff.data_end_offset)))?;
- }
- LittleEndian::write_u64(
- &mut self.mbuff.buffer[(self.mbuff.data_offset)..],
- mem.as_ptr() as u64,
- );
- LittleEndian::write_u64(
- &mut self.mbuff.buffer[(self.mbuff.data_end_offset)..],
- mem.as_ptr() as u64 + mem.len() as u64,
- );
- match &self.parent.cranelift_prog {
- Some(prog) => Ok(prog.execute(
- mem_ptr,
- mem.len(),
- self.mbuff.buffer.as_ptr() as *mut u8,
- self.mbuff.buffer.len(),
- )),
- None => Err(Error::new(
- ErrorKind::Other,
- "Error: program has not been compiled with cranelift",
- )),
- }
- }
- }
- /// A virtual machine to run eBPF program. This kind of VM is used for programs expecting to work
- /// directly on the memory area representing packet data.
- ///
- /// # Examples
- ///
- /// ```
- /// let prog = &[
- /// 0x71, 0x11, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, // ldxb r1[0x04], r1
- /// 0x07, 0x01, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, // add r1, 0x22
- /// 0xbf, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, r1
- /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit
- /// ];
- /// let mem = &mut [
- /// 0xaa, 0xbb, 0x11, 0x22, 0xcc, 0xdd
- /// ];
- ///
- /// // Instantiate a VM.
- /// let vm = rbpf::EbpfVmRaw::new(Some(prog)).unwrap();
- ///
- /// // Provide only a reference to the packet data.
- /// let res = vm.execute_program(mem).unwrap();
- /// assert_eq!(res, 0x22cc);
- /// ```
- pub struct EbpfVmRaw<'a> {
- parent: EbpfVmMbuff<'a>,
- }
- impl<'a> EbpfVmRaw<'a> {
- /// Create a new virtual machine instance, and load an eBPF program into that instance.
- /// When attempting to load the program, it passes through a simple verifier.
- ///
- /// # Examples
- ///
- /// ```
- /// let prog = &[
- /// 0x71, 0x11, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, // ldxb r1[0x04], r1
- /// 0x07, 0x01, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, // add r1, 0x22
- /// 0xbf, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, r1
- /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit
- /// ];
- ///
- /// // Instantiate a VM.
- /// let vm = rbpf::EbpfVmRaw::new(Some(prog)).unwrap();
- /// ```
- pub fn new(prog: Option<&'a [u8]>) -> Result<EbpfVmRaw<'a>, Error> {
- let parent = EbpfVmMbuff::new(prog)?;
- Ok(EbpfVmRaw { parent })
- }
- /// Load a new eBPF program into the virtual machine instance.
- ///
- /// # Examples
- ///
- /// ```
- /// let prog1 = &[
- /// 0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, 0
- /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit
- /// ];
- /// let prog2 = &[
- /// 0x71, 0x11, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, // ldxb r1[0x04], r1
- /// 0x07, 0x01, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, // add r1, 0x22
- /// 0xbf, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, r1
- /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit
- /// ];
- ///
- /// let mem = &mut [
- /// 0xaa, 0xbb, 0x11, 0x22, 0xcc, 0x27,
- /// ];
- ///
- /// let mut vm = rbpf::EbpfVmRaw::new(Some(prog1)).unwrap();
- /// vm.set_program(prog2);
- ///
- /// let res = vm.execute_program(mem).unwrap();
- /// assert_eq!(res, 0x22cc);
- /// ```
- pub fn set_program(&mut self, prog: &'a [u8]) -> Result<(), Error> {
- self.parent.set_program(prog)?;
- Ok(())
- }
- /// Set a new verifier function. The function should return an `Error` if the program should be
- /// rejected by the virtual machine. If a program has been loaded to the VM already, the
- /// verifier is immediately run.
- ///
- /// # Examples
- ///
- /// ```
- /// use rbpf::{Error, ErrorKind};
- /// use rbpf::ebpf;
- ///
- /// // Define a simple verifier function.
- /// fn verifier(prog: &[u8]) -> Result<(), Error> {
- /// let last_insn = ebpf::get_insn(prog, (prog.len() / ebpf::INSN_SIZE) - 1);
- /// if last_insn.opc != ebpf::EXIT {
- /// return Err(Error::new(ErrorKind::Other,
- /// "[Verifier] Error: program does not end with “EXIT” instruction"));
- /// }
- /// Ok(())
- /// }
- ///
- /// let prog1 = &[
- /// 0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, 0
- /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit
- /// ];
- ///
- /// // Instantiate a VM.
- /// let mut vm = rbpf::EbpfVmMbuff::new(Some(prog1)).unwrap();
- /// // Change the verifier.
- /// vm.set_verifier(verifier).unwrap();
- /// ```
- pub fn set_verifier(&mut self, verifier: Verifier) -> Result<(), Error> {
- self.parent.set_verifier(verifier)
- }
- /// Register a built-in or user-defined helper function in order to use it later from within
- /// the eBPF program. The helper is registered into a hashmap, so the `key` can be any `u32`.
- ///
- /// If using JIT-compiled eBPF programs, be sure to register all helpers before compiling the
- /// program. You should be able to change registered helpers after compiling, but not to add
- /// new ones (i.e. with new keys).
- ///
- /// # Examples
- ///
- /// ```
- /// #[cfg(feature = "std")] {
- /// use rbpf::helpers;
- ///
- /// let prog = &[
- /// 0x79, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ldxdw r1, r1[0x00]
- /// 0xb7, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r2, 0
- /// 0xb7, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r3, 0
- /// 0xb7, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r4, 0
- /// 0xb7, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r5, 0
- /// 0x85, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, // call helper with key 1
- /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit
- /// ];
- ///
- /// let mem = &mut [
- /// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01
- /// ];
- ///
- /// // Instantiate a VM.
- /// let mut vm = rbpf::EbpfVmRaw::new(Some(prog)).unwrap();
- ///
- /// // Register a helper. This helper will store the result of the square root of r1 into r0.
- /// vm.register_helper(1, helpers::sqrti);
- ///
- /// let res = vm.execute_program(mem).unwrap();
- /// assert_eq!(res, 0x10000000);
- /// }
- /// ```
- pub fn register_helper(
- &mut self,
- key: u32,
- function: fn(u64, u64, u64, u64, u64) -> u64,
- ) -> Result<(), Error> {
- self.parent.register_helper(key, function)
- }
- /// Register a set of built-in or user-defined helper functions in order to use them later from
- /// within the eBPF program. The helpers are registered into a hashmap, so the `key` can be any
- /// `u32`.
- #[allow(clippy::type_complexity)]
- pub fn register_helper_set(
- &mut self,
- helpers: &HashMap<u32, fn(u64, u64, u64, u64, u64) -> u64>,
- ) -> Result<(), Error> {
- for (key, function) in helpers {
- self.parent.register_helper(*key, *function)?;
- }
- Ok(())
- }
- /// Execute the program loaded, with the given packet data.
- ///
- /// # Examples
- ///
- /// ```
- /// let prog = &[
- /// 0x71, 0x11, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, // ldxb r1[0x04], r1
- /// 0x07, 0x01, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, // add r1, 0x22
- /// 0xbf, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, r1
- /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit
- /// ];
- ///
- /// let mem = &mut [
- /// 0xaa, 0xbb, 0x11, 0x22, 0xcc, 0x27
- /// ];
- ///
- /// let mut vm = rbpf::EbpfVmRaw::new(Some(prog)).unwrap();
- ///
- /// let res = vm.execute_program(mem).unwrap();
- /// assert_eq!(res, 0x22cc);
- /// ```
- pub fn execute_program(&self, mem: &'a mut [u8]) -> Result<u64, Error> {
- self.parent.execute_program(mem, &[])
- }
- /// JIT-compile the loaded program. No argument required for this.
- ///
- /// If using helper functions, be sure to register them into the VM before calling this
- /// function.
- ///
- /// # Examples
- ///
- /// ```
- /// let prog = &[
- /// 0x71, 0x11, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, // ldxb r1[0x04], r1
- /// 0x07, 0x01, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, // add r1, 0x22
- /// 0xbf, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, r1
- /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit
- /// ];
- ///
- /// let mut vm = rbpf::EbpfVmRaw::new(Some(prog)).unwrap();
- ///
- /// vm.jit_compile();
- /// ```
- #[cfg(all(not(windows), feature = "std"))]
- pub fn jit_compile(&mut self) -> Result<(), Error> {
- let prog = match self.parent.prog {
- Some(prog) => prog,
- None => Err(Error::new(
- ErrorKind::Other,
- "Error: No program set, call prog_set() to load one",
- ))?,
- };
- self.parent.jit = Some(jit::JitMemory::new(
- prog,
- &self.parent.helpers,
- false,
- false,
- )?);
- Ok(())
- }
- /// Execute the previously JIT-compiled program, with the given packet data, in a manner very
- /// similar to `execute_program()`.
- ///
- /// # Safety
- ///
- /// **WARNING:** JIT-compiled assembly code is not safe, in particular there is no runtime
- /// check for memory access; so if the eBPF program attempts erroneous accesses, this may end
- /// very bad (program may segfault). It may be wise to check that the program works with the
- /// interpreter before running the JIT-compiled version of it.
- ///
- /// For this reason the function should be called from within an `unsafe` bloc.
- ///
- /// # Examples
- ///
- /// ```
- /// let prog = &[
- /// 0x71, 0x11, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, // ldxb r1[0x04], r1
- /// 0x07, 0x01, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, // add r1, 0x22
- /// 0xbf, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, r1
- /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit
- /// ];
- ///
- /// let mem = &mut [
- /// 0xaa, 0xbb, 0x11, 0x22, 0xcc, 0x27
- /// ];
- ///
- /// let mut vm = rbpf::EbpfVmRaw::new(Some(prog)).unwrap();
- ///
- /// # #[cfg(all(not(windows), feature = "std"))]
- /// vm.jit_compile();
- ///
- /// # #[cfg(all(not(windows), feature = "std"))]
- /// unsafe {
- /// let res = vm.execute_program_jit(mem).unwrap();
- /// assert_eq!(res, 0x22cc);
- /// }
- /// ```
- #[cfg(all(not(windows), feature = "std"))]
- pub unsafe fn execute_program_jit(&self, mem: &'a mut [u8]) -> Result<u64, Error> {
- let mut mbuff = vec![];
- self.parent.execute_program_jit(mem, &mut mbuff)
- }
- /// Compile the loaded program using the Cranelift JIT.
- ///
- /// If using helper functions, be sure to register them into the VM before calling this
- /// function.
- ///
- /// # Examples
- ///
- /// ```
- /// let prog = &[
- /// 0x71, 0x11, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, // ldxb r1[0x04], r1
- /// 0x07, 0x01, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, // add r1, 0x22
- /// 0xbf, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, r1
- /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit
- /// ];
- ///
- /// let mut vm = rbpf::EbpfVmRaw::new(Some(prog)).unwrap();
- ///
- /// vm.cranelift_compile();
- /// ```
- #[cfg(feature = "cranelift")]
- pub fn cranelift_compile(&mut self) -> Result<(), Error> {
- use crate::cranelift::CraneliftCompiler;
- let prog = match self.parent.prog {
- Some(prog) => prog,
- None => Err(Error::new(
- ErrorKind::Other,
- "Error: No program set, call prog_set() to load one",
- ))?,
- };
- let mut compiler = CraneliftCompiler::new(self.parent.helpers.clone());
- let program = compiler.compile_function(prog)?;
- self.parent.cranelift_prog = Some(program);
- Ok(())
- }
- /// Execute the previously compiled program, with the given packet data, in a manner very
- /// similar to `execute_program()`.
- ///
- /// # Examples
- ///
- /// ```
- /// let prog = &[
- /// 0x71, 0x11, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, // ldxb r1[0x04], r1
- /// 0x07, 0x01, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, // add r1, 0x22
- /// 0xbf, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, r1
- /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit
- /// ];
- ///
- /// let mem = &mut [
- /// 0xaa, 0xbb, 0x11, 0x22, 0xcc, 0x27
- /// ];
- ///
- /// let mut vm = rbpf::EbpfVmRaw::new(Some(prog)).unwrap();
- ///
- /// vm.cranelift_compile();
- ///
- /// let res = vm.execute_program_cranelift(mem).unwrap();
- /// assert_eq!(res, 0x22cc);
- /// ```
- #[cfg(feature = "cranelift")]
- pub fn execute_program_cranelift(&self, mem: &'a mut [u8]) -> Result<u64, Error> {
- let mut mbuff = vec![];
- self.parent.execute_program_cranelift(mem, &mut mbuff)
- }
- }
- /// A virtual machine to run eBPF program. This kind of VM is used for programs that do not work
- /// with any memory area—no metadata buffer, no packet data either.
- ///
- /// # Examples
- ///
- /// ```
- /// let prog = &[
- /// 0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, 0
- /// 0xb7, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, // mov r1, 1
- /// 0xb7, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, // mov r2, 2
- /// 0xb7, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, // mov r3, 3
- /// 0xb7, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, // mov r4, 4
- /// 0xb7, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, // mov r5, 5
- /// 0xb7, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, // mov r6, 6
- /// 0xb7, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, // mov r7, 7
- /// 0xb7, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, // mov r8, 8
- /// 0x4f, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // or r0, r5
- /// 0x47, 0x00, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, // or r0, 0xa0
- /// 0x57, 0x00, 0x00, 0x00, 0xa3, 0x00, 0x00, 0x00, // and r0, 0xa3
- /// 0xb7, 0x09, 0x00, 0x00, 0x91, 0x00, 0x00, 0x00, // mov r9, 0x91
- /// 0x5f, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // and r0, r9
- /// 0x67, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, // lsh r0, 32
- /// 0x67, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, // lsh r0, 22
- /// 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // lsh r0, r8
- /// 0x77, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, // rsh r0, 32
- /// 0x77, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, // rsh r0, 19
- /// 0x7f, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // rsh r0, r7
- /// 0xa7, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, // xor r0, 0x03
- /// 0xaf, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // xor r0, r2
- /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit
- /// ];
- ///
- /// // Instantiate a VM.
- /// let vm = rbpf::EbpfVmNoData::new(Some(prog)).unwrap();
- ///
- /// // Provide only a reference to the packet data.
- /// let res = vm.execute_program().unwrap();
- /// assert_eq!(res, 0x11);
- /// ```
- pub struct EbpfVmNoData<'a> {
- parent: EbpfVmRaw<'a>,
- }
- impl<'a> EbpfVmNoData<'a> {
- /// Create a new virtual machine instance, and load an eBPF program into that instance.
- /// When attempting to load the program, it passes through a simple verifier.
- ///
- /// # Examples
- ///
- /// ```
- /// let prog = &[
- /// 0xb7, 0x00, 0x00, 0x00, 0x11, 0x22, 0x00, 0x00, // mov r0, 0x2211
- /// 0xdc, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, // be16 r0
- /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit
- /// ];
- ///
- /// // Instantiate a VM.
- /// let vm = rbpf::EbpfVmNoData::new(Some(prog));
- /// ```
- pub fn new(prog: Option<&'a [u8]>) -> Result<EbpfVmNoData<'a>, Error> {
- let parent = EbpfVmRaw::new(prog)?;
- Ok(EbpfVmNoData { parent })
- }
- /// Load a new eBPF program into the virtual machine instance.
- ///
- /// # Examples
- ///
- /// ```
- /// let prog1 = &[
- /// 0xb7, 0x00, 0x00, 0x00, 0x11, 0x22, 0x00, 0x00, // mov r0, 0x2211
- /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit
- /// ];
- /// let prog2 = &[
- /// 0xb7, 0x00, 0x00, 0x00, 0x11, 0x22, 0x00, 0x00, // mov r0, 0x2211
- /// 0xdc, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, // be16 r0
- /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit
- /// ];
- ///
- /// let mut vm = rbpf::EbpfVmNoData::new(Some(prog1)).unwrap();
- ///
- /// let res = vm.execute_program().unwrap();
- /// assert_eq!(res, 0x2211);
- ///
- /// vm.set_program(prog2);
- ///
- /// let res = vm.execute_program().unwrap();
- /// assert_eq!(res, 0x1122);
- /// ```
- pub fn set_program(&mut self, prog: &'a [u8]) -> Result<(), Error> {
- self.parent.set_program(prog)?;
- Ok(())
- }
- /// Set a new verifier function. The function should return an `Error` if the program should be
- /// rejected by the virtual machine. If a program has been loaded to the VM already, the
- /// verifier is immediately run.
- ///
- /// # Examples
- ///
- /// ```
- /// use rbpf::{Error, ErrorKind};
- /// use rbpf::ebpf;
- ///
- /// // Define a simple verifier function.
- /// fn verifier(prog: &[u8]) -> Result<(), Error> {
- /// let last_insn = ebpf::get_insn(prog, (prog.len() / ebpf::INSN_SIZE) - 1);
- /// if last_insn.opc != ebpf::EXIT {
- /// return Err(Error::new(ErrorKind::Other,
- /// "[Verifier] Error: program does not end with “EXIT” instruction"));
- /// }
- /// Ok(())
- /// }
- ///
- /// let prog1 = &[
- /// 0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, 0
- /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit
- /// ];
- ///
- /// // Instantiate a VM.
- /// let mut vm = rbpf::EbpfVmMbuff::new(Some(prog1)).unwrap();
- /// // Change the verifier.
- /// vm.set_verifier(verifier).unwrap();
- /// ```
- pub fn set_verifier(&mut self, verifier: Verifier) -> Result<(), Error> {
- self.parent.set_verifier(verifier)
- }
- /// Register a built-in or user-defined helper function in order to use it later from within
- /// the eBPF program. The helper is registered into a hashmap, so the `key` can be any `u32`.
- ///
- /// If using JIT-compiled eBPF programs, be sure to register all helpers before compiling the
- /// program. You should be able to change registered helpers after compiling, but not to add
- /// new ones (i.e. with new keys).
- ///
- /// # Examples
- ///
- /// ```
- /// #[cfg(feature = "std")] {
- /// use rbpf::helpers;
- ///
- /// let prog = &[
- /// 0xb7, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, // mov r1, 0x010000000
- /// 0xb7, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r2, 0
- /// 0xb7, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r3, 0
- /// 0xb7, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r4, 0
- /// 0xb7, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r5, 0
- /// 0x85, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, // call helper with key 1
- /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit
- /// ];
- ///
- /// let mut vm = rbpf::EbpfVmNoData::new(Some(prog)).unwrap();
- ///
- /// // Register a helper. This helper will store the result of the square root of r1 into r0.
- /// vm.register_helper(1, helpers::sqrti).unwrap();
- ///
- /// let res = vm.execute_program().unwrap();
- /// assert_eq!(res, 0x1000);
- /// }
- /// ```
- pub fn register_helper(
- &mut self,
- key: u32,
- function: fn(u64, u64, u64, u64, u64) -> u64,
- ) -> Result<(), Error> {
- self.parent.register_helper(key, function)
- }
- /// JIT-compile the loaded program. No argument required for this.
- ///
- /// If using helper functions, be sure to register them into the VM before calling this
- /// function.
- ///
- /// # Examples
- ///
- /// ```
- /// let prog = &[
- /// 0xb7, 0x00, 0x00, 0x00, 0x11, 0x22, 0x00, 0x00, // mov r0, 0x2211
- /// 0xdc, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, // be16 r0
- /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit
- /// ];
- ///
- /// let mut vm = rbpf::EbpfVmNoData::new(Some(prog)).unwrap();
- ///
- ///
- /// vm.jit_compile();
- /// ```
- #[cfg(all(not(windows), feature = "std"))]
- pub fn jit_compile(&mut self) -> Result<(), Error> {
- self.parent.jit_compile()
- }
- /// Execute the program loaded, without providing pointers to any memory area whatsoever.
- ///
- /// # Examples
- ///
- /// ```
- /// let prog = &[
- /// 0xb7, 0x00, 0x00, 0x00, 0x11, 0x22, 0x00, 0x00, // mov r0, 0x2211
- /// 0xdc, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, // be16 r0
- /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit
- /// ];
- ///
- /// let vm = rbpf::EbpfVmNoData::new(Some(prog)).unwrap();
- ///
- /// // For this kind of VM, the `execute_program()` function needs no argument.
- /// let res = vm.execute_program().unwrap();
- /// assert_eq!(res, 0x1122);
- /// ```
- pub fn execute_program(&self) -> Result<u64, Error> {
- self.parent.execute_program(&mut [])
- }
- /// Execute the previously JIT-compiled program, without providing pointers to any memory area
- /// whatsoever, in a manner very similar to `execute_program()`.
- ///
- /// # Safety
- ///
- /// **WARNING:** JIT-compiled assembly code is not safe, in particular there is no runtime
- /// check for memory access; so if the eBPF program attempts erroneous accesses, this may end
- /// very bad (program may segfault). It may be wise to check that the program works with the
- /// interpreter before running the JIT-compiled version of it.
- ///
- /// For this reason the function should be called from within an `unsafe` bloc.
- ///
- /// # Examples
- ///
- /// ```
- /// let prog = &[
- /// 0xb7, 0x00, 0x00, 0x00, 0x11, 0x22, 0x00, 0x00, // mov r0, 0x2211
- /// 0xdc, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, // be16 r0
- /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit
- /// ];
- ///
- /// let mut vm = rbpf::EbpfVmNoData::new(Some(prog)).unwrap();
- ///
- /// # #[cfg(all(not(windows), feature = "std"))]
- /// vm.jit_compile();
- ///
- /// # #[cfg(all(not(windows), feature = "std"))]
- /// unsafe {
- /// let res = vm.execute_program_jit().unwrap();
- /// assert_eq!(res, 0x1122);
- /// }
- /// ```
- #[cfg(all(not(windows), feature = "std"))]
- pub unsafe fn execute_program_jit(&self) -> Result<u64, Error> {
- self.parent.execute_program_jit(&mut [])
- }
- /// Compile the loaded program using the Cranelift JIT.
- ///
- /// If using helper functions, be sure to register them into the VM before calling this
- /// function.
- ///
- /// # Examples
- ///
- /// ```
- /// let prog = &[
- /// 0xb7, 0x00, 0x00, 0x00, 0x11, 0x22, 0x00, 0x00, // mov r0, 0x2211
- /// 0xdc, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, // be16 r0
- /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit
- /// ];
- ///
- /// let mut vm = rbpf::EbpfVmNoData::new(Some(prog)).unwrap();
- ///
- ///
- /// vm.cranelift_compile();
- /// ```
- #[cfg(feature = "cranelift")]
- pub fn cranelift_compile(&mut self) -> Result<(), Error> {
- self.parent.cranelift_compile()
- }
- /// Execute the previously JIT-compiled program, without providing pointers to any memory area
- /// whatsoever, in a manner very similar to `execute_program()`.
- ///
- /// # Examples
- ///
- /// ```
- /// let prog = &[
- /// 0xb7, 0x00, 0x00, 0x00, 0x11, 0x22, 0x00, 0x00, // mov r0, 0x2211
- /// 0xdc, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, // be16 r0
- /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit
- /// ];
- ///
- /// let mut vm = rbpf::EbpfVmNoData::new(Some(prog)).unwrap();
- ///
- /// vm.cranelift_compile();
- ///
- /// let res = vm.execute_program_cranelift().unwrap();
- /// assert_eq!(res, 0x1122);
- /// ```
- #[cfg(feature = "cranelift")]
- pub fn execute_program_cranelift(&self) -> Result<u64, Error> {
- self.parent.execute_program_cranelift(&mut [])
- }
- }
- /// EbpfVm with Owned data
- pub struct EbpfVmRawOwned {
- parent: EbpfVmRaw<'static>,
- data_len: usize,
- data_cap: usize,
- }
- impl EbpfVmRawOwned {
- /// Create a new virtual machine instance, and load an eBPF program into that instance.
- /// When attempting to load the program, it passes through a simple verifier.
- pub fn new(prog: Option<Vec<u8>>) -> Result<EbpfVmRawOwned, Error> {
- let (prog, data_len, data_cap) = match prog {
- Some(prog) => {
- let data_len = prog.len();
- let data_cap = prog.capacity();
- let slice = prog.leak();
- let slice = unsafe { core::slice::from_raw_parts(slice.as_ptr(), data_len) };
- (Some(slice), data_len, data_cap)
- }
- None => (None, 0, 0),
- };
- let parent = EbpfVmRaw::new(prog)?;
- Ok(Self {
- parent,
- data_len,
- data_cap,
- })
- }
- /// Load a new eBPF program into the virtual machine instance
- pub fn set_program(&mut self, prog: Vec<u8>) -> Result<(), Error> {
- self.data_len = prog.len();
- self.data_cap = prog.capacity();
- let slice = prog.leak();
- self.parent.set_program(slice)?;
- Ok(())
- }
- /// Set a new verifier function. The function should return an Error if the program should be rejected by the virtual machine.
- /// If a program has been loaded to the VM already, the verifier is immediately run.
- pub fn set_verifier(&mut self, verifier: Verifier) -> Result<(), Error> {
- self.parent.set_verifier(verifier)
- }
- /// Register a built-in or user-defined helper function in order to use it later from within the eBPF program.
- /// The helper is registered into a hashmap, so the key can be any u32.
- /// If using JIT-compiled eBPF programs, be sure to register all helpers before compiling the program.
- /// You should be able to change registered helpers after compiling, but not to add new ones (i. e. with new keys).
- pub fn register_helper(
- &mut self,
- key: u32,
- function: fn(u64, u64, u64, u64, u64) -> u64,
- ) -> Result<(), Error> {
- self.parent.register_helper(key, function)
- }
- /// Register a set of built-in or user-defined helper functions in order to use them later from
- /// within the eBPF program. The helpers are registered into a hashmap, so the `key` can be any
- /// `u32`.
- #[allow(clippy::type_complexity)]
- pub fn register_helper_set(
- &mut self,
- helpers: &HashMap<u32, fn(u64, u64, u64, u64, u64) -> u64>,
- ) -> Result<(), Error> {
- for (key, function) in helpers {
- self.parent.register_helper(*key, *function)?;
- }
- Ok(())
- }
- /// Execute the previously JIT-compiled program, with the given packet data, in a manner very similar to execute_program().
- ///
- /// Safety
- ///
- /// **WARNING:** JIT-compiled assembly code is not safe, in particular there is no runtime check for memory access;
- /// so if the eBPF program attempts erroneous accesses, this may end very bad (program may segfault).
- /// It may be wise to check that the program works with the interpreter before running the JIT-compiled version of it.
- ///
- /// For this reason the function should be called from within an unsafe bloc.
- pub fn execute_program(&self, mem: &mut [u8]) -> Result<u64, Error> {
- self.parent.execute_program(mem)
- }
- }
- impl Drop for EbpfVmRawOwned {
- fn drop(&mut self) {
- match self.parent.parent.prog {
- Some(prog) => unsafe {
- let ptr = prog.as_ptr();
- let _prog = Vec::from_raw_parts(ptr as *mut u8, self.data_len, self.data_cap);
- },
- None => {}
- };
- }
- }
|