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}