solana_program_runtime/
memory.rs

1//! Memory translation utilities.
2
3use {
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/// Error types for memory translation operations.
10#[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// Do not use this directly
26#[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// Do not use this directly
38#[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// Do not use this directly
59#[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
104/// CPI-specific version with intentionally different lifetime signature.
105/// This version is missing lifetime 'a of the return type in the parameter &MemoryMapping.
106pub 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
114/// CPI-specific version with intentionally different lifetime signature.
115/// This version is missing lifetime 'a of the return type in the parameter &MemoryMapping.
116pub 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}