uBPF
|
#include <ubpf_config.h>
#include <stdio.h>
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
Go to the source code of this file.
Macros | |
#define | UBPF_MAX_INSTS 65536 |
Default maximum number of instructions that a program can contain. More... | |
#define | UBPF_MAX_CALL_DEPTH 8 |
Default maximum number of nested calls in the VM. More... | |
#define | UBPF_EBPF_STACK_SIZE (UBPF_MAX_CALL_DEPTH * 512) |
Default stack size for the eBPF program. Must be divisible by 16. More... | |
#define | UBPF_EBPF_LOCAL_FUNCTION_STACK_SIZE 256 |
Default stack size for each local eBPF function. More... | |
#define | UBPF_EBPF_NONVOLATILE_SIZE (sizeof(uint64_t) * 5) |
Typedefs | |
typedef uint64_t(* | ubpf_jit_fn) (void *mem, size_t mem_len) |
Opaque type for a uBPF JIT compiled function. More... | |
typedef uint64_t(* | ubpf_jit_ex_fn) (void *mem, size_t mem_len, uint8_t *stack, size_t stack_len) |
Opaque type for a uBPF JIT compiled function with external stack. More... | |
typedef uint64_t(* | external_function_t) (uint64_t p0, uint64_t p1, uint64_t p2, uint64_t p3, uint64_t p4) |
The type of an external helper function. More... | |
typedef uint64_t(* | external_function_dispatcher_t) (uint64_t, uint64_t, uint64_t, uint64_t, uint64_t, unsigned int index, void *cookie) |
The type of an external helper dispatcher function. More... | |
typedef bool(* | external_function_validate_t) (unsigned int index, const struct ubpf_vm *vm) |
The type of an external helper validation function. More... | |
typedef int(* | stack_usage_calculator_t) (const struct ubpf_vm *vm, uint16_t pc, void *cookie) |
The type of a stack usage calculator callback function. More... | |
typedef uint64_t(* | ubpf_data_relocation) (void *user_context, const uint8_t *data, uint64_t data_size, const char *symbol_name, uint64_t symbol_offset, uint64_t symbol_size) |
Data relocation function that is called by the VM when it encounters a R_BPF_64_64 relocation in the maps section of the ELF file. More... | |
typedef bool(* | ubpf_bounds_check) (void *context, uint64_t addr, uint64_t size) |
Function that is called by the VM to check if a memory access is within bounds. More... | |
typedef void(* | ubpf_debug_fn) (void *context, int program_counter, const uint64_t registers[16], const uint8_t *stack_start, size_t stack_length, uint64_t register_mask, const uint8_t *stack_mask_start) |
A function to invoke before each instruction. More... | |
Enumerations | |
enum | JitMode { ExtendedJitMode , BasicJitMode } |
Enum to describe JIT mode. More... | |
Functions | |
struct ubpf_vm * | ubpf_create (void) |
Create a new uBPF VM. More... | |
void | ubpf_destroy (struct ubpf_vm *vm) |
Free a uBPF VM. More... | |
bool | ubpf_toggle_bounds_check (struct ubpf_vm *vm, bool enable) |
Enable / disable bounds_check. Bounds check is enabled by default, but it may be too restrictive. More... | |
void | ubpf_set_error_print (struct ubpf_vm *vm, int(*error_printf)(FILE *stream, const char *format,...)) |
Set the function to be invoked if the program hits a fatal error. More... | |
external_function_t | as_external_function_t (void *f) |
Cast an external function to external_function_t. More... | |
int | ubpf_register (struct ubpf_vm *vm, unsigned int index, const char *name, external_function_t fn) |
Register an external function. The immediate field of a CALL instruction is an index into an array of functions registered by the user. This API associates a function with an index. More... | |
int | ubpf_register_external_dispatcher (struct ubpf_vm *vm, external_function_dispatcher_t dispatcher, external_function_validate_t validater) |
Register a function that dispatches to external helpers The immediate field of a CALL instruction is an index of a helper function to invoke. This API sets a callback that will choose the helper function to invoke (based on the index) and then invoke it. This API also sets a callback that the validator will use to determine if a given index is a valid external function. More... | |
int | ubpf_register_stack_usage_calculator (struct ubpf_vm *vm, stack_usage_calculator_t calculator, void *cookie) |
Register a function that will be called during eBPF program validation to determine stack usage for a local function. More... | |
int | ubpf_load (struct ubpf_vm *vm, const void *code, uint32_t code_len, char **errmsg) |
Load code into a VM. This must be done before calling ubpf_exec or ubpf_compile and after registering all functions. More... | |
void | ubpf_unload_code (struct ubpf_vm *vm) |
Unload code from a VM. More... | |
int | ubpf_exec (const struct ubpf_vm *vm, void *mem, size_t mem_len, uint64_t *bpf_return_value) |
Execute a BPF program in the VM using the interpreter. More... | |
int | ubpf_exec_ex (const struct ubpf_vm *vm, void *mem, size_t mem_len, uint64_t *bpf_return_value, uint8_t *stack, size_t stack_len) |
ubpf_jit_fn | ubpf_compile (struct ubpf_vm *vm, char **errmsg) |
Compile a BPF program in the VM to native code. More... | |
ubpf_jit_ex_fn | ubpf_compile_ex (struct ubpf_vm *vm, char **errmsg, enum JitMode jit_mode) |
Compile a BPF program in the VM to native code. More... | |
ubpf_jit_fn | ubpf_copy_jit (struct ubpf_vm *vm, void *buffer, size_t size, char **errmsg) |
Copy the JIT'd program code to the given buffer. More... | |
int | ubpf_translate (struct ubpf_vm *vm, uint8_t *buffer, size_t *size, char **errmsg) |
Translate the eBPF byte code to machine code. More... | |
int | ubpf_translate_ex (struct ubpf_vm *vm, uint8_t *buffer, size_t *size, char **errmsg, enum JitMode jit_mode) |
Translate the eBPF byte code to machine code. More... | |
int | ubpf_set_unwind_function_index (struct ubpf_vm *vm, unsigned int idx) |
Instruct the uBPF runtime to apply unwind-on-success semantics to a helper function. If the function returns 0, the uBPF runtime will end execution of the eBPF program and immediately return control to the caller. This is used for implementing function like the "bpf_tail_call" helper. More... | |
void | ubpf_set_registers (struct ubpf_vm *vm, uint64_t *regs) |
Override the storage location for the BPF registers in the VM. More... | |
uint64_t * | ubpf_get_registers (const struct ubpf_vm *vm) |
Retrieve the storage location for the BPF registers in the VM. More... | |
int | ubpf_set_pointer_secret (struct ubpf_vm *vm, uint64_t secret) |
Optional secret to improve ROP protection. More... | |
int | ubpf_register_data_relocation (struct ubpf_vm *vm, void *user_context, ubpf_data_relocation relocation) |
Set a relocation function for the VM. More... | |
int | ubpf_register_data_bounds_check (struct ubpf_vm *vm, void *user_context, ubpf_bounds_check bounds_check) |
Set a bounds check function for the VM. More... | |
int | ubpf_set_jit_code_size (struct ubpf_vm *vm, size_t code_size) |
Set a size for the buffer allocated to machine code generated during JIT compilation. The JIT compiler allocates a buffer to store the code while it is being generated. The default may be too big for some embedded platforms. Use this to customize the size of that buffer. Note: The buffer being sized here is not the final location of the machine code returned by ubpf_compile – that buffer is perfectly sized to match the size of the generated machine code. More... | |
int | ubpf_set_instruction_limit (struct ubpf_vm *vm, uint32_t limit, uint32_t *previous_limit) |
Set the instruction limit for the VM. This is the maximum number of instructions that a program may execute during a call to ubpf_exec. It has no effect on JIT'd programs. More... | |
bool | ubpf_toggle_undefined_behavior_check (struct ubpf_vm *vm, bool enable) |
Enable or disable undefined behavior checks. Undefined behavior includes reading from uninitialized memory or using uninitialized registers. Default is disabled to preserve performance and compatibility with existing eBPF programs. More... | |
int | ubpf_register_debug_fn (struct ubpf_vm *vm, void *context, ubpf_debug_fn debug_function) |
Add option to invoke a debug function before each instruction. Note: This only applies to the interpreter and not the JIT. More... | |
#define UBPF_EBPF_LOCAL_FUNCTION_STACK_SIZE 256 |
Default stack size for each local eBPF function.
The size of the stack for each local eBPF function multiplied by the max call depth (UBPF_EBPF_STACK_SIZE) must be less than or equal to the total stack size for the eBPF VM (UBPF_EBPF_STACK_SIZE).
#define UBPF_EBPF_NONVOLATILE_SIZE (sizeof(uint64_t) * 5) |
#define UBPF_EBPF_STACK_SIZE (UBPF_MAX_CALL_DEPTH * 512) |
Default stack size for the eBPF program. Must be divisible by 16.
#define UBPF_MAX_CALL_DEPTH 8 |
Default maximum number of nested calls in the VM.
#define UBPF_MAX_INSTS 65536 |
Default maximum number of instructions that a program can contain.
typedef uint64_t(* external_function_dispatcher_t) (uint64_t, uint64_t, uint64_t, uint64_t, uint64_t, unsigned int index, void *cookie) |
The type of an external helper dispatcher function.
typedef uint64_t(* external_function_t) (uint64_t p0, uint64_t p1, uint64_t p2, uint64_t p3, uint64_t p4) |
The type of an external helper function.
Note: There is an implicit void *
-typed 6th parameter that users can access if they choose. That sixth parameter's value is the value of the pointer given by the user of ubpf_exec, ubpf_exec_ex, and the function generated by ubpf_translate as the first argument.
typedef bool(* external_function_validate_t) (unsigned int index, const struct ubpf_vm *vm) |
The type of an external helper validation function.
typedef int(* stack_usage_calculator_t) (const struct ubpf_vm *vm, uint16_t pc, void *cookie) |
The type of a stack usage calculator callback function.
See ubpf_register_stack_usage_calculator for additional information.
typedef bool(* ubpf_bounds_check) (void *context, uint64_t addr, uint64_t size) |
Function that is called by the VM to check if a memory access is within bounds.
[in] | context | The user context that was passed to ubpf_register_data_bounds_check. |
[in] | addr | The address to check. |
[in] | size | The size of the memory access. |
True | The memory access is within bounds. |
False | The memory access is out of bounds. |
typedef uint64_t(* ubpf_data_relocation) (void *user_context, const uint8_t *data, uint64_t data_size, const char *symbol_name, uint64_t symbol_offset, uint64_t symbol_size) |
Data relocation function that is called by the VM when it encounters a R_BPF_64_64 relocation in the maps section of the ELF file.
[in] | user_context | The user context that was passed to ubpf_register_data_relocation. |
[in] | data | Pointer to start of the map section. |
[in] | data_size | Size of the map section. |
[in] | symbol_name | Name of the symbol that is referenced. |
[in] | symbol_offset | Offset of the symbol relative to the start of the map section. |
[in] | symbol_size | Size of the symbol. |
typedef void(* ubpf_debug_fn) (void *context, int program_counter, const uint64_t registers[16], const uint8_t *stack_start, size_t stack_length, uint64_t register_mask, const uint8_t *stack_mask_start) |
A function to invoke before each instruction.
[in,out] | context | Context passed in to ubpf_register_debug_fn. |
[in] | program_counter | Current instruction pointer. |
[in] | registers | Array of 11 registers representing the VM state. |
[in] | stack_start | Pointer to the beginning of the stack. |
[in] | stack_length | Size of the stack in bytes. |
[in] | register_mask | Bitmask of registers that have been modified since the start of the program. Each set bit represents 1 modified register. LSB corresponds to register 0 and so on. |
[in] | stack_mask_start | Bitmask of the stack that has been modified since the start of the program. Each set bit represents 1 byte of the stack that has been modified. LSB corresponds to the first byte relative to stack_start and the MSB corresponds to the last byte. Note that the stack grows downwards, so the byte corresponding to the MSB is the first byte of the stack from the POV of the program and LSB is the last byte. |
typedef uint64_t(* ubpf_jit_ex_fn) (void *mem, size_t mem_len, uint8_t *stack, size_t stack_len) |
Opaque type for a uBPF JIT compiled function with external stack.
typedef uint64_t(* ubpf_jit_fn) (void *mem, size_t mem_len) |
Opaque type for a uBPF JIT compiled function.
enum JitMode |
Enum to describe JIT mode.
ExtendedJitMode specifies that an invocation of that code have 4 parameters:
BasicJitMode specifies that an invocation of that code have 2 parameters:
Enumerator | |
---|---|
ExtendedJitMode | |
BasicJitMode |
external_function_t as_external_function_t | ( | void * | f | ) |
Cast an external function to external_function_t.
Some external functions may not use all the parameters (or may use the implicit 6th parameter) and, therefore, not match the external_function_t typedef. Use this for a conversion.
[in] | f | The function to cast to match the signature of an external function. |
The | external function, as external_function_t. |
ubpf_jit_fn ubpf_compile | ( | struct ubpf_vm * | vm, |
char ** | errmsg | ||
) |
Compile a BPF program in the VM to native code.
A program must be loaded into the VM and all external functions (or the external helper dispatcher) must be registered before calling this function.
The JITer executes in basic mode when invoked through this function.
[in] | vm | The VM to compile the program in. |
[out] | errmsg | The error message, if any. This should be freed by the caller. |
ubpf_jit_ex_fn ubpf_compile_ex | ( | struct ubpf_vm * | vm, |
char ** | errmsg, | ||
enum JitMode | jit_mode | ||
) |
Compile a BPF program in the VM to native code.
A program must be loaded into the VM and all external functions (or the external helper dispatcher) must be registered before calling this function.
The JITer executes in the prescribed mode when invoked through this function. If jit_mode is basic, the caller will have to cast the function pointer to the appropriate type (ubpf_jit_fn).
[in] | vm | The VM to compile the program in. |
[out] | errmsg | The error message, if any. This should be freed by the caller. |
[in] | jit_mode | The mode in which to execute the JITer – basic or extended. |
ubpf_jit_fn ubpf_copy_jit | ( | struct ubpf_vm * | vm, |
void * | buffer, | ||
size_t | size, | ||
char ** | errmsg | ||
) |
Copy the JIT'd program code to the given buffer.
A program must have been loaded into the VM and already JIT'd before calling this function.
Note: Caller must know the mode in which the JITer was executed and may need to cast the result to the appropriate type (e.g., ubpf_jit_ex_fn).
[in] | vm | The VM of the already JIT'd program. |
[out] | errmsg | The error message, if any. This should be freed by the caller. |
struct ubpf_vm* ubpf_create | ( | void | ) |
Create a new uBPF VM.
void ubpf_destroy | ( | struct ubpf_vm * | vm | ) |
Free a uBPF VM.
[in] | vm | The VM to free. |
int ubpf_exec | ( | const struct ubpf_vm * | vm, |
void * | mem, | ||
size_t | mem_len, | ||
uint64_t * | bpf_return_value | ||
) |
Execute a BPF program in the VM using the interpreter.
A program must be loaded into the VM and all external functions must be registered before calling this function.
[in] | vm | The VM to execute the program in. |
[in] | mem | The memory to pass to the program. |
[in] | mem_len | The length of the memory. |
[in] | bpf_return_value | The value of the r0 register when the program exits. |
0 | Success. |
-1 | Failure. |
int ubpf_exec_ex | ( | const struct ubpf_vm * | vm, |
void * | mem, | ||
size_t | mem_len, | ||
uint64_t * | bpf_return_value, | ||
uint8_t * | stack, | ||
size_t | stack_len | ||
) |
uint64_t* ubpf_get_registers | ( | const struct ubpf_vm * | vm | ) |
Retrieve the storage location for the BPF registers in the VM.
[in] | vm | The VM to get the register storage from. |
int ubpf_load | ( | struct ubpf_vm * | vm, |
const void * | code, | ||
uint32_t | code_len, | ||
char ** | errmsg | ||
) |
Load code into a VM. This must be done before calling ubpf_exec or ubpf_compile and after registering all functions.
'code' should point to eBPF bytecodes and 'code_len' should be the size in bytes of that buffer.
[in] | vm | The VM to load the code into. |
[in] | code | The eBPF bytecodes to load. |
[in] | code_len | The length of the eBPF bytecodes. |
[out] | errmsg | The error message, if any. This should be freed by the caller. |
0 | Success. |
-1 | Failure. |
int ubpf_register | ( | struct ubpf_vm * | vm, |
unsigned int | index, | ||
const char * | name, | ||
external_function_t | fn | ||
) |
Register an external function. The immediate field of a CALL instruction is an index into an array of functions registered by the user. This API associates a function with an index.
[in] | vm | The VM to register the function on. |
[in] | index | The index to register the function at. |
[in] | name | The human readable name of the function. |
[in] | fn | The function to register. |
0 | Success. |
-1 | Failure. |
int ubpf_register_data_bounds_check | ( | struct ubpf_vm * | vm, |
void * | user_context, | ||
ubpf_bounds_check | bounds_check | ||
) |
Set a bounds check function for the VM.
[in] | vm | The VM to set the bounds check function for. |
[in] | user_context | The user context to pass to the bounds check function. |
[in] | bounds_check | The bounds check function. |
0 | Success. |
-1 | Failure. |
int ubpf_register_data_relocation | ( | struct ubpf_vm * | vm, |
void * | user_context, | ||
ubpf_data_relocation | relocation | ||
) |
Set a relocation function for the VM.
[in] | vm | The VM to set the relocation function for. |
[in] | relocation | The relocation function. |
int ubpf_register_debug_fn | ( | struct ubpf_vm * | vm, |
void * | context, | ||
ubpf_debug_fn | debug_function | ||
) |
Add option to invoke a debug function before each instruction. Note: This only applies to the interpreter and not the JIT.
[in] | vm | VM to add the option to. |
[in] | debug_fn | Function to invoke before each instruction. Pass NULL to remove the function. |
0 | Success. |
-1 | Failure. |
int ubpf_register_external_dispatcher | ( | struct ubpf_vm * | vm, |
external_function_dispatcher_t | dispatcher, | ||
external_function_validate_t | validater | ||
) |
Register a function that dispatches to external helpers The immediate field of a CALL instruction is an index of a helper function to invoke. This API sets a callback that will choose the helper function to invoke (based on the index) and then invoke it. This API also sets a callback that the validator will use to determine if a given index is a valid external function.
[in] | vm | The VM to register the function on. |
[in] | dispatcher | The callback that will dispatch to the external helper. |
[in] | validater | The callback that will validate that a given index is valid for an external helper. |
0 | Success. |
-1 | Failure. |
int ubpf_register_stack_usage_calculator | ( | struct ubpf_vm * | vm, |
stack_usage_calculator_t | calculator, | ||
void * | cookie | ||
) |
Register a function that will be called during eBPF program validation to determine stack usage for a local function.
In eBPF, the frame pointer is a read-only register. Therefore, the eBPF interpreter or the eBPF JITer need to know the stack usage for each local function so that the frame pointer can be adjusted properly on behalf of the calling function. The callback registered here has access to a cookie for context (specified in the call to this function), the PC (in the eBPF program) of the first instruction of a local function and the ubpf_vm
.
The callback's job is to calculate the amount of stack space used by the local function that starts at the given PC.
If the callback returns 0 or there is no callback registered, the eBPF interpreter/JITer assume that the local function uses the maximum stack available according to the spec (512K).
[in] | vm | The VM to register the callback with. |
[in] | dispatcher | The callback that will be invoked to determine the amount of stack usage for a local function that starts at ... |
[in] | pc | The pc of the function whose stack usage the callback must caculate. |
0 | Success. |
-1 | Failure. |
void ubpf_set_error_print | ( | struct ubpf_vm * | vm, |
int(*)(FILE *stream, const char *format,...) | error_printf | ||
) |
Set the function to be invoked if the program hits a fatal error.
[in] | vm | The VM to set the error function on. |
[in] | error_printf | The function to be invoked on fatal error. |
int ubpf_set_instruction_limit | ( | struct ubpf_vm * | vm, |
uint32_t | limit, | ||
uint32_t * | previous_limit | ||
) |
Set the instruction limit for the VM. This is the maximum number of instructions that a program may execute during a call to ubpf_exec. It has no effect on JIT'd programs.
[in] | vm | The VM to set the instruction limit for. |
[in] | limit | The maximum number of instructions that a program may execute or 0 for no limit. |
[out] | previous_limit | Optional pointer to store the previous instruction limit. |
0 | Success. |
-1 | Failure. |
int ubpf_set_jit_code_size | ( | struct ubpf_vm * | vm, |
size_t | code_size | ||
) |
Set a size for the buffer allocated to machine code generated during JIT compilation. The JIT compiler allocates a buffer to store the code while it is being generated. The default may be too big for some embedded platforms. Use this to customize the size of that buffer. Note: The buffer being sized here is not the final location of the machine code returned by ubpf_compile – that buffer is perfectly sized to match the size of the generated machine code.
[in] | vm | The VM to set the buffer size for. |
[in] | code_size | The size of the buffer to use. |
0 | Success. |
-1 | Failure. |
int ubpf_set_pointer_secret | ( | struct ubpf_vm * | vm, |
uint64_t | secret | ||
) |
Optional secret to improve ROP protection.
[in] | vm | The VM to set the secret for. |
[in] | secret | Optional secret to improve ROP protection. Returns 0 on success, -1 on error (e.g. if the secret is set after the instructions are loaded). |
void ubpf_set_registers | ( | struct ubpf_vm * | vm, |
uint64_t * | regs | ||
) |
Override the storage location for the BPF registers in the VM.
[in] | vm | The VM to set the register storage in. |
[in] | regs | The register storage. |
int ubpf_set_unwind_function_index | ( | struct ubpf_vm * | vm, |
unsigned int | idx | ||
) |
Instruct the uBPF runtime to apply unwind-on-success semantics to a helper function. If the function returns 0, the uBPF runtime will end execution of the eBPF program and immediately return control to the caller. This is used for implementing function like the "bpf_tail_call" helper.
[in] | vm | The VM to set the unwind helper in. |
[in] | idx | Index of the helper function to unwind on success. |
0 | Success. |
-1 | Failure. |
bool ubpf_toggle_bounds_check | ( | struct ubpf_vm * | vm, |
bool | enable | ||
) |
Enable / disable bounds_check. Bounds check is enabled by default, but it may be too restrictive.
[in] | vm | The VM to enable / disable bounds check on. |
[in] | enable | Enable bounds check if true, disable if false. |
true | Bounds check was previously enabled. |
bool ubpf_toggle_undefined_behavior_check | ( | struct ubpf_vm * | vm, |
bool | enable | ||
) |
Enable or disable undefined behavior checks. Undefined behavior includes reading from uninitialized memory or using uninitialized registers. Default is disabled to preserve performance and compatibility with existing eBPF programs.
[in] | vm | VM to enable or disable undefined behavior checks on. |
[in] | enable | Enable undefined behavior checks if true, disable if false. |
true | Undefined behavior checks were previously enabled. |
false | Undefined behavior checks were previously disabled. |
int ubpf_translate | ( | struct ubpf_vm * | vm, |
uint8_t * | buffer, | ||
size_t * | size, | ||
char ** | errmsg | ||
) |
Translate the eBPF byte code to machine code.
A program must be loaded into the VM and all external functions must be registered before calling this function.
The JITer executes in basic mode when invoked through this function.
[in] | vm | The VM to translate the program in. |
[out] | buffer | The buffer to store the translated code in. |
[in] | size | The size of the buffer. |
[out] | errmsg | The error message, if any. This should be freed by the caller. |
0 | Success. |
-1 | Failure. |
int ubpf_translate_ex | ( | struct ubpf_vm * | vm, |
uint8_t * | buffer, | ||
size_t * | size, | ||
char ** | errmsg, | ||
enum JitMode | jit_mode | ||
) |
Translate the eBPF byte code to machine code.
A program must be loaded into the VM and all external functions must be registered before calling this function.
The JITer executes in the prescribed mode when invoked through this function.
[in] | vm | The VM to translate the program in. |
[out] | buffer | The buffer to store the translated code in. |
[in] | size | The size of the buffer. |
[out] | errmsg | The error message, if any. This should be freed by the caller. |
[in] | jit_mode | The mode in which to execute the JITer – basic or extended. |
0 | Success. |
-1 | Failure. |
void ubpf_unload_code | ( | struct ubpf_vm * | vm | ) |
Unload code from a VM.
The VM must be reloaded with code before calling ubpf_exec or ubpf_compile.
[in] | vm | The VM to unload the code from. |