Skip to main content

solana_sbpf/
vm.rs

1#![allow(clippy::arithmetic_side_effects)]
2// Derived from uBPF <https://github.com/iovisor/ubpf>
3// Copyright 2015 Big Switch Networks, Inc
4//      (uBPF: VM architecture, parts of the interpreter, originally in C)
5// Copyright 2016 6WIND S.A. <quentin.monnet@6wind.com>
6//      (Translation to Rust, MetaBuff/multiple classes addition, hashmaps for syscalls)
7// Copyright 2020 Solana Maintainers <maintainers@solana.com>
8//
9// Licensed under the Apache License, Version 2.0 <http://www.apache.org/licenses/LICENSE-2.0> or
10// the MIT license <http://opensource.org/licenses/MIT>, at your option. This file may not be
11// copied, modified, or distributed except according to those terms.
12
13//! Virtual machine for eBPF programs.
14
15use 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};
24// Re-export defaults for direct access without the module path.
25pub 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/// Returns (and if not done before generates) the encryption key for the VM pointer
39#[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
50/// Default VM configuration settings.
51pub(crate) mod defaults {
52    const DEFAULT_STACK_FRAME_SIZE: usize = 4_096;
53
54    /// Returns the stack frame size in bytes.
55    ///
56    /// With the `conf-stack-frame-size` feature enabled, the size can be overridden
57    /// at runtime via the `VM_STACK_FRAME_SIZE` environment variable. The value is
58    /// read once and cached. If not set, the default is always returned.
59    ///
60    /// Note: the `conf-stack-frame-size` variant can't be `const fn` (it uses
61    /// `OnceLock`), while the production variant is `const fn`. Callers that need
62    /// `const` evaluation (e.g. array sizes, const generics) should be aware that
63    /// those uses will not compile when `conf-stack-frame-size` is enabled.
64    #[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    /// Returns the stack frame size in bytes.
94    #[cfg(not(feature = "conf-stack-frame-size"))]
95    pub const fn get_stack_frame_size() -> usize {
96        DEFAULT_STACK_FRAME_SIZE
97    }
98}
99
100/// Specify the execution method.
101pub enum ExecutionMode {
102    /// Execute the program in an interpreted mode.
103    Interpreted,
104    /// Execute the program in JIT mode.
105    ///
106    /// The program must be JIT compiled.
107    Jit,
108    /// Allow JIT execution, if compiled. Otherwise fallback to interpreted.
109    PreferJit,
110}
111
112/// VM configuration settings
113#[derive(Debug, Clone, PartialEq, Eq)]
114pub struct Config {
115    /// Maximum call depth
116    pub max_call_depth: usize,
117    /// Size of a stack frame in bytes, must match the size specified in the LLVM BPF backend
118    pub stack_frame_size: usize,
119    /// Enables the use of MemoryMapping and MemoryRegion for address translation
120    pub enable_address_translation: bool,
121    /// Enables gaps in VM address space between the stack frames
122    pub enable_stack_frame_gaps: bool,
123    /// Maximal pc distance after which a new instruction meter validation is emitted by the JIT
124    pub instruction_meter_checkpoint_distance: usize,
125    /// Enable instruction meter and limiting
126    pub enable_instruction_meter: bool,
127    /// Enable instruction tracing
128    pub enable_register_tracing: bool,
129    /// Enable dynamic string allocation for labels
130    pub enable_symbol_and_section_labels: bool,
131    /// Reject ELF files containing issues that the verifier did not catch before (up to v0.2.21)
132    pub reject_broken_elfs: bool,
133    #[cfg(feature = "jit")]
134    /// Ratio of native host instructions per random no-op in JIT (0 = OFF)
135    pub noop_instruction_rate: u32,
136    #[cfg(feature = "jit")]
137    /// Enable disinfection of immediate values and offsets provided by the user in JIT
138    pub sanitize_user_provided_values: bool,
139    /// Avoid copying read only sections when possible
140    pub optimize_rodata: bool,
141    /// Allow a memory region at age zero in the aligned memory mapping
142    pub allow_memory_region_zero: bool,
143    /// Use aligned memory mapping
144    pub aligned_memory_mapping: bool,
145    /// Allowed [SBPFVersion]s
146    pub enabled_sbpf_versions: std::ops::RangeInclusive<SBPFVersion>,
147}
148
149impl Config {
150    /// Returns the size of the stack memory region
151    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
180/// Static constructors for Executable
181impl<C: ContextObject> Executable<C> {
182    /// Creates an executable from an ELF file
183    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    /// Creates an executable from machine code
188    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
199/// Runtime context
200pub trait ContextObject {
201    /// Consume instructions from meter
202    fn consume(&mut self, amount: u64);
203    /// Get the number of remaining instructions allowed
204    fn get_remaining(&self) -> u64;
205    /// Return a mutable pointer to the active MemoryMapping
206    fn active_mapping_ptr(&mut self) -> ptr::NonNull<MemoryMapping>;
207}
208
209/// Statistic of taken branches (from a recorded trace)
210pub struct DynamicAnalysis {
211    /// Maximal edge counter value
212    pub edge_counter_max: usize,
213    /// src_node, dst_node, edge_counter
214    pub edges: BTreeMap<usize, BTreeMap<usize, usize>>,
215}
216
217impl DynamicAnalysis {
218    /// Accumulates a trace
219    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/// A call frame used for function calls inside the Interpreter
244#[derive(Clone, Default)]
245pub struct CallFrame {
246    /// The caller saved registers
247    pub caller_saved_registers: [u64; ebpf::SCRATCH_REGS],
248    /// The callers frame pointer
249    pub frame_pointer: u64,
250    /// The target_pc of the exit instruction which returns back to the caller
251    pub target_pc: u64,
252}
253
254/// Indices of slots inside [EbpfVm]
255pub enum RuntimeEnvironmentSlot {
256    /// [EbpfVm::host_stack_pointer]
257    HostStackPointer = offset_of!(EbpfVm<DummyContextObject>, host_stack_pointer) as isize,
258    /// [EbpfVm::call_depth]
259    CallDepth = offset_of!(EbpfVm<DummyContextObject>, call_depth) as isize,
260    /// [EbpfVm::context_object_pointer]
261    ContextObjectPointer = offset_of!(EbpfVm<DummyContextObject>, context_object_pointer) as isize,
262    /// [EbpfVm::previous_instruction_meter]
263    PreviousInstructionMeter =
264        offset_of!(EbpfVm<DummyContextObject>, previous_instruction_meter) as isize,
265    /// [EbpfVm::due_insn_count]
266    DueInsnCount = offset_of!(EbpfVm<DummyContextObject>, due_insn_count) as isize,
267    /// [EbpfVm::stopwatch_numerator]
268    StopwatchNumerator = offset_of!(EbpfVm<DummyContextObject>, stopwatch_numerator) as isize,
269    /// [EbpfVm::stopwatch_denominator]
270    StopwatchDenominator = offset_of!(EbpfVm<DummyContextObject>, stopwatch_denominator) as isize,
271    /// [EbpfVm::registers]
272    Registers = offset_of!(EbpfVm<DummyContextObject>, registers) as isize,
273    /// [EbpfVm::program_result]
274    ProgramResult = offset_of!(EbpfVm<DummyContextObject>, program_result) as isize,
275    /// [EbpfVm::memory_mapping]
276    MemoryMapping = offset_of!(EbpfVm<DummyContextObject>, memory_mapping) as isize,
277    /// [EbpfVm::register_trace]
278    RegisterTrace = offset_of!(EbpfVm<DummyContextObject>, register_trace) as isize,
279}
280
281/// A virtual machine to run eBPF programs.
282///
283/// # Examples
284///
285/// ```
286/// use solana_sbpf::{
287///     aligned_memory::AlignedMemory,
288///     ebpf,
289///     elf::Executable,
290///     memory_region::{MemoryMapping, MemoryRegion},
291///     program::{BuiltinProgram, FunctionRegistry, SBPFVersion},
292///     verifier::RequisiteVerifier,
293///     vm::{CallFrame, Config, EbpfVm, ExecutionMode},
294/// };
295/// use test_utils::TestContextObject;
296///
297/// let prog = &[
298///     0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // add64 r0, 0
299///     0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00  // exit
300/// ];
301/// let mut mem: [u8; _] = [0xaa, 0xbb, 0x11, 0x22, 0xcc, 0xdd];
302///
303/// let loader = std::sync::Arc::new(BuiltinProgram::new_mock());
304/// let function_registry = FunctionRegistry::default();
305/// let mut executable = Executable::<TestContextObject>::from_text_bytes(prog, loader.clone(), SBPFVersion::V4, function_registry).unwrap();
306/// executable.verify::<RequisiteVerifier>().unwrap();
307/// let mut context_object = TestContextObject::new(2);
308/// let sbpf_version = executable.get_sbpf_version();
309///
310/// let mut stack = AlignedMemory::<{ebpf::HOST_ALIGN}>::zero_filled(executable.get_config().stack_size());
311/// let stack_len = stack.len();
312/// let mut heap = AlignedMemory::<{ebpf::HOST_ALIGN}>::with_capacity(0);
313///
314/// let regions: Vec<MemoryRegion> = vec![
315///     executable.get_ro_region(),
316///     MemoryRegion::new(&mut stack, ebpf::MM_STACK_START),
317///     MemoryRegion::new(&mut heap, ebpf::MM_HEAP_START),
318///     MemoryRegion::new(&raw mut mem, ebpf::MM_INPUT_START),
319/// ];
320///;
321/// context_object.memory_mapping = unsafe {
322///     MemoryMapping::new(regions, executable.get_config(), sbpf_version).unwrap()
323/// };
324///
325/// let mut vm = EbpfVm::new(loader, sbpf_version, &mut context_object, stack_len);
326///
327/// let mut call_frames = vec![CallFrame::default(); executable.get_config().max_call_depth];
328/// let (instruction_count, result) = vm.execute_program(
329///     &executable,
330///     &mut ExecutionMode::Interpreted,
331///     &mut call_frames,
332/// );
333/// assert_eq!(instruction_count, 2);
334/// assert_eq!(result.unwrap(), 0);
335/// ```
336#[repr(C)]
337pub struct EbpfVm<'a, C: ContextObject> {
338    /// Needed to exit from the guest back into the host
339    pub host_stack_pointer: *mut u64,
340    /// The current call depth.
341    ///
342    /// Incremented on calls and decremented on exits. It's used to enforce
343    /// config.max_call_depth and to know when to terminate execution.
344    pub call_depth: u64,
345    /// Pointer to ContextObject
346    pub(crate) context_object_pointer: ptr::NonNull<C>,
347    /// The lifetime for the context object pointer
348    context_object_lifetime: PhantomData<&'a mut C>,
349    /// Last return value of instruction_meter.get_remaining()
350    pub previous_instruction_meter: u64,
351    /// Outstanding value to instruction_meter.consume()
352    pub due_insn_count: u64,
353    /// CPU cycles accumulated by the stop watch
354    pub stopwatch_numerator: u64,
355    /// Number of times the stop watch was used
356    pub stopwatch_denominator: u64,
357    /// Registers inlined
358    pub registers: [u64; 12],
359    /// ProgramResult inlined
360    pub program_result: ProgramResult,
361    /// MemoryMapping inlined
362    pub(crate) memory_mapping: ptr::NonNull<MemoryMapping>,
363    /// Loader built-in program
364    pub loader: Arc<BuiltinProgram<C>>,
365    /// Collector for the instruction trace
366    pub register_trace: Vec<RegisterTraceEntry>,
367    /// TCP port for the debugger interface
368    #[cfg(feature = "debugger")]
369    pub debug_port: Option<u16>,
370    /// Debug metadata passed
371    #[cfg(feature = "debugger")]
372    pub debug_metadata: Option<String>,
373}
374
375impl<'a, C: ContextObject> EbpfVm<'a, C> {
376    /// Creates a new virtual machine instance.
377    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    /// Execute the program
417    ///
418    /// Use `mode` parameter to request a specific execution type. This function will write back
419    /// the execution mode used back to the reference passed in.
420    ///
421    /// It is required to provide `call_frames` when executing in interpreted mode.
422    /// `call_frames` must be large enough to hold the executable config's `max_call_depth`
423    /// frames.
424    ///
425    /// Returns the instruction meter count (CUs) and the execution result of the program.
426    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    /// Invokes a built-in function
493    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    /// Build a `VmAddress` containing a (potentially) encrypted host pointer to self.
505    ///
506    /// Note that this type is effectively a mutable pointer to `self` and although it valid to
507    /// create multiple of these addresses, using them to violate the Rust mutable references'
508    /// uniqueness rule is not sound.
509    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    /// Get a reference to the context object referenced by this EbpfVm.
518    pub fn context(&mut self) -> &mut C {
519        // SAFETY: we've the unique reference to self here, so there can't be other live references
520        // to `C` either, whether via the memory_mapping or the context_object_pointer itself.
521        //
522        // The `context_object_pointer` is pointing at a valid-to-dereference `C` at all times
523        // through the EbpfVm lifetime.
524        //
525        // Note: for that reason we are intentionally tying the lifetime of the returned `C` to the
526        // lifetime of `&mut self`, rather than returning `&'a mut C`, which would allow aliasing
527        // the returned reference.
528        unsafe { self.context_object_pointer.as_mut() }
529    }
530
531    // Intentionally not public. Users are expected to store their memory mapping inside – and
532    // access from – C.
533    pub(crate) fn memory(&mut self) -> &mut MemoryMapping {
534        // SAFETY: we've the unique reference to self here, so there can't be other live references
535        // to `C` either, whether via the memory_mapping or the context_object_pointer itself.
536        //
537        // The `context_object_pointer` is pointing at a valid-to-dereference `C` at all times
538        // through the EbpfVm lifetime.
539        unsafe { self.memory_mapping.as_mut() }
540    }
541}
542
543/// Encrypted address to the [`EbpfVm`] object.
544#[repr(transparent)]
545pub struct EncryptedHostAddressToEbpfVm<C>(
546    // This ends up having to be public to the crate because inline assembly wants to deal with
547    // integers, not `VmAddress` (even though VmAddress has the same layout.)
548    pub(crate) u64,
549    PhantomData<C>,
550);
551
552impl<C: ContextObject> EncryptedHostAddressToEbpfVm<C> {
553    /// Work on [`EbpfVm`] pointed to by this address.
554    ///
555    /// ## Safety
556    ///
557    /// Multiple concurrently live addresses can reference the same [`EbpfVm`] but under no
558    /// circumstances may they be used to create multiple concurrent mutable references to the
559    /// `EbpfVm`.
560    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        // SAFETY: we've recovered the same pointer address as that of the reference used to
564        // produce this offset address in the first place.
565        // SAFETY: The mutable reference is unique due to invariant being passed onto the caller.
566        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}