1#![allow(clippy::arithmetic_side_effects)]
2use crate::{
16 ebpf,
17 elf::Executable,
18 error::{EbpfError, ProgramResult},
19 interpreter::Interpreter,
20 memory_region::MemoryMapping,
21 program::{BuiltinFunction, BuiltinProgram, FunctionRegistry, SBPFVersion},
22 static_analysis::{Analysis, DummyContextObject, RegisterTraceEntry},
23};
24pub use defaults::get_stack_frame_size;
26use std::{collections::BTreeMap, fmt::Debug, marker::PhantomData, mem::offset_of, ptr};
27
28#[cfg(feature = "shuttle-test")]
29use shuttle::sync::Arc;
30#[cfg(not(feature = "shuttle-test"))]
31use std::sync::Arc;
32
33#[cfg(all(feature = "jit", not(feature = "shuttle-test")))]
34use rand::{thread_rng, Rng};
35#[cfg(all(feature = "jit", feature = "shuttle-test"))]
36use shuttle::rand::{thread_rng, Rng};
37
38#[cfg(feature = "jit")]
40pub fn get_runtime_environment_key() -> i32 {
41 static RUNTIME_ENVIRONMENT_KEY: std::sync::OnceLock<i32> = std::sync::OnceLock::new();
42 *RUNTIME_ENVIRONMENT_KEY.get_or_init(|| thread_rng().gen::<i32>() >> 1)
43}
44
45#[cfg(not(feature = "jit"))]
46pub fn get_runtime_environment_key() -> i32 {
47 0
48}
49
50pub(crate) mod defaults {
52 const DEFAULT_STACK_FRAME_SIZE: usize = 4_096;
53
54 #[cfg(feature = "conf-stack-frame-size")]
65 #[inline(always)]
66 pub fn get_stack_frame_size() -> usize {
67 static STACK_FRAME_SIZE_CACHE: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
68 *STACK_FRAME_SIZE_CACHE.get_or_init(|| {
69 let size = std::env::var("VM_STACK_FRAME_SIZE")
70 .ok()
71 .and_then(|v| {
72 v.parse::<usize>().ok().filter(|sfz| *sfz > 0).or_else(|| {
73 log::warn!(
74 "Invalid VM_STACK_FRAME_SIZE={}, falling back to {}.",
75 v,
76 DEFAULT_STACK_FRAME_SIZE
77 );
78 None
79 })
80 })
81 .unwrap_or(DEFAULT_STACK_FRAME_SIZE);
82 if size != DEFAULT_STACK_FRAME_SIZE {
83 log::warn!(
84 "VM_STACK_FRAME_SIZE is set to {} (default: {}).",
85 size,
86 DEFAULT_STACK_FRAME_SIZE
87 );
88 }
89 size
90 })
91 }
92
93 #[cfg(not(feature = "conf-stack-frame-size"))]
95 pub const fn get_stack_frame_size() -> usize {
96 DEFAULT_STACK_FRAME_SIZE
97 }
98}
99
100pub enum ExecutionMode {
102 Interpreted,
104 Jit,
108 PreferJit,
110}
111
112#[derive(Debug, Clone, PartialEq, Eq)]
114pub struct Config {
115 pub max_call_depth: usize,
117 pub stack_frame_size: usize,
119 pub enable_address_translation: bool,
121 pub enable_stack_frame_gaps: bool,
123 pub instruction_meter_checkpoint_distance: usize,
125 pub enable_instruction_meter: bool,
127 pub enable_register_tracing: bool,
129 pub enable_symbol_and_section_labels: bool,
131 pub reject_broken_elfs: bool,
133 #[cfg(feature = "jit")]
134 pub noop_instruction_rate: u32,
136 #[cfg(feature = "jit")]
137 pub sanitize_user_provided_values: bool,
139 pub optimize_rodata: bool,
141 pub allow_memory_region_zero: bool,
143 pub aligned_memory_mapping: bool,
145 pub enabled_sbpf_versions: std::ops::RangeInclusive<SBPFVersion>,
147}
148
149impl Config {
150 pub fn stack_size(&self) -> usize {
152 self.stack_frame_size * self.max_call_depth
153 }
154}
155
156impl Default for Config {
157 fn default() -> Self {
158 Self {
159 max_call_depth: 64,
160 stack_frame_size: defaults::get_stack_frame_size(),
161 enable_address_translation: true,
162 enable_stack_frame_gaps: true,
163 instruction_meter_checkpoint_distance: 10000,
164 enable_instruction_meter: true,
165 enable_register_tracing: false,
166 enable_symbol_and_section_labels: false,
167 reject_broken_elfs: false,
168 #[cfg(feature = "jit")]
169 noop_instruction_rate: 256,
170 #[cfg(feature = "jit")]
171 sanitize_user_provided_values: true,
172 optimize_rodata: true,
173 allow_memory_region_zero: true,
174 aligned_memory_mapping: false,
175 enabled_sbpf_versions: SBPFVersion::V0..=SBPFVersion::V4,
176 }
177 }
178}
179
180impl<C: ContextObject> Executable<C> {
182 pub fn from_elf(elf_bytes: &[u8], loader: Arc<BuiltinProgram<C>>) -> Result<Self, EbpfError> {
184 let executable = Executable::load(elf_bytes, loader)?;
185 Ok(executable)
186 }
187 pub fn from_text_bytes(
189 text_bytes: &[u8],
190 loader: Arc<BuiltinProgram<C>>,
191 sbpf_version: SBPFVersion,
192 function_registry: FunctionRegistry<usize>,
193 ) -> Result<Self, EbpfError> {
194 Executable::new_from_text_bytes(text_bytes, loader, sbpf_version, function_registry)
195 .map_err(EbpfError::ElfError)
196 }
197}
198
199pub trait ContextObject {
201 fn consume(&mut self, amount: u64);
203 fn get_remaining(&self) -> u64;
205 fn active_mapping_ptr(&mut self) -> ptr::NonNull<MemoryMapping>;
207}
208
209pub struct DynamicAnalysis {
211 pub edge_counter_max: usize,
213 pub edges: BTreeMap<usize, BTreeMap<usize, usize>>,
215}
216
217impl DynamicAnalysis {
218 pub fn new(register_trace: &[[u64; 12]], analysis: &Analysis) -> Self {
220 let mut result = Self {
221 edge_counter_max: 0,
222 edges: BTreeMap::new(),
223 };
224 let mut last_basic_block = usize::MAX;
225 for traced_instruction in register_trace.iter() {
226 let pc = traced_instruction[11] as usize;
227 if analysis.cfg_nodes.contains_key(&pc) {
228 let counter = result
229 .edges
230 .entry(last_basic_block)
231 .or_default()
232 .entry(pc)
233 .or_insert(0);
234 *counter += 1;
235 result.edge_counter_max = result.edge_counter_max.max(*counter);
236 last_basic_block = pc;
237 }
238 }
239 result
240 }
241}
242
243#[derive(Clone, Default)]
245pub struct CallFrame {
246 pub caller_saved_registers: [u64; ebpf::SCRATCH_REGS],
248 pub frame_pointer: u64,
250 pub target_pc: u64,
252}
253
254pub enum RuntimeEnvironmentSlot {
256 HostStackPointer = offset_of!(EbpfVm<DummyContextObject>, host_stack_pointer) as isize,
258 CallDepth = offset_of!(EbpfVm<DummyContextObject>, call_depth) as isize,
260 ContextObjectPointer = offset_of!(EbpfVm<DummyContextObject>, context_object_pointer) as isize,
262 PreviousInstructionMeter =
264 offset_of!(EbpfVm<DummyContextObject>, previous_instruction_meter) as isize,
265 DueInsnCount = offset_of!(EbpfVm<DummyContextObject>, due_insn_count) as isize,
267 StopwatchNumerator = offset_of!(EbpfVm<DummyContextObject>, stopwatch_numerator) as isize,
269 StopwatchDenominator = offset_of!(EbpfVm<DummyContextObject>, stopwatch_denominator) as isize,
271 Registers = offset_of!(EbpfVm<DummyContextObject>, registers) as isize,
273 ProgramResult = offset_of!(EbpfVm<DummyContextObject>, program_result) as isize,
275 MemoryMapping = offset_of!(EbpfVm<DummyContextObject>, memory_mapping) as isize,
277 RegisterTrace = offset_of!(EbpfVm<DummyContextObject>, register_trace) as isize,
279}
280
281#[repr(C)]
337pub struct EbpfVm<'a, C: ContextObject> {
338 pub host_stack_pointer: *mut u64,
340 pub call_depth: u64,
345 pub(crate) context_object_pointer: ptr::NonNull<C>,
347 context_object_lifetime: PhantomData<&'a mut C>,
349 pub previous_instruction_meter: u64,
351 pub due_insn_count: u64,
353 pub stopwatch_numerator: u64,
355 pub stopwatch_denominator: u64,
357 pub registers: [u64; 12],
359 pub program_result: ProgramResult,
361 pub(crate) memory_mapping: ptr::NonNull<MemoryMapping>,
363 pub loader: Arc<BuiltinProgram<C>>,
365 pub register_trace: Vec<RegisterTraceEntry>,
367 #[cfg(feature = "debugger")]
369 pub debug_port: Option<u16>,
370 #[cfg(feature = "debugger")]
372 pub debug_metadata: Option<String>,
373}
374
375impl<'a, C: ContextObject> EbpfVm<'a, C> {
376 pub fn new(
378 loader: Arc<BuiltinProgram<C>>,
379 sbpf_version: SBPFVersion,
380 context_object: &'a mut C,
381 stack_len: usize,
382 ) -> Self {
383 let config = loader.get_config();
384 let mut registers = [0u64; 12];
385 registers[ebpf::FRAME_PTR_REG] =
386 ebpf::MM_STACK_START.saturating_add(if !sbpf_version.manual_stack_frame_bump() {
387 config.stack_frame_size
388 } else {
389 stack_len
390 } as u64);
391
392 let memory_mapping = context_object.active_mapping_ptr();
393 EbpfVm {
394 host_stack_pointer: std::ptr::null_mut(),
395 call_depth: 0,
396 context_object_pointer: ptr::NonNull::from_mut(context_object),
397 context_object_lifetime: PhantomData,
398 previous_instruction_meter: 0,
399 due_insn_count: 0,
400 stopwatch_numerator: 0,
401 stopwatch_denominator: 0,
402 registers,
403 program_result: ProgramResult::Ok(0),
404 memory_mapping,
405 loader,
406 #[cfg(feature = "debugger")]
407 debug_port: std::env::var("VM_DEBUG_PORT")
408 .ok()
409 .and_then(|v| v.parse::<u16>().ok()),
410 #[cfg(feature = "debugger")]
411 debug_metadata: None,
412 register_trace: Vec::default(),
413 }
414 }
415
416 pub fn execute_program(
427 &mut self,
428 executable: &Executable<C>,
429 mode: &mut ExecutionMode,
430 call_frames: &mut [CallFrame],
431 ) -> (u64, ProgramResult) {
432 debug_assert!(Arc::ptr_eq(&self.loader, executable.get_loader()));
433 self.registers[11] = executable.get_entrypoint_instruction_offset() as u64;
434 let config = executable.get_config();
435 let initial_insn_count = self.context().get_remaining();
436 self.previous_instruction_meter = initial_insn_count;
437 self.due_insn_count = 0;
438 self.program_result = ProgramResult::Ok(0);
439
440 'execute: {
441 match *mode {
442 ExecutionMode::Interpreted => {}
443
444 #[cfg(all(feature = "jit", not(target_os = "windows"), target_arch = "x86_64"))]
445 ExecutionMode::PreferJit => {
446 if let Some(compiled_program) = executable.get_compiled_program() {
447 *mode = ExecutionMode::Jit;
448 break 'execute compiled_program.invoke(config, self, self.registers);
449 }
450 }
451 #[cfg(not(all(
452 feature = "jit",
453 not(target_os = "windows"),
454 target_arch = "x86_64"
455 )))]
456 ExecutionMode::PreferJit => {}
457
458 #[cfg(all(feature = "jit", not(target_os = "windows"), target_arch = "x86_64"))]
459 ExecutionMode::Jit => {
460 let Some(compiled_program) = executable.get_compiled_program() else {
461 return (0, ProgramResult::Err(EbpfError::JitNotCompiled));
462 };
463 *mode = ExecutionMode::Jit;
464 break 'execute compiled_program.invoke(config, self, self.registers);
465 }
466 #[cfg(not(all(
467 feature = "jit",
468 not(target_os = "windows"),
469 target_arch = "x86_64"
470 )))]
471 ExecutionMode::Jit => return (0, ProgramResult::Err(EbpfError::JitNotCompiled)),
472 }
473
474 *mode = ExecutionMode::Interpreted;
475 let interpreter = Interpreter::new(self, executable, self.registers, call_frames);
476 break 'execute run_interpreter(interpreter);
477 }
478
479 let instruction_count = if config.enable_instruction_meter {
480 let due_insn_count = self.due_insn_count;
481 let context = self.context();
482 context.consume(due_insn_count);
483 initial_insn_count.saturating_sub(context.get_remaining())
484 } else {
485 0
486 };
487 let mut result = ProgramResult::Ok(0);
488 std::mem::swap(&mut result, &mut self.program_result);
489 (instruction_count, result)
490 }
491
492 pub fn invoke_function(&mut self, function: BuiltinFunction<C>) {
494 function(
495 self.encrypted_host_address(),
496 self.registers[1],
497 self.registers[2],
498 self.registers[3],
499 self.registers[4],
500 self.registers[5],
501 );
502 }
503
504 pub(crate) fn encrypted_host_address(&mut self) -> EncryptedHostAddressToEbpfVm<C> {
510 let addr = (&raw mut *self).expose_provenance() as isize;
511 EncryptedHostAddressToEbpfVm(
512 addr.wrapping_add(get_runtime_environment_key() as isize) as usize as u64,
513 PhantomData,
514 )
515 }
516
517 pub fn context(&mut self) -> &mut C {
519 unsafe { self.context_object_pointer.as_mut() }
529 }
530
531 pub(crate) fn memory(&mut self) -> &mut MemoryMapping {
534 unsafe { self.memory_mapping.as_mut() }
540 }
541}
542
543#[repr(transparent)]
545pub struct EncryptedHostAddressToEbpfVm<C>(
546 pub(crate) u64,
549 PhantomData<C>,
550);
551
552impl<C: ContextObject> EncryptedHostAddressToEbpfVm<C> {
553 pub unsafe fn with_vm<R>(&mut self, cb: impl FnOnce(&mut EbpfVm<'_, C>) -> R) -> R {
561 let addr = (self.0 as usize as isize)
562 .wrapping_sub(crate::vm::get_runtime_environment_key() as isize);
563 let vm = unsafe {
567 std::ptr::with_exposed_provenance_mut::<crate::vm::EbpfVm<C>>(addr as usize)
568 .as_mut()
569 .unwrap()
570 };
571 cb(vm)
572 }
573}
574
575#[cold]
576#[inline(never)]
577#[cfg(feature = "debugger")]
578fn run_interpreter<C: ContextObject>(mut interpreter: Interpreter<C>) {
579 let debug_port = interpreter.vm.debug_port.clone();
580 if let Some(debug_port) = debug_port {
581 crate::debugger::execute(&mut interpreter, debug_port);
582 } else {
583 while interpreter.step() {}
584 }
585}
586
587#[cold]
588#[inline(never)]
589#[cfg(not(feature = "debugger"))]
590fn run_interpreter<C: ContextObject>(mut interpreter: Interpreter<C>) {
591 while interpreter.step() {}
592}
593
594#[cfg(test)]
595mod tests {
596 use crate::{
597 memory_region::MemoryMapping,
598 program::{BuiltinProgram, SBPFVersion},
599 vm::{Config, ContextObject, RuntimeEnvironmentSlot},
600 };
601 use std::{ptr::NonNull, sync::Arc};
602
603 #[test]
604 fn test_runtime_environment_slots() {
605 struct DummyContextObject(MemoryMapping);
606 impl ContextObject for DummyContextObject {
607 fn consume(&mut self, _: u64) {
608 todo!()
609 }
610 fn get_remaining(&self) -> u64 {
611 todo!()
612 }
613 fn active_mapping_ptr(&mut self) -> NonNull<MemoryMapping> {
614 NonNull::from_mut(&mut self.0)
615 }
616 }
617 let version = SBPFVersion::V4;
618 let config = Config::default();
619 let mut context_object =
620 unsafe { DummyContextObject(MemoryMapping::new(vec![], &config, version).unwrap()) };
621 let env = super::EbpfVm::new(
622 Arc::new(BuiltinProgram::new_mock()),
623 version,
624 &mut context_object,
625 4096,
626 );
627
628 macro_rules! check_slot {
629 ($env:expr, $entry:ident, $slot:ident) => {
630 assert_eq!(
631 unsafe {
632 std::ptr::addr_of!($env.$entry)
633 .cast::<u8>()
634 .offset_from(std::ptr::addr_of!($env).cast::<u8>()) as usize
635 },
636 RuntimeEnvironmentSlot::$slot as usize,
637 );
638 };
639 }
640
641 check_slot!(env, host_stack_pointer, HostStackPointer);
642 check_slot!(env, call_depth, CallDepth);
643 check_slot!(env, context_object_pointer, ContextObjectPointer);
644 check_slot!(env, previous_instruction_meter, PreviousInstructionMeter);
645 check_slot!(env, due_insn_count, DueInsnCount);
646 check_slot!(env, stopwatch_numerator, StopwatchNumerator);
647 check_slot!(env, stopwatch_denominator, StopwatchDenominator);
648 check_slot!(env, registers, Registers);
649 check_slot!(env, program_result, ProgramResult);
650 check_slot!(env, memory_mapping, MemoryMapping);
651 check_slot!(env, register_trace, RegisterTrace);
652 }
653}