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