Skip to main content

solana_program_runtime/
serialization.rs

1#![allow(clippy::arithmetic_side_effects)]
2
3use {
4    crate::memory_context::SerializedAccountMetadata,
5    solana_instruction_error::InstructionError,
6    solana_program_entrypoint::{BPF_ALIGN_OF_U128, MAX_PERMITTED_DATA_INCREASE, NON_DUP_MARKER},
7    solana_pubkey::Pubkey,
8    solana_sbpf::{
9        aligned_memory::{AlignedMemory, Pod},
10        ebpf::{HOST_ALIGN, MM_INPUT_START},
11        memory_region::MemoryRegion,
12    },
13    solana_sdk_ids::bpf_loader_deprecated,
14    solana_system_interface::MAX_PERMITTED_DATA_LENGTH,
15    solana_transaction_context::{
16        IndexOfAccount, MAX_ACCOUNTS_PER_INSTRUCTION, instruction::InstructionContext,
17        instruction_accounts::BorrowedInstructionAccount,
18    },
19    std::mem::{self, size_of},
20};
21
22/// Modifies the memory mapping in serialization and CPI return for virtual_address_space_adjustments
23pub fn modify_memory_region_of_account(
24    account: &mut BorrowedInstructionAccount<'_, '_>,
25    region: &mut MemoryRegion,
26) {
27    let data_ptr = region.host_buffer().ptr() as *mut u8;
28    let new_buffer = std::ptr::slice_from_raw_parts_mut(data_ptr, account.get_data().len());
29    if account.can_data_be_changed().is_ok() {
30        unsafe {
31            // SAFETY:
32            // Contract from `MemoryRegion::redirect`: The memory pointed to by the MemoryRegions
33            // must point to a valid object live for the duration of this MemoryMapping.
34            //
35            // TODO(nagisa): Local reasoning for this contract is infeasible. In particular for the
36            // `serialization.rs` code it is pretty easy to see that the regions passed in will
37            // always be larger than `account.get_data().len()`. However for `cpi.rs` callsite this
38            // is not as easy to prove and relies on careful coordination between any code that
39            // might increase the account data buffer length.
40            region.redirect(new_buffer);
41        }
42        region.access_violation_handler_payload = Some(account.get_index_in_transaction());
43    } else {
44        unsafe {
45            // SAFETY:
46            //
47            // Contract from `MemoryRegion::redirect`: same as for the call above.
48            // Evidence: same as for the call above.
49            region.redirect(new_buffer.cast_const());
50        }
51        region.access_violation_handler_payload = None;
52    }
53}
54
55/// Creates the memory mapping in serialization and CPI return for account_data_direct_mapping
56pub fn create_memory_region_of_account(
57    account: &mut BorrowedInstructionAccount<'_, '_>,
58    vaddr: u64,
59) -> Result<MemoryRegion, InstructionError> {
60    let can_data_be_changed = account.can_data_be_changed().is_ok();
61    let mut memory_region = if can_data_be_changed && !account.is_shared() {
62        MemoryRegion::new(&raw mut account.get_data_mut()?[..], vaddr)
63    } else {
64        MemoryRegion::new(&raw const account.get_data()[..], vaddr)
65    };
66    if can_data_be_changed {
67        memory_region.access_violation_handler_payload = Some(account.get_index_in_transaction());
68    }
69    Ok(memory_region)
70}
71
72#[expect(dead_code)]
73enum SerializeAccount<'a, 'ix_data> {
74    Account(IndexOfAccount, BorrowedInstructionAccount<'a, 'ix_data>),
75    Duplicate(IndexOfAccount),
76}
77
78struct Serializer {
79    buffer: AlignedMemory<HOST_ALIGN>,
80    regions: Vec<MemoryRegion>,
81    vaddr: u64,
82    region_start: usize,
83    is_loader_v1: bool,
84    virtual_address_space_adjustments: bool,
85    account_data_direct_mapping: bool,
86}
87
88impl Serializer {
89    fn new(
90        size: usize,
91        start_addr: u64,
92        is_loader_v1: bool,
93        virtual_address_space_adjustments: bool,
94        account_data_direct_mapping: bool,
95    ) -> Serializer {
96        Serializer {
97            buffer: AlignedMemory::with_capacity(size),
98            regions: Vec::new(),
99            region_start: 0,
100            vaddr: start_addr,
101            is_loader_v1,
102            virtual_address_space_adjustments,
103            account_data_direct_mapping,
104        }
105    }
106
107    fn fill_write(&mut self, num: usize, value: u8) -> std::io::Result<()> {
108        self.buffer.fill_write(num, value)
109    }
110
111    fn write<T: Pod>(&mut self, value: T) -> u64 {
112        self.debug_assert_alignment::<T>();
113        let vaddr = self
114            .vaddr
115            .saturating_add(self.buffer.len() as u64)
116            .saturating_sub(self.region_start as u64);
117        // Safety:
118        // in serialize_parameters_(aligned|unaligned) first we compute the
119        // required size then we write into the newly allocated buffer. There's
120        // no need to check bounds at every write.
121        //
122        // AlignedMemory::write_unchecked _does_ debug_assert!() that the capacity
123        // is enough, so in the unlikely case we introduce a bug in the size
124        // computation, tests will abort.
125        unsafe {
126            self.buffer.write_unchecked(value);
127        }
128
129        vaddr
130    }
131
132    fn write_all(&mut self, value: &[u8]) -> u64 {
133        let vaddr = self
134            .vaddr
135            .saturating_add(self.buffer.len() as u64)
136            .saturating_sub(self.region_start as u64);
137        // Safety:
138        // see write() - the buffer is guaranteed to be large enough
139        unsafe {
140            self.buffer.write_all_unchecked(value);
141        }
142
143        vaddr
144    }
145
146    fn write_account(
147        &mut self,
148        account: &mut BorrowedInstructionAccount<'_, '_>,
149    ) -> Result<u64, InstructionError> {
150        if !self.virtual_address_space_adjustments {
151            let vm_data_addr = self.vaddr.saturating_add(self.buffer.len() as u64);
152            self.write_all(account.get_data());
153            if !self.is_loader_v1 {
154                let align_offset =
155                    (account.get_data().len() as *const u8).align_offset(BPF_ALIGN_OF_U128);
156                self.fill_write(MAX_PERMITTED_DATA_INCREASE + align_offset, 0)
157                    .map_err(|_| InstructionError::InvalidArgument)?;
158            }
159            Ok(vm_data_addr)
160        } else {
161            self.push_region();
162            let vm_data_addr = self.vaddr;
163            if !self.account_data_direct_mapping {
164                self.write_all(account.get_data());
165                if !self.is_loader_v1 {
166                    self.fill_write(MAX_PERMITTED_DATA_INCREASE, 0)
167                        .map_err(|_| InstructionError::InvalidArgument)?;
168                }
169            }
170            let address_space_reserved_for_account = if !self.is_loader_v1 {
171                account
172                    .get_data()
173                    .len()
174                    .saturating_add(MAX_PERMITTED_DATA_INCREASE)
175            } else {
176                account.get_data().len()
177            };
178            if address_space_reserved_for_account > 0 {
179                if !self.account_data_direct_mapping {
180                    self.push_region();
181                    let region = self.regions.last_mut().unwrap();
182                    modify_memory_region_of_account(account, region);
183                } else {
184                    let new_region = create_memory_region_of_account(account, self.vaddr)?;
185                    self.vaddr += address_space_reserved_for_account as u64;
186                    self.regions.push(new_region);
187                }
188            }
189            if !self.is_loader_v1 {
190                let align_offset =
191                    (account.get_data().len() as *const u8).align_offset(BPF_ALIGN_OF_U128);
192                if !self.account_data_direct_mapping {
193                    self.fill_write(align_offset, 0)
194                        .map_err(|_| InstructionError::InvalidArgument)?;
195                } else {
196                    // The deserialization code is going to align the vm_addr to
197                    // BPF_ALIGN_OF_U128. Always add one BPF_ALIGN_OF_U128 worth of
198                    // padding and shift the start of the next region, so that once
199                    // vm_addr is aligned, the corresponding host_addr is aligned
200                    // too.
201                    self.fill_write(BPF_ALIGN_OF_U128, 0)
202                        .map_err(|_| InstructionError::InvalidArgument)?;
203                    self.region_start += BPF_ALIGN_OF_U128.saturating_sub(align_offset);
204                }
205            }
206            Ok(vm_data_addr)
207        }
208    }
209
210    fn push_region(&mut self) {
211        let range = self.region_start..self.buffer.len();
212        let region_slice = self.buffer.as_slice_mut().get_mut(range.clone()).unwrap();
213        self.regions
214            .push(MemoryRegion::new(&raw mut region_slice[..], self.vaddr));
215        self.region_start = range.end;
216        self.vaddr += range.len() as u64;
217    }
218
219    fn finish(mut self) -> (AlignedMemory<HOST_ALIGN>, Vec<MemoryRegion>) {
220        self.push_region();
221        debug_assert_eq!(self.region_start, self.buffer.len());
222        (self.buffer, self.regions)
223    }
224
225    fn debug_assert_alignment<T>(&self) {
226        debug_assert!(
227            self.is_loader_v1
228                || self
229                    .buffer
230                    .as_slice()
231                    .as_ptr_range()
232                    .end
233                    .align_offset(mem::align_of::<T>())
234                    == 0
235        );
236    }
237}
238
239/// The result of serializing the parameters for a program's execution.
240pub struct SerializationResult {
241    /// The serialized parameters for the program's execution.
242    pub parameter_bytes: AlignedMemory<HOST_ALIGN>,
243
244    /// The memory regions that were serialized into `parameter_bytes`.
245    pub regions: Vec<MemoryRegion>,
246
247    /// Metadata about the accounts that were serialized into `parameter_bytes`.
248    pub accounts_metadata: Vec<SerializedAccountMetadata>,
249
250    /// The VM address of the instruction data in `parameter_bytes`.
251    pub instruction_data_offset: usize,
252
253    /// The VM address of the array of account pointers in `parameter_bytes`,
254    /// if `direct_account_pointers_in_program_input` was enabled during
255    /// serialization.
256    pub account_pointers_offset: Option<usize>,
257}
258
259pub fn serialize_parameters(
260    instruction_context: &InstructionContext,
261    virtual_address_space_adjustments: bool,
262    account_data_direct_mapping: bool,
263    direct_account_pointers_in_program_input: bool,
264) -> Result<SerializationResult, InstructionError> {
265    let num_ix_accounts = instruction_context.get_number_of_instruction_accounts();
266    if num_ix_accounts > MAX_ACCOUNTS_PER_INSTRUCTION as IndexOfAccount {
267        return Err(InstructionError::MaxAccountsExceeded);
268    }
269
270    let program_id = *instruction_context.get_program_key()?;
271    let is_loader_deprecated =
272        instruction_context.get_program_owner()? == bpf_loader_deprecated::id();
273
274    let accounts = (0..instruction_context.get_number_of_instruction_accounts())
275        .map(|instruction_account_index| {
276            if let Some(index) = instruction_context
277                .is_instruction_account_duplicate(instruction_account_index)
278                .unwrap()
279            {
280                SerializeAccount::Duplicate(index)
281            } else {
282                let account = instruction_context
283                    .try_borrow_instruction_account(instruction_account_index)
284                    .unwrap();
285                SerializeAccount::Account(instruction_account_index, account)
286            }
287        })
288        // fun fact: jemalloc is good at caching tiny allocations like this one,
289        // so collecting here is actually faster than passing the iterator
290        // around, since the iterator does the work to produce its items each
291        // time it's iterated on.
292        .collect::<Vec<_>>();
293
294    if is_loader_deprecated {
295        // Used by loader-v1 (bpf_loader_deprecated)
296        let (parameter_bytes, regions, accounts_metadata, instruction_data_offset) =
297            serialize_parameters_for_abiv0(
298                accounts,
299                instruction_context.get_instruction_data(),
300                &program_id,
301                virtual_address_space_adjustments,
302                account_data_direct_mapping,
303            )?;
304        Ok(SerializationResult {
305            parameter_bytes,
306            regions,
307            accounts_metadata,
308            instruction_data_offset,
309            account_pointers_offset: None,
310        })
311    } else {
312        // Used by loader-v2 (bpf_loader) and loader-v3 (bpf_loader_upgradeable)
313        serialize_parameters_for_abiv1(
314            accounts,
315            instruction_context.get_instruction_data(),
316            &program_id,
317            virtual_address_space_adjustments,
318            account_data_direct_mapping,
319            // SIMD-0449: only available on ABIv1
320            direct_account_pointers_in_program_input,
321        )
322    }
323}
324
325pub fn deserialize_parameters(
326    instruction_context: &InstructionContext,
327    virtual_address_space_adjustments: bool,
328    account_data_direct_mapping: bool,
329    buffer: &[u8],
330    accounts_metadata: &[SerializedAccountMetadata],
331) -> Result<(), InstructionError> {
332    let is_loader_deprecated =
333        instruction_context.get_program_owner()? == bpf_loader_deprecated::id();
334    let account_lengths = accounts_metadata.iter().map(|a| a.original_data_len);
335    if is_loader_deprecated {
336        // Used by loader-v1 (bpf_loader_deprecated)
337        deserialize_parameters_for_abiv0(
338            instruction_context,
339            virtual_address_space_adjustments,
340            account_data_direct_mapping,
341            buffer,
342            account_lengths,
343        )
344    } else {
345        // Used by loader-v2 (bpf_loader) and loader-v3 (bpf_loader_upgradeable)
346        deserialize_parameters_for_abiv1(
347            instruction_context,
348            virtual_address_space_adjustments,
349            account_data_direct_mapping,
350            buffer,
351            account_lengths,
352        )
353    }
354}
355
356fn serialize_parameters_for_abiv0(
357    accounts: Vec<SerializeAccount>,
358    instruction_data: &[u8],
359    program_id: &Pubkey,
360    virtual_address_space_adjustments: bool,
361    account_data_direct_mapping: bool,
362) -> Result<
363    (
364        AlignedMemory<HOST_ALIGN>,
365        Vec<MemoryRegion>,
366        Vec<SerializedAccountMetadata>,
367        usize,
368    ),
369    InstructionError,
370> {
371    // Calculate size in order to alloc once
372    let mut size = size_of::<u64>();
373    for account in &accounts {
374        size += 1; // dup
375        match account {
376            SerializeAccount::Duplicate(_) => {}
377            SerializeAccount::Account(_, account) => {
378                size += size_of::<u8>() // is_signer
379                + size_of::<u8>() // is_writable
380                + size_of::<Pubkey>() // key
381                + size_of::<u64>()  // lamports
382                + size_of::<u64>()  // data len
383                + size_of::<Pubkey>() // owner
384                + size_of::<u8>() // executable
385                + size_of::<u64>(); // rent_epoch
386                if !(virtual_address_space_adjustments && account_data_direct_mapping) {
387                    size += account.get_data().len();
388                }
389            }
390        }
391    }
392    size += size_of::<u64>() // instruction data len
393         + instruction_data.len() // instruction data
394         + size_of::<Pubkey>(); // program id
395
396    let mut s = Serializer::new(
397        size,
398        MM_INPUT_START,
399        true,
400        virtual_address_space_adjustments,
401        account_data_direct_mapping,
402    );
403
404    let mut accounts_metadata: Vec<SerializedAccountMetadata> = Vec::with_capacity(accounts.len());
405    s.write::<u64>((accounts.len() as u64).to_le());
406    for account in accounts {
407        match account {
408            SerializeAccount::Duplicate(position) => {
409                accounts_metadata.push(accounts_metadata.get(position as usize).unwrap().clone());
410                s.write(position as u8);
411            }
412            SerializeAccount::Account(_, mut account) => {
413                let vm_addr = s.write::<u8>(NON_DUP_MARKER);
414                s.write::<u8>(account.is_signer() as u8);
415                s.write::<u8>(account.is_writable() as u8);
416                let vm_key_addr = s.write_all(account.get_key().as_ref());
417                let vm_lamports_addr = s.write::<u64>(account.get_lamports().to_le());
418                s.write::<u64>((account.get_data().len() as u64).to_le());
419                let vm_data_addr = s.write_account(&mut account)?;
420                let vm_owner_addr = s.write_all(account.get_owner().as_ref());
421                #[expect(deprecated)]
422                s.write::<u8>(account.is_executable() as u8);
423                let rent_epoch = u64::MAX;
424                s.write::<u64>(rent_epoch.to_le());
425                accounts_metadata.push(SerializedAccountMetadata {
426                    vm_addr,
427                    original_data_len: account.get_data().len(),
428                    vm_key_addr,
429                    vm_lamports_addr,
430                    vm_owner_addr,
431                    vm_data_addr,
432                });
433            }
434        };
435    }
436    s.write::<u64>((instruction_data.len() as u64).to_le());
437    let instruction_data_offset = s.write_all(instruction_data);
438    s.write_all(program_id.as_ref());
439
440    let (mem, regions) = s.finish();
441    Ok((
442        mem,
443        regions,
444        accounts_metadata,
445        instruction_data_offset as usize,
446    ))
447}
448
449fn deserialize_parameters_for_abiv0<I: IntoIterator<Item = usize>>(
450    instruction_context: &InstructionContext,
451    virtual_address_space_adjustments: bool,
452    account_data_direct_mapping: bool,
453    buffer: &[u8],
454    account_lengths: I,
455) -> Result<(), InstructionError> {
456    let mut start = size_of::<u64>(); // number of accounts
457    for (instruction_account_index, pre_len) in
458        (0..instruction_context.get_number_of_instruction_accounts()).zip(account_lengths)
459    {
460        let duplicate =
461            instruction_context.is_instruction_account_duplicate(instruction_account_index)?;
462        start += 1; // is_dup
463        if duplicate.is_none() {
464            let mut borrowed_account =
465                instruction_context.try_borrow_instruction_account(instruction_account_index)?;
466            start += size_of::<u8>(); // is_signer
467            start += size_of::<u8>(); // is_writable
468            start += size_of::<Pubkey>(); // key
469            let lamports = buffer
470                .get(start..start.saturating_add(8))
471                .map(<[u8; 8]>::try_from)
472                .and_then(Result::ok)
473                .map(u64::from_le_bytes)
474                .ok_or(InstructionError::InvalidArgument)?;
475            if borrowed_account.get_lamports() != lamports {
476                borrowed_account.set_lamports(lamports)?;
477            }
478            start += size_of::<u64>() // lamports
479                + size_of::<u64>(); // data length
480            if !virtual_address_space_adjustments {
481                let data = buffer
482                    .get(start..start + pre_len)
483                    .ok_or(InstructionError::InvalidArgument)?;
484                // The redundant check helps to avoid the expensive data comparison if we can
485                match borrowed_account.can_data_be_resized(pre_len) {
486                    Ok(()) => borrowed_account.set_data_from_slice(data)?,
487                    Err(err) if borrowed_account.get_data() != data => return Err(err),
488                    _ => {}
489                }
490            } else if !account_data_direct_mapping && borrowed_account.can_data_be_changed().is_ok()
491            {
492                let data = buffer
493                    .get(start..start + pre_len)
494                    .ok_or(InstructionError::InvalidArgument)?;
495                borrowed_account.set_data_from_slice(data)?;
496            } else if borrowed_account.get_data().len() != pre_len {
497                borrowed_account.set_data_length(pre_len)?;
498            }
499            if !(virtual_address_space_adjustments && account_data_direct_mapping) {
500                start += pre_len; // data
501            }
502            start += size_of::<Pubkey>() // owner
503                + size_of::<u8>() // executable
504                + size_of::<u64>(); // rent_epoch
505        }
506    }
507    Ok(())
508}
509
510fn serialize_parameters_for_abiv1(
511    accounts: Vec<SerializeAccount>,
512    instruction_data: &[u8],
513    program_id: &Pubkey,
514    virtual_address_space_adjustments: bool,
515    account_data_direct_mapping: bool,
516    direct_account_pointers_program_input: bool,
517) -> Result<SerializationResult, InstructionError> {
518    let mut accounts_metadata = Vec::with_capacity(accounts.len());
519    // Calculate size in order to alloc once
520    let mut size = size_of::<u64>();
521    for account in &accounts {
522        size += 1; // dup
523        match account {
524            SerializeAccount::Duplicate(_) => size += 7, // padding to 64-bit aligned
525            SerializeAccount::Account(_, account) => {
526                let data_len = account.get_data().len();
527                size += size_of::<u8>() // is_signer
528                + size_of::<u8>() // is_writable
529                + size_of::<u8>() // executable
530                + size_of::<u32>() // original_data_len
531                + size_of::<Pubkey>()  // key
532                + size_of::<Pubkey>() // owner
533                + size_of::<u64>()  // lamports
534                + size_of::<u64>()  // data len
535                + size_of::<u64>(); // rent epoch
536                if !(virtual_address_space_adjustments && account_data_direct_mapping) {
537                    size += data_len
538                        + MAX_PERMITTED_DATA_INCREASE
539                        + (data_len as *const u8).align_offset(BPF_ALIGN_OF_U128);
540                } else {
541                    size += BPF_ALIGN_OF_U128;
542                }
543            }
544        }
545    }
546    size += size_of::<u64>() // data len
547    + instruction_data.len()
548    + size_of::<Pubkey>(); // program id;
549
550    // reserve space for the array of account pointers if SIMD-0449 is enabled
551    let account_pointers_padding = if direct_account_pointers_program_input {
552        let padding = (size as *const u8).align_offset(BPF_ALIGN_OF_U128);
553        size += padding + accounts.len() * size_of::<u64>();
554        Some(padding)
555    } else {
556        None
557    };
558
559    let mut s = Serializer::new(
560        size,
561        MM_INPUT_START,
562        false,
563        virtual_address_space_adjustments,
564        account_data_direct_mapping,
565    );
566
567    // Serialize into the buffer
568    s.write::<u64>((accounts.len() as u64).to_le());
569    for account in accounts {
570        match account {
571            SerializeAccount::Account(_, mut borrowed_account) => {
572                let vm_addr = s.write::<u8>(NON_DUP_MARKER);
573                s.write::<u8>(borrowed_account.is_signer() as u8);
574                s.write::<u8>(borrowed_account.is_writable() as u8);
575                #[expect(deprecated)]
576                s.write::<u8>(borrowed_account.is_executable() as u8);
577                s.write_all(&[0u8, 0, 0, 0]);
578                let vm_key_addr = s.write_all(borrowed_account.get_key().as_ref());
579                let vm_owner_addr = s.write_all(borrowed_account.get_owner().as_ref());
580                let vm_lamports_addr = s.write::<u64>(borrowed_account.get_lamports().to_le());
581                s.write::<u64>((borrowed_account.get_data().len() as u64).to_le());
582                let vm_data_addr = s.write_account(&mut borrowed_account)?;
583                let rent_epoch = u64::MAX;
584                s.write::<u64>(rent_epoch.to_le());
585                accounts_metadata.push(SerializedAccountMetadata {
586                    vm_addr,
587                    original_data_len: borrowed_account.get_data().len(),
588                    vm_key_addr,
589                    vm_owner_addr,
590                    vm_lamports_addr,
591                    vm_data_addr,
592                });
593            }
594            SerializeAccount::Duplicate(position) => {
595                accounts_metadata.push(accounts_metadata.get(position as usize).unwrap().clone());
596                s.write::<u8>(position as u8);
597                s.write_all(&[0u8, 0, 0, 0, 0, 0, 0]);
598            }
599        };
600    }
601    s.write::<u64>((instruction_data.len() as u64).to_le());
602    let instruction_data_offset = s.write_all(instruction_data);
603    // Determines the address of the array of account pointers, if account
604    // pointers are being passed in the program input (SIMD-0449).
605    let account_pointers_offset = {
606        let vaddr = s.write_all(program_id.as_ref()) as usize;
607
608        if let Some(padding) = account_pointers_padding {
609            // Add padding before the array of account pointers to reach
610            // 8-byte alignment (BPF_ALIGN_OF_U128).
611            s.fill_write(padding, 0)
612                .map_err(|_| InstructionError::InvalidArgument)?;
613            for entry in accounts_metadata.iter() {
614                s.write::<u64>(entry.vm_addr.to_le());
615            }
616            // Compute the address of the array of account pointers.
617            Some(vaddr + size_of::<Pubkey>() + padding)
618        } else {
619            None
620        }
621    };
622
623    let (mem, regions) = s.finish();
624    Ok(SerializationResult {
625        parameter_bytes: mem,
626        regions,
627        accounts_metadata,
628        instruction_data_offset: instruction_data_offset as usize,
629        account_pointers_offset,
630    })
631}
632
633fn deserialize_parameters_for_abiv1<I: IntoIterator<Item = usize>>(
634    instruction_context: &InstructionContext,
635    virtual_address_space_adjustments: bool,
636    account_data_direct_mapping: bool,
637    buffer: &[u8],
638    account_lengths: I,
639) -> Result<(), InstructionError> {
640    let mut start = size_of::<u64>(); // number of accounts
641    for (instruction_account_index, pre_len) in
642        (0..instruction_context.get_number_of_instruction_accounts()).zip(account_lengths)
643    {
644        let duplicate =
645            instruction_context.is_instruction_account_duplicate(instruction_account_index)?;
646        start += size_of::<u8>(); // position
647        if duplicate.is_some() {
648            start += 7; // padding to 64-bit aligned
649        } else {
650            let mut borrowed_account =
651                instruction_context.try_borrow_instruction_account(instruction_account_index)?;
652            start += size_of::<u8>() // is_signer
653                + size_of::<u8>() // is_writable
654                + size_of::<u8>() // executable
655                + size_of::<u32>() // original_data_len
656                + size_of::<Pubkey>(); // key
657            let owner = buffer
658                .get(start..start + size_of::<Pubkey>())
659                .ok_or(InstructionError::InvalidArgument)?;
660            start += size_of::<Pubkey>(); // owner
661            let lamports = buffer
662                .get(start..start.saturating_add(8))
663                .map(<[u8; 8]>::try_from)
664                .and_then(Result::ok)
665                .map(u64::from_le_bytes)
666                .ok_or(InstructionError::InvalidArgument)?;
667            if borrowed_account.get_lamports() != lamports {
668                borrowed_account.set_lamports(lamports)?;
669            }
670            start += size_of::<u64>(); // lamports
671            let post_len = buffer
672                .get(start..start.saturating_add(8))
673                .map(<[u8; 8]>::try_from)
674                .and_then(Result::ok)
675                .map(u64::from_le_bytes)
676                .ok_or(InstructionError::InvalidArgument)? as usize;
677            start += size_of::<u64>(); // data length
678            if post_len.saturating_sub(pre_len) > MAX_PERMITTED_DATA_INCREASE
679                || post_len > MAX_PERMITTED_DATA_LENGTH as usize
680            {
681                return Err(InstructionError::InvalidRealloc);
682            }
683            if !virtual_address_space_adjustments {
684                let data = buffer
685                    .get(start..start + post_len)
686                    .ok_or(InstructionError::InvalidArgument)?;
687                // The redundant check helps to avoid the expensive data comparison if we can
688                match borrowed_account.can_data_be_resized(post_len) {
689                    Ok(()) => borrowed_account.set_data_from_slice(data)?,
690                    Err(err) if borrowed_account.get_data() != data => return Err(err),
691                    _ => {}
692                }
693            } else if !account_data_direct_mapping && borrowed_account.can_data_be_changed().is_ok()
694            {
695                let data = buffer
696                    .get(start..start + post_len)
697                    .ok_or(InstructionError::InvalidArgument)?;
698                borrowed_account.set_data_from_slice(data)?;
699            } else if borrowed_account.get_data().len() != post_len {
700                borrowed_account.set_data_length(post_len)?;
701            }
702            start += if !(virtual_address_space_adjustments && account_data_direct_mapping) {
703                let alignment_offset = (pre_len as *const u8).align_offset(BPF_ALIGN_OF_U128);
704                pre_len // data
705                    .saturating_add(MAX_PERMITTED_DATA_INCREASE) // realloc padding
706                    .saturating_add(alignment_offset)
707            } else {
708                // See Serializer::write_account() as to why we have this
709                BPF_ALIGN_OF_U128
710            };
711            start += size_of::<u64>(); // rent_epoch
712            if borrowed_account.get_owner().to_bytes() != owner {
713                // Change the owner at the end so that we are allowed to change the lamports and data before
714                borrowed_account.set_owner(owner)?;
715            }
716        }
717    }
718    Ok(())
719}
720
721#[cfg(test)]
722#[allow(clippy::indexing_slicing)]
723mod tests {
724    use {
725        super::*,
726        crate::with_mock_invoke_context,
727        solana_account::{Account, AccountSharedData, ReadableAccount},
728        solana_account_info::AccountInfo,
729        solana_program_entrypoint::deserialize,
730        solana_rent::Rent,
731        solana_sbpf::{memory_region::MemoryMapping, program::SBPFVersion, vm::Config},
732        solana_sdk_ids::{bpf_loader, bpf_loader_upgradeable},
733        solana_system_interface::MAX_PERMITTED_ACCOUNTS_DATA_ALLOCATIONS_PER_TRANSACTION,
734        solana_transaction_context::{
735            MAX_ACCOUNTS_PER_TRANSACTION, instruction_accounts::InstructionAccount,
736            transaction::TransactionContext,
737        },
738        std::{
739            borrow::Cow,
740            cell::RefCell,
741            mem::transmute,
742            rc::Rc,
743            slice::{from_raw_parts, from_raw_parts_mut},
744        },
745        test_case::test_case,
746    };
747
748    fn deduplicated_instruction_accounts(
749        transaction_indexes: &[IndexOfAccount],
750        is_writable: fn(usize) -> bool,
751    ) -> Vec<InstructionAccount> {
752        transaction_indexes
753            .iter()
754            .enumerate()
755            .map(|(index_in_instruction, index_in_transaction)| {
756                InstructionAccount::new(
757                    *index_in_transaction,
758                    false,
759                    is_writable(index_in_instruction),
760                )
761            })
762            .collect()
763    }
764
765    #[test_case(false; "direct_account_pointers_in_program_input disabled")]
766    #[test_case(true; "direct_account_pointers_in_program_input enabled")]
767    fn test_serialize_parameters_with_many_accounts(
768        direct_account_pointers_in_program_input: bool,
769    ) {
770        struct TestCase {
771            num_ix_accounts: usize,
772            append_dup_account: bool,
773            expected_err: Option<InstructionError>,
774            name: &'static str,
775        }
776
777        for virtual_address_space_adjustments in [false, true] {
778            for TestCase {
779                num_ix_accounts,
780                append_dup_account,
781                expected_err,
782                name,
783            } in [
784                TestCase {
785                    name: "serialize max accounts with cap",
786                    num_ix_accounts: MAX_ACCOUNTS_PER_INSTRUCTION,
787                    append_dup_account: false,
788                    expected_err: None,
789                },
790                TestCase {
791                    name: "serialize too many accounts with cap",
792                    num_ix_accounts: MAX_ACCOUNTS_PER_INSTRUCTION + 1,
793                    append_dup_account: false,
794                    expected_err: Some(InstructionError::MaxAccountsExceeded),
795                },
796                TestCase {
797                    name: "serialize too many accounts and append dup with cap",
798                    num_ix_accounts: MAX_ACCOUNTS_PER_INSTRUCTION,
799                    append_dup_account: true,
800                    expected_err: Some(InstructionError::MaxAccountsExceeded),
801                },
802            ] {
803                let program_id = solana_pubkey::new_rand();
804                let mut transaction_accounts = vec![(
805                    program_id,
806                    AccountSharedData::from(Account {
807                        lamports: 0,
808                        data: vec![],
809                        owner: bpf_loader::id(),
810                        executable: true,
811                        rent_epoch: 0,
812                    }),
813                )];
814                for _ in 0..num_ix_accounts {
815                    transaction_accounts.push((
816                        Pubkey::new_unique(),
817                        AccountSharedData::from(Account {
818                            lamports: 0,
819                            data: vec![],
820                            owner: program_id,
821                            executable: false,
822                            rent_epoch: 0,
823                        }),
824                    ));
825                }
826
827                let transaction_accounts_indexes: Vec<IndexOfAccount> =
828                    (0..num_ix_accounts as u16).collect();
829                let mut instruction_accounts =
830                    deduplicated_instruction_accounts(&transaction_accounts_indexes, |_| false);
831                if append_dup_account {
832                    instruction_accounts.push(instruction_accounts.last().cloned().unwrap());
833                }
834                let instruction_data = vec![];
835                let num_transaction_accounts =
836                    transaction_accounts.len().min(MAX_ACCOUNTS_PER_TRANSACTION);
837
838                with_mock_invoke_context!(
839                    invoke_context,
840                    transaction_context,
841                    transaction_accounts
842                );
843                if instruction_accounts.len() > MAX_ACCOUNTS_PER_INSTRUCTION {
844                    // Oversized instructions are rejected before serialization.
845                    let dedup_map = vec![u8::MAX; num_transaction_accounts];
846                    assert_eq!(
847                        invoke_context
848                            .transaction_context
849                            .configure_instruction_at_index(
850                                0,
851                                0,
852                                instruction_accounts,
853                                dedup_map,
854                                Cow::Owned(instruction_data.clone()),
855                                Some(0),
856                            ),
857                        Err(InstructionError::MaxAccountsExceeded),
858                        "{name} test case failed",
859                    );
860                    continue;
861                }
862                invoke_context
863                    .transaction_context
864                    .configure_top_level_instruction_for_tests(
865                        0,
866                        instruction_accounts,
867                        instruction_data.clone(),
868                    )
869                    .unwrap();
870                invoke_context.push().unwrap();
871                let instruction_context = invoke_context
872                    .transaction_context
873                    .get_current_instruction_context()
874                    .unwrap();
875
876                let serialization_result = serialize_parameters(
877                    &instruction_context,
878                    virtual_address_space_adjustments,
879                    false, // account_data_direct_mapping
880                    direct_account_pointers_in_program_input,
881                );
882                assert_eq!(
883                    serialization_result.as_ref().err(),
884                    expected_err.as_ref(),
885                    "{name} test case failed",
886                );
887                if expected_err.is_some() {
888                    continue;
889                }
890
891                let SerializationResult {
892                    parameter_bytes: mut serialized,
893                    regions,
894                    account_pointers_offset,
895                    ..
896                } = serialization_result.unwrap();
897                let mut serialized_regions = unsafe {
898                    // SAFETY: test code, serialize_parameters should be constructing valid regions.
899                    concat_regions(&regions)
900                };
901                let (de_program_id, de_accounts, de_instruction_data) = unsafe {
902                    deserialize(
903                        if !virtual_address_space_adjustments {
904                            serialized.as_slice_mut()
905                        } else {
906                            serialized_regions.as_slice_mut()
907                        }
908                        .first_mut()
909                        .unwrap() as *mut u8,
910                    )
911                };
912                assert_eq!(de_program_id, &program_id);
913                assert_eq!(de_instruction_data, &instruction_data);
914                assert_eq!(
915                    direct_account_pointers_in_program_input,
916                    account_pointers_offset.is_some()
917                );
918                for account_info in de_accounts {
919                    let index_in_transaction = invoke_context
920                        .transaction_context
921                        .find_index_of_account(account_info.key)
922                        .unwrap();
923                    let account = invoke_context
924                        .transaction_context
925                        .accounts()
926                        .try_borrow(index_in_transaction)
927                        .unwrap();
928                    assert_eq!(account.lamports(), account_info.lamports());
929                    assert_eq!(account.data(), &account_info.data.borrow()[..]);
930                    assert_eq!(account.owner(), account_info.owner);
931                    assert_eq!(account.executable(), account_info.executable);
932                    #[allow(deprecated)]
933                    {
934                        // Using the sdk entrypoint, the rent-epoch is skipped
935                        assert_eq!(0, account_info._unused);
936                    }
937                }
938            }
939        }
940    }
941
942    #[test_case(false; "direct_account_pointers_in_program_input disabled")]
943    #[test_case(true; "direct_account_pointers_in_program_input enabled")]
944    fn test_serialize_parameters(direct_account_pointers_in_program_input: bool) {
945        for virtual_address_space_adjustments in [false, true] {
946            let program_id = solana_pubkey::new_rand();
947            let transaction_accounts = vec![
948                (
949                    program_id,
950                    AccountSharedData::from(Account {
951                        lamports: 0,
952                        data: vec![],
953                        owner: bpf_loader::id(),
954                        executable: true,
955                        rent_epoch: 0,
956                    }),
957                ),
958                (
959                    solana_pubkey::new_rand(),
960                    AccountSharedData::from(Account {
961                        lamports: 1,
962                        data: vec![1u8, 2, 3, 4, 5],
963                        owner: bpf_loader::id(),
964                        executable: false,
965                        rent_epoch: 100,
966                    }),
967                ),
968                (
969                    solana_pubkey::new_rand(),
970                    AccountSharedData::from(Account {
971                        lamports: 2,
972                        data: vec![11u8, 12, 13, 14, 15, 16, 17, 18, 19],
973                        owner: bpf_loader::id(),
974                        executable: true,
975                        rent_epoch: 200,
976                    }),
977                ),
978                (
979                    solana_pubkey::new_rand(),
980                    AccountSharedData::from(Account {
981                        lamports: 3,
982                        data: vec![],
983                        owner: bpf_loader::id(),
984                        executable: false,
985                        rent_epoch: 3100,
986                    }),
987                ),
988                (
989                    solana_pubkey::new_rand(),
990                    AccountSharedData::from(Account {
991                        lamports: 4,
992                        data: vec![1u8, 2, 3, 4, 5],
993                        owner: bpf_loader::id(),
994                        executable: false,
995                        rent_epoch: 100,
996                    }),
997                ),
998                (
999                    solana_pubkey::new_rand(),
1000                    AccountSharedData::from(Account {
1001                        lamports: 5,
1002                        data: vec![11u8, 12, 13, 14, 15, 16, 17, 18, 19],
1003                        owner: bpf_loader::id(),
1004                        executable: true,
1005                        rent_epoch: 200,
1006                    }),
1007                ),
1008                (
1009                    solana_pubkey::new_rand(),
1010                    AccountSharedData::from(Account {
1011                        lamports: 6,
1012                        data: vec![],
1013                        owner: bpf_loader::id(),
1014                        executable: false,
1015                        rent_epoch: 3100,
1016                    }),
1017                ),
1018                (
1019                    program_id,
1020                    AccountSharedData::from(Account {
1021                        lamports: 0,
1022                        data: vec![],
1023                        owner: bpf_loader_deprecated::id(),
1024                        executable: true,
1025                        rent_epoch: 0,
1026                    }),
1027                ),
1028            ];
1029            let instruction_accounts =
1030                deduplicated_instruction_accounts(&[1, 1, 2, 3, 4, 4, 5, 6], |index| index >= 4);
1031            let instruction_data = vec![1u8, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11];
1032            let original_accounts = transaction_accounts.clone();
1033            with_mock_invoke_context!(invoke_context, transaction_context, transaction_accounts);
1034            invoke_context
1035                .transaction_context
1036                .configure_top_level_instruction_for_tests(
1037                    0,
1038                    instruction_accounts.clone(),
1039                    instruction_data.clone(),
1040                )
1041                .unwrap();
1042            invoke_context.push().unwrap();
1043            let instruction_context = invoke_context
1044                .transaction_context
1045                .get_current_instruction_context()
1046                .unwrap();
1047
1048            // check serialize_parameters_for_abiv1
1049            let SerializationResult {
1050                parameter_bytes: mut serialized,
1051                regions,
1052                accounts_metadata,
1053                account_pointers_offset,
1054                ..
1055            } = serialize_parameters(
1056                &instruction_context,
1057                virtual_address_space_adjustments,
1058                false, // account_data_direct_mapping
1059                direct_account_pointers_in_program_input,
1060            )
1061            .unwrap();
1062
1063            let mut serialized_regions = unsafe {
1064                // SAFETY: test code, serialize_parameters should be constructing valid regions.
1065                concat_regions(&regions)
1066            };
1067            if !virtual_address_space_adjustments {
1068                assert_eq!(serialized.as_slice(), serialized_regions.as_slice());
1069            }
1070            let (de_program_id, de_accounts, de_instruction_data) = unsafe {
1071                deserialize(
1072                    if !virtual_address_space_adjustments {
1073                        serialized.as_slice_mut()
1074                    } else {
1075                        serialized_regions.as_slice_mut()
1076                    }
1077                    .first_mut()
1078                    .unwrap() as *mut u8,
1079                )
1080            };
1081
1082            assert_eq!(&program_id, de_program_id);
1083            assert_eq!(instruction_data, de_instruction_data);
1084            assert_eq!(
1085                (de_instruction_data.first().unwrap() as *const u8).align_offset(BPF_ALIGN_OF_U128),
1086                0
1087            );
1088            assert_eq!(
1089                direct_account_pointers_in_program_input,
1090                account_pointers_offset.is_some()
1091            );
1092            for account_info in de_accounts {
1093                let index_in_transaction = invoke_context
1094                    .transaction_context
1095                    .find_index_of_account(account_info.key)
1096                    .unwrap();
1097                let account = invoke_context
1098                    .transaction_context
1099                    .accounts()
1100                    .try_borrow(index_in_transaction)
1101                    .unwrap();
1102                assert_eq!(account.lamports(), account_info.lamports());
1103                assert_eq!(account.data(), &account_info.data.borrow()[..]);
1104                assert_eq!(account.owner(), account_info.owner);
1105                assert_eq!(account.executable(), account_info.executable);
1106                #[allow(deprecated)]
1107                {
1108                    // Using the sdk entrypoint, the rent-epoch is skipped
1109                    assert_eq!(0, account_info._unused);
1110                }
1111
1112                assert_eq!(
1113                    (*account_info.lamports.borrow() as *const u64).align_offset(BPF_ALIGN_OF_U128),
1114                    0
1115                );
1116                assert_eq!(
1117                    account_info
1118                        .data
1119                        .borrow()
1120                        .as_ptr()
1121                        .align_offset(BPF_ALIGN_OF_U128),
1122                    0
1123                );
1124            }
1125
1126            deserialize_parameters(
1127                &instruction_context,
1128                virtual_address_space_adjustments,
1129                false, // account_data_direct_mapping
1130                serialized.as_slice(),
1131                &accounts_metadata,
1132            )
1133            .unwrap();
1134            for (index_in_transaction, (_key, original_account)) in
1135                original_accounts.iter().enumerate()
1136            {
1137                let account = invoke_context
1138                    .transaction_context
1139                    .accounts()
1140                    .try_borrow(index_in_transaction as IndexOfAccount)
1141                    .unwrap();
1142                assert_eq!(&*account, original_account);
1143            }
1144
1145            invoke_context.pop().unwrap();
1146            // check serialize_parameters_for_abiv0
1147            invoke_context
1148                .transaction_context
1149                .configure_top_level_instruction_for_tests(
1150                    7,
1151                    instruction_accounts,
1152                    instruction_data.clone(),
1153                )
1154                .unwrap();
1155            invoke_context.push().unwrap();
1156            let instruction_context = invoke_context
1157                .transaction_context
1158                .get_current_instruction_context()
1159                .unwrap();
1160
1161            let SerializationResult {
1162                parameter_bytes: mut serialized,
1163                regions,
1164                accounts_metadata: account_lengths,
1165                account_pointers_offset,
1166                ..
1167            } = serialize_parameters(
1168                &instruction_context,
1169                virtual_address_space_adjustments,
1170                false, // account_data_direct_mapping
1171                direct_account_pointers_in_program_input,
1172            )
1173            .unwrap();
1174            let mut serialized_regions = unsafe {
1175                // SAFETY: test code, serialize_parameters should be constructing valid regions.
1176                concat_regions(&regions)
1177            };
1178
1179            let (de_program_id, de_accounts, de_instruction_data) = unsafe {
1180                deserialize_for_abiv0(
1181                    if !virtual_address_space_adjustments {
1182                        serialized.as_slice_mut()
1183                    } else {
1184                        serialized_regions.as_slice_mut()
1185                    }
1186                    .first_mut()
1187                    .unwrap() as *mut u8,
1188                )
1189            };
1190            assert_eq!(&program_id, de_program_id);
1191            assert_eq!(instruction_data, de_instruction_data);
1192            // abiv0 does not support direct account pointers in program input.
1193            assert!(account_pointers_offset.is_none());
1194            for account_info in de_accounts {
1195                let index_in_transaction = invoke_context
1196                    .transaction_context
1197                    .find_index_of_account(account_info.key)
1198                    .unwrap();
1199                let account = invoke_context
1200                    .transaction_context
1201                    .accounts()
1202                    .try_borrow(index_in_transaction)
1203                    .unwrap();
1204                assert_eq!(account.lamports(), account_info.lamports());
1205                assert_eq!(account.data(), &account_info.data.borrow()[..]);
1206                assert_eq!(account.owner(), account_info.owner);
1207                assert_eq!(account.executable(), account_info.executable);
1208                #[allow(deprecated)]
1209                {
1210                    assert_eq!(u64::MAX, account_info._unused);
1211                }
1212            }
1213
1214            deserialize_parameters(
1215                &instruction_context,
1216                virtual_address_space_adjustments,
1217                false, // account_data_direct_mapping
1218                serialized.as_slice(),
1219                &account_lengths,
1220            )
1221            .unwrap();
1222            for (index_in_transaction, (_key, original_account)) in
1223                original_accounts.iter().enumerate()
1224            {
1225                let account = invoke_context
1226                    .transaction_context
1227                    .accounts()
1228                    .try_borrow(index_in_transaction as IndexOfAccount)
1229                    .unwrap();
1230                assert_eq!(&*account, original_account);
1231            }
1232        }
1233    }
1234
1235    #[test_case(false; "direct_account_pointers_in_program_input disabled")]
1236    #[test_case(true; "direct_account_pointers_in_program_input enabled")]
1237    fn test_serialize_parameters_mask_out_rent_epoch_in_vm_serialization(
1238        direct_account_pointers_in_program_input: bool,
1239    ) {
1240        let transaction_accounts = vec![
1241            (
1242                solana_pubkey::new_rand(),
1243                AccountSharedData::from(Account {
1244                    lamports: 0,
1245                    data: vec![],
1246                    owner: bpf_loader::id(),
1247                    executable: true,
1248                    rent_epoch: 0,
1249                }),
1250            ),
1251            (
1252                solana_pubkey::new_rand(),
1253                AccountSharedData::from(Account {
1254                    lamports: 1,
1255                    data: vec![1u8, 2, 3, 4, 5],
1256                    owner: bpf_loader::id(),
1257                    executable: false,
1258                    rent_epoch: 100,
1259                }),
1260            ),
1261            (
1262                solana_pubkey::new_rand(),
1263                AccountSharedData::from(Account {
1264                    lamports: 2,
1265                    data: vec![11u8, 12, 13, 14, 15, 16, 17, 18, 19],
1266                    owner: bpf_loader::id(),
1267                    executable: true,
1268                    rent_epoch: 200,
1269                }),
1270            ),
1271            (
1272                solana_pubkey::new_rand(),
1273                AccountSharedData::from(Account {
1274                    lamports: 3,
1275                    data: vec![],
1276                    owner: bpf_loader::id(),
1277                    executable: false,
1278                    rent_epoch: 300,
1279                }),
1280            ),
1281            (
1282                solana_pubkey::new_rand(),
1283                AccountSharedData::from(Account {
1284                    lamports: 4,
1285                    data: vec![1u8, 2, 3, 4, 5],
1286                    owner: bpf_loader::id(),
1287                    executable: false,
1288                    rent_epoch: 100,
1289                }),
1290            ),
1291            (
1292                solana_pubkey::new_rand(),
1293                AccountSharedData::from(Account {
1294                    lamports: 5,
1295                    data: vec![11u8, 12, 13, 14, 15, 16, 17, 18, 19],
1296                    owner: bpf_loader::id(),
1297                    executable: true,
1298                    rent_epoch: 200,
1299                }),
1300            ),
1301            (
1302                solana_pubkey::new_rand(),
1303                AccountSharedData::from(Account {
1304                    lamports: 6,
1305                    data: vec![],
1306                    owner: bpf_loader::id(),
1307                    executable: false,
1308                    rent_epoch: 3100,
1309                }),
1310            ),
1311            (
1312                solana_pubkey::new_rand(),
1313                AccountSharedData::from(Account {
1314                    lamports: 0,
1315                    data: vec![],
1316                    owner: bpf_loader_deprecated::id(),
1317                    executable: true,
1318                    rent_epoch: 0,
1319                }),
1320            ),
1321        ];
1322        let instruction_accounts =
1323            deduplicated_instruction_accounts(&[1, 1, 2, 3, 4, 4, 5, 6], |index| index >= 4);
1324        with_mock_invoke_context!(invoke_context, transaction_context, transaction_accounts);
1325        invoke_context
1326            .transaction_context
1327            .configure_top_level_instruction_for_tests(0, instruction_accounts.clone(), vec![])
1328            .unwrap();
1329        invoke_context.push().unwrap();
1330        let instruction_context = invoke_context
1331            .transaction_context
1332            .get_current_instruction_context()
1333            .unwrap();
1334
1335        // check serialize_parameters_for_abiv1
1336        let SerializationResult {
1337            parameter_bytes: _serialized,
1338            regions,
1339            ..
1340        } = serialize_parameters(
1341            &instruction_context,
1342            true,
1343            false, // account_data_direct_mapping
1344            direct_account_pointers_in_program_input,
1345        )
1346        .unwrap();
1347
1348        let mut serialized_regions = unsafe {
1349            // SAFETY: test code, serialize_parameters should be constructing valid regions.
1350            concat_regions(&regions)
1351        };
1352        let (_de_program_id, de_accounts, _de_instruction_data) = unsafe {
1353            deserialize(serialized_regions.as_slice_mut().first_mut().unwrap() as *mut u8)
1354        };
1355
1356        for account_info in de_accounts {
1357            // Using program-entrypoint, the rent-epoch will always be 0
1358            #[allow(deprecated)]
1359            {
1360                assert_eq!(0, account_info._unused);
1361            }
1362        }
1363
1364        // check serialize_parameters_for_abiv0
1365        invoke_context
1366            .transaction_context
1367            .configure_top_level_instruction_for_tests(7, instruction_accounts, vec![])
1368            .unwrap();
1369        invoke_context.push().unwrap();
1370        let instruction_context = invoke_context
1371            .transaction_context
1372            .get_current_instruction_context()
1373            .unwrap();
1374
1375        let SerializationResult {
1376            parameter_bytes: _serialized,
1377            regions,
1378            ..
1379        } = serialize_parameters(
1380            &instruction_context,
1381            true,
1382            false, // account_data_direct_mapping
1383            direct_account_pointers_in_program_input,
1384        )
1385        .unwrap();
1386        let mut serialized_regions = unsafe {
1387            // SAFETY: test code, serialize_parameters should be constructing valid regions.
1388            concat_regions(&regions)
1389        };
1390
1391        let (_de_program_id, de_accounts, _de_instruction_data) = unsafe {
1392            deserialize_for_abiv0(serialized_regions.as_slice_mut().first_mut().unwrap() as *mut u8)
1393        };
1394        for account_info in de_accounts {
1395            #[allow(deprecated)]
1396            {
1397                assert_eq!(account_info._unused, u64::MAX);
1398            }
1399        }
1400    }
1401
1402    // the old bpf_loader in-program deserializer bpf_loader::id()
1403    #[deny(unsafe_op_in_unsafe_fn)]
1404    unsafe fn deserialize_for_abiv0<'a>(
1405        input: *mut u8,
1406    ) -> (&'a Pubkey, Vec<AccountInfo<'a>>, &'a [u8]) {
1407        // this boring boilerplate struct is needed until inline const...
1408        struct Ptr<T>(std::marker::PhantomData<T>);
1409        impl<T> Ptr<T> {
1410            const COULD_BE_UNALIGNED: bool = std::mem::align_of::<T>() > 1;
1411
1412            #[inline(always)]
1413            fn read_possibly_unaligned(input: *mut u8, offset: usize) -> T {
1414                unsafe {
1415                    let src = input.add(offset) as *const T;
1416                    if Self::COULD_BE_UNALIGNED {
1417                        src.read_unaligned()
1418                    } else {
1419                        src.read()
1420                    }
1421                }
1422            }
1423
1424            // rustc inserts debug_assert! for misaligned pointer dereferences when
1425            // deserializing, starting from [1]. so, use std::mem::transmute as the last resort
1426            // while preventing clippy from complaining to suggest not to use it.
1427            // [1]: https://github.com/rust-lang/rust/commit/22a7a19f9333bc1fcba97ce444a3515cb5fb33e6
1428            // as for the ub nature of the misaligned pointer dereference, this is
1429            // acceptable in this code, given that this is cfg(test) and it's cared only with
1430            // x86-64 and the target only incurs some performance penalty, not like segfaults
1431            // in other targets.
1432            #[inline(always)]
1433            fn ref_possibly_unaligned<'a>(input: *mut u8, offset: usize) -> &'a T {
1434                #[allow(clippy::transmute_ptr_to_ref)]
1435                unsafe {
1436                    transmute(input.add(offset) as *const T)
1437                }
1438            }
1439
1440            // See ref_possibly_unaligned's comment
1441            #[inline(always)]
1442            fn mut_possibly_unaligned<'a>(input: *mut u8, offset: usize) -> &'a mut T {
1443                #[allow(clippy::transmute_ptr_to_ref)]
1444                unsafe {
1445                    transmute(input.add(offset) as *mut T)
1446                }
1447            }
1448        }
1449
1450        let mut offset: usize = 0;
1451
1452        // number of accounts present
1453
1454        let num_accounts = Ptr::<u64>::read_possibly_unaligned(input, offset) as usize;
1455        offset += size_of::<u64>();
1456
1457        // account Infos
1458
1459        let mut accounts = Vec::with_capacity(num_accounts);
1460        for _ in 0..num_accounts {
1461            let dup_info = Ptr::<u8>::read_possibly_unaligned(input, offset);
1462            offset += size_of::<u8>();
1463            if dup_info == NON_DUP_MARKER {
1464                let is_signer = Ptr::<u8>::read_possibly_unaligned(input, offset) != 0;
1465                offset += size_of::<u8>();
1466
1467                let is_writable = Ptr::<u8>::read_possibly_unaligned(input, offset) != 0;
1468                offset += size_of::<u8>();
1469
1470                let key = Ptr::<Pubkey>::ref_possibly_unaligned(input, offset);
1471                offset += size_of::<Pubkey>();
1472
1473                let lamports = Rc::new(RefCell::new(Ptr::mut_possibly_unaligned(input, offset)));
1474                offset += size_of::<u64>();
1475
1476                let data_len = Ptr::<u64>::read_possibly_unaligned(input, offset) as usize;
1477                offset += size_of::<u64>();
1478
1479                let data = Rc::new(RefCell::new(unsafe {
1480                    from_raw_parts_mut(input.add(offset), data_len)
1481                }));
1482                offset += data_len;
1483
1484                let owner: &Pubkey = Ptr::<Pubkey>::ref_possibly_unaligned(input, offset);
1485                offset += size_of::<Pubkey>();
1486
1487                let executable = Ptr::<u8>::read_possibly_unaligned(input, offset) != 0;
1488                offset += size_of::<u8>();
1489
1490                let unused = Ptr::<u64>::read_possibly_unaligned(input, offset);
1491                offset += size_of::<u64>();
1492
1493                #[allow(deprecated)]
1494                accounts.push(AccountInfo {
1495                    key,
1496                    is_signer,
1497                    is_writable,
1498                    lamports,
1499                    data,
1500                    owner,
1501                    executable,
1502                    _unused: unused,
1503                });
1504            } else {
1505                // duplicate account, clone the original
1506                accounts.push(accounts.get(dup_info as usize).unwrap().clone());
1507            }
1508        }
1509
1510        // instruction data
1511
1512        let instruction_data_len = Ptr::<u64>::read_possibly_unaligned(input, offset) as usize;
1513        offset += size_of::<u64>();
1514
1515        let instruction_data = unsafe { from_raw_parts(input.add(offset), instruction_data_len) };
1516        offset += instruction_data_len;
1517
1518        // program Id
1519
1520        let program_id = Ptr::<Pubkey>::ref_possibly_unaligned(input, offset);
1521
1522        (program_id, accounts, instruction_data)
1523    }
1524
1525    /// # Safety
1526    ///
1527    /// All memory regions must be pointing to valid to dereference host buffers.
1528    unsafe fn concat_regions(regions: &[MemoryRegion]) -> AlignedMemory<HOST_ALIGN> {
1529        let last_region = regions.last().unwrap();
1530        let last_region_vm_addr = last_region.vm_addr_range().start;
1531        let mut mem = AlignedMemory::zero_filled(
1532            (last_region_vm_addr - MM_INPUT_START + last_region.len() as u64) as usize,
1533        );
1534        for region in regions {
1535            let vm_start = region.vm_addr_range().start;
1536            let buffer = region.host_buffer().ptr();
1537            mem.as_slice_mut()[(vm_start - MM_INPUT_START) as usize..][..buffer.len()]
1538                .copy_from_slice(unsafe {
1539                    // SAFETY:
1540                    // Contract from `<*const [u8]>::as_ref_unchecked`: ensure that the pointer is
1541                    // convertible to reference.
1542                    // Evidence: The contract delegated to the callers.
1543                    buffer.as_ref_unchecked()
1544                })
1545        }
1546        mem
1547    }
1548
1549    #[test]
1550    fn test_access_violation_handler() {
1551        let program_id = Pubkey::new_unique();
1552        let shared_account = AccountSharedData::new(0, 4, &program_id);
1553        let mut transaction_context = TransactionContext::new(
1554            vec![
1555                (
1556                    Pubkey::new_unique(),
1557                    AccountSharedData::new(0, 4, &program_id),
1558                ), // readonly
1559                (Pubkey::new_unique(), shared_account.clone()), // writable shared
1560                (
1561                    Pubkey::new_unique(),
1562                    AccountSharedData::new(0, 0, &program_id),
1563                ), // another writable account
1564                (
1565                    Pubkey::new_unique(),
1566                    AccountSharedData::new(
1567                        0,
1568                        MAX_PERMITTED_DATA_LENGTH as usize - 0x100,
1569                        &program_id,
1570                    ),
1571                ), // almost max sized writable account
1572                (
1573                    Pubkey::new_unique(),
1574                    AccountSharedData::new(0, 0, &program_id),
1575                ), // writable dummy to burn accounts_resize_delta
1576                (
1577                    Pubkey::new_unique(),
1578                    AccountSharedData::new(0, 0x3000, &program_id),
1579                ), // writable dummy to burn accounts_resize_delta
1580                (program_id, AccountSharedData::default()),     // program
1581            ],
1582            Rent::default(),
1583            /* max_instruction_stack_depth */ 1,
1584            /* max_instruction_trace_length */ 1,
1585            /* number_of_top_level_instructions */ 1,
1586        );
1587        let transaction_accounts_indexes = [0, 1, 2, 3, 4, 5];
1588        let instruction_accounts =
1589            deduplicated_instruction_accounts(&transaction_accounts_indexes, |index| index > 0);
1590        transaction_context
1591            .configure_top_level_instruction_for_tests(6, instruction_accounts, vec![])
1592            .unwrap();
1593        transaction_context.push(true).unwrap();
1594        let instruction_context = transaction_context
1595            .get_current_instruction_context()
1596            .unwrap();
1597        let account_start_offsets = [
1598            MM_INPUT_START,
1599            MM_INPUT_START + 4 + MAX_PERMITTED_DATA_INCREASE as u64,
1600            MM_INPUT_START + (4 + MAX_PERMITTED_DATA_INCREASE as u64) * 2,
1601            MM_INPUT_START + (4 + MAX_PERMITTED_DATA_INCREASE as u64) * 3,
1602        ];
1603        let regions = account_start_offsets
1604            .iter()
1605            .enumerate()
1606            .map(|(index_in_instruction, account_start_offset)| {
1607                create_memory_region_of_account(
1608                    &mut instruction_context
1609                        .try_borrow_instruction_account(index_in_instruction as IndexOfAccount)
1610                        .unwrap(),
1611                    *account_start_offset,
1612                )
1613                .unwrap()
1614            })
1615            .collect::<Vec<_>>();
1616        let config = Config {
1617            aligned_memory_mapping: false,
1618            ..Config::default()
1619        };
1620        let mut memory_mapping = unsafe {
1621            MemoryMapping::new_with_access_violation_handler(
1622                regions,
1623                &config,
1624                SBPFVersion::V3,
1625                transaction_context.access_violation_handler(true, true),
1626            )
1627            .unwrap()
1628        };
1629
1630        // Reading readonly account is allowed
1631        memory_mapping
1632            .load::<u32>(account_start_offsets[0])
1633            .unwrap();
1634
1635        // Reading writable account is allowed
1636        memory_mapping
1637            .load::<u32>(account_start_offsets[1])
1638            .unwrap();
1639
1640        // Reading beyond readonly accounts current size is denied
1641        memory_mapping
1642            .load::<u32>(account_start_offsets[0] + 4)
1643            .unwrap_err();
1644
1645        // Writing to readonly account is denied
1646        memory_mapping
1647            .store::<u32>(0, account_start_offsets[0])
1648            .unwrap_err();
1649
1650        // Writing to shared writable account makes it unique (CoW logic.)
1651        // It has been previously been made non-unique at the beginning of
1652        // the test through a clone.
1653        let _shared_account_ref = shared_account;
1654        assert!(
1655            transaction_context
1656                .accounts()
1657                .try_borrow_mut(1)
1658                .unwrap()
1659                .is_shared()
1660        );
1661        memory_mapping
1662            .store::<u32>(0, account_start_offsets[1])
1663            .unwrap();
1664        assert!(
1665            !transaction_context
1666                .accounts()
1667                .try_borrow_mut(1)
1668                .unwrap()
1669                .is_shared()
1670        );
1671        assert_eq!(
1672            transaction_context
1673                .accounts()
1674                .try_borrow(1)
1675                .unwrap()
1676                .data()
1677                .len(),
1678            4,
1679        );
1680
1681        // Reading beyond writable accounts current size grows is denied
1682        memory_mapping
1683            .load::<u32>(account_start_offsets[1] + 4)
1684            .unwrap_err();
1685
1686        // Writing beyond writable accounts current size grows it
1687        // to original length plus MAX_PERMITTED_DATA_INCREASE
1688        memory_mapping
1689            .store::<u32>(0, account_start_offsets[1] + 4)
1690            .unwrap();
1691        assert_eq!(
1692            transaction_context
1693                .accounts()
1694                .try_borrow(1)
1695                .unwrap()
1696                .data()
1697                .len(),
1698            4 + MAX_PERMITTED_DATA_INCREASE,
1699        );
1700        assert!(
1701            transaction_context
1702                .accounts()
1703                .try_borrow(1)
1704                .unwrap()
1705                .data()
1706                .len()
1707                < 0x3000
1708        );
1709
1710        // Writing beyond almost max sized writable accounts current size only grows it
1711        // to MAX_PERMITTED_DATA_LENGTH
1712        memory_mapping
1713            .store::<u32>(0, account_start_offsets[3] + MAX_PERMITTED_DATA_LENGTH - 4)
1714            .unwrap();
1715        assert_eq!(
1716            transaction_context
1717                .accounts()
1718                .try_borrow(3)
1719                .unwrap()
1720                .data()
1721                .len(),
1722            MAX_PERMITTED_DATA_LENGTH as usize,
1723        );
1724
1725        // Accessing the rest of the address space reserved for
1726        // the almost max sized writable account is denied
1727        memory_mapping
1728            .load::<u32>(account_start_offsets[3] + MAX_PERMITTED_DATA_LENGTH)
1729            .unwrap_err();
1730        memory_mapping
1731            .store::<u32>(0, account_start_offsets[3] + MAX_PERMITTED_DATA_LENGTH)
1732            .unwrap_err();
1733
1734        // Burn through most of the accounts_resize_delta budget
1735        let remaining_allowed_growth: usize = 0x700;
1736        for index_in_instruction in 4..6 {
1737            let mut borrowed_account = instruction_context
1738                .try_borrow_instruction_account(index_in_instruction)
1739                .unwrap();
1740            borrowed_account
1741                .set_data_from_slice(&vec![0u8; MAX_PERMITTED_DATA_LENGTH as usize])
1742                .unwrap();
1743        }
1744        assert_eq!(
1745            transaction_context.accounts().resize_delta(),
1746            MAX_PERMITTED_ACCOUNTS_DATA_ALLOCATIONS_PER_TRANSACTION
1747                - remaining_allowed_growth as i64,
1748        );
1749
1750        // Writing beyond empty writable accounts current size
1751        // only grows it to fill up MAX_PERMITTED_ACCOUNTS_DATA_ALLOCATIONS_PER_TRANSACTION
1752        memory_mapping
1753            .store::<u32>(0, account_start_offsets[2] + 0x500)
1754            .unwrap();
1755        assert_eq!(
1756            transaction_context
1757                .accounts()
1758                .try_borrow(2)
1759                .unwrap()
1760                .data()
1761                .len(),
1762            remaining_allowed_growth,
1763        );
1764    }
1765
1766    #[test]
1767    fn test_regression_initial_serialized_account_region_does_not_include_resize_affordance() {
1768        let program_id = Pubkey::new_unique();
1769        let transaction_accounts = vec![
1770            (
1771                Pubkey::new_unique(),
1772                AccountSharedData::new(0, 4, &program_id),
1773            ),
1774            (
1775                solana_pubkey::new_rand(),
1776                AccountSharedData::from(Account {
1777                    lamports: 0,
1778                    data: b"agave".into(),
1779                    owner: bpf_loader_upgradeable::id(),
1780                    executable: false,
1781                    rent_epoch: 0,
1782                }),
1783            ),
1784        ];
1785        with_mock_invoke_context!(invoke_context, transaction_context, transaction_accounts);
1786        invoke_context
1787            .transaction_context
1788            .configure_top_level_instruction_for_tests(
1789                0,
1790                vec![InstructionAccount::new(1, false, true)],
1791                vec![],
1792            )
1793            .unwrap();
1794        invoke_context.push().unwrap();
1795        let instruction_context = invoke_context
1796            .transaction_context
1797            .get_current_instruction_context()
1798            .unwrap();
1799        let SerializationResult {
1800            regions,
1801            accounts_metadata,
1802            ..
1803        } = crate::serialization::serialize_parameters(
1804            &instruction_context,
1805            true,  // virtual_address_space_adjustments
1806            false, // account_data_direct_mapping
1807            false, // direct_account_pointers_in_program_input
1808        )
1809        .unwrap();
1810        let config = Config {
1811            aligned_memory_mapping: false,
1812            ..Config::default()
1813        };
1814        let memory_mapping =
1815            unsafe { MemoryMapping::new(regions, &config, SBPFVersion::V3).unwrap() };
1816        let account_metadata = &accounts_metadata[0];
1817        let vm_data_addr = account_metadata.vm_data_addr;
1818        let (_region_index, region) = memory_mapping.find_region(vm_data_addr).unwrap();
1819        assert_eq!(region.len(), 5);
1820    }
1821}