1#[cfg(feature = "svm-internal")]
4use qualifier_attr::qualifiers;
5use {
6 crate::{
7 execution_budget::MAX_INSTRUCTION_STACK_DEPTH_SIMD_0268,
8 invoke_context::{BpfAllocator, InvokeContext},
9 mem_pool::VmMemoryPool,
10 memory_context::{MemoryContext, SerializedAccountMetadata},
11 program_cache_entry::ProgramCacheEntry,
12 serialization, stable_log,
13 },
14 solana_instruction::error::InstructionError,
15 solana_program_entrypoint::{MAX_PERMITTED_DATA_INCREASE, SUCCESS},
16 solana_sbpf::{
17 ebpf::{self, MM_HEAP_START, MM_RODATA_START, MM_STACK_START},
18 elf::Executable,
19 error::{EbpfError, ProgramResult},
20 memory_region::{AccessType, MemoryMapping, MemoryRegion},
21 vm::{ContextObject, EbpfVm, ExecutionMode},
22 },
23 solana_sdk_ids::bpf_loader_deprecated,
24 solana_svm_log_collector::ic_logger_msg,
25 solana_svm_measure::measure::Measure,
26 solana_transaction_context::IndexOfAccount,
27 std::{cell::RefCell, mem, time::Duration},
28};
29
30thread_local! {
31 pub static MEMORY_POOL: RefCell<VmMemoryPool> = RefCell::new(VmMemoryPool::new());
32}
33
34pub fn calculate_heap_cost(heap_size: u32, heap_cost: u64) -> u64 {
36 const KIBIBYTE: u64 = 1024;
37 const PAGE_SIZE_KB: u64 = 32;
38 let mut rounded_heap_size = u64::from(heap_size);
39 rounded_heap_size =
40 rounded_heap_size.saturating_add(PAGE_SIZE_KB.saturating_mul(KIBIBYTE).saturating_sub(1));
41 rounded_heap_size
42 .checked_div(PAGE_SIZE_KB.saturating_mul(KIBIBYTE))
43 .expect("PAGE_SIZE_KB * KIBIBYTE > 0")
44 .saturating_sub(1)
45 .saturating_mul(heap_cost)
46}
47
48#[cfg_attr(feature = "svm-internal", qualifiers(pub))]
54pub unsafe fn create_vm<'a, 'b>(
55 program: &'a Executable<InvokeContext<'b, 'b>>,
56 invoke_context: &'a mut InvokeContext<'b, 'b>,
57 stack: *mut [u8],
58 heap: *mut [u8],
59) -> Result<EbpfVm<'a, InvokeContext<'b, 'b>>, Box<dyn std::error::Error>> {
60 let stack_size = stack.len();
61 unsafe {
62 configure_program_regions(invoke_context, program, stack, heap)?;
64 }
65 Ok(EbpfVm::new(
66 program.get_loader().clone(),
67 program.get_sbpf_version(),
68 invoke_context,
69 stack_size,
70 ))
71}
72
73unsafe fn configure_program_regions<C: ContextObject>(
79 invoke_context: &mut InvokeContext,
80 executable: &Executable<C>,
81 stack: *mut [u8],
82 heap: *mut [u8],
83) -> Result<(), Box<dyn std::error::Error>> {
84 let mapping = invoke_context.memory_contexts.memory_mapping_mut()?;
85 let regions = mapping.get_regions_mut();
86 let [ro_area, stack_area, heap_area, ..] = regions else {
87 panic!("the regions vector must have at least three entries")
88 };
89 *ro_area = executable.get_ro_region();
90 let sbpf_version = executable.get_sbpf_version();
91 let config = executable.get_config();
92 *stack_area = MemoryRegion::new_gapped(
93 stack,
94 MM_STACK_START,
95 if sbpf_version.stack_frame_gaps() && config.enable_stack_frame_gaps {
96 config.stack_frame_size as u64
97 } else {
98 0
99 },
100 );
101 *heap_area = MemoryRegion::new(heap, MM_HEAP_START);
102 mapping
103 .initialize()
104 .map_err(|err| Box::new(err) as Box<dyn std::error::Error>)
105}
106
107#[macro_export]
109macro_rules! create_vm {
110 ($vm:ident, $program:expr, $invoke_context:expr $(,)?) => {
111 let invoke_context = &*$invoke_context;
112 let stack_size = $program.get_config().stack_size();
113 let heap_size = invoke_context.get_compute_budget().heap_size;
114 let heap_cost_result =
115 invoke_context
116 .compute_meter
117 .consume_checked($crate::__private::calculate_heap_cost(
118 heap_size,
119 invoke_context.get_execution_cost().heap_cost,
120 ));
121 let $vm = heap_cost_result.and_then(|_| {
122 let (mut stack, mut heap) = $crate::__private::MEMORY_POOL
123 .with_borrow_mut(|pool| (pool.get_stack(stack_size), pool.get_heap(heap_size)));
124 let vm = $crate::__private::create_vm(
125 $program,
126 $invoke_context,
127 stack
128 .as_slice_mut()
129 .get_mut(..stack_size)
130 .expect("invalid stack size"),
131 heap.as_slice_mut()
132 .get_mut(..heap_size as usize)
133 .expect("invalid heap size"),
134 );
135 vm.map(|vm| (vm, stack, heap))
136 });
137 };
138}
139
140unsafe fn set_memory_context<'b>(
145 additional_initialized_regions: Vec<MemoryRegion>,
146 accounts_metadata: Vec<SerializedAccountMetadata>,
147 invoke_context: &mut InvokeContext<'b, 'b>,
148 executable: &Executable<InvokeContext<'b, 'b>>,
149 virtual_address_space_adjustments: bool,
150 account_data_direct_mapping: bool,
151) -> Result<(), Box<dyn std::error::Error>> {
152 let heap_size = invoke_context.get_compute_budget().heap_size;
153 let regions = [
154 MemoryRegion::new_empty(MM_RODATA_START),
155 MemoryRegion::new_empty(MM_STACK_START),
156 MemoryRegion::new_empty(MM_HEAP_START),
157 ]
158 .into_iter()
159 .chain(additional_initialized_regions)
160 .collect();
161 let memory_mapping = unsafe {
162 MemoryMapping::new_uninitialized(
165 regions,
166 executable.get_config(),
167 executable.get_sbpf_version(),
168 invoke_context.transaction_context.access_violation_handler(
169 virtual_address_space_adjustments,
170 account_data_direct_mapping,
171 ),
172 )
173 };
174
175 invoke_context
176 .memory_contexts
177 .set_memory_context_abi_v1(MemoryContext::new(
178 BpfAllocator::new(heap_size as u64),
179 accounts_metadata,
180 memory_mapping,
181 ))
182 .map_err(|err| Box::new(err) as Box<dyn std::error::Error>)
183}
184
185#[cfg_attr(feature = "svm-internal", qualifiers(pub))]
186pub fn execute<'a, 'b: 'a>(
187 executable: &'a Executable<InvokeContext<'static, 'static>>,
188 invoke_context: &'a mut InvokeContext<'b, 'b>,
189 cache_entry: &ProgramCacheEntry,
190) -> Result<(), Box<dyn std::error::Error>> {
191 let executable = unsafe {
194 mem::transmute::<
195 &'a Executable<InvokeContext<'static, 'static>>,
196 &'a Executable<InvokeContext<'b, 'b>>,
197 >(executable)
198 };
199 let log_collector = invoke_context.get_log_collector();
200 let transaction_context = &invoke_context.transaction_context;
201 let instruction_context = transaction_context.get_current_instruction_context()?;
202 let program_id = *instruction_context.get_program_key()?;
203 let is_loader_deprecated =
204 instruction_context.get_program_owner()? == bpf_loader_deprecated::id();
205 let virtual_address_space_adjustments = invoke_context
206 .get_feature_set()
207 .virtual_address_space_adjustments;
208 let account_data_direct_mapping = invoke_context.get_feature_set().account_data_direct_mapping;
209 let direct_account_pointers_in_program_input = invoke_context
210 .get_feature_set()
211 .direct_account_pointers_in_program_input;
212
213 let mut serialize_time = Measure::start("serialize");
214 let (parameter_bytes, regions, accounts_metadata, instruction_data_offset) =
215 serialization::serialize_parameters(
216 &instruction_context,
217 virtual_address_space_adjustments,
218 account_data_direct_mapping,
219 direct_account_pointers_in_program_input,
220 )?;
221 serialize_time.stop();
222
223 let account_region_addrs = accounts_metadata
226 .iter()
227 .map(|m| {
228 let vm_end = m
229 .vm_data_addr
230 .saturating_add(m.original_data_len as u64)
231 .saturating_add(if !is_loader_deprecated {
232 MAX_PERMITTED_DATA_INCREASE as u64
233 } else {
234 0
235 });
236 m.vm_data_addr..vm_end
237 })
238 .collect::<Vec<_>>();
239
240 #[cfg(feature = "sbpf-debugger")]
241 let (debug_port, debug_metadata) = if invoke_context.debug_port.is_some() {
242 (
243 invoke_context.debug_port,
244 Some(format!(
245 "program_id={};cpi_level={};caller={}",
246 program_id,
247 instruction_context.get_stack_height().saturating_sub(1),
248 invoke_context
249 .get_stack_height()
250 .checked_sub(2)
251 .and_then(|nesting_level| {
252 transaction_context
253 .get_instruction_context_at_nesting_level(nesting_level)
254 .ok()
255 })
256 .and_then(|ctx| ctx.get_program_key().ok())
257 .map(|key| key.to_string())
258 .unwrap_or_else(|| "none".into())
259 )),
260 )
261 } else {
262 (None, None)
263 };
264
265 let mut create_vm_time = Measure::start("create_vm");
266 unsafe {
267 set_memory_context(
271 regions,
272 accounts_metadata,
273 invoke_context,
274 executable,
275 virtual_address_space_adjustments,
276 account_data_direct_mapping,
277 )?
278 };
279
280 let execution_result = {
281 let mut execution_mode = ExecutionMode::PreferJit;
282
283 #[cfg(feature = "sbpf-debugger")]
284 if invoke_context.debug_port.is_some() {
285 execution_mode = ExecutionMode::Interpreted;
286 }
287
288 let compute_meter_prev = invoke_context.get_remaining();
289 let mapped_heap_len = invoke_context.get_compute_budget().heap_size as usize;
290 let (mut vm, stack, heap) = unsafe {
291 create_vm!(vm, executable, invoke_context);
294 match vm {
295 Ok(info) => info,
296 Err(e) => {
297 ic_logger_msg!(log_collector, "Failed to create SBF VM: {}", e);
298 return Err(Box::new(InstructionError::ProgramEnvironmentSetupFailure));
299 }
300 }
301 };
302
303 create_vm_time.stop();
304 #[cfg(feature = "sbpf-debugger")]
305 {
306 vm.debug_port = debug_port;
307 vm.debug_metadata = debug_metadata;
308 }
309
310 let execute_time = Measure::start("execute");
311 let prev_nested_exec_time = vm.context().total_nested_exec_time;
312
313 vm.registers[1] = ebpf::MM_INPUT_START;
314 vm.registers[2] = instruction_data_offset as u64;
315 let mut call_frames =
316 MEMORY_POOL.with_borrow_mut(|memory_pool| memory_pool.get_call_frames());
317 let (compute_units_consumed, result) =
318 vm.execute_program(executable, &mut execution_mode, &mut call_frames);
319 let register_trace = std::mem::take(&mut vm.register_trace);
320 MEMORY_POOL.with_borrow_mut(|memory_pool| {
321 memory_pool.put_stack(stack);
322 memory_pool.put_heap(heap, mapped_heap_len);
323 memory_pool.put_call_frames(call_frames);
324 debug_assert!(memory_pool.stack_len() <= MAX_INSTRUCTION_STACK_DEPTH_SIMD_0268);
325 debug_assert!(memory_pool.heap_len() <= MAX_INSTRUCTION_STACK_DEPTH_SIMD_0268);
326 });
327 drop(vm);
328 invoke_context.insert_register_trace(register_trace);
329
330 let total_execute_ns = execute_time.end_as_ns();
332 let nested_execution_time_delta = invoke_context
333 .total_nested_exec_time
334 .saturating_sub(prev_nested_exec_time);
335 let this_call_ns =
336 total_execute_ns.saturating_sub(nested_execution_time_delta.as_nanos() as u64);
337 invoke_context.total_nested_exec_time = invoke_context
338 .total_nested_exec_time
339 .saturating_add(Duration::from_nanos(this_call_ns));
340 let this_call_us = this_call_ns / 1000;
341 invoke_context.timings.execute_us += this_call_us;
342 match execution_mode {
343 ExecutionMode::Interpreted => cache_entry.stats.interpreter_executed(this_call_us),
344 ExecutionMode::Jit => cache_entry.stats.jit_executed(this_call_us),
345 ExecutionMode::PreferJit => { }
346 }
347
348 ic_logger_msg!(
349 log_collector,
350 "Program {} consumed {} of {} compute units",
351 &program_id,
352 compute_units_consumed,
353 compute_meter_prev
354 );
355 let (_returned_from_program_id, return_data) =
356 invoke_context.transaction_context.get_return_data();
357 if !return_data.is_empty() {
358 stable_log::program_return(&log_collector, &program_id, return_data);
359 }
360 match result {
361 ProgramResult::Ok(status) if status != SUCCESS => {
362 let error: InstructionError = status.into();
363 Err(Box::new(error) as Box<dyn std::error::Error>)
364 }
365 ProgramResult::Err(mut error) => {
366 if invoke_context
370 .get_feature_set()
371 .deplete_cu_meter_on_vm_failure
372 && !matches!(error, EbpfError::SyscallError(_))
373 {
374 invoke_context.consume(invoke_context.get_remaining());
385 }
386
387 if virtual_address_space_adjustments {
388 if let EbpfError::SyscallError(err) = error {
389 error = err
390 .downcast::<EbpfError>()
391 .map(|err| *err)
392 .unwrap_or_else(EbpfError::SyscallError);
393 }
394 if let EbpfError::AccessViolation(access_type, vm_addr, len, _section_name) =
395 error
396 {
397 if let Some((instruction_account_index, vm_addr_range)) =
401 account_region_addrs
402 .iter()
403 .enumerate()
404 .find(|(_, vm_addr_range)| vm_addr_range.contains(&vm_addr))
405 {
406 let transaction_context = &invoke_context.transaction_context;
407 let instruction_context =
408 transaction_context.get_current_instruction_context()?;
409 let account = instruction_context.try_borrow_instruction_account(
410 instruction_account_index as IndexOfAccount,
411 )?;
412 if vm_addr.saturating_add(len) <= vm_addr_range.end {
413 let is_access_outside_of_data = vm_addr
416 .saturating_add(len)
417 .saturating_sub(vm_addr_range.start)
418 as usize
419 > account.get_data().len();
420 error = EbpfError::SyscallError(Box::new(match access_type {
421 AccessType::Store => {
422 if let Err(err) = account.can_data_be_changed() {
423 err
424 } else {
425 debug_assert!(is_access_outside_of_data);
428 InstructionError::InvalidRealloc
429 }
430 }
431 AccessType::Load => {
432 debug_assert!(is_access_outside_of_data);
434 if account.can_data_be_changed().is_err() {
435 InstructionError::AccountDataTooSmall
438 } else {
439 InstructionError::InvalidRealloc
441 }
442 }
443 }));
444 }
445 }
446 }
447 }
448 Err(if let EbpfError::SyscallError(err) = error {
449 err
450 } else {
451 error.into()
452 })
453 }
454 _ => Ok(()),
455 }
456 };
457
458 fn deserialize_parameters(
459 invoke_context: &mut InvokeContext,
460 parameter_bytes: &[u8],
461 virtual_address_space_adjustments: bool,
462 account_data_direct_mapping: bool,
463 ) -> Result<(), InstructionError> {
464 serialization::deserialize_parameters(
465 &invoke_context
466 .transaction_context
467 .get_current_instruction_context()?,
468 virtual_address_space_adjustments,
469 account_data_direct_mapping,
470 parameter_bytes,
471 &invoke_context
472 .memory_contexts
473 .memory_context_abi_v1()?
474 .accounts_metadata,
475 )
476 }
477
478 let mut deserialize_time = Measure::start("deserialize");
479 let execute_or_deserialize_result = execution_result.and_then(|_| {
480 deserialize_parameters(
481 invoke_context,
482 parameter_bytes.as_slice(),
483 virtual_address_space_adjustments,
484 account_data_direct_mapping,
485 )
486 .map_err(|error| Box::new(error) as Box<dyn std::error::Error>)
487 });
488 deserialize_time.stop();
489
490 invoke_context.timings.serialize_us += serialize_time.as_us();
492 invoke_context.timings.create_vm_us += create_vm_time.as_us();
493 invoke_context.timings.deserialize_us += deserialize_time.as_us();
494
495 execute_or_deserialize_result
496}