Skip to content

Commit

Permalink
programs/bpf_loader: fix non-portable cross-VM transmutation of Instr…
Browse files Browse the repository at this point in the history
…uction
  • Loading branch information
riptl committed Jul 10, 2022
1 parent 9547b00 commit aebcee8
Showing 1 changed file with 66 additions and 21 deletions.
87 changes: 66 additions & 21 deletions programs/bpf_loader/src/syscalls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -2027,6 +2028,62 @@ struct CallerAccount<'a> {
}
type TranslatedAccounts<'a> = Vec<(usize, Option<CallerAccount<'a>>)>;

/// Stable representation of a `Vec<T>` in the SBFv2 Rust runtime.
/// Only supports the global allocator.
#[repr(C)]
struct RustVMVec<T: Sized> {
ptr: u64,
raw_cap: u64,
raw_len: u64,
_phantom: PhantomData<T>,
}

impl<T: Sized> RustVMVec<T> {
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<BpfError>> {
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<AccountMeta>,
data: RustVMVec<u8>,
}

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<BpfError>> {
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<RefMut<&'a mut InvokeContext<'b>>, EbpfError<BpfError>>;
Expand Down Expand Up @@ -2093,30 +2150,18 @@ impl<'a, 'b> SyscallInvokeSigned<'a, 'b> for SyscallInvokeSignedRust<'a, 'b> {
memory_mapping: &mut MemoryMapping,
invoke_context: &mut InvokeContext,
) -> Result<Instruction, EbpfError<BpfError>> {
let ix = translate_type::<Instruction>(
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::<AccountMeta>(
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::<u8>(
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,
Expand Down

0 comments on commit aebcee8

Please sign in to comment.