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, RegisterTraceEntry},
23};
24use std::{collections::BTreeMap, fmt::Debug};
25
26#[cfg(feature = "shuttle-test")]
27use shuttle::sync::Arc;
28#[cfg(not(feature = "shuttle-test"))]
29use std::sync::Arc;
30
31#[cfg(all(feature = "jit", not(feature = "shuttle-test")))]
32use rand::{thread_rng, Rng};
33#[cfg(all(feature = "jit", feature = "shuttle-test"))]
34use shuttle::rand::{thread_rng, Rng};
35
36#[cfg(feature = "jit")]
40const PROGRAM_ENVIRONMENT_KEY_SHIFT: u32 = 4;
41#[cfg(feature = "jit")]
42static RUNTIME_ENVIRONMENT_KEY: std::sync::OnceLock<i32> = std::sync::OnceLock::<i32>::new();
43
44pub fn get_runtime_environment_key() -> i32 {
46 #[cfg(feature = "jit")]
47 {
48 *RUNTIME_ENVIRONMENT_KEY
49 .get_or_init(|| thread_rng().gen::<i32>() >> PROGRAM_ENVIRONMENT_KEY_SHIFT)
50 }
51 #[cfg(not(feature = "jit"))]
52 0
53}
54
55#[derive(Debug, Clone, PartialEq, Eq)]
57pub struct Config {
58 pub max_call_depth: usize,
60 pub stack_frame_size: usize,
62 pub enable_address_translation: bool,
64 pub enable_stack_frame_gaps: bool,
66 pub instruction_meter_checkpoint_distance: usize,
68 pub enable_instruction_meter: bool,
70 pub enable_register_tracing: bool,
72 pub enable_symbol_and_section_labels: bool,
74 pub reject_broken_elfs: bool,
76 #[cfg(feature = "jit")]
77 pub noop_instruction_rate: u32,
79 #[cfg(feature = "jit")]
80 pub sanitize_user_provided_values: bool,
82 pub optimize_rodata: bool,
84 pub aligned_memory_mapping: bool,
86 pub enabled_sbpf_versions: std::ops::RangeInclusive<SBPFVersion>,
88}
89
90impl Config {
91 pub fn stack_size(&self) -> usize {
93 self.stack_frame_size * self.max_call_depth
94 }
95}
96
97impl Default for Config {
98 fn default() -> Self {
99 Self {
100 max_call_depth: 64,
101 stack_frame_size: 4_096,
102 enable_address_translation: true,
103 enable_stack_frame_gaps: true,
104 instruction_meter_checkpoint_distance: 10000,
105 enable_instruction_meter: true,
106 enable_register_tracing: false,
107 enable_symbol_and_section_labels: false,
108 reject_broken_elfs: false,
109 #[cfg(feature = "jit")]
110 noop_instruction_rate: 256,
111 #[cfg(feature = "jit")]
112 sanitize_user_provided_values: true,
113 optimize_rodata: true,
114 aligned_memory_mapping: true,
115 enabled_sbpf_versions: SBPFVersion::V0..=SBPFVersion::V4,
116 }
117 }
118}
119
120impl<C: ContextObject> Executable<C> {
122 pub fn from_elf(elf_bytes: &[u8], loader: Arc<BuiltinProgram<C>>) -> Result<Self, EbpfError> {
124 let executable = Executable::load(elf_bytes, loader)?;
125 Ok(executable)
126 }
127 pub fn from_text_bytes(
129 text_bytes: &[u8],
130 loader: Arc<BuiltinProgram<C>>,
131 sbpf_version: SBPFVersion,
132 function_registry: FunctionRegistry<usize>,
133 ) -> Result<Self, EbpfError> {
134 Executable::new_from_text_bytes(text_bytes, loader, sbpf_version, function_registry)
135 .map_err(EbpfError::ElfError)
136 }
137}
138
139pub trait ContextObject {
141 fn consume(&mut self, amount: u64);
143 fn get_remaining(&self) -> u64;
145}
146
147pub struct DynamicAnalysis {
149 pub edge_counter_max: usize,
151 pub edges: BTreeMap<usize, BTreeMap<usize, usize>>,
153}
154
155impl DynamicAnalysis {
156 pub fn new(register_trace: &[[u64; 12]], analysis: &Analysis) -> Self {
158 let mut result = Self {
159 edge_counter_max: 0,
160 edges: BTreeMap::new(),
161 };
162 let mut last_basic_block = usize::MAX;
163 for traced_instruction in register_trace.iter() {
164 let pc = traced_instruction[11] as usize;
165 if analysis.cfg_nodes.contains_key(&pc) {
166 let counter = result
167 .edges
168 .entry(last_basic_block)
169 .or_default()
170 .entry(pc)
171 .or_insert(0);
172 *counter += 1;
173 result.edge_counter_max = result.edge_counter_max.max(*counter);
174 last_basic_block = pc;
175 }
176 }
177 result
178 }
179}
180
181#[derive(Clone, Default)]
183pub struct CallFrame {
184 pub caller_saved_registers: [u64; ebpf::SCRATCH_REGS],
186 pub frame_pointer: u64,
188 pub target_pc: u64,
190}
191
192pub enum RuntimeEnvironmentSlot {
194 HostStackPointer = 0,
196 CallDepth = 1,
198 ContextObjectPointer = 2,
200 PreviousInstructionMeter = 3,
202 DueInsnCount = 4,
204 StopwatchNumerator = 5,
206 StopwatchDenominator = 6,
208 Registers = 7,
210 ProgramResult = 19,
212 MemoryMapping = 27,
214 RegisterTrace = 54,
216}
217
218#[repr(C)]
272pub struct EbpfVm<'a, C: ContextObject> {
273 pub host_stack_pointer: *mut u64,
275 pub call_depth: u64,
280 pub context_object_pointer: &'a mut C,
282 pub previous_instruction_meter: u64,
284 pub due_insn_count: u64,
286 pub stopwatch_numerator: u64,
288 pub stopwatch_denominator: u64,
290 pub registers: [u64; 12],
292 pub program_result: ProgramResult,
294 pub memory_mapping: MemoryMapping<'a>,
296 pub call_frames: Vec<CallFrame>,
298 pub loader: Arc<BuiltinProgram<C>>,
300 pub register_trace: Vec<RegisterTraceEntry>,
302 #[cfg(feature = "debugger")]
304 pub debug_port: Option<u16>,
305}
306
307impl<'a, C: ContextObject> EbpfVm<'a, C> {
308 pub fn new(
310 loader: Arc<BuiltinProgram<C>>,
311 sbpf_version: SBPFVersion,
312 context_object: &'a mut C,
313 mut memory_mapping: MemoryMapping<'a>,
314 stack_len: usize,
315 ) -> Self {
316 let config = loader.get_config();
317 let mut registers = [0u64; 12];
318 registers[ebpf::FRAME_PTR_REG] =
319 ebpf::MM_STACK_START.saturating_add(if sbpf_version.dynamic_stack_frames() {
320 stack_len
322 } else {
323 config.stack_frame_size
325 } as u64);
326 if !config.enable_address_translation {
327 memory_mapping = MemoryMapping::new_identity();
328 }
329 EbpfVm {
330 host_stack_pointer: std::ptr::null_mut(),
331 call_depth: 0,
332 context_object_pointer: context_object,
333 previous_instruction_meter: 0,
334 due_insn_count: 0,
335 stopwatch_numerator: 0,
336 stopwatch_denominator: 0,
337 registers,
338 program_result: ProgramResult::Ok(0),
339 memory_mapping,
340 call_frames: vec![CallFrame::default(); config.max_call_depth],
341 loader,
342 #[cfg(feature = "debugger")]
343 debug_port: std::env::var("VM_DEBUG_PORT")
344 .ok()
345 .and_then(|v| v.parse::<u16>().ok()),
346 register_trace: Vec::default(),
347 }
348 }
349
350 pub fn execute_program(
354 &mut self,
355 executable: &Executable<C>,
356 interpreted: bool,
357 ) -> (u64, ProgramResult) {
358 debug_assert!(Arc::ptr_eq(&self.loader, executable.get_loader()));
359 self.registers[11] = executable.get_entrypoint_instruction_offset() as u64;
360 let config = executable.get_config();
361 let initial_insn_count = self.context_object_pointer.get_remaining();
362 self.previous_instruction_meter = initial_insn_count;
363 self.due_insn_count = 0;
364 self.program_result = ProgramResult::Ok(0);
365 if interpreted {
366 #[cfg(feature = "debugger")]
367 let debug_port = self.debug_port.clone();
368 let mut interpreter = Interpreter::new(self, executable, self.registers);
369 #[cfg(feature = "debugger")]
370 if let Some(debug_port) = debug_port {
371 crate::debugger::execute(&mut interpreter, debug_port);
372 } else {
373 while interpreter.step() {}
374 }
375 #[cfg(not(feature = "debugger"))]
376 while interpreter.step() {}
377 } else {
378 #[cfg(all(feature = "jit", not(target_os = "windows"), target_arch = "x86_64"))]
379 {
380 let compiled_program = match executable
381 .get_compiled_program()
382 .ok_or_else(|| EbpfError::JitNotCompiled)
383 {
384 Ok(compiled_program) => compiled_program,
385 Err(error) => return (0, ProgramResult::Err(error)),
386 };
387 compiled_program.invoke(config, self, self.registers);
388 }
389 #[cfg(not(all(feature = "jit", not(target_os = "windows"), target_arch = "x86_64")))]
390 {
391 return (0, ProgramResult::Err(EbpfError::JitNotCompiled));
392 }
393 };
394 let instruction_count = if config.enable_instruction_meter {
395 self.context_object_pointer.consume(self.due_insn_count);
396 initial_insn_count.saturating_sub(self.context_object_pointer.get_remaining())
397 } else {
398 0
399 };
400 let mut result = ProgramResult::Ok(0);
401 std::mem::swap(&mut result, &mut self.program_result);
402 (instruction_count, result)
403 }
404
405 pub fn invoke_function(&mut self, function: BuiltinFunction<C>) {
407 function(
408 unsafe {
409 std::ptr::addr_of_mut!(*self)
410 .cast::<u64>()
411 .offset(get_runtime_environment_key() as isize)
412 .cast::<Self>()
413 },
414 self.registers[1],
415 self.registers[2],
416 self.registers[3],
417 self.registers[4],
418 self.registers[5],
419 );
420 }
421}