Skip to main content

solana_program_runtime/
memory.rs

1//! Memory translation utilities.
2
3use {solana_sbpf::memory_region::MemoryMapping, solana_transaction_context::vm_slice::VmSlice};
4
5/// Error types for memory translation operations.
6#[derive(Debug, thiserror::Error, PartialEq, Eq, Clone)]
7pub enum MemoryTranslationError {
8    #[error("Unaligned pointer")]
9    UnalignedPointer,
10    #[error("InvalidLength")]
11    InvalidLength,
12}
13
14// Do not use this directly
15#[macro_export]
16macro_rules! translate_inner {
17    ($memory_mapping:expr, $map:ident, $access_type:expr, $vm_addr:expr, $len:expr $(,)?) => {
18        Result::<$crate::solana_sbpf::memory_region::HostBuffer, Box<dyn std::error::Error>>::from(
19            $memory_mapping
20                .$map($access_type, $vm_addr, $len)
21                .map_err(|err| err.into()),
22        )
23    };
24}
25
26// Do not use this directly
27#[macro_export]
28macro_rules! translate_type_inner {
29    ($memory_mapping:expr, AccessType::Store, $vm_addr:expr, $T:ty, $check_aligned:expr $(,)?) => {{
30        let host_addr = $crate::translate_inner!(
31            $memory_mapping,
32            map,
33            $crate::solana_sbpf::memory_region::AccessType::Store,
34            $vm_addr,
35            size_of::<$T>() as u64
36        )?;
37        let ptr = host_addr.ptr_mut().cast::<$T>();
38        if $check_aligned && !ptr.is_aligned() {
39            Err($crate::memory::MemoryTranslationError::UnalignedPointer.into())
40        } else {
41            Ok(unsafe { ptr.as_mut_unchecked() })
42        }
43    }};
44    ($memory_mapping:expr, AccessType::Load, $vm_addr:expr, $T:ty, $check_aligned:expr $(,)?) => {{
45        let host_addr = $crate::translate_inner!(
46            $memory_mapping,
47            map,
48            $crate::solana_sbpf::memory_region::AccessType::Load,
49            $vm_addr,
50            size_of::<$T>() as u64
51        )?;
52        let ptr = host_addr.ptr().cast::<$T>();
53        if $check_aligned && !ptr.is_aligned() {
54            Err($crate::memory::MemoryTranslationError::UnalignedPointer.into())
55        } else {
56            Ok(unsafe { ptr.as_ref_unchecked() })
57        }
58    }};
59}
60
61// Do not use this directly
62#[macro_export]
63macro_rules! translate_slice_inner {
64    ($memory_mapping:expr, AccessType::Store, $vm_addr:expr, $len:expr, $T:ty, $check_aligned:expr $(,)?) => {{
65        if $len == 0 {
66            Ok(std::ptr::slice_from_raw_parts_mut(
67                std::ptr::dangling_mut::<$T>(),
68                0,
69            ))
70        } else {
71            let total_size = $len.saturating_mul(size_of::<$T>() as u64);
72            if isize::try_from(total_size).is_err() {
73                Err($crate::memory::MemoryTranslationError::InvalidLength.into())
74            } else {
75                match $crate::translate_inner!(
76                    $memory_mapping,
77                    map,
78                    $crate::solana_sbpf::memory_region::AccessType::Store,
79                    $vm_addr,
80                    total_size
81                ) {
82                    Err(e) => Err(e),
83                    Ok(host_buf) if $check_aligned && !host_buf.ptr().cast::<$T>().is_aligned() => {
84                        Err($crate::memory::MemoryTranslationError::UnalignedPointer.into())
85                    }
86                    Ok(host_buf) => Ok(std::ptr::slice_from_raw_parts_mut(
87                        host_buf.ptr_mut().cast(),
88                        $len as usize,
89                    )),
90                }
91            }
92        }
93    }};
94    ($memory_mapping:expr, AccessType::Load, $vm_addr:expr, $len:expr, $T:ty, $check_aligned:expr $(,)?) => {{
95        if $len == 0 {
96            Ok(std::ptr::slice_from_raw_parts(
97                std::ptr::dangling_mut::<$T>(),
98                0,
99            ))
100        } else {
101            let total_size = $len.saturating_mul(size_of::<$T>() as u64);
102            if isize::try_from(total_size).is_err() {
103                Err($crate::memory::MemoryTranslationError::InvalidLength.into())
104            } else {
105                match $crate::translate_inner!(
106                    $memory_mapping,
107                    map,
108                    $crate::solana_sbpf::memory_region::AccessType::Load,
109                    $vm_addr,
110                    total_size
111                ) {
112                    Err(e) => Err(e),
113                    Ok(host_buf) if $check_aligned && !host_buf.ptr().cast::<$T>().is_aligned() => {
114                        Err($crate::memory::MemoryTranslationError::UnalignedPointer.into())
115                    }
116                    Ok(host_buf) => Ok(std::ptr::slice_from_raw_parts(
117                        host_buf.ptr().cast(),
118                        $len as usize,
119                    )),
120                }
121            }
122        }
123    }};
124}
125
126pub fn translate_type<'a, T>(
127    memory_mapping: &MemoryMapping,
128    vm_addr: u64,
129    check_aligned: bool,
130) -> Result<&'a T, Box<dyn std::error::Error>> {
131    translate_type_inner!(memory_mapping, AccessType::Load, vm_addr, T, check_aligned)
132}
133
134pub fn translate_slice<T>(
135    memory_mapping: &MemoryMapping,
136    vm_addr: u64,
137    len: u64,
138    check_aligned: bool,
139) -> Result<&[T], Box<dyn std::error::Error>> {
140    translate_slice_inner!(
141        memory_mapping,
142        AccessType::Load,
143        vm_addr,
144        len,
145        T,
146        check_aligned,
147    )
148    .map(|value| unsafe {
149        // SAFETY: `translate_slice_inner` is guaranteed to return a dereferenceable memory region.
150        // This is producing a shared/read-only slice to the memory, so the uniqueness invariants
151        // aren't relevant.
152        &*value
153    })
154}
155
156/// CPI-specific version with intentionally different lifetime signature.
157/// This version is missing lifetime 'a of the return type in the parameter &MemoryMapping.
158pub fn translate_type_mut_for_cpi<'a, T>(
159    memory_mapping: &MemoryMapping,
160    vm_addr: u64,
161    check_aligned: bool,
162) -> Result<&'a mut T, Box<dyn std::error::Error>> {
163    translate_type_inner!(memory_mapping, AccessType::Store, vm_addr, T, check_aligned)
164}
165
166/// CPI-specific version with intentionally different lifetime signature.
167/// This version is missing lifetime 'a of the return type in the parameter &MemoryMapping.
168///
169/// # Safety
170///
171/// * The caller must ensure that this function does not violate mutable reference uniqueness
172///   constraints;
173/// * The caller must ensure that the lifetime of the returned slice does not outlive the backing
174///   data.
175pub unsafe fn translate_slice_mut_for_cpi<'a, T>(
176    memory_mapping: &MemoryMapping,
177    vm_addr: u64,
178    len: u64,
179    check_aligned: bool,
180) -> Result<&'a mut [T], Box<dyn std::error::Error>> {
181    translate_slice_inner!(
182        memory_mapping,
183        AccessType::Store,
184        vm_addr,
185        len,
186        T,
187        check_aligned,
188    )
189    .map(|p| unsafe {
190        // SAFETY: the invariants for producing mutable references delegated to the callers.
191        &mut *p
192    })
193}
194
195pub fn translate_vm_slice<'a, T>(
196    slice: &VmSlice<T>,
197    memory_mapping: &'a MemoryMapping,
198    check_aligned: bool,
199) -> Result<&'a [T], Box<dyn std::error::Error>> {
200    translate_slice::<T>(memory_mapping, slice.ptr(), slice.len(), check_aligned)
201}