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