use {solana_sbpf::memory_region::MemoryMapping, solana_transaction_context::vm_slice::VmSlice};
#[derive(Debug, thiserror::Error, PartialEq, Eq, Clone)]
pub enum MemoryTranslationError {
#[error("Unaligned pointer")]
UnalignedPointer,
#[error("InvalidLength")]
InvalidLength,
}
#[macro_export]
macro_rules! translate_inner {
($memory_mapping:expr, $map:ident, $access_type:expr, $vm_addr:expr, $len:expr $(,)?) => {
Result::<$crate::solana_sbpf::memory_region::HostBuffer, Box<dyn std::error::Error>>::from(
$memory_mapping
.$map($access_type, $vm_addr, $len)
.map_err(|err| err.into()),
)
};
}
#[macro_export]
macro_rules! translate_type_inner {
($memory_mapping:expr, AccessType::Store, $vm_addr:expr, $T:ty, $check_aligned:expr $(,)?) => {{
let host_addr = $crate::translate_inner!(
$memory_mapping,
map,
$crate::solana_sbpf::memory_region::AccessType::Store,
$vm_addr,
size_of::<$T>() as u64
)?;
let ptr = host_addr.ptr_mut().cast::<$T>();
if $check_aligned && !ptr.is_aligned() {
Err($crate::memory::MemoryTranslationError::UnalignedPointer.into())
} else {
Ok(unsafe { ptr.as_mut_unchecked() })
}
}};
($memory_mapping:expr, AccessType::Load, $vm_addr:expr, $T:ty, $check_aligned:expr $(,)?) => {{
let host_addr = $crate::translate_inner!(
$memory_mapping,
map,
$crate::solana_sbpf::memory_region::AccessType::Load,
$vm_addr,
size_of::<$T>() as u64
)?;
let ptr = host_addr.ptr().cast::<$T>();
if $check_aligned && !ptr.is_aligned() {
Err($crate::memory::MemoryTranslationError::UnalignedPointer.into())
} else {
Ok(unsafe { ptr.as_ref_unchecked() })
}
}};
}
#[macro_export]
macro_rules! translate_slice_inner {
($memory_mapping:expr, AccessType::Store, $vm_addr:expr, $len:expr, $T:ty, $check_aligned:expr $(,)?) => {{
if $len == 0 {
Ok(std::ptr::slice_from_raw_parts_mut(
std::ptr::dangling_mut::<$T>(),
0,
))
} else {
let total_size = $len.saturating_mul(size_of::<$T>() as u64);
if isize::try_from(total_size).is_err() {
Err($crate::memory::MemoryTranslationError::InvalidLength.into())
} else {
match $crate::translate_inner!(
$memory_mapping,
map,
$crate::solana_sbpf::memory_region::AccessType::Store,
$vm_addr,
total_size
) {
Err(e) => Err(e),
Ok(host_buf) if $check_aligned && !host_buf.ptr().cast::<$T>().is_aligned() => {
Err($crate::memory::MemoryTranslationError::UnalignedPointer.into())
}
Ok(host_buf) => Ok(std::ptr::slice_from_raw_parts_mut(
host_buf.ptr_mut().cast(),
$len as usize,
)),
}
}
}
}};
($memory_mapping:expr, AccessType::Load, $vm_addr:expr, $len:expr, $T:ty, $check_aligned:expr $(,)?) => {{
if $len == 0 {
Ok(std::ptr::slice_from_raw_parts(
std::ptr::dangling_mut::<$T>(),
0,
))
} else {
let total_size = $len.saturating_mul(size_of::<$T>() as u64);
if isize::try_from(total_size).is_err() {
Err($crate::memory::MemoryTranslationError::InvalidLength.into())
} else {
match $crate::translate_inner!(
$memory_mapping,
map,
$crate::solana_sbpf::memory_region::AccessType::Load,
$vm_addr,
total_size
) {
Err(e) => Err(e),
Ok(host_buf) if $check_aligned && !host_buf.ptr().cast::<$T>().is_aligned() => {
Err($crate::memory::MemoryTranslationError::UnalignedPointer.into())
}
Ok(host_buf) => Ok(std::ptr::slice_from_raw_parts(
host_buf.ptr().cast(),
$len as usize,
)),
}
}
}
}};
}
pub fn translate_type<'a, T>(
memory_mapping: &MemoryMapping,
vm_addr: u64,
check_aligned: bool,
) -> Result<&'a T, Box<dyn std::error::Error>> {
translate_type_inner!(memory_mapping, AccessType::Load, vm_addr, T, check_aligned)
}
pub fn translate_slice<T>(
memory_mapping: &MemoryMapping,
vm_addr: u64,
len: u64,
check_aligned: bool,
) -> Result<&[T], Box<dyn std::error::Error>> {
translate_slice_inner!(
memory_mapping,
AccessType::Load,
vm_addr,
len,
T,
check_aligned,
)
.map(|value| unsafe {
&*value
})
}
pub fn translate_type_mut_for_cpi<'a, T>(
memory_mapping: &MemoryMapping,
vm_addr: u64,
check_aligned: bool,
) -> Result<&'a mut T, Box<dyn std::error::Error>> {
translate_type_inner!(memory_mapping, AccessType::Store, vm_addr, T, check_aligned)
}
pub unsafe fn translate_slice_mut_for_cpi<'a, T>(
memory_mapping: &MemoryMapping,
vm_addr: u64,
len: u64,
check_aligned: bool,
) -> Result<&'a mut [T], Box<dyn std::error::Error>> {
translate_slice_inner!(
memory_mapping,
AccessType::Store,
vm_addr,
len,
T,
check_aligned,
)
.map(|p| unsafe {
&mut *p
})
}
pub fn translate_vm_slice<'a, T>(
slice: &VmSlice<T>,
memory_mapping: &'a MemoryMapping,
check_aligned: bool,
) -> Result<&'a [T], Box<dyn std::error::Error>> {
translate_slice::<T>(memory_mapping, slice.ptr(), slice.len(), check_aligned)
}