Skip to content

Commit

Permalink
Added VmSlice class, a slice in the virtual address space. (#12)
Browse files Browse the repository at this point in the history
* Added VmSlice class (untested). A slice in the virtual address space.

* Fixed up the Rust, removed iterator because not currently needed

* Added a block comment of description

* Update programs/bpf_loader/src/syscalls/mod.rs

comment tweak

Co-authored-by: Petar Vujović <[email protected]>

* Simplified the VmSlice so it matches a real slice's structure. Implemented one use of it (out of six)

* Finished integration of VmSlice

* Added is_empty and an (unsafe, unchecked) resize function that adjusts vector length

---------

Co-authored-by: Petar Vujović <[email protected]>
  • Loading branch information
jason-nitro and petarvujovic98 authored Dec 19, 2024
1 parent f0d7e4f commit bbf5700
Show file tree
Hide file tree
Showing 3 changed files with 136 additions and 43 deletions.
16 changes: 6 additions & 10 deletions programs/bpf_loader/src/syscalls/cpi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -511,7 +511,7 @@ impl SyscallInvokeSigned for SyscallInvokeSignedRust {
) -> Result<Vec<Pubkey>, Error> {
let mut signers = Vec::new();
if signers_seeds_len > 0 {
let signers_seeds = translate_slice::<&[&[u8]]>(
let signers_seeds = translate_slice_of_slices::<VmSlice<u8>>(
memory_mapping,
signers_seeds_addr,
signers_seeds_len,
Expand All @@ -521,10 +521,10 @@ impl SyscallInvokeSigned for SyscallInvokeSignedRust {
return Err(Box::new(SyscallError::TooManySigners));
}
for signer_seeds in signers_seeds.iter() {
let untranslated_seeds = translate_slice::<&[u8]>(
let untranslated_seeds = translate_slice_of_slices::<u8>(
memory_mapping,
signer_seeds.as_ptr() as *const _ as u64,
signer_seeds.len() as u64,
signer_seeds.ptr(),
signer_seeds.len(),
invoke_context.get_check_aligned(),
)?;
if untranslated_seeds.len() > MAX_SEEDS {
Expand All @@ -533,12 +533,8 @@ impl SyscallInvokeSigned for SyscallInvokeSignedRust {
let seeds = untranslated_seeds
.iter()
.map(|untranslated_seed| {
translate_slice::<u8>(
memory_mapping,
untranslated_seed.as_ptr() as *const _ as u64,
untranslated_seed.len() as u64,
invoke_context.get_check_aligned(),
)
untranslated_seed
.translate(memory_mapping, invoke_context.get_check_aligned())
})
.collect::<Result<Vec<_>, Error>>()?;
let signer = Pubkey::create_program_address(&seeds, program_id)
Expand Down
11 changes: 3 additions & 8 deletions programs/bpf_loader/src/syscalls/logging.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ declare_builtin_function!(

consume_compute_meter(invoke_context, budget.syscall_base_cost)?;

let untranslated_fields = translate_slice::<&[u8]>(
let untranslated_fields = translate_slice_of_slices::<u8>(
memory_mapping,
addr,
len,
Expand All @@ -137,18 +137,13 @@ declare_builtin_function!(
invoke_context,
untranslated_fields
.iter()
.fold(0, |total, e| total.saturating_add(e.len() as u64)),
.fold(0, |total, e| total.saturating_add(e.len())),
)?;

let mut fields = Vec::with_capacity(untranslated_fields.len());

for untranslated_field in untranslated_fields {
fields.push(translate_slice::<u8>(
memory_mapping,
untranslated_field.as_ptr() as *const _ as u64,
untranslated_field.len() as u64,
invoke_context.get_check_aligned(),
)?);
fields.push(untranslated_field.translate(memory_mapping, invoke_context.get_check_aligned())?);
}

let log_collector = invoke_context.get_log_collector();
Expand Down
152 changes: 127 additions & 25 deletions programs/bpf_loader/src/syscalls/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ pub use self::{
SyscallGetSysvar,
},
};

#[allow(deprecated)]
use {
solana_bn254::prelude::{
Expand Down Expand Up @@ -59,6 +60,7 @@ use {
solana_type_overrides::sync::Arc,
std::{
alloc::Layout,
marker::PhantomData,
mem::{align_of, size_of},
slice::from_raw_parts_mut,
str::{from_utf8, Utf8Error},
Expand Down Expand Up @@ -226,6 +228,66 @@ impl HasherImpl for Keccak256Hasher {
}
}

// The VmSlice class is used for cases when you need a slice that is stored in the BPF
// interpreter's virtual address space. Because this source code can be compiled with
// addresses of different bit depths, we cannot assume that the 64-bit BPF interpreter's
// pointer sizes can be mapped to physical pointer sizes. In particular, if you need a
// slice-of-slices in the virtual space, the inner slices will be different sizes in a
// 32-bit app build than in the 64-bit virtual space. Therefore instead of a slice-of-slices,
// you should implement a slice-of-VmSlices, which can then use VmSlice::translate() to
// map to the physical address.
// This class must consist only of 16 bytes: a u64 ptr and a u64 len, to match the 64-bit
// implementation of a slice in Rust. The PhantomData entry takes up 0 bytes.
pub struct VmSlice<T> {
ptr: u64,
len: u64,
resource_type: PhantomData<T>,
}

impl<T> VmSlice<T> {
pub fn new(ptr: u64, len: u64) -> Self {
VmSlice {
ptr,
len,
resource_type: PhantomData,
}
}

pub fn ptr(&self) -> u64 {
self.ptr
}
pub fn len(&self) -> u64 {
self.len
}

pub fn is_empty(&self) -> bool {
self.len == 0
}

/// Adjust the length of the vector. This is unchecked, and it assumes that the pointer
/// points to valid memory of the correct length after vm-translation.
pub fn resize(&mut self, len: u64) {
self.len = len;
}

/// Returns a slice using a mapped physical address
pub fn translate(
&self,
memory_mapping: &MemoryMapping,
check_aligned: bool,
) -> Result<&[T], Error> {
translate_slice::<T>(memory_mapping, self.ptr, self.len, check_aligned)
}

pub fn translate_mut(
&mut self,
memory_mapping: &MemoryMapping,
check_aligned: bool,
) -> Result<&mut [T], Error> {
translate_slice_mut::<T>(memory_mapping, self.ptr, self.len, check_aligned)
}
}

fn consume_compute_meter(invoke_context: &InvokeContext, amount: u64) -> Result<(), Error> {
invoke_context.consume_checked(amount)?;
Ok(())
Expand Down Expand Up @@ -618,6 +680,62 @@ fn translate_slice<'a, T>(
.map(|value| &*value)
}

fn translate_slice_of_slices_inner<'a, T>(
memory_mapping: &MemoryMapping,
access_type: AccessType,
vm_addr: u64,
len: u64,
check_aligned: bool,
) -> Result<&'a mut [VmSlice<T>], Error> {
if len == 0 {
return Ok(&mut []);
}

let total_size = len.saturating_mul(size_of::<VmSlice<T>>() as u64);
if isize::try_from(total_size).is_err() {
return Err(SyscallError::InvalidLength.into());
}

let host_addr = translate(memory_mapping, access_type, vm_addr, total_size)?;

if check_aligned && !address_is_aligned::<VmSlice<T>>(host_addr) {
return Err(SyscallError::UnalignedPointer.into());
}
Ok(unsafe { from_raw_parts_mut(host_addr as *mut VmSlice<T>, len as usize) })
}

#[allow(dead_code)]
fn translate_slice_of_slices_mut<'a, T>(
memory_mapping: &MemoryMapping,
vm_addr: u64,
len: u64,
check_aligned: bool,
) -> Result<&'a mut [VmSlice<T>], Error> {
translate_slice_of_slices_inner::<T>(
memory_mapping,
AccessType::Store,
vm_addr,
len,
check_aligned,
)
}

fn translate_slice_of_slices<'a, T>(
memory_mapping: &MemoryMapping,
vm_addr: u64,
len: u64,
check_aligned: bool,
) -> Result<&'a [VmSlice<T>], Error> {
translate_slice_of_slices_inner::<T>(
memory_mapping,
AccessType::Load,
vm_addr,
len,
check_aligned,
)
.map(|value| &*value)
}

/// Take a virtual pointer to a string (points to SBF VM memory space), translate it
/// pass it to a user-defined work function
fn translate_string_and_do(
Expand Down Expand Up @@ -724,22 +842,17 @@ fn translate_and_check_program_address_inputs<'a>(
check_aligned: bool,
) -> Result<(Vec<&'a [u8]>, &'a Pubkey), Error> {
let untranslated_seeds =
translate_slice::<&[u8]>(memory_mapping, seeds_addr, seeds_len, check_aligned)?;
translate_slice_of_slices::<u8>(memory_mapping, seeds_addr, seeds_len, check_aligned)?;
if untranslated_seeds.len() > MAX_SEEDS {
return Err(SyscallError::BadSeeds(PubkeyError::MaxSeedLengthExceeded).into());
}
let seeds = untranslated_seeds
.iter()
.map(|untranslated_seed| {
if untranslated_seed.len() > MAX_SEED_LEN {
if untranslated_seed.len() > MAX_SEED_LEN as u64 {
return Err(SyscallError::BadSeeds(PubkeyError::MaxSeedLengthExceeded).into());
}
translate_slice::<u8>(
memory_mapping,
untranslated_seed.as_ptr() as *const _ as u64,
untranslated_seed.len() as u64,
check_aligned,
)
untranslated_seed.translate(memory_mapping, check_aligned)
})
.collect::<Result<Vec<_>, Error>>()?;
let program_id = translate_type::<Pubkey>(memory_mapping, program_id_addr, check_aligned)?;
Expand Down Expand Up @@ -1795,22 +1908,15 @@ declare_builtin_function!(
poseidon::HASH_BYTES as u64,
invoke_context.get_check_aligned(),
)?;
let inputs = translate_slice::<&[u8]>(
let inputs = translate_slice_of_slices::<u8>(
memory_mapping,
vals_addr,
vals_len,
invoke_context.get_check_aligned(),
)?;
let inputs = inputs
.iter()
.map(|input| {
translate_slice::<u8>(
memory_mapping,
input.as_ptr() as *const _ as u64,
input.len() as u64,
invoke_context.get_check_aligned(),
)
})
.map(|input| input.translate(memory_mapping, invoke_context.get_check_aligned()))
.collect::<Result<Vec<_>, Error>>()?;

let simplify_alt_bn128_syscall_error_codes = invoke_context
Expand Down Expand Up @@ -2011,22 +2117,18 @@ declare_builtin_function!(
)?;
let mut hasher = H::create_hasher();
if vals_len > 0 {
let vals = translate_slice::<&[u8]>(
let vals = translate_slice_of_slices::<u8>(
memory_mapping,
vals_addr,
vals_len,
invoke_context.get_check_aligned(),
)?;

for val in vals.iter() {
let bytes = translate_slice::<u8>(
memory_mapping,
val.as_ptr() as u64,
val.len() as u64,
invoke_context.get_check_aligned(),
)?;
let bytes = val.translate(memory_mapping, invoke_context.get_check_aligned())?;
let cost = compute_budget.mem_op_base_cost.max(
hash_byte_cost.saturating_mul(
(val.len() as u64)
val.len()
.checked_div(2)
.expect("div by non-zero literal"),
),
Expand Down

0 comments on commit bbf5700

Please sign in to comment.