diff --git a/programs/bpf_loader/src/syscalls.rs b/programs/bpf_loader/src/syscalls.rs index 23ab57b7f498c7..d214d2f2aff4e7 100644 --- a/programs/bpf_loader/src/syscalls.rs +++ b/programs/bpf_loader/src/syscalls.rs @@ -46,6 +46,7 @@ use { std::{ alloc::Layout, cell::{Ref, RefCell, RefMut}, + marker::PhantomData, mem::{align_of, size_of}, rc::Rc, slice::from_raw_parts_mut, @@ -2027,6 +2028,62 @@ struct CallerAccount<'a> { } type TranslatedAccounts<'a> = Vec<(usize, Option>)>; +/// Stable representation of a `Vec` in the SBFv2 Rust runtime. +/// Only supports the global allocator. +#[repr(C)] +struct RustVMVec { + ptr: u64, + raw_cap: u64, + raw_len: u64, + _phantom: PhantomData, +} + +impl RustVMVec { + fn len(&self) -> usize { + // An u64 to usize conversion can only fail in the case of an overflow. + // Realistically, this only happens on 32-bit architectures. + // Assuming available memory in a VM will never exceed 4 GiB, + // "saturating" to UINT32_MAX safely reaches the "slice too large" error path + // that a comparable larger value would also trigger on a 64-bit architecture. + self.raw_len.try_into().unwrap_or(usize::MAX) + } + + /// Returns a slice to the elements pointed to by the Vec object. + fn try_as_slice( + &self, + memory_mapping: &MemoryMapping, + invoke_context: &InvokeContext, + ) -> Result<&[T], EbpfError> { + translate_slice( + memory_mapping, + self.ptr, + self.raw_len, + invoke_context.get_check_aligned(), + invoke_context.get_check_size(), + ) + } +} + +/// Stable representation of a `solana_program::instruction::Instruction` +/// in the SBFv2 Rust runtime. +#[repr(C)] +struct RustVMInstruction { + program_id: Pubkey, + accounts: RustVMVec, + data: RustVMVec, +} + +impl RustVMInstruction { + /// Returns a reference to an Instruction object at the given VM memory address. + fn from_vm_memory<'a>( + memory_mapping: &MemoryMapping<'a>, + vm_addr: u64, + invoke_context: &InvokeContext<'a>, + ) -> Result<&'a Self, EbpfError> { + translate_type(memory_mapping, vm_addr, invoke_context.get_check_aligned()) + } +} + /// Implemented by language specific data structure translators trait SyscallInvokeSigned<'a, 'b> { fn get_context_mut(&self) -> Result>, EbpfError>; @@ -2093,30 +2150,18 @@ impl<'a, 'b> SyscallInvokeSigned<'a, 'b> for SyscallInvokeSignedRust<'a, 'b> { memory_mapping: &mut MemoryMapping, invoke_context: &mut InvokeContext, ) -> Result> { - let ix = translate_type::( - memory_mapping, - addr, - invoke_context.get_check_aligned(), - )?; + let ix = RustVMInstruction::from_vm_memory(memory_mapping, addr, invoke_context)?; check_instruction_size(ix.accounts.len(), ix.data.len(), invoke_context)?; - let accounts = translate_slice::( - memory_mapping, - ix.accounts.as_ptr() as u64, - ix.accounts.len() as u64, - invoke_context.get_check_aligned(), - invoke_context.get_check_size(), - )? - .to_vec(); - let data = translate_slice::( - memory_mapping, - ix.data.as_ptr() as u64, - ix.data.len() as u64, - invoke_context.get_check_aligned(), - invoke_context.get_check_size(), - )? - .to_vec(); + let accounts = ix + .accounts + .try_as_slice(memory_mapping, invoke_context)? + .to_vec(); + let data = ix + .data + .try_as_slice(memory_mapping, invoke_context)? + .to_vec(); Ok(Instruction { program_id: ix.program_id, accounts,