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, 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/// Shift the RUNTIME_ENVIRONMENT_KEY by this many bits to the LSB
37///
38/// 3 bits for 8 Byte alignment, and 1 bit to have encoding space for the RuntimeEnvironment.
39#[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
44/// Returns (and if not done before generates) the encryption key for the VM pointer
45pub 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/// VM configuration settings
56#[derive(Debug, Clone, PartialEq, Eq)]
57pub struct Config {
58    /// Maximum call depth
59    pub max_call_depth: usize,
60    /// Size of a stack frame in bytes, must match the size specified in the LLVM BPF backend
61    pub stack_frame_size: usize,
62    /// Enables the use of MemoryMapping and MemoryRegion for address translation
63    pub enable_address_translation: bool,
64    /// Enables gaps in VM address space between the stack frames
65    pub enable_stack_frame_gaps: bool,
66    /// Maximal pc distance after which a new instruction meter validation is emitted by the JIT
67    pub instruction_meter_checkpoint_distance: usize,
68    /// Enable instruction meter and limiting
69    pub enable_instruction_meter: bool,
70    /// Enable instruction tracing
71    pub enable_register_tracing: bool,
72    /// Enable dynamic string allocation for labels
73    pub enable_symbol_and_section_labels: bool,
74    /// Reject ELF files containing issues that the verifier did not catch before (up to v0.2.21)
75    pub reject_broken_elfs: bool,
76    #[cfg(feature = "jit")]
77    /// Ratio of native host instructions per random no-op in JIT (0 = OFF)
78    pub noop_instruction_rate: u32,
79    #[cfg(feature = "jit")]
80    /// Enable disinfection of immediate values and offsets provided by the user in JIT
81    pub sanitize_user_provided_values: bool,
82    /// Avoid copying read only sections when possible
83    pub optimize_rodata: bool,
84    /// Use aligned memory mapping
85    pub aligned_memory_mapping: bool,
86    /// Allowed [SBPFVersion]s
87    pub enabled_sbpf_versions: std::ops::RangeInclusive<SBPFVersion>,
88}
89
90impl Config {
91    /// Returns the size of the stack memory region
92    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
120/// Static constructors for Executable
121impl<C: ContextObject> Executable<C> {
122    /// Creates an executable from an ELF file
123    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    /// Creates an executable from machine code
128    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
139/// Runtime context
140pub trait ContextObject {
141    /// Consume instructions from meter
142    fn consume(&mut self, amount: u64);
143    /// Get the number of remaining instructions allowed
144    fn get_remaining(&self) -> u64;
145}
146
147/// Statistic of taken branches (from a recorded trace)
148pub struct DynamicAnalysis {
149    /// Maximal edge counter value
150    pub edge_counter_max: usize,
151    /// src_node, dst_node, edge_counter
152    pub edges: BTreeMap<usize, BTreeMap<usize, usize>>,
153}
154
155impl DynamicAnalysis {
156    /// Accumulates a trace
157    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/// A call frame used for function calls inside the Interpreter
182#[derive(Clone, Default)]
183pub struct CallFrame {
184    /// The caller saved registers
185    pub caller_saved_registers: [u64; ebpf::SCRATCH_REGS],
186    /// The callers frame pointer
187    pub frame_pointer: u64,
188    /// The target_pc of the exit instruction which returns back to the caller
189    pub target_pc: u64,
190}
191
192/// Indices of slots inside [EbpfVm]
193pub enum RuntimeEnvironmentSlot {
194    /// [EbpfVm::host_stack_pointer]
195    HostStackPointer = 0,
196    /// [EbpfVm::call_depth]
197    CallDepth = 1,
198    /// [EbpfVm::context_object_pointer]
199    ContextObjectPointer = 2,
200    /// [EbpfVm::previous_instruction_meter]
201    PreviousInstructionMeter = 3,
202    /// [EbpfVm::due_insn_count]
203    DueInsnCount = 4,
204    /// [EbpfVm::stopwatch_numerator]
205    StopwatchNumerator = 5,
206    /// [EbpfVm::stopwatch_denominator]
207    StopwatchDenominator = 6,
208    /// [EbpfVm::registers]
209    Registers = 7,
210    /// [EbpfVm::program_result]
211    ProgramResult = 19,
212    /// [EbpfVm::memory_mapping]
213    MemoryMapping = 27,
214    /// [EbpfVm::register_trace]
215    RegisterTrace = 54,
216}
217
218/// A virtual machine to run eBPF programs.
219///
220/// # Examples
221///
222/// ```
223/// use solana_sbpf::{
224///     aligned_memory::AlignedMemory,
225///     ebpf,
226///     elf::Executable,
227///     memory_region::{MemoryMapping, MemoryRegion},
228///     program::{BuiltinProgram, FunctionRegistry, SBPFVersion},
229///     verifier::RequisiteVerifier,
230///     vm::{Config, EbpfVm},
231/// };
232/// use test_utils::TestContextObject;
233///
234/// let prog = &[
235///     0x07, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // add64 r10, 0
236///     0x9d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00  // exit
237/// ];
238/// let mem = &mut [
239///     0xaa, 0xbb, 0x11, 0x22, 0xcc, 0xdd
240/// ];
241///
242/// let loader = std::sync::Arc::new(BuiltinProgram::new_mock());
243/// let function_registry = FunctionRegistry::default();
244/// let mut executable = Executable::<TestContextObject>::from_text_bytes(prog, loader.clone(), SBPFVersion::V4, function_registry).unwrap();
245/// executable.verify::<RequisiteVerifier>().unwrap();
246/// let mut context_object = TestContextObject::new(2);
247/// let sbpf_version = executable.get_sbpf_version();
248///
249/// let mut stack = AlignedMemory::<{ebpf::HOST_ALIGN}>::zero_filled(executable.get_config().stack_size());
250/// let stack_len = stack.len();
251/// let mut heap = AlignedMemory::<{ebpf::HOST_ALIGN}>::with_capacity(0);
252///
253/// let regions: Vec<MemoryRegion> = vec![
254///     executable.get_ro_region(),
255///     MemoryRegion::new_writable(
256///         stack.as_slice_mut(),
257///         ebpf::MM_STACK_START,
258///     ),
259///     MemoryRegion::new_writable(heap.as_slice_mut(), ebpf::MM_HEAP_START),
260///     MemoryRegion::new_writable(mem, ebpf::MM_INPUT_START),
261/// ];
262///
263/// let memory_mapping = MemoryMapping::new(regions, executable.get_config(), sbpf_version).unwrap();
264///
265/// let mut vm = EbpfVm::new(loader, sbpf_version, &mut context_object, memory_mapping, stack_len);
266///
267/// let (instruction_count, result) = vm.execute_program(&executable, true);
268/// assert_eq!(instruction_count, 2);
269/// assert_eq!(result.unwrap(), 0);
270/// ```
271#[repr(C)]
272pub struct EbpfVm<'a, C: ContextObject> {
273    /// Needed to exit from the guest back into the host
274    pub host_stack_pointer: *mut u64,
275    /// The current call depth.
276    ///
277    /// Incremented on calls and decremented on exits. It's used to enforce
278    /// config.max_call_depth and to know when to terminate execution.
279    pub call_depth: u64,
280    /// Pointer to ContextObject
281    pub context_object_pointer: &'a mut C,
282    /// Last return value of instruction_meter.get_remaining()
283    pub previous_instruction_meter: u64,
284    /// Outstanding value to instruction_meter.consume()
285    pub due_insn_count: u64,
286    /// CPU cycles accumulated by the stop watch
287    pub stopwatch_numerator: u64,
288    /// Number of times the stop watch was used
289    pub stopwatch_denominator: u64,
290    /// Registers inlined
291    pub registers: [u64; 12],
292    /// ProgramResult inlined
293    pub program_result: ProgramResult,
294    /// MemoryMapping inlined
295    pub memory_mapping: MemoryMapping<'a>,
296    /// Stack of CallFrames used by the Interpreter
297    pub call_frames: Vec<CallFrame>,
298    /// Loader built-in program
299    pub loader: Arc<BuiltinProgram<C>>,
300    /// Collector for the instruction trace
301    pub register_trace: Vec<RegisterTraceEntry>,
302    /// TCP port for the debugger interface
303    #[cfg(feature = "debugger")]
304    pub debug_port: Option<u16>,
305}
306
307impl<'a, C: ContextObject> EbpfVm<'a, C> {
308    /// Creates a new virtual machine instance.
309    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                // the stack is fully descending, frames start as empty and change size anytime r11 is modified
321                stack_len
322            } else {
323                // within a frame the stack grows down, but frames are ascending
324                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    /// Execute the program
351    ///
352    /// If interpreted = `false` then the JIT compiled executable is used.
353    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    /// Invokes a built-in function
406    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}