solana_program_runtime/
serialization.rs

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