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