Skip to main content

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,
7};
8
9/// Error types for memory translation operations.
10#[derive(Debug, thiserror::Error, PartialEq, Eq, Clone)]
11pub enum MemoryTranslationError {
12    #[error("Unaligned pointer")]
13    UnalignedPointer,
14    #[error("InvalidLength")]
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            Ok(std::ptr::slice_from_raw_parts_mut(
64                std::ptr::dangling_mut::<$T>(),
65                0,
66            ))
67        } else {
68            let total_size = $len.saturating_mul(size_of::<$T>() as u64);
69            if isize::try_from(total_size).is_err() {
70                Err($crate::memory::MemoryTranslationError::InvalidLength.into())
71            } else {
72                match $crate::translate_inner!(
73                    $memory_mapping,
74                    map,
75                    $access_type,
76                    $vm_addr,
77                    total_size
78                ) {
79                    Err(e) => Err(e),
80                    Ok(host_addr)
81                        if $check_aligned
82                            && !$crate::memory::address_is_aligned::<$T>(host_addr) =>
83                    {
84                        Err($crate::memory::MemoryTranslationError::UnalignedPointer.into())
85                    }
86                    Ok(host_addr) => Ok(std::ptr::slice_from_raw_parts_mut(
87                        host_addr as *mut $T,
88                        $len as usize,
89                    )),
90                }
91            }
92        }
93    }};
94}
95
96pub fn translate_type<'a, T>(
97    memory_mapping: &MemoryMapping,
98    vm_addr: u64,
99    check_aligned: bool,
100) -> Result<&'a T, Box<dyn std::error::Error>> {
101    translate_type_inner!(memory_mapping, AccessType::Load, vm_addr, T, check_aligned)
102        .map(|value| &*value)
103}
104
105pub fn translate_slice<T>(
106    memory_mapping: &MemoryMapping,
107    vm_addr: u64,
108    len: u64,
109    check_aligned: bool,
110) -> Result<&[T], Box<dyn std::error::Error>> {
111    translate_slice_inner!(
112        memory_mapping,
113        AccessType::Load,
114        vm_addr,
115        len,
116        T,
117        check_aligned,
118    )
119    .map(|value| unsafe {
120        // SAFETY: `translate_slice_inner` is guaranteed to return a dereferenceable memory region.
121        // This is producing a shared/read-only slice to the memory, so the uniqueness invariants
122        // aren't relevant.
123        &*value
124    })
125}
126
127/// CPI-specific version with intentionally different lifetime signature.
128/// This version is missing lifetime 'a of the return type in the parameter &MemoryMapping.
129pub fn translate_type_mut_for_cpi<'a, T>(
130    memory_mapping: &MemoryMapping,
131    vm_addr: u64,
132    check_aligned: bool,
133) -> Result<&'a mut T, Box<dyn std::error::Error>> {
134    translate_type_inner!(memory_mapping, AccessType::Store, vm_addr, T, check_aligned)
135}
136
137/// CPI-specific version with intentionally different lifetime signature.
138/// This version is missing lifetime 'a of the return type in the parameter &MemoryMapping.
139///
140/// # Safety
141///
142/// * The caller must ensure that this function does not violate mutable reference uniqueness
143///   constraints;
144/// * The caller must ensure that the lifetime of the returned slice does not outlive the backing
145///   data.
146pub unsafe fn translate_slice_mut_for_cpi<'a, T>(
147    memory_mapping: &MemoryMapping,
148    vm_addr: u64,
149    len: u64,
150    check_aligned: bool,
151) -> Result<&'a mut [T], Box<dyn std::error::Error>> {
152    translate_slice_inner!(
153        memory_mapping,
154        AccessType::Store,
155        vm_addr,
156        len,
157        T,
158        check_aligned,
159    )
160    .map(|p| unsafe {
161        // SAFETY: the invariants for producing mutable references delegated to the callers.
162        &mut *p
163    })
164}
165
166pub fn translate_vm_slice<'a, T>(
167    slice: &VmSlice<T>,
168    memory_mapping: &'a MemoryMapping,
169    check_aligned: bool,
170) -> Result<&'a [T], Box<dyn std::error::Error>> {
171    translate_slice::<T>(memory_mapping, slice.ptr(), slice.len(), check_aligned)
172}