pub struct EbpfVmMbuff<'a> { /* private fields */ }
Expand description

A virtual machine to run eBPF program. This kind of VM is used for programs expecting to work on a metadata buffer containing pointers to packet data.

Examples

let prog = &[
    0x79, 0x11, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, // Load mem from mbuff at offset 8 into R1.
    0x69, 0x10, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // ldhx r1[2], r0
    0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00  // exit
];
let mem = &mut [
    0xaa, 0xbb, 0x11, 0x22, 0xcc, 0xdd
];

// Just for the example we create our metadata buffer from scratch, and we store the pointers
// to packet data start and end in it.
let mut mbuff = [0u8; 32];
unsafe {
    let mut data     = mbuff.as_ptr().offset(8)  as *mut u64;
    let mut data_end = mbuff.as_ptr().offset(24) as *mut u64;
    *data     = mem.as_ptr() as u64;
    *data_end = mem.as_ptr() as u64 + mem.len() as u64;
}

// Instantiate a VM.
let mut vm = solana_rbpf::EbpfVmMbuff::new(Some(prog)).unwrap();

// Provide both a reference to the packet data, and to the metadata buffer.
let res = vm.execute_program(mem, &mut mbuff).unwrap();
assert_eq!(res, 0x2211);

Implementations

Create a new virtual machine instance, and load an eBPF program into that instance. When attempting to load the program, it passes through a simple verifier.

Examples
let prog = &[
    0x79, 0x11, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, // Load mem from mbuff into R1.
    0x69, 0x10, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // ldhx r1[2], r0
    0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00  // exit
];

// Instantiate a VM.
let mut vm = solana_rbpf::EbpfVmMbuff::new(Some(prog)).unwrap();

Load a new eBPF program into the virtual machine instance.

Examples
let prog1 = &[
    0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, 0
    0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00  // exit
];
let prog2 = &[
    0x79, 0x11, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, // Load mem from mbuff into R1.
    0x69, 0x10, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // ldhx r1[2], r0
    0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00  // exit
];

// Instantiate a VM.
let mut vm = solana_rbpf::EbpfVmMbuff::new(Some(prog1)).unwrap();
vm.set_program(prog2).unwrap();

Load a new eBPF program into the virtual machine instance.

Set a new verifier function. The function should return an Error if the program should be rejected by the virtual machine. If a program has been loaded to the VM already, the verifier is immediately run.

Examples
use std::io::{Error, ErrorKind};
use solana_rbpf::ebpf;

// Define a simple verifier function.
fn verifier(prog: &[u8]) -> Result<(), Error> {
    let last_insn = ebpf::get_insn(prog, (prog.len() / ebpf::INSN_SIZE) - 1);
    if last_insn.opc != ebpf::EXIT {
        return Err(Error::new(ErrorKind::Other,
                   "[Verifier] Error: program does not end with “EXIT” instruction"));
    }
    Ok(())
}

let prog1 = &[
    0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, 0
    0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00  // exit
];

// Instantiate a VM.
let mut vm = solana_rbpf::EbpfVmMbuff::new(Some(prog1)).unwrap();
// Change the verifier.
vm.set_verifier(verifier).unwrap();

Set a cap on the maximum number of instructions that a program may execute. If the maximum is set to zero, then no cap will be applied.

Examples
use std::io::{Error, ErrorKind};
use solana_rbpf::ebpf;

let prog = &[
    0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, 0
    0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00  // exit
];

// Instantiate a VM.
let mut vm = solana_rbpf::EbpfVmMbuff::new(Some(prog)).unwrap();
// Set maximum instruction count.
vm.set_max_instruction_count(1000).unwrap();

Returns the number of instructions executed by the last program.

Examples
use std::io::{Error, ErrorKind};
use solana_rbpf::ebpf;

let prog = &[
    0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, 0
    0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00  // exit
];
 
let mem = &mut [
    0xaa, 0xbb, 0x11, 0x22, 0xcc, 0xdd
];
 
// Just for the example we create our metadata buffer from scratch, and we store the
// pointers to packet data start and end in it.
let mut mbuff = [0u8; 32];
unsafe {
    let mut data     = mbuff.as_ptr().offset(8)  as *mut u64;
    let mut data_end = mbuff.as_ptr().offset(24) as *mut u64;
    *data     = mem.as_ptr() as u64;
    *data_end = mem.as_ptr() as u64 + mem.len() as u64;
}

// Instantiate a VM.
let mut vm = solana_rbpf::EbpfVmMbuff::new(Some(prog)).unwrap();
// Execute the program.
let res = vm.execute_program(mem, &mut mbuff).unwrap();
// Get the number of instructions executed.
let count = vm.get_last_instruction_count();

Register a built-in or user-defined helper function in order to use it later from within the eBPF program. The helper is registered into a hashmap, so the key can be any u32.

If using JIT-compiled eBPF programs, be sure to register all helpers before compiling the program. You should be able to change registered helpers after compiling, but not to add new ones (i.e. with new keys).

Examples
use solana_rbpf::helpers;

// This program was compiled with clang, from a C program containing the following single
// instruction: `return bpf_trace_printk("foo %c %c %c\n", 10, 1, 2, 3);`
let prog = &[
    0x18, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // load 0 as u64 into r1 (That would be
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // replaced by tc by the address of
                                                    // the format string, in the .map
                                                    // section of the ELF file).
    0xb7, 0x02, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, // mov r2, 10
    0xb7, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, // mov r3, 1
    0xb7, 0x04, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, // mov r4, 2
    0xb7, 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, // mov r5, 3
    0x85, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, // call helper with key 6
    0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00  // exit
];

// Instantiate a VM.
let mut vm = solana_rbpf::EbpfVmMbuff::new(Some(prog)).unwrap();

// Register a helper.
// On running the program this helper will print the content of registers r3, r4 and r5 to
// standard output.
vm.register_helper(6, helpers::bpf_trace_printf).unwrap();

Register a user-defined helper function in order to use it later from within the eBPF program. Normally helper functions are referred to by an index. (See helpers) but this function takes the name of the function. The name is then hashed into a 32 bit number and used in the call instructions imm field. If calling set_elf then the elf’s relocations must reference this symbol using the same name. This can usually be achieved by building the elf with unresolved symbols (think extern foo(void)). If providing a program directly via set_program then any call instructions must already have the hash of the symbol name in its imm field. To generate the correct hash of the symbol name use ebpf::helpers::hash_symbol_name.

Helper functions may treat their arguments as pointers, but there are safety issues in doing so. To protect against bad pointer usage the VM will call the helper verifier function before calling the real helper. The user-supplied helper verifier should be implemented so that it checks the usage of the pointers and returns an error if a problem is encountered. For example, if the helper function treats argument 1 as a pointer to a string then the helper verification function must validate that argument 1 is indeed a valid pointer and that it is fully contained in one of the provided memory regions.

This function can be used along with jitted programs but be aware that unlike interpreted programs, jitted programs will not call the verification functions. If you don’t inherently trust the parameters being passed to helpers then jitted programs must only use helper’s arguments as values.

If using JIT-compiled eBPF programs, be sure to register all helpers before compiling the program. You should be able to change registered helpers after compiling, but not to add new ones (i.e. with new keys).

Execute the program loaded, with the given packet data and metadata buffer.

If the program is made to be compatible with Linux kernel, it is expected to load the address of the beginning and of the end of the memory area used for packet data from the metadata buffer, at some appointed offsets. It is up to the user to ensure that these pointers are correctly stored in the buffer.

Examples
let prog = &[
    0x79, 0x11, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, // Load mem from mbuff into R1.
    0x69, 0x10, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // ldhx r1[2], r0
    0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00  // exit
];
let mem = &mut [
    0xaa, 0xbb, 0x11, 0x22, 0xcc, 0xdd
];

// Just for the example we create our metadata buffer from scratch, and we store the
// pointers to packet data start and end in it.
let mut mbuff = [0u8; 32];
unsafe {
    let mut data     = mbuff.as_ptr().offset(8)  as *mut u64;
    let mut data_end = mbuff.as_ptr().offset(24) as *mut u64;
    *data     = mem.as_ptr() as u64;
    *data_end = mem.as_ptr() as u64 + mem.len() as u64;
}

// Instantiate a VM.
let mut vm = solana_rbpf::EbpfVmMbuff::new(Some(prog)).unwrap();

// Provide both a reference to the packet data, and to the metadata buffer.
let res = vm.execute_program(mem, &mut mbuff).unwrap();
assert_eq!(res, 0x2211);

JIT-compile the loaded program. No argument required for this.

If using helper functions, be sure to register them into the VM before calling this function.

Examples
let prog = &[
    0x79, 0x11, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, // Load mem from mbuff into R1.
    0x69, 0x10, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // ldhx r1[2], r0
    0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00  // exit
];

// Instantiate a VM.
let mut vm = solana_rbpf::EbpfVmMbuff::new(Some(prog)).unwrap();

vm.jit_compile();

Execute the previously JIT-compiled program, with the given packet data and metadata buffer, in a manner very similar to execute_program().

If the program is made to be compatible with Linux kernel, it is expected to load the address of the beginning and of the end of the memory area used for packet data from the metadata buffer, at some appointed offsets. It is up to the user to ensure that these pointers are correctly stored in the buffer.

Safety

WARNING: JIT-compiled assembly code is not safe, in particular there is no runtime check for memory access; so if the eBPF program attempts erroneous accesses, this may end very bad (program may segfault). It may be wise to check that the program works with the interpreter before running the JIT-compiled version of it.

For this reason the function should be called from within an unsafe bloc.

Examples
let prog = &[
    0x79, 0x11, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, // Load mem from mbuff into r1.
    0x69, 0x10, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // ldhx r1[2], r0
    0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00  // exit
];
let mem = &mut [
    0xaa, 0xbb, 0x11, 0x22, 0xcc, 0xdd
];

// Just for the example we create our metadata buffer from scratch, and we store the
// pointers to packet data start and end in it.
let mut mbuff = [0u8; 32];
unsafe {
    let mut data     = mbuff.as_ptr().offset(8)  as *mut u64;
    let mut data_end = mbuff.as_ptr().offset(24) as *mut u64;
    *data     = mem.as_ptr() as u64;
    *data_end = mem.as_ptr() as u64 + mem.len() as u64;
}

// Instantiate a VM.
let mut vm = solana_rbpf::EbpfVmMbuff::new(Some(prog)).unwrap();

vm.jit_compile();

// Provide both a reference to the packet data, and to the metadata buffer.
unsafe {
    let res = vm.execute_program_jit(mem, &mut mbuff).unwrap();
    assert_eq!(res, 0x2211);
}

Auto Trait Implementations

Blanket Implementations

Gets the TypeId of self. Read more
Immutably borrows from an owned value. Read more
Mutably borrows from an owned value. Read more

Returns the argument unchanged.

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

The alignment of pointer.
The type for initializers.
Initializes a with the given initializer. Read more
Dereferences the given pointer. Read more
Mutably dereferences the given pointer. Read more
Drops the object pointed to by the given pointer. Read more
The type returned in the event of a conversion error.
Performs the conversion.
The type returned in the event of a conversion error.
Performs the conversion.