Skip to main content

sbpf_runtime/cpi/
sync.rs

1use {
2    crate::cpi::request::CallerAccountInfo,
3    sbpf_vm::{errors::SbpfVmResult, memory::Memory},
4    solana_account::Account,
5    solana_address::Address,
6    std::collections::HashMap,
7};
8
9/// Maximum bytes an account's data can grow during CPI.
10const MAX_PERMITTED_DATA_INCREASE: usize = 10240;
11
12/// Sync current account state from the caller's VM memory into the account store before CPI.
13pub fn sync_from_caller(
14    memory: &Memory,
15    caller_accounts: &[CallerAccountInfo],
16    accounts: &mut HashMap<Address, Account>,
17) -> SbpfVmResult<()> {
18    for info in caller_accounts {
19        let lamports = memory.read_u64(info.lamports_addr)?;
20        let data = memory
21            .read_bytes(info.data_addr, info.data_len as usize)?
22            .to_vec();
23        let owner_bytes = memory.read_bytes(info.owner_addr, 32)?;
24        let owner = Address::new_from_array(owner_bytes.try_into().unwrap());
25
26        let account = accounts.entry(info.pubkey).or_default();
27        account.lamports = lamports;
28        account.data = data;
29        account.owner = owner;
30    }
31    Ok(())
32}
33
34/// Sync updated account state back to the caller's VM memory after CPI.
35pub fn sync_to_caller(
36    memory: &mut Memory,
37    caller_accounts: &[CallerAccountInfo],
38    accounts: &HashMap<Address, Account>,
39) -> SbpfVmResult<()> {
40    for info in caller_accounts {
41        if !info.is_writable {
42            continue;
43        }
44        let Some(account) = accounts.get(&info.pubkey) else {
45            continue;
46        };
47
48        // Sync lamports and owner.
49        memory.write_u64(info.lamports_addr, account.lamports)?;
50        memory.write_bytes(info.owner_addr, account.owner.as_ref())?;
51
52        let prev_len = info.data_len as usize;
53        let post_len = account.data.len();
54        let max_allowed = prev_len.saturating_add(MAX_PERMITTED_DATA_INCREASE);
55
56        if post_len > max_allowed {
57            return Err(sbpf_vm::errors::SbpfVmError::SyscallError(format!(
58                "Account data realloc limited to {}",
59                MAX_PERMITTED_DATA_INCREASE
60            )));
61        }
62
63        // Handle change in data length.
64        if prev_len != post_len {
65            if post_len < prev_len {
66                let zeros = vec![0u8; prev_len - post_len];
67                memory.write_bytes(info.data_addr + post_len as u64, &zeros)?;
68            }
69            memory.write_u64(info.vm_data_len_addr, post_len as u64)?;
70            memory.write_u64(info.data_addr.saturating_sub(8), post_len as u64)?;
71        }
72
73        // Copy the actual data bytes.
74        memory.write_bytes(info.data_addr, &account.data)?;
75    }
76    Ok(())
77}