Ver Fonte

program-runtime: create new `memory` module (#7836)

* program-runtime: create new `memory` module

* program-runtime: add translation macros from syscalls lib

* program-runtime: add translation functions from syscalls lib

* Revert "program-runtime: add translation functions from syscalls lib"

This reverts commit f5eac6fc12aa0b2b9663b466422c5c9bee256a8f.

* program-runtime: add cpi-specific translation functions from syscalls lib
Joe C há 2 meses atrás
pai
commit
592bb0cb12

+ 1 - 0
Cargo.lock

@@ -9712,6 +9712,7 @@ dependencies = [
  "solana-transaction",
  "solana-transaction-context",
  "test-case",
+ "thiserror 2.0.16",
 ]
 
 [[package]]

+ 1 - 0
program-runtime/Cargo.toml

@@ -63,6 +63,7 @@ solana-system-interface = { workspace = true }
 solana-sysvar = { workspace = true, features = ["bincode"] }
 solana-sysvar-id = { workspace = true }
 solana-transaction-context = { workspace = true }
+thiserror = { workspace = true }
 
 [dev-dependencies]
 assert_matches = { workspace = true }

+ 1 - 0
program-runtime/src/lib.rs

@@ -7,6 +7,7 @@ pub mod execution_budget;
 pub mod invoke_context;
 pub mod loaded_programs;
 pub mod mem_pool;
+pub mod memory;
 pub mod serialization;
 pub mod stable_log;
 pub mod sysvar_cache;

+ 129 - 0
program-runtime/src/memory.rs

@@ -0,0 +1,129 @@
+//! Memory translation utilities.
+
+use {
+    solana_sbpf::memory_region::{AccessType, MemoryMapping},
+    std::{mem::align_of, slice::from_raw_parts_mut},
+};
+
+/// Error types for memory translation operations.
+#[derive(Debug, thiserror::Error, PartialEq, Eq)]
+pub enum MemoryTranslationError {
+    #[error("Unaligned pointer")]
+    UnalignedPointer,
+    #[error("Invalid length")]
+    InvalidLength,
+}
+
+pub fn address_is_aligned<T>(address: u64) -> bool {
+    (address as *mut T as usize)
+        .checked_rem(align_of::<T>())
+        .map(|rem| rem == 0)
+        .expect("T to be non-zero aligned")
+}
+
+// Do not use this directly
+#[macro_export]
+macro_rules! translate_inner {
+    ($memory_mapping:expr, $map:ident, $access_type:expr, $vm_addr:expr, $len:expr $(,)?) => {
+        Result::<u64, Box<dyn std::error::Error>>::from(
+            $memory_mapping
+                .$map($access_type, $vm_addr, $len)
+                .map_err(|err| err.into()),
+        )
+    };
+}
+
+// Do not use this directly
+#[macro_export]
+macro_rules! translate_type_inner {
+    ($memory_mapping:expr, $access_type:expr, $vm_addr:expr, $T:ty, $check_aligned:expr $(,)?) => {{
+        let host_addr = $crate::translate_inner!(
+            $memory_mapping,
+            map,
+            $access_type,
+            $vm_addr,
+            size_of::<$T>() as u64
+        )?;
+        if !$check_aligned {
+            Ok(unsafe { std::mem::transmute::<u64, &mut $T>(host_addr) })
+        } else if !$crate::memory::address_is_aligned::<$T>(host_addr) {
+            Err($crate::memory::MemoryTranslationError::UnalignedPointer.into())
+        } else {
+            Ok(unsafe { &mut *(host_addr as *mut $T) })
+        }
+    }};
+}
+
+// Do not use this directly
+#[macro_export]
+macro_rules! translate_slice_inner {
+    ($memory_mapping:expr, $access_type:expr, $vm_addr:expr, $len:expr, $T:ty, $check_aligned:expr $(,)?) => {{
+        if $len == 0 {
+            return Ok(&mut []);
+        }
+        let total_size = $len.saturating_mul(size_of::<$T>() as u64);
+        if isize::try_from(total_size).is_err() {
+            return Err($crate::memory::MemoryTranslationError::InvalidLength.into());
+        }
+        let host_addr =
+            $crate::translate_inner!($memory_mapping, map, $access_type, $vm_addr, total_size)?;
+        if $check_aligned && !$crate::memory::address_is_aligned::<$T>(host_addr) {
+            return Err($crate::memory::MemoryTranslationError::UnalignedPointer.into());
+        }
+        Ok(unsafe { from_raw_parts_mut(host_addr as *mut $T, $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)
+        .map(|value| &*value)
+}
+
+pub fn translate_slice<'a, T>(
+    memory_mapping: &MemoryMapping,
+    vm_addr: u64,
+    len: u64,
+    check_aligned: bool,
+) -> Result<&'a [T], Box<dyn std::error::Error>> {
+    translate_slice_inner!(
+        memory_mapping,
+        AccessType::Load,
+        vm_addr,
+        len,
+        T,
+        check_aligned,
+    )
+    .map(|value| &*value)
+}
+
+/// CPI-specific version with intentionally different lifetime signature.
+/// This version is missing lifetime 'a of the return type in the parameter &MemoryMapping.
+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)
+}
+
+/// CPI-specific version with intentionally different lifetime signature.
+/// This version is missing lifetime 'a of the return type in the parameter &MemoryMapping.
+pub 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,
+    )
+}

+ 1 - 0
programs/sbf/Cargo.lock

@@ -7589,6 +7589,7 @@ dependencies = [
  "solana-sysvar",
  "solana-sysvar-id",
  "solana-transaction-context",
+ "thiserror 2.0.16",
 ]
 
 [[package]]

+ 23 - 37
syscalls/src/cpi.rs

@@ -1,10 +1,13 @@
 use {
     super::*,
-    crate::{translate_inner, translate_slice_inner, translate_type_inner},
     solana_instruction::Instruction,
     solana_loader_v3_interface::instruction as bpf_loader_upgradeable,
     solana_program_runtime::{
         invoke_context::SerializedAccountMetadata,
+        memory::{
+            translate_slice, translate_slice_mut_for_cpi, translate_type,
+            translate_type_mut_for_cpi,
+        },
         serialization::{create_memory_region_of_account, modify_memory_region_of_account},
     },
     solana_sbpf::ebpf,
@@ -37,31 +40,6 @@ fn check_account_info_pointer(
     Ok(())
 }
 
-// This version is missing lifetime 'a of the return type in the parameter &MemoryMapping.
-fn translate_type_mut<'a, T>(
-    memory_mapping: &MemoryMapping,
-    vm_addr: u64,
-    check_aligned: bool,
-) -> Result<&'a mut T, Error> {
-    translate_type_inner!(memory_mapping, AccessType::Store, vm_addr, T, check_aligned)
-}
-// This version is missing the lifetime 'a of the return type in the parameter &MemoryMapping.
-fn translate_slice_mut<'a, T>(
-    memory_mapping: &MemoryMapping,
-    vm_addr: u64,
-    len: u64,
-    check_aligned: bool,
-) -> Result<&'a mut [T], Error> {
-    translate_slice_inner!(
-        memory_mapping,
-        AccessType::Store,
-        vm_addr,
-        len,
-        T,
-        check_aligned,
-    )
-}
-
 /// Host side representation of AccountInfo or SolAccountInfo passed to the CPI syscall.
 ///
 /// At the start of a CPI, this can be different from the data stored in the
@@ -98,7 +76,7 @@ impl<'a> CallerAccount<'a> {
             Ok(&mut [])
         } else if stricter_abi_and_runtime_constraints {
             // Workaround the memory permissions (as these are from the PoV of being inside the VM)
-            let serialization_ptr = translate_slice_mut::<u8>(
+            let serialization_ptr = translate_slice_mut_for_cpi::<u8>(
                 memory_mapping,
                 solana_sbpf::ebpf::MM_INPUT_START,
                 1,
@@ -113,7 +91,7 @@ impl<'a> CallerAccount<'a> {
                 ))
             }
         } else {
-            translate_slice_mut::<u8>(
+            translate_slice_mut_for_cpi::<u8>(
                 memory_mapping,
                 vm_addr,
                 len,
@@ -171,10 +149,10 @@ impl<'a> CallerAccount<'a> {
                     "lamports",
                 )?;
             }
-            translate_type_mut::<u64>(memory_mapping, *ptr, check_aligned)?
+            translate_type_mut_for_cpi::<u64>(memory_mapping, *ptr, check_aligned)?
         };
 
-        let owner = translate_type_mut::<Pubkey>(
+        let owner = translate_type_mut_for_cpi::<Pubkey>(
             memory_mapping,
             account_info.owner as *const _ as u64,
             check_aligned,
@@ -219,7 +197,8 @@ impl<'a> CallerAccount<'a> {
                     return Err(SyscallError::InvalidPointer.into());
                 }
             }
-            let ref_to_len_in_vm = translate_type_mut::<u64>(memory_mapping, vm_len_addr, false)?;
+            let ref_to_len_in_vm =
+                translate_type_mut_for_cpi::<u64>(memory_mapping, vm_len_addr, false)?;
             let vm_data_addr = data.as_ptr() as u64;
             let serialized_data = CallerAccount::get_serialized_data(
                 memory_mapping,
@@ -286,10 +265,16 @@ impl<'a> CallerAccount<'a> {
 
         // account_info points to host memory. The addresses used internally are
         // in vm space so they need to be translated.
-        let lamports =
-            translate_type_mut::<u64>(memory_mapping, account_info.lamports_addr, check_aligned)?;
-        let owner =
-            translate_type_mut::<Pubkey>(memory_mapping, account_info.owner_addr, check_aligned)?;
+        let lamports = translate_type_mut_for_cpi::<u64>(
+            memory_mapping,
+            account_info.lamports_addr,
+            check_aligned,
+        )?;
+        let owner = translate_type_mut_for_cpi::<Pubkey>(
+            memory_mapping,
+            account_info.owner_addr,
+            check_aligned,
+        )?;
 
         consume_compute_meter(
             invoke_context,
@@ -314,7 +299,8 @@ impl<'a> CallerAccount<'a> {
         let vm_len_addr = vm_addr
             .saturating_add(&account_info.data_len as *const u64 as u64)
             .saturating_sub(account_info as *const _ as *const u64 as u64);
-        let ref_to_len_in_vm = translate_type_mut::<u64>(memory_mapping, vm_len_addr, false)?;
+        let ref_to_len_in_vm =
+            translate_type_mut_for_cpi::<u64>(memory_mapping, vm_len_addr, false)?;
 
         Ok(CallerAccount {
             lamports,
@@ -1241,7 +1227,7 @@ fn update_caller_account(
         *caller_account.ref_to_len_in_vm = post_len as u64;
 
         // this is the len field in the serialized parameters
-        let serialized_len_ptr = translate_type_mut::<u64>(
+        let serialized_len_ptr = translate_type_mut_for_cpi::<u64>(
             memory_mapping,
             caller_account
                 .vm_data_addr

+ 5 - 58
syscalls/src/lib.rs

@@ -29,7 +29,8 @@ use {
     solana_program_runtime::{
         execution_budget::{SVMTransactionExecutionBudget, SVMTransactionExecutionCost},
         invoke_context::InvokeContext,
-        stable_log,
+        memory::MemoryTranslationError,
+        stable_log, translate_inner, translate_slice_inner, translate_type_inner,
     },
     solana_pubkey::{Pubkey, PubkeyError, MAX_SEEDS, MAX_SEED_LEN, PUBKEY_BYTES},
     solana_sbpf::{
@@ -121,6 +122,8 @@ pub enum SyscallError {
     InvalidPointer,
     #[error("Arithmetic overflow")]
     ArithmeticOverflow,
+    #[error(transparent)]
+    MemoryTranslation(#[from] MemoryTranslationError),
 }
 
 type Error = Box<dyn std::error::Error>;
@@ -549,63 +552,6 @@ pub fn create_program_runtime_environment_v2<'a>(
     BuiltinProgram::new_loader(config)
 }
 
-fn address_is_aligned<T>(address: u64) -> bool {
-    (address as *mut T as usize)
-        .checked_rem(align_of::<T>())
-        .map(|rem| rem == 0)
-        .expect("T to be non-zero aligned")
-}
-
-// Do not use this directly
-#[macro_export]
-macro_rules! translate_inner {
-    ($memory_mapping:expr, $map:ident, $access_type:expr, $vm_addr:expr, $len:expr $(,)?) => {
-        Result::<u64, Error>::from(
-            $memory_mapping
-                .$map($access_type, $vm_addr, $len)
-                .map_err(|err| err.into()),
-        )
-    };
-}
-// Do not use this directly
-#[macro_export]
-macro_rules! translate_type_inner {
-    ($memory_mapping:expr, $access_type:expr, $vm_addr:expr, $T:ty, $check_aligned:expr $(,)?) => {{
-        let host_addr = translate_inner!(
-            $memory_mapping,
-            map,
-            $access_type,
-            $vm_addr,
-            size_of::<$T>() as u64
-        )?;
-        if !$check_aligned {
-            Ok(unsafe { std::mem::transmute::<u64, &mut $T>(host_addr) })
-        } else if !address_is_aligned::<$T>(host_addr) {
-            Err(SyscallError::UnalignedPointer.into())
-        } else {
-            Ok(unsafe { &mut *(host_addr as *mut $T) })
-        }
-    }};
-}
-// Do not use this directly
-#[macro_export]
-macro_rules! translate_slice_inner {
-    ($memory_mapping:expr, $access_type:expr, $vm_addr:expr, $len:expr, $T:ty, $check_aligned:expr $(,)?) => {{
-        if $len == 0 {
-            return Ok(&mut []);
-        }
-        let total_size = $len.saturating_mul(size_of::<$T>() as u64);
-        if isize::try_from(total_size).is_err() {
-            return Err(SyscallError::InvalidLength.into());
-        }
-        let host_addr = translate_inner!($memory_mapping, map, $access_type, $vm_addr, total_size)?;
-        if $check_aligned && !address_is_aligned::<$T>(host_addr) {
-            return Err(SyscallError::UnalignedPointer.into());
-        }
-        Ok(unsafe { from_raw_parts_mut(host_addr as *mut $T, $len as usize) })
-    }};
-}
-
 fn translate_type<'a, T>(
     memory_mapping: &'a MemoryMapping,
     vm_addr: u64,
@@ -2134,6 +2080,7 @@ mod tests {
         solana_program_runtime::{
             execution_budget::MAX_HEAP_FRAME_BYTES,
             invoke_context::{BpfAllocator, InvokeContext, SyscallContext},
+            memory::address_is_aligned,
             with_mock_invoke_context,
         },
         solana_sbpf::{