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