1use {
4 solana_sbpf::memory_region::{AccessType, MemoryMapping},
5 solana_transaction_context::vm_slice::VmSlice,
6 std::{mem::align_of, slice::from_raw_parts_mut},
7};
8
9#[derive(Debug, thiserror::Error, PartialEq, Eq)]
11pub enum MemoryTranslationError {
12 #[error("Unaligned pointer")]
13 UnalignedPointer,
14 #[error("Invalid length")]
15 InvalidLength,
16}
17
18pub fn address_is_aligned<T>(address: u64) -> bool {
19 (address as *mut T as usize)
20 .checked_rem(align_of::<T>())
21 .map(|rem| rem == 0)
22 .expect("T to be non-zero aligned")
23}
24
25#[macro_export]
27macro_rules! translate_inner {
28 ($memory_mapping:expr, $map:ident, $access_type:expr, $vm_addr:expr, $len:expr $(,)?) => {
29 Result::<u64, Box<dyn std::error::Error>>::from(
30 $memory_mapping
31 .$map($access_type, $vm_addr, $len)
32 .map_err(|err| err.into()),
33 )
34 };
35}
36
37#[macro_export]
39macro_rules! translate_type_inner {
40 ($memory_mapping:expr, $access_type:expr, $vm_addr:expr, $T:ty, $check_aligned:expr $(,)?) => {{
41 let host_addr = $crate::translate_inner!(
42 $memory_mapping,
43 map,
44 $access_type,
45 $vm_addr,
46 size_of::<$T>() as u64
47 )?;
48 if !$check_aligned {
49 Ok(unsafe { std::mem::transmute::<u64, &mut $T>(host_addr) })
50 } else if !$crate::memory::address_is_aligned::<$T>(host_addr) {
51 Err($crate::memory::MemoryTranslationError::UnalignedPointer.into())
52 } else {
53 Ok(unsafe { &mut *(host_addr as *mut $T) })
54 }
55 }};
56}
57
58#[macro_export]
60macro_rules! translate_slice_inner {
61 ($memory_mapping:expr, $access_type:expr, $vm_addr:expr, $len:expr, $T:ty, $check_aligned:expr $(,)?) => {{
62 if $len == 0 {
63 return Ok(&mut []);
64 }
65 let total_size = $len.saturating_mul(size_of::<$T>() as u64);
66 if isize::try_from(total_size).is_err() {
67 return Err($crate::memory::MemoryTranslationError::InvalidLength.into());
68 }
69 let host_addr =
70 $crate::translate_inner!($memory_mapping, map, $access_type, $vm_addr, total_size)?;
71 if $check_aligned && !$crate::memory::address_is_aligned::<$T>(host_addr) {
72 return Err($crate::memory::MemoryTranslationError::UnalignedPointer.into());
73 }
74 Ok(unsafe { from_raw_parts_mut(host_addr as *mut $T, $len as usize) })
75 }};
76}
77
78pub fn translate_type<'a, T>(
79 memory_mapping: &MemoryMapping,
80 vm_addr: u64,
81 check_aligned: bool,
82) -> Result<&'a T, Box<dyn std::error::Error>> {
83 translate_type_inner!(memory_mapping, AccessType::Load, vm_addr, T, check_aligned)
84 .map(|value| &*value)
85}
86
87pub fn translate_slice<'a, T>(
88 memory_mapping: &MemoryMapping,
89 vm_addr: u64,
90 len: u64,
91 check_aligned: bool,
92) -> Result<&'a [T], Box<dyn std::error::Error>> {
93 translate_slice_inner!(
94 memory_mapping,
95 AccessType::Load,
96 vm_addr,
97 len,
98 T,
99 check_aligned,
100 )
101 .map(|value| &*value)
102}
103
104pub fn translate_type_mut_for_cpi<'a, T>(
107 memory_mapping: &MemoryMapping,
108 vm_addr: u64,
109 check_aligned: bool,
110) -> Result<&'a mut T, Box<dyn std::error::Error>> {
111 translate_type_inner!(memory_mapping, AccessType::Store, vm_addr, T, check_aligned)
112}
113
114pub fn translate_slice_mut_for_cpi<'a, T>(
117 memory_mapping: &MemoryMapping,
118 vm_addr: u64,
119 len: u64,
120 check_aligned: bool,
121) -> Result<&'a mut [T], Box<dyn std::error::Error>> {
122 translate_slice_inner!(
123 memory_mapping,
124 AccessType::Store,
125 vm_addr,
126 len,
127 T,
128 check_aligned,
129 )
130}
131
132pub fn translate_vm_slice<'a, T>(
133 slice: &VmSlice<T>,
134 memory_mapping: &'a MemoryMapping,
135 check_aligned: bool,
136) -> Result<&'a [T], Box<dyn std::error::Error>> {
137 translate_slice::<T>(memory_mapping, slice.ptr(), slice.len(), check_aligned)
138}