Skip to content
This repository was archived by the owner on Jan 10, 2025. It is now read-only.

Commit ae16614

Browse files
authored
Refactor - vm interface of BuiltinFunctions (#535)
* Replaces all context parameters of BuiltinFunction with a reference to the vm. * Deduplicates instruction metering of interpreter and JIT by moving it into declare_builtin_function. * Moves get_runtime_environment_key() from JIT into VM. * Passes the encrypted VM pointer to BuiltinFunctions. * Puts due_insn_count into the VM. * Moves the calculation of due_insn_count from the VM into the JIT epilogue. * Makes $ContextObject generic in declare_builtin_function!().
1 parent b1b50f7 commit ae16614

File tree

5 files changed

+91
-92
lines changed

5 files changed

+91
-92
lines changed

fuzz/fuzz_targets/common.rs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ pub struct ConfigTemplate {
1212
enable_stack_frame_gaps: bool,
1313
enable_symbol_and_section_labels: bool,
1414
sanitize_user_provided_values: bool,
15-
encrypt_runtime_environment: bool,
1615
reject_callx_r10: bool,
1716
optimize_rodata: bool,
1817
}
@@ -27,7 +26,6 @@ impl<'a> Arbitrary<'a> for ConfigTemplate {
2726
enable_stack_frame_gaps: bools & (1 << 0) != 0,
2827
enable_symbol_and_section_labels: bools & (1 << 1) != 0,
2928
sanitize_user_provided_values: bools & (1 << 3) != 0,
30-
encrypt_runtime_environment: bools & (1 << 4) != 0,
3129
reject_callx_r10: bools & (1 << 6) != 0,
3230
optimize_rodata: bools & (1 << 9) != 0,
3331
})
@@ -51,7 +49,6 @@ impl From<ConfigTemplate> for Config {
5149
enable_stack_frame_gaps,
5250
enable_symbol_and_section_labels,
5351
sanitize_user_provided_values,
54-
encrypt_runtime_environment,
5552
reject_callx_r10,
5653
optimize_rodata,
5754
} => Config {
@@ -61,7 +58,6 @@ impl From<ConfigTemplate> for Config {
6158
enable_symbol_and_section_labels,
6259
noop_instruction_rate,
6360
sanitize_user_provided_values,
64-
encrypt_runtime_environment,
6561
reject_callx_r10,
6662
optimize_rodata,
6763
..Default::default()

src/interpreter.rs

Lines changed: 8 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use crate::{
1616
ebpf::{self, STACK_PTR_REG},
1717
elf::Executable,
1818
error::EbpfError,
19-
vm::{Config, ContextObject, EbpfVm, ProgramResult},
19+
vm::{get_runtime_environment_key, Config, ContextObject, EbpfVm, ProgramResult},
2020
};
2121

2222
/// Virtual memory operation helper.
@@ -91,7 +91,6 @@ pub struct Interpreter<'a, 'b, C: ContextObject> {
9191
pub(crate) executable: &'a Executable<C>,
9292
pub(crate) program: &'a [u8],
9393
pub(crate) program_vm_addr: u64,
94-
pub(crate) due_insn_count: u64,
9594

9695
/// General purpose registers and pc
9796
pub reg: [u64; 12],
@@ -115,7 +114,6 @@ impl<'a, 'b, C: ContextObject> Interpreter<'a, 'b, C> {
115114
executable,
116115
program,
117116
program_vm_addr,
118-
due_insn_count: 0,
119117
reg: registers,
120118
#[cfg(feature = "debugger")]
121119
debug_state: DebugState::Continue,
@@ -161,7 +159,7 @@ impl<'a, 'b, C: ContextObject> Interpreter<'a, 'b, C> {
161159
pub fn step(&mut self) -> bool {
162160
let config = &self.executable.get_config();
163161

164-
self.due_insn_count += 1;
162+
self.vm.due_insn_count += 1;
165163
let mut next_pc = self.reg[11] + 1;
166164
if next_pc as usize * ebpf::INSN_SIZE > self.program.len() {
167165
throw_error!(self, EbpfError::ExecutionOverrun);
@@ -456,7 +454,7 @@ impl<'a, 'b, C: ContextObject> Interpreter<'a, 'b, C> {
456454
}
457455
check_pc!(self, next_pc, (target_pc - self.program_vm_addr) / ebpf::INSN_SIZE as u64);
458456
if self.executable.get_sbpf_version().static_syscalls() && self.executable.get_function_registry().lookup_by_key(next_pc as u32).is_none() {
459-
self.due_insn_count += 1;
457+
self.vm.due_insn_count += 1;
460458
self.reg[11] = next_pc;
461459
throw_error!(self, EbpfError::UnsupportedInstruction);
462460
}
@@ -476,27 +474,20 @@ impl<'a, 'b, C: ContextObject> Interpreter<'a, 'b, C> {
476474
if let Some((_function_name, function)) = self.executable.get_loader().get_function_registry().lookup_by_key(insn.imm as u32) {
477475
resolved = true;
478476

479-
if config.enable_instruction_meter {
480-
self.vm.context_object_pointer.consume(self.due_insn_count);
481-
}
482-
self.due_insn_count = 0;
477+
self.vm.due_insn_count = self.vm.previous_instruction_meter - self.vm.due_insn_count;
483478
function(
484-
self.vm.context_object_pointer,
479+
unsafe { (self.vm as *mut _ as *mut u64).offset(get_runtime_environment_key() as isize) as *mut _ },
485480
self.reg[1],
486481
self.reg[2],
487482
self.reg[3],
488483
self.reg[4],
489484
self.reg[5],
490-
&mut self.vm.memory_mapping,
491-
&mut self.vm.program_result,
492485
);
486+
self.vm.due_insn_count = 0;
493487
self.reg[0] = match &self.vm.program_result {
494488
ProgramResult::Ok(value) => *value,
495489
ProgramResult::Err(_err) => return false,
496490
};
497-
if config.enable_instruction_meter {
498-
self.vm.previous_instruction_meter = self.vm.context_object_pointer.get_remaining();
499-
}
500491
}
501492
}
502493

@@ -519,7 +510,7 @@ impl<'a, 'b, C: ContextObject> Interpreter<'a, 'b, C> {
519510

520511
ebpf::EXIT => {
521512
if self.vm.call_depth == 0 {
522-
if config.enable_instruction_meter && self.due_insn_count > self.vm.previous_instruction_meter {
513+
if config.enable_instruction_meter && self.vm.due_insn_count > self.vm.previous_instruction_meter {
523514
throw_error!(self, EbpfError::ExceededMaxInstructions);
524515
}
525516
self.vm.program_result = ProgramResult::Ok(self.reg[0]);
@@ -542,7 +533,7 @@ impl<'a, 'b, C: ContextObject> Interpreter<'a, 'b, C> {
542533
_ => throw_error!(self, EbpfError::UnsupportedInstruction),
543534
}
544535

545-
if config.enable_instruction_meter && self.due_insn_count >= self.vm.previous_instruction_meter {
536+
if config.enable_instruction_meter && self.vm.due_insn_count >= self.vm.previous_instruction_meter {
546537
self.reg[11] += 1;
547538
throw_error!(self, EbpfError::ExceededMaxInstructions);
548539
}

src/jit.rs

Lines changed: 19 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -21,20 +21,14 @@ use crate::{
2121
allocate_pages, free_pages, get_system_page_size, protect_pages, round_to_page_size,
2222
},
2323
memory_region::{AccessType, MemoryMapping},
24-
vm::{Config, ContextObject, EbpfVm, ProgramResult},
24+
vm::{get_runtime_environment_key, Config, ContextObject, EbpfVm, ProgramResult},
2525
x86::*,
2626
};
2727

28-
/// Shift the RUNTIME_ENVIRONMENT_KEY by this many bits to the LSB
29-
///
30-
/// 3 bits for 8 Byte alignment, and 1 bit to have encoding space for the RuntimeEnvironment.
31-
const PROGRAM_ENVIRONMENT_KEY_SHIFT: u32 = 4;
3228
const MAX_EMPTY_PROGRAM_MACHINE_CODE_LENGTH: usize = 4096;
3329
const MAX_MACHINE_CODE_LENGTH_PER_INSTRUCTION: usize = 110;
3430
const MACHINE_CODE_PER_INSTRUCTION_METER_CHECKPOINT: usize = 13;
3531

36-
static RUNTIME_ENVIRONMENT_KEY: std::sync::OnceLock<i32> = std::sync::OnceLock::<i32>::new();
37-
3832
pub struct JitProgram {
3933
/// OS page size in bytes and the alignment of the sections
4034
page_size: usize,
@@ -100,10 +94,8 @@ impl JitProgram {
10094
_config: &Config,
10195
vm: &mut EbpfVm<C>,
10296
registers: [u64; 12],
103-
) -> i64 {
97+
) {
10498
unsafe {
105-
let mut instruction_meter =
106-
(vm.previous_instruction_meter as i64).wrapping_add(registers[11] as i64);
10799
std::arch::asm!(
108100
// RBP and RBX must be saved and restored manually in the current version of rustc and llvm.
109101
"push rbx",
@@ -124,19 +116,17 @@ impl JitProgram {
124116
"mov rbp, [r11 + 0x50]",
125117
"mov r11, [r11 + 0x58]",
126118
"call r10",
127-
"mov rax, rbx",
128119
"pop rbp",
129120
"pop rbx",
130121
host_stack_pointer = in(reg) &mut vm.host_stack_pointer,
131-
inlateout("rax") instruction_meter,
132-
inlateout("rdi") (vm as *mut _ as *mut u64).offset(*RUNTIME_ENVIRONMENT_KEY.get().unwrap() as isize) => _,
122+
inlateout("rdi") (vm as *mut _ as *mut u64).offset(get_runtime_environment_key() as isize) => _,
123+
inlateout("rax") (vm.previous_instruction_meter as i64).wrapping_add(registers[11] as i64) => _,
133124
inlateout("r10") self.pc_section[registers[11] as usize] => _,
134125
inlateout("r11") &registers => _,
135126
lateout("rsi") _, lateout("rdx") _, lateout("rcx") _, lateout("r8") _,
136127
lateout("r9") _, lateout("r12") _, lateout("r13") _, lateout("r14") _, lateout("r15") _,
137128
// lateout("rbp") _, lateout("rbx") _,
138129
);
139-
instruction_meter
140130
}
141131
}
142132

@@ -255,11 +245,12 @@ enum RuntimeEnvironmentSlot {
255245
StackPointer = 2,
256246
ContextObjectPointer = 3,
257247
PreviousInstructionMeter = 4,
258-
StopwatchNumerator = 5,
259-
StopwatchDenominator = 6,
260-
Registers = 7,
261-
ProgramResult = 19,
262-
MemoryMapping = 27,
248+
DueInsnCount = 5,
249+
StopwatchNumerator = 6,
250+
StopwatchDenominator = 7,
251+
Registers = 8,
252+
ProgramResult = 20,
253+
MemoryMapping = 28,
263254
}
264255

265256
/* Explaination of the Instruction Meter
@@ -357,13 +348,7 @@ impl<'a, C: ContextObject> JitCompiler<'a, C> {
357348
code_length_estimate += pc / config.instruction_meter_checkpoint_distance * MACHINE_CODE_PER_INSTRUCTION_METER_CHECKPOINT;
358349
}
359350

360-
let runtime_environment_key = *RUNTIME_ENVIRONMENT_KEY.get_or_init(|| {
361-
if config.encrypt_runtime_environment {
362-
rand::thread_rng().gen::<i32>() >> PROGRAM_ENVIRONMENT_KEY_SHIFT
363-
} else {
364-
0
365-
}
366-
});
351+
let runtime_environment_key = get_runtime_environment_key();
367352
let mut diversification_rng = SmallRng::from_rng(rand::thread_rng()).map_err(|_| EbpfError::JitNotCompiled)?;
368353

369354
Ok(Self {
@@ -1326,6 +1311,10 @@ impl<'a, C: ContextObject> JitCompiler<'a, C> {
13261311
if self.config.enable_instruction_meter {
13271312
self.emit_ins(X86Instruction::alu(OperandSize::S64, 0x81, 5, REGISTER_INSTRUCTION_METER, 1, None)); // REGISTER_INSTRUCTION_METER -= 1;
13281313
self.emit_ins(X86Instruction::alu(OperandSize::S64, 0x29, REGISTER_SCRATCH, REGISTER_INSTRUCTION_METER, 0, None)); // REGISTER_INSTRUCTION_METER -= pc;
1314+
// *DueInsnCount = *PreviousInstructionMeter - REGISTER_INSTRUCTION_METER;
1315+
self.emit_ins(X86Instruction::alu(OperandSize::S64, 0x2B, REGISTER_INSTRUCTION_METER, REGISTER_PTR_TO_VM, 0, Some(X86IndirectAccess::Offset(self.slot_in_vm(RuntimeEnvironmentSlot::PreviousInstructionMeter))))); // REGISTER_INSTRUCTION_METER -= *PreviousInstructionMeter;
1316+
self.emit_ins(X86Instruction::alu(OperandSize::S64, 0xf7, 3, REGISTER_INSTRUCTION_METER, 0, None)); // REGISTER_INSTRUCTION_METER = -REGISTER_INSTRUCTION_METER;
1317+
self.emit_ins(X86Instruction::store(OperandSize::S64, REGISTER_INSTRUCTION_METER, REGISTER_PTR_TO_VM, X86IndirectAccess::Offset(self.slot_in_vm(RuntimeEnvironmentSlot::DueInsnCount)))); // *DueInsnCount = REGISTER_INSTRUCTION_METER;
13291318
}
13301319
// Print stop watch value
13311320
fn stopwatch_result(numerator: u64, denominator: u64) {
@@ -1398,29 +1387,18 @@ impl<'a, C: ContextObject> JitCompiler<'a, C> {
13981387
self.set_anchor(ANCHOR_EXTERNAL_FUNCTION_CALL);
13991388
self.emit_ins(X86Instruction::push_immediate(OperandSize::S64, -1)); // Used as PC value in error case, acts as stack padding otherwise
14001389
if self.config.enable_instruction_meter {
1401-
// REGISTER_INSTRUCTION_METER = *PreviousInstructionMeter - REGISTER_INSTRUCTION_METER;
1402-
self.emit_ins(X86Instruction::alu(OperandSize::S64, 0x2B, REGISTER_INSTRUCTION_METER, REGISTER_PTR_TO_VM, 0, Some(X86IndirectAccess::Offset(self.slot_in_vm(RuntimeEnvironmentSlot::PreviousInstructionMeter))))); // REGISTER_INSTRUCTION_METER -= *PreviousInstructionMeter;
1403-
self.emit_ins(X86Instruction::alu(OperandSize::S64, 0xf7, 3, REGISTER_INSTRUCTION_METER, 0, None)); // REGISTER_INSTRUCTION_METER = -REGISTER_INSTRUCTION_METER;
1404-
self.emit_rust_call(Value::Constant64(C::consume as *const u8 as i64, false), &[
1405-
Argument { index: 1, value: Value::Register(REGISTER_INSTRUCTION_METER) },
1406-
Argument { index: 0, value: Value::RegisterIndirect(REGISTER_PTR_TO_VM, self.slot_in_vm(RuntimeEnvironmentSlot::ContextObjectPointer), false) },
1407-
], None);
1390+
self.emit_ins(X86Instruction::store(OperandSize::S64, REGISTER_INSTRUCTION_METER, REGISTER_PTR_TO_VM, X86IndirectAccess::Offset(self.slot_in_vm(RuntimeEnvironmentSlot::DueInsnCount)))); // *DueInsnCount = REGISTER_INSTRUCTION_METER;
14081391
}
14091392
self.emit_rust_call(Value::Register(REGISTER_SCRATCH), &[
1410-
Argument { index: 7, value: Value::RegisterPlusConstant32(REGISTER_PTR_TO_VM, self.slot_in_vm(RuntimeEnvironmentSlot::ProgramResult), false) },
1411-
Argument { index: 6, value: Value::RegisterPlusConstant32(REGISTER_PTR_TO_VM, self.slot_in_vm(RuntimeEnvironmentSlot::MemoryMapping), false) },
14121393
Argument { index: 5, value: Value::Register(ARGUMENT_REGISTERS[5]) },
14131394
Argument { index: 4, value: Value::Register(ARGUMENT_REGISTERS[4]) },
14141395
Argument { index: 3, value: Value::Register(ARGUMENT_REGISTERS[3]) },
14151396
Argument { index: 2, value: Value::Register(ARGUMENT_REGISTERS[2]) },
14161397
Argument { index: 1, value: Value::Register(ARGUMENT_REGISTERS[1]) },
1417-
Argument { index: 0, value: Value::RegisterIndirect(REGISTER_PTR_TO_VM, self.slot_in_vm(RuntimeEnvironmentSlot::ContextObjectPointer), false) },
1398+
Argument { index: 0, value: Value::Register(REGISTER_PTR_TO_VM) },
14181399
], None);
14191400
if self.config.enable_instruction_meter {
1420-
self.emit_rust_call(Value::Constant64(C::get_remaining as *const u8 as i64, false), &[
1421-
Argument { index: 0, value: Value::RegisterIndirect(REGISTER_PTR_TO_VM, self.slot_in_vm(RuntimeEnvironmentSlot::ContextObjectPointer), false) },
1422-
], Some(REGISTER_INSTRUCTION_METER));
1423-
self.emit_ins(X86Instruction::store(OperandSize::S64, REGISTER_INSTRUCTION_METER, REGISTER_PTR_TO_VM, X86IndirectAccess::Offset(self.slot_in_vm(RuntimeEnvironmentSlot::PreviousInstructionMeter)))); // *PreviousInstructionMeter = REGISTER_INSTRUCTION_METER;
1401+
self.emit_ins(X86Instruction::load(OperandSize::S64, REGISTER_PTR_TO_VM, REGISTER_INSTRUCTION_METER, X86IndirectAccess::Offset(self.slot_in_vm(RuntimeEnvironmentSlot::PreviousInstructionMeter)))); // REGISTER_INSTRUCTION_METER = *PreviousInstructionMeter;
14241402
}
14251403

14261404
// Test if result indicates that an error occured
@@ -1650,6 +1628,7 @@ mod tests {
16501628
check_slot!(env, stack_pointer, StackPointer);
16511629
check_slot!(env, context_object_pointer, ContextObjectPointer);
16521630
check_slot!(env, previous_instruction_meter, PreviousInstructionMeter);
1631+
check_slot!(env, due_insn_count, DueInsnCount);
16531632
check_slot!(env, stopwatch_numerator, StopwatchNumerator);
16541633
check_slot!(env, stopwatch_denominator, StopwatchDenominator);
16551634
check_slot!(env, registers, Registers);

src/program.rs

Lines changed: 44 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,7 @@ use {
33
crate::{
44
ebpf,
55
elf::ElfError,
6-
memory_region::MemoryMapping,
7-
vm::{Config, ContextObject, ProgramResult},
6+
vm::{Config, ContextObject, EbpfVm},
87
},
98
std::collections::{btree_map::Entry, BTreeMap},
109
};
@@ -210,8 +209,7 @@ impl<T: Copy + PartialEq> FunctionRegistry<T> {
210209
}
211210

212211
/// Syscall function without context
213-
pub type BuiltinFunction<C> =
214-
fn(&mut C, u64, u64, u64, u64, u64, &mut MemoryMapping, &mut ProgramResult);
212+
pub type BuiltinFunction<C> = fn(*mut EbpfVm<C>, u64, u64, u64, u64, u64);
215213

216214
/// Represents the interface to a fixed functionality program
217215
#[derive(Eq)]
@@ -279,10 +277,9 @@ impl<C: ContextObject> std::fmt::Debug for BuiltinProgram<C> {
279277
fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
280278
writeln!(f, "{:?}", unsafe {
281279
// `derive(Debug)` does not know that `C: ContextObject` does not need to implement `Debug`
282-
std::mem::transmute::<
283-
&FunctionRegistry<BuiltinFunction<C>>,
284-
&FunctionRegistry<BuiltinFunction<*const ()>>,
285-
>(&self.functions)
280+
std::mem::transmute::<&FunctionRegistry<BuiltinFunction<C>>, &FunctionRegistry<usize>>(
281+
&self.functions,
282+
)
286283
})?;
287284
Ok(())
288285
}
@@ -291,28 +288,55 @@ impl<C: ContextObject> std::fmt::Debug for BuiltinProgram<C> {
291288
/// Generates an adapter for a BuiltinFunction between the Rust and the VM interface
292289
#[macro_export]
293290
macro_rules! declare_builtin_function {
294-
($(#[$attr:meta])* $name:ident, $rust:item) => {
291+
($(#[$attr:meta])* $name:ident, fn rust(
292+
$vm:ident : &mut $ContextObject:ty,
293+
$arg_a:ident : u64,
294+
$arg_b:ident : u64,
295+
$arg_c:ident : u64,
296+
$arg_d:ident : u64,
297+
$arg_e:ident : u64,
298+
$memory_mapping:ident : &mut $MemoryMapping:ty,
299+
) -> Result<u64, EbpfError> $rust:tt) => {
295300
$(#[$attr])*
296301
pub struct $name {}
297302
impl $name {
298303
/// Rust interface
299-
$rust
304+
pub fn rust(
305+
$vm: &mut $ContextObject,
306+
$arg_a: u64,
307+
$arg_b: u64,
308+
$arg_c: u64,
309+
$arg_d: u64,
310+
$arg_e: u64,
311+
$memory_mapping: &mut $MemoryMapping,
312+
) -> Result<u64, EbpfError> {
313+
$rust
314+
}
300315
/// VM interface
301316
#[allow(clippy::too_many_arguments)]
302317
pub fn vm(
303-
context_object: &mut TestContextObject,
304-
arg_a: u64,
305-
arg_b: u64,
306-
arg_c: u64,
307-
arg_d: u64,
308-
arg_e: u64,
309-
memory_mapping: &mut $crate::memory_region::MemoryMapping,
310-
program_result: &mut $crate::vm::ProgramResult,
318+
$vm: *mut $crate::vm::EbpfVm<$ContextObject>,
319+
$arg_a: u64,
320+
$arg_b: u64,
321+
$arg_c: u64,
322+
$arg_d: u64,
323+
$arg_e: u64,
311324
) {
325+
use $crate::vm::ContextObject;
326+
let vm = unsafe {
327+
&mut *(($vm as *mut u64).offset(-($crate::vm::get_runtime_environment_key() as isize)) as *mut $crate::vm::EbpfVm<$ContextObject>)
328+
};
329+
let config = vm.loader.get_config();
330+
if config.enable_instruction_meter {
331+
vm.context_object_pointer.consume(vm.previous_instruction_meter - vm.due_insn_count);
332+
}
312333
let converted_result: $crate::vm::ProgramResult = Self::rust(
313-
context_object, arg_a, arg_b, arg_c, arg_d, arg_e, memory_mapping,
334+
vm.context_object_pointer, $arg_a, $arg_b, $arg_c, $arg_d, $arg_e, &mut vm.memory_mapping,
314335
).into();
315-
*program_result = converted_result;
336+
vm.program_result = converted_result;
337+
if config.enable_instruction_meter {
338+
vm.previous_instruction_meter = vm.context_object_pointer.get_remaining();
339+
}
316340
}
317341
}
318342
};

0 commit comments

Comments
 (0)