diff --git a/programs/bpf_loader/src/syscalls/cpi.rs b/programs/bpf_loader/src/syscalls/cpi.rs index f569812565d3e3..98529807643349 100644 --- a/programs/bpf_loader/src/syscalls/cpi.rs +++ b/programs/bpf_loader/src/syscalls/cpi.rs @@ -84,42 +84,29 @@ 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: &sbf_rust::Instruction = + sbf_rust::Ptr::new(addr).translate(memory_mapping, invoke_context)?; - check_instruction_size(ix.accounts.len(), ix.data.len(), invoke_context)?; + check_instruction_size(ix.accounts.raw_len, ix.data.raw_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 accounts = ix + .accounts + .translate(memory_mapping, invoke_context)? + .iter() + .map(AccountMeta::from) + .collect::>(); - let ix_data_len = ix.data.len() as u64; if invoke_context .feature_set .is_active(&feature_set::loosen_cpi_size_restriction::id()) { invoke_context.get_compute_meter().consume( - (ix_data_len) + (ix.data.raw_len) .saturating_div(invoke_context.get_compute_budget().cpi_bytes_per_unit), )?; } - let data = translate_slice::( - memory_mapping, - ix.data.as_ptr() as u64, - ix_data_len, - invoke_context.get_check_aligned(), - invoke_context.get_check_size(), - )? - .to_vec(); + let data = ix.data.translate(memory_mapping, invoke_context)?.to_vec(); Ok(Instruction { program_id: ix.program_id, accounts, @@ -136,7 +123,7 @@ impl<'a, 'b> SyscallInvokeSigned<'a, 'b> for SyscallInvokeSignedRust<'a, 'b> { memory_mapping: &mut MemoryMapping, invoke_context: &mut InvokeContext, ) -> Result, EbpfError> { - let account_infos = translate_slice::( + let account_infos = translate_slice::( memory_mapping, account_infos_addr, account_infos_len, @@ -146,64 +133,44 @@ impl<'a, 'b> SyscallInvokeSigned<'a, 'b> for SyscallInvokeSignedRust<'a, 'b> { check_account_infos(account_infos.len(), invoke_context)?; let account_info_keys = account_infos .iter() - .map(|account_info| { - translate_type::( - memory_mapping, - account_info.key as *const _ as u64, - invoke_context.get_check_aligned(), - ) - }) + .map(|account_info| account_info.key.translate(memory_mapping, invoke_context)) .collect::, EbpfError>>()?; - let translate = |account_info: &AccountInfo, invoke_context: &InvokeContext| { + let translate = |account_info: &sbf_rust::AccountInfo, invoke_context: &InvokeContext| { // Translate the account from user space - let lamports = { - // Double translate lamports out of RefCell - let ptr = translate_type::( - memory_mapping, - account_info.lamports.as_ptr() as u64, - invoke_context.get_check_aligned(), - )?; - translate_type_mut::(memory_mapping, *ptr, invoke_context.get_check_aligned())? - }; - let owner = translate_type_mut::( - memory_mapping, - account_info.owner as *const _ as u64, - invoke_context.get_check_aligned(), - )?; + let lamports = account_info + .lamports + .translate(memory_mapping, invoke_context)? + .value + .value + .translate_mut(memory_mapping, invoke_context)?; + let owner = account_info + .owner + .translate_mut(memory_mapping, invoke_context)?; let (data, vm_data_addr, ref_to_len_in_vm) = { - // Double translate data out of RefCell - let data = *translate_type::<&[u8]>( - memory_mapping, - account_info.data.as_ptr() as *const _ as u64, - invoke_context.get_check_aligned(), - )?; + // Translate the account from user space + let data = &account_info + .data + .translate(memory_mapping, invoke_context)? + .value + .value; invoke_context.get_compute_meter().consume( - (data.len() as u64) + data.len .saturating_div(invoke_context.get_compute_budget().cpi_bytes_per_unit), )?; - let translated = translate( - memory_mapping, - AccessType::Store, - (account_info.data.as_ptr() as *const u64 as u64) - .saturating_add(size_of::() as u64), - 8, - )? as *mut u64; - let ref_to_len_in_vm = unsafe { &mut *translated }; - let vm_data_addr = data.as_ptr() as u64; + let data_vm_slice = &mut account_info + .data + .translate_mut(memory_mapping, invoke_context)? + .value + .value; + let ref_to_len_in_vm = &mut data_vm_slice.len; ( - translate_slice_mut::( - memory_mapping, - vm_data_addr, - data.len() as u64, - invoke_context.get_check_aligned(), - invoke_context.get_check_size(), - )?, - vm_data_addr, + data.translate_mut(memory_mapping, invoke_context)?, + data.ptr, ref_to_len_in_vm, ) }; @@ -240,7 +207,7 @@ impl<'a, 'b> SyscallInvokeSigned<'a, 'b> for SyscallInvokeSignedRust<'a, 'b> { ) -> Result, EbpfError> { let mut signers = Vec::new(); if signers_seeds_len > 0 { - let signers_seeds = translate_slice::<&[&[u8]]>( + let signers_seeds = translate_slice::>>( memory_mapping, signers_seeds_addr, signers_seeds_len, @@ -251,13 +218,7 @@ impl<'a, 'b> SyscallInvokeSigned<'a, 'b> for SyscallInvokeSignedRust<'a, 'b> { return Err(SyscallError::TooManySigners.into()); } for signer_seeds in signers_seeds.iter() { - let untranslated_seeds = translate_slice::<&[u8]>( - memory_mapping, - signer_seeds.as_ptr() as *const _ as u64, - signer_seeds.len() as u64, - invoke_context.get_check_aligned(), - invoke_context.get_check_size(), - )?; + let untranslated_seeds = signer_seeds.translate(memory_mapping, invoke_context)?; if untranslated_seeds.len() > MAX_SEEDS { return Err(SyscallError::InstructionError( InstructionError::MaxSeedLengthExceeded, @@ -267,13 +228,7 @@ impl<'a, 'b> SyscallInvokeSigned<'a, 'b> for SyscallInvokeSignedRust<'a, 'b> { let seeds = untranslated_seeds .iter() .map(|untranslated_seed| { - translate_slice::( - memory_mapping, - untranslated_seed.as_ptr() as *const _ as u64, - untranslated_seed.len() as u64, - invoke_context.get_check_aligned(), - invoke_context.get_check_size(), - ) + untranslated_seed.translate(memory_mapping, invoke_context) }) .collect::, EbpfError>>()?; let signer = Pubkey::create_program_address(&seeds, program_id) @@ -384,11 +339,7 @@ impl<'a, 'b> SyscallInvokeSigned<'a, 'b> for SyscallInvokeSignedC<'a, 'b> { invoke_context.get_check_aligned(), )?; - check_instruction_size( - ix_c.accounts_len as usize, - ix_c.data_len as usize, - invoke_context, - )?; + check_instruction_size(ix_c.accounts_len, ix_c.data_len, invoke_context)?; let program_id = translate_type::( memory_mapping, ix_c.program_id_addr, @@ -401,22 +352,10 @@ impl<'a, 'b> SyscallInvokeSigned<'a, 'b> for SyscallInvokeSignedC<'a, 'b> { invoke_context.get_check_aligned(), invoke_context.get_check_size(), )?; - - let ix_data_len = ix_c.data_len as u64; - if invoke_context - .feature_set - .is_active(&feature_set::loosen_cpi_size_restriction::id()) - { - invoke_context.get_compute_meter().consume( - (ix_data_len) - .saturating_div(invoke_context.get_compute_budget().cpi_bytes_per_unit), - )?; - } - let data = translate_slice::( memory_mapping, ix_c.data_addr, - ix_data_len, + ix_c.data_len as u64, invoke_context.get_check_aligned(), invoke_context.get_check_size(), )? @@ -745,15 +684,14 @@ where } fn check_instruction_size( - num_accounts: usize, - data_len: usize, + num_accounts: u64, + data_len: u64, invoke_context: &mut InvokeContext, ) -> Result<(), EbpfError> { if invoke_context .feature_set .is_active(&feature_set::loosen_cpi_size_restriction::id()) { - let data_len = data_len as u64; let max_data_len = MAX_CPI_INSTRUCTION_DATA_LEN; if data_len > max_data_len { return Err(SyscallError::MaxInstructionDataLenExceeded { @@ -763,7 +701,6 @@ fn check_instruction_size( .into()); } - let num_accounts = num_accounts as u64; let max_accounts = MAX_CPI_INSTRUCTION_ACCOUNTS as u64; if num_accounts > max_accounts { return Err(SyscallError::MaxInstructionAccountsExceeded { @@ -773,9 +710,9 @@ fn check_instruction_size( .into()); } } else { - let max_size = invoke_context.get_compute_budget().max_cpi_instruction_size; + let max_size = invoke_context.get_compute_budget().max_cpi_instruction_size as u64; let size = num_accounts - .saturating_mul(size_of::()) + .saturating_mul(size_of::() as u64) .saturating_add(data_len); if size > max_size { return Err(SyscallError::InstructionTooLarge(size, max_size).into()); diff --git a/programs/bpf_loader/src/syscalls/mod.rs b/programs/bpf_loader/src/syscalls/mod.rs index 2c33dd96d1725c..b1329308ad042d 100644 --- a/programs/bpf_loader/src/syscalls/mod.rs +++ b/programs/bpf_loader/src/syscalls/mod.rs @@ -29,7 +29,6 @@ use { }, solana_sdk::{ account::{ReadableAccount, WritableAccount}, - account_info::AccountInfo, blake3, bpf_loader, bpf_loader_deprecated, bpf_loader_upgradeable, entrypoint::{BPF_ALIGN_OF_U128, MAX_PERMITTED_DATA_INCREASE, SUCCESS}, feature_set::{ @@ -70,6 +69,7 @@ use { mod cpi; mod logging; mod mem_ops; +pub(crate) mod sbf_rust; mod sysvar; /// Maximum signers @@ -99,7 +99,7 @@ pub enum SyscallError { #[error("Too many signers")] TooManySigners, #[error("Instruction passed to inner instruction is too large ({0} > {1})")] - InstructionTooLarge(usize, usize), + InstructionTooLarge(u64, u64), #[error("Too many accounts passed to inner instruction")] TooManyAccounts, #[error("Overlapping copy")] diff --git a/programs/bpf_loader/src/syscalls/sbf_rust.rs b/programs/bpf_loader/src/syscalls/sbf_rust.rs new file mode 100644 index 00000000000000..3c3131dc71eab8 --- /dev/null +++ b/programs/bpf_loader/src/syscalls/sbf_rust.rs @@ -0,0 +1,168 @@ +use { + crate::{ + syscalls::{translate_slice, translate_slice_mut, translate_type, translate_type_mut}, + BpfError, + }, + solana_program_runtime::invoke_context::InvokeContext, + solana_rbpf::{error::EbpfError, memory_region::MemoryMapping}, + solana_sdk::{instruction::AccountMeta as NativeAccountMeta, pubkey::Pubkey}, + std::marker::PhantomData, +}; + +/// Stable representation of a `Vec` in the SBFv2 Rust runtime. +/// Only supports the global allocator. +// Size: 0x18 (24 bytes) +#[repr(C)] +pub(crate) struct StdVec { + pub(crate) ptr: u64, // 0x00 (8 bytes) + pub(crate) raw_cap: u64, // 0x08 (8 bytes) + pub(crate) raw_len: u64, // 0x10 (8 bytes) + _phantom: PhantomData, +} + +impl StdVec { + /// Returns a slice to the elements pointed to by the Vec object. + /// + /// Panics on 32-bit hosts if the `raw_len` parameter exceeds 2^31. + pub(crate) fn translate( + &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::AccountMeta` +/// in the SBFv2 Rust runtime. +// Size: 0x?? (?? bytes) +#[derive(Clone)] +#[repr(C)] +pub(crate) struct AccountMeta { + pub(crate) pubkey: Pubkey, + pub(crate) is_signer: bool, + pub(crate) is_writable: bool, +} + +impl From<&AccountMeta> for NativeAccountMeta { + fn from(meta: &AccountMeta) -> Self { + Self { + pubkey: meta.pubkey, + is_signer: meta.is_signer, + is_writable: meta.is_writable, + } + } +} + +/// Stable representation of a `solana_program::instruction::Instruction` +/// in the SBFv2 Rust runtime. +// Size: 0x50 (80 bytes) +#[repr(C)] +pub(crate) struct Instruction { + pub(crate) accounts: StdVec, // 0x00 (24 bytes) + pub(crate) data: StdVec, // 0x18 (24 bytes) + pub(crate) program_id: Pubkey, // 0x30 (32 bytes) +} + +#[repr(C)] +pub(crate) struct Ptr { + pub(crate) ptr: u64, + _phantom: PhantomData, +} + +impl Ptr { + pub(crate) fn new(vm_addr: u64) -> Self { + Self { + ptr: vm_addr, + _phantom: PhantomData::default(), + } + } + + pub(crate) fn translate<'a>( + &self, + memory_mapping: &MemoryMapping, + invoke_context: &InvokeContext, + ) -> Result<&'a T, EbpfError> { + translate_type(memory_mapping, self.ptr, invoke_context.get_check_aligned()) + } + + pub(crate) fn translate_mut<'a>( + &self, + memory_mapping: &MemoryMapping, + invoke_context: &InvokeContext, + ) -> Result<&'a mut T, EbpfError> { + translate_type_mut(memory_mapping, self.ptr, invoke_context.get_check_aligned()) + } +} + +#[repr(C)] +pub(crate) struct StdRcBox { + _strong: u64, + _weak: u64, + pub(crate) value: T, +} + +#[repr(C)] +pub(crate) struct StdRefCell { + _borrow: i64, + pub(crate) value: T, +} + +#[repr(C)] +pub(crate) struct Slice { + pub(crate) ptr: u64, + pub(crate) len: u64, + _phantom: PhantomData, +} + +impl Slice { + pub(crate) fn translate<'a>( + &self, + memory_mapping: &MemoryMapping, + invoke_context: &InvokeContext, + ) -> Result<&'a [T], EbpfError> { + translate_slice( + memory_mapping, + self.ptr, + self.len, + invoke_context.get_check_aligned(), + invoke_context.get_check_size(), + ) + } + + pub(crate) fn translate_mut<'a>( + &self, + memory_mapping: &MemoryMapping, + invoke_context: &InvokeContext, + ) -> Result<&'a mut [T], EbpfError> { + translate_slice_mut( + memory_mapping, + self.ptr, + self.len, + invoke_context.get_check_aligned(), + invoke_context.get_check_size(), + ) + } +} + +/// Stable instruction of a `solana_program::account_info::AccountInfo` +/// in the SBFv2 Rust runtime. +// Size: 0x30 (48 bytes) +#[repr(C)] +pub(crate) struct AccountInfo { + pub(crate) key: Ptr, // 0x00 (8 bytes) + pub(crate) lamports: Ptr>>>, // 0x08 (8 bytes) + pub(crate) data: Ptr>>>, // 0x10 (8 bytes) + pub(crate) owner: Ptr, // 0x18 (8 bytes) + pub(crate) rent_epoch: u64, // 0x20 (8 bytes) + pub(crate) is_signer: bool, // 0x28 (1 byte) + pub(crate) is_writable: bool, // 0x29 (1 byte) + pub(crate) executable: bool, // 0x2a (1 byte) + _padding: [u8; 5], // 0x30 (5 bytes) +}