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
22pub fn modify_memory_region_of_account(
24 account: &mut BorrowedInstructionAccount<'_, '_>,
25 region: &mut MemoryRegion,
26) {
27 if account.can_data_be_changed().is_ok() {
28 unsafe {
29 region.redirect(region.host_buffer().mutable());
35 }
36 region.access_violation_handler_payload = Some(account.get_index_in_transaction());
37 } else {
38 region.make_immutable();
39 region.access_violation_handler_payload = None;
40 }
41}
42
43pub fn create_memory_region_of_account(
45 account: &mut BorrowedInstructionAccount<'_, '_>,
46 vaddr: u64,
47) -> Result<MemoryRegion, InstructionError> {
48 let can_data_be_changed = account.can_data_be_changed().is_ok();
49 let mut memory_region = if can_data_be_changed && !account.is_shared() {
50 MemoryRegion::new(&raw mut account.get_data_mut()?[..], vaddr)
51 } else {
52 MemoryRegion::new(&raw const account.get_data()[..], vaddr)
53 };
54 if can_data_be_changed {
55 memory_region.access_violation_handler_payload = Some(account.get_index_in_transaction());
56 }
57 Ok(memory_region)
58}
59
60#[expect(dead_code)]
61enum SerializeAccount<'a, 'ix_data> {
62 Account(IndexOfAccount, BorrowedInstructionAccount<'a, 'ix_data>),
63 Duplicate(IndexOfAccount),
64}
65
66struct Serializer {
67 buffer: AlignedMemory<HOST_ALIGN>,
68 regions: Vec<MemoryRegion>,
69 vaddr: u64,
70 region_start: usize,
71 is_loader_v1: bool,
72 virtual_address_space_adjustments: bool,
73 account_data_direct_mapping: bool,
74}
75
76impl Serializer {
77 fn new(
78 size: usize,
79 start_addr: u64,
80 is_loader_v1: bool,
81 virtual_address_space_adjustments: bool,
82 account_data_direct_mapping: bool,
83 ) -> Serializer {
84 Serializer {
85 buffer: AlignedMemory::with_capacity(size),
86 regions: Vec::new(),
87 region_start: 0,
88 vaddr: start_addr,
89 is_loader_v1,
90 virtual_address_space_adjustments,
91 account_data_direct_mapping,
92 }
93 }
94
95 fn fill_write(&mut self, num: usize, value: u8) -> std::io::Result<()> {
96 self.buffer.fill_write(num, value)
97 }
98
99 fn write<T: Pod>(&mut self, value: T) -> u64 {
100 self.debug_assert_alignment::<T>();
101 let vaddr = self
102 .vaddr
103 .saturating_add(self.buffer.len() as u64)
104 .saturating_sub(self.region_start as u64);
105 unsafe {
114 self.buffer.write_unchecked(value);
115 }
116
117 vaddr
118 }
119
120 fn write_all(&mut self, value: &[u8]) -> u64 {
121 let vaddr = self
122 .vaddr
123 .saturating_add(self.buffer.len() as u64)
124 .saturating_sub(self.region_start as u64);
125 unsafe {
128 self.buffer.write_all_unchecked(value);
129 }
130
131 vaddr
132 }
133
134 fn write_account(
135 &mut self,
136 account: &mut BorrowedInstructionAccount<'_, '_>,
137 ) -> Result<u64, InstructionError> {
138 if !self.virtual_address_space_adjustments {
139 let vm_data_addr = self.vaddr.saturating_add(self.buffer.len() as u64);
140 self.write_all(account.get_data());
141 if !self.is_loader_v1 {
142 let align_offset =
143 (account.get_data().len() as *const u8).align_offset(BPF_ALIGN_OF_U128);
144 self.fill_write(MAX_PERMITTED_DATA_INCREASE + align_offset, 0)
145 .map_err(|_| InstructionError::InvalidArgument)?;
146 }
147 Ok(vm_data_addr)
148 } else {
149 self.push_region();
150 let vm_data_addr = self.vaddr;
151 if !self.account_data_direct_mapping {
152 self.write_all(account.get_data());
153 if !self.is_loader_v1 {
154 self.fill_write(MAX_PERMITTED_DATA_INCREASE, 0)
155 .map_err(|_| InstructionError::InvalidArgument)?;
156 }
157 }
158 let address_space_reserved_for_account = if !self.is_loader_v1 {
159 account
160 .get_data()
161 .len()
162 .saturating_add(MAX_PERMITTED_DATA_INCREASE)
163 } else {
164 account.get_data().len()
165 };
166 if address_space_reserved_for_account > 0 {
167 if !self.account_data_direct_mapping {
168 self.push_region();
169 let region = self.regions.last_mut().unwrap();
170 modify_memory_region_of_account(account, region);
171 } else {
172 let new_region = create_memory_region_of_account(account, self.vaddr)?;
173 self.vaddr += address_space_reserved_for_account as u64;
174 self.regions.push(new_region);
175 }
176 }
177 if !self.is_loader_v1 {
178 let align_offset =
179 (account.get_data().len() as *const u8).align_offset(BPF_ALIGN_OF_U128);
180 if !self.account_data_direct_mapping {
181 self.fill_write(align_offset, 0)
182 .map_err(|_| InstructionError::InvalidArgument)?;
183 } else {
184 self.fill_write(BPF_ALIGN_OF_U128, 0)
190 .map_err(|_| InstructionError::InvalidArgument)?;
191 self.region_start += BPF_ALIGN_OF_U128.saturating_sub(align_offset);
192 }
193 }
194 Ok(vm_data_addr)
195 }
196 }
197
198 fn push_region(&mut self) {
199 let range = self.region_start..self.buffer.len();
200 let region_slice = self.buffer.as_slice_mut().get_mut(range.clone()).unwrap();
201 self.regions
202 .push(MemoryRegion::new(&raw mut region_slice[..], self.vaddr));
203 self.region_start = range.end;
204 self.vaddr += range.len() as u64;
205 }
206
207 fn finish(mut self) -> (AlignedMemory<HOST_ALIGN>, Vec<MemoryRegion>) {
208 self.push_region();
209 debug_assert_eq!(self.region_start, self.buffer.len());
210 (self.buffer, self.regions)
211 }
212
213 fn debug_assert_alignment<T>(&self) {
214 debug_assert!(
215 self.is_loader_v1
216 || self
217 .buffer
218 .as_slice()
219 .as_ptr_range()
220 .end
221 .align_offset(mem::align_of::<T>())
222 == 0
223 );
224 }
225}
226
227pub fn serialize_parameters(
228 instruction_context: &InstructionContext,
229 virtual_address_space_adjustments: bool,
230 account_data_direct_mapping: bool,
231 direct_account_pointers_in_program_input: bool,
232) -> Result<
233 (
234 AlignedMemory<HOST_ALIGN>,
235 Vec<MemoryRegion>,
236 Vec<SerializedAccountMetadata>,
237 usize,
238 ),
239 InstructionError,
240> {
241 let num_ix_accounts = instruction_context.get_number_of_instruction_accounts();
242 if num_ix_accounts > MAX_ACCOUNTS_PER_INSTRUCTION as IndexOfAccount {
243 return Err(InstructionError::MaxAccountsExceeded);
244 }
245
246 let program_id = *instruction_context.get_program_key()?;
247 let is_loader_deprecated =
248 instruction_context.get_program_owner()? == bpf_loader_deprecated::id();
249
250 let accounts = (0..instruction_context.get_number_of_instruction_accounts())
251 .map(|instruction_account_index| {
252 if let Some(index) = instruction_context
253 .is_instruction_account_duplicate(instruction_account_index)
254 .unwrap()
255 {
256 SerializeAccount::Duplicate(index)
257 } else {
258 let account = instruction_context
259 .try_borrow_instruction_account(instruction_account_index)
260 .unwrap();
261 SerializeAccount::Account(instruction_account_index, account)
262 }
263 })
264 .collect::<Vec<_>>();
269
270 if is_loader_deprecated {
271 serialize_parameters_for_abiv0(
273 accounts,
274 instruction_context.get_instruction_data(),
275 &program_id,
276 virtual_address_space_adjustments,
277 account_data_direct_mapping,
278 )
279 } else {
280 serialize_parameters_for_abiv1(
282 accounts,
283 instruction_context.get_instruction_data(),
284 &program_id,
285 virtual_address_space_adjustments,
286 account_data_direct_mapping,
287 direct_account_pointers_in_program_input,
289 )
290 }
291}
292
293pub fn deserialize_parameters(
294 instruction_context: &InstructionContext,
295 virtual_address_space_adjustments: bool,
296 account_data_direct_mapping: bool,
297 buffer: &[u8],
298 accounts_metadata: &[SerializedAccountMetadata],
299) -> Result<(), InstructionError> {
300 let is_loader_deprecated =
301 instruction_context.get_program_owner()? == bpf_loader_deprecated::id();
302 let account_lengths = accounts_metadata.iter().map(|a| a.original_data_len);
303 if is_loader_deprecated {
304 deserialize_parameters_for_abiv0(
306 instruction_context,
307 virtual_address_space_adjustments,
308 account_data_direct_mapping,
309 buffer,
310 account_lengths,
311 )
312 } else {
313 deserialize_parameters_for_abiv1(
315 instruction_context,
316 virtual_address_space_adjustments,
317 account_data_direct_mapping,
318 buffer,
319 account_lengths,
320 )
321 }
322}
323
324fn serialize_parameters_for_abiv0(
325 accounts: Vec<SerializeAccount>,
326 instruction_data: &[u8],
327 program_id: &Pubkey,
328 virtual_address_space_adjustments: bool,
329 account_data_direct_mapping: bool,
330) -> Result<
331 (
332 AlignedMemory<HOST_ALIGN>,
333 Vec<MemoryRegion>,
334 Vec<SerializedAccountMetadata>,
335 usize,
336 ),
337 InstructionError,
338> {
339 let mut size = size_of::<u64>();
341 for account in &accounts {
342 size += 1; match account {
344 SerializeAccount::Duplicate(_) => {}
345 SerializeAccount::Account(_, account) => {
346 size += size_of::<u8>() + size_of::<u8>() + size_of::<Pubkey>() + size_of::<u64>() + size_of::<u64>() + size_of::<Pubkey>() + size_of::<u8>() + size_of::<u64>(); if !(virtual_address_space_adjustments && account_data_direct_mapping) {
355 size += account.get_data().len();
356 }
357 }
358 }
359 }
360 size += size_of::<u64>() + instruction_data.len() + size_of::<Pubkey>(); let mut s = Serializer::new(
365 size,
366 MM_INPUT_START,
367 true,
368 virtual_address_space_adjustments,
369 account_data_direct_mapping,
370 );
371
372 let mut accounts_metadata: Vec<SerializedAccountMetadata> = Vec::with_capacity(accounts.len());
373 s.write::<u64>((accounts.len() as u64).to_le());
374 for account in accounts {
375 match account {
376 SerializeAccount::Duplicate(position) => {
377 accounts_metadata.push(accounts_metadata.get(position as usize).unwrap().clone());
378 s.write(position as u8);
379 }
380 SerializeAccount::Account(_, mut account) => {
381 let vm_addr = s.write::<u8>(NON_DUP_MARKER);
382 s.write::<u8>(account.is_signer() as u8);
383 s.write::<u8>(account.is_writable() as u8);
384 let vm_key_addr = s.write_all(account.get_key().as_ref());
385 let vm_lamports_addr = s.write::<u64>(account.get_lamports().to_le());
386 s.write::<u64>((account.get_data().len() as u64).to_le());
387 let vm_data_addr = s.write_account(&mut account)?;
388 let vm_owner_addr = s.write_all(account.get_owner().as_ref());
389 #[expect(deprecated)]
390 s.write::<u8>(account.is_executable() as u8);
391 let rent_epoch = u64::MAX;
392 s.write::<u64>(rent_epoch.to_le());
393 accounts_metadata.push(SerializedAccountMetadata {
394 vm_addr,
395 original_data_len: account.get_data().len(),
396 vm_key_addr,
397 vm_lamports_addr,
398 vm_owner_addr,
399 vm_data_addr,
400 });
401 }
402 };
403 }
404 s.write::<u64>((instruction_data.len() as u64).to_le());
405 let instruction_data_offset = s.write_all(instruction_data);
406 s.write_all(program_id.as_ref());
407
408 let (mem, regions) = s.finish();
409 Ok((
410 mem,
411 regions,
412 accounts_metadata,
413 instruction_data_offset as usize,
414 ))
415}
416
417fn deserialize_parameters_for_abiv0<I: IntoIterator<Item = usize>>(
418 instruction_context: &InstructionContext,
419 virtual_address_space_adjustments: bool,
420 account_data_direct_mapping: bool,
421 buffer: &[u8],
422 account_lengths: I,
423) -> Result<(), InstructionError> {
424 let mut start = size_of::<u64>(); for (instruction_account_index, pre_len) in
426 (0..instruction_context.get_number_of_instruction_accounts()).zip(account_lengths)
427 {
428 let duplicate =
429 instruction_context.is_instruction_account_duplicate(instruction_account_index)?;
430 start += 1; if duplicate.is_none() {
432 let mut borrowed_account =
433 instruction_context.try_borrow_instruction_account(instruction_account_index)?;
434 start += size_of::<u8>(); start += size_of::<u8>(); start += size_of::<Pubkey>(); let lamports = buffer
438 .get(start..start.saturating_add(8))
439 .map(<[u8; 8]>::try_from)
440 .and_then(Result::ok)
441 .map(u64::from_le_bytes)
442 .ok_or(InstructionError::InvalidArgument)?;
443 if borrowed_account.get_lamports() != lamports {
444 borrowed_account.set_lamports(lamports)?;
445 }
446 start += size_of::<u64>() + size_of::<u64>(); if !virtual_address_space_adjustments {
449 let data = buffer
450 .get(start..start + pre_len)
451 .ok_or(InstructionError::InvalidArgument)?;
452 match borrowed_account.can_data_be_resized(pre_len) {
454 Ok(()) => borrowed_account.set_data_from_slice(data)?,
455 Err(err) if borrowed_account.get_data() != data => return Err(err),
456 _ => {}
457 }
458 } else if !account_data_direct_mapping && borrowed_account.can_data_be_changed().is_ok()
459 {
460 let data = buffer
461 .get(start..start + pre_len)
462 .ok_or(InstructionError::InvalidArgument)?;
463 borrowed_account.set_data_from_slice(data)?;
464 } else if borrowed_account.get_data().len() != pre_len {
465 borrowed_account.set_data_length(pre_len)?;
466 }
467 if !(virtual_address_space_adjustments && account_data_direct_mapping) {
468 start += pre_len; }
470 start += size_of::<Pubkey>() + size_of::<u8>() + size_of::<u64>(); }
474 }
475 Ok(())
476}
477
478fn serialize_parameters_for_abiv1(
479 accounts: Vec<SerializeAccount>,
480 instruction_data: &[u8],
481 program_id: &Pubkey,
482 virtual_address_space_adjustments: bool,
483 account_data_direct_mapping: bool,
484 direct_account_pointers_program_input: bool,
485) -> Result<
486 (
487 AlignedMemory<HOST_ALIGN>,
488 Vec<MemoryRegion>,
489 Vec<SerializedAccountMetadata>,
490 usize,
491 ),
492 InstructionError,
493> {
494 let mut accounts_metadata = Vec::with_capacity(accounts.len());
495 let mut size = size_of::<u64>();
497 for account in &accounts {
498 size += 1; match account {
500 SerializeAccount::Duplicate(_) => size += 7, SerializeAccount::Account(_, account) => {
502 let data_len = account.get_data().len();
503 size += size_of::<u8>() + size_of::<u8>() + size_of::<u8>() + size_of::<u32>() + size_of::<Pubkey>() + size_of::<Pubkey>() + size_of::<u64>() + size_of::<u64>() + size_of::<u64>(); if !(virtual_address_space_adjustments && account_data_direct_mapping) {
513 size += data_len
514 + MAX_PERMITTED_DATA_INCREASE
515 + (data_len as *const u8).align_offset(BPF_ALIGN_OF_U128);
516 } else {
517 size += BPF_ALIGN_OF_U128;
518 }
519 }
520 }
521 }
522 size += size_of::<u64>() + instruction_data.len()
524 + size_of::<Pubkey>(); let account_pointers_offset = if direct_account_pointers_program_input {
528 let offset = (size as *const u8).align_offset(BPF_ALIGN_OF_U128);
529 size += offset + accounts.len() * size_of::<u64>();
530 Some(offset)
531 } else {
532 None
533 };
534
535 let mut s = Serializer::new(
536 size,
537 MM_INPUT_START,
538 false,
539 virtual_address_space_adjustments,
540 account_data_direct_mapping,
541 );
542
543 s.write::<u64>((accounts.len() as u64).to_le());
545 for account in accounts {
546 match account {
547 SerializeAccount::Account(_, mut borrowed_account) => {
548 let vm_addr = s.write::<u8>(NON_DUP_MARKER);
549 s.write::<u8>(borrowed_account.is_signer() as u8);
550 s.write::<u8>(borrowed_account.is_writable() as u8);
551 #[expect(deprecated)]
552 s.write::<u8>(borrowed_account.is_executable() as u8);
553 s.write_all(&[0u8, 0, 0, 0]);
554 let vm_key_addr = s.write_all(borrowed_account.get_key().as_ref());
555 let vm_owner_addr = s.write_all(borrowed_account.get_owner().as_ref());
556 let vm_lamports_addr = s.write::<u64>(borrowed_account.get_lamports().to_le());
557 s.write::<u64>((borrowed_account.get_data().len() as u64).to_le());
558 let vm_data_addr = s.write_account(&mut borrowed_account)?;
559 let rent_epoch = u64::MAX;
560 s.write::<u64>(rent_epoch.to_le());
561 accounts_metadata.push(SerializedAccountMetadata {
562 vm_addr,
563 original_data_len: borrowed_account.get_data().len(),
564 vm_key_addr,
565 vm_owner_addr,
566 vm_lamports_addr,
567 vm_data_addr,
568 });
569 }
570 SerializeAccount::Duplicate(position) => {
571 accounts_metadata.push(accounts_metadata.get(position as usize).unwrap().clone());
572 s.write::<u8>(position as u8);
573 s.write_all(&[0u8, 0, 0, 0, 0, 0, 0]);
574 }
575 };
576 }
577 s.write::<u64>((instruction_data.len() as u64).to_le());
578 let instruction_data_offset = s.write_all(instruction_data);
579 s.write_all(program_id.as_ref());
580
581 if let Some(offset) = account_pointers_offset {
582 s.fill_write(offset, 0)
585 .map_err(|_| InstructionError::InvalidArgument)?;
586 for entry in accounts_metadata.iter() {
587 s.write::<u64>(entry.vm_addr.to_le());
588 }
589 }
590
591 let (mem, regions) = s.finish();
592 Ok((
593 mem,
594 regions,
595 accounts_metadata,
596 instruction_data_offset as usize,
597 ))
598}
599
600fn deserialize_parameters_for_abiv1<I: IntoIterator<Item = usize>>(
601 instruction_context: &InstructionContext,
602 virtual_address_space_adjustments: bool,
603 account_data_direct_mapping: bool,
604 buffer: &[u8],
605 account_lengths: I,
606) -> Result<(), InstructionError> {
607 let mut start = size_of::<u64>(); for (instruction_account_index, pre_len) in
609 (0..instruction_context.get_number_of_instruction_accounts()).zip(account_lengths)
610 {
611 let duplicate =
612 instruction_context.is_instruction_account_duplicate(instruction_account_index)?;
613 start += size_of::<u8>(); if duplicate.is_some() {
615 start += 7; } else {
617 let mut borrowed_account =
618 instruction_context.try_borrow_instruction_account(instruction_account_index)?;
619 start += size_of::<u8>() + size_of::<u8>() + size_of::<u8>() + size_of::<u32>() + size_of::<Pubkey>(); let owner = buffer
625 .get(start..start + size_of::<Pubkey>())
626 .ok_or(InstructionError::InvalidArgument)?;
627 start += size_of::<Pubkey>(); let lamports = buffer
629 .get(start..start.saturating_add(8))
630 .map(<[u8; 8]>::try_from)
631 .and_then(Result::ok)
632 .map(u64::from_le_bytes)
633 .ok_or(InstructionError::InvalidArgument)?;
634 if borrowed_account.get_lamports() != lamports {
635 borrowed_account.set_lamports(lamports)?;
636 }
637 start += size_of::<u64>(); let post_len = buffer
639 .get(start..start.saturating_add(8))
640 .map(<[u8; 8]>::try_from)
641 .and_then(Result::ok)
642 .map(u64::from_le_bytes)
643 .ok_or(InstructionError::InvalidArgument)? as usize;
644 start += size_of::<u64>(); if post_len.saturating_sub(pre_len) > MAX_PERMITTED_DATA_INCREASE
646 || post_len > MAX_PERMITTED_DATA_LENGTH as usize
647 {
648 return Err(InstructionError::InvalidRealloc);
649 }
650 if !virtual_address_space_adjustments {
651 let data = buffer
652 .get(start..start + post_len)
653 .ok_or(InstructionError::InvalidArgument)?;
654 match borrowed_account.can_data_be_resized(post_len) {
656 Ok(()) => borrowed_account.set_data_from_slice(data)?,
657 Err(err) if borrowed_account.get_data() != data => return Err(err),
658 _ => {}
659 }
660 } else if !account_data_direct_mapping && borrowed_account.can_data_be_changed().is_ok()
661 {
662 let data = buffer
663 .get(start..start + post_len)
664 .ok_or(InstructionError::InvalidArgument)?;
665 borrowed_account.set_data_from_slice(data)?;
666 } else if borrowed_account.get_data().len() != post_len {
667 borrowed_account.set_data_length(post_len)?;
668 }
669 start += if !(virtual_address_space_adjustments && account_data_direct_mapping) {
670 let alignment_offset = (pre_len as *const u8).align_offset(BPF_ALIGN_OF_U128);
671 pre_len .saturating_add(MAX_PERMITTED_DATA_INCREASE) .saturating_add(alignment_offset)
674 } else {
675 BPF_ALIGN_OF_U128
677 };
678 start += size_of::<u64>(); if borrowed_account.get_owner().to_bytes() != owner {
680 borrowed_account.set_owner(owner)?;
682 }
683 }
684 }
685 Ok(())
686}
687
688#[cfg(test)]
689#[allow(clippy::indexing_slicing)]
690mod tests {
691 use {
692 super::*,
693 crate::with_mock_invoke_context,
694 solana_account::{Account, AccountSharedData, ReadableAccount},
695 solana_account_info::AccountInfo,
696 solana_program_entrypoint::deserialize,
697 solana_rent::Rent,
698 solana_sbpf::{memory_region::MemoryMapping, program::SBPFVersion, vm::Config},
699 solana_sdk_ids::bpf_loader,
700 solana_system_interface::MAX_PERMITTED_ACCOUNTS_DATA_ALLOCATIONS_PER_TRANSACTION,
701 solana_transaction_context::{
702 MAX_ACCOUNTS_PER_TRANSACTION, instruction_accounts::InstructionAccount,
703 transaction::TransactionContext,
704 },
705 std::{
706 borrow::Cow,
707 cell::RefCell,
708 mem::transmute,
709 rc::Rc,
710 slice::{from_raw_parts, from_raw_parts_mut},
711 },
712 test_case::test_case,
713 };
714
715 fn deduplicated_instruction_accounts(
716 transaction_indexes: &[IndexOfAccount],
717 is_writable: fn(usize) -> bool,
718 ) -> Vec<InstructionAccount> {
719 transaction_indexes
720 .iter()
721 .enumerate()
722 .map(|(index_in_instruction, index_in_transaction)| {
723 InstructionAccount::new(
724 *index_in_transaction,
725 false,
726 is_writable(index_in_instruction),
727 )
728 })
729 .collect()
730 }
731
732 #[test_case(false; "direct_account_pointers_in_program_input disabled")]
733 #[test_case(true; "direct_account_pointers_in_program_input enabled")]
734 fn test_serialize_parameters_with_many_accounts(
735 direct_account_pointers_in_program_input: bool,
736 ) {
737 struct TestCase {
738 num_ix_accounts: usize,
739 append_dup_account: bool,
740 expected_err: Option<InstructionError>,
741 name: &'static str,
742 }
743
744 for virtual_address_space_adjustments in [false, true] {
745 for TestCase {
746 num_ix_accounts,
747 append_dup_account,
748 expected_err,
749 name,
750 } in [
751 TestCase {
752 name: "serialize max accounts with cap",
753 num_ix_accounts: MAX_ACCOUNTS_PER_INSTRUCTION,
754 append_dup_account: false,
755 expected_err: None,
756 },
757 TestCase {
758 name: "serialize too many accounts with cap",
759 num_ix_accounts: MAX_ACCOUNTS_PER_INSTRUCTION + 1,
760 append_dup_account: false,
761 expected_err: Some(InstructionError::MaxAccountsExceeded),
762 },
763 TestCase {
764 name: "serialize too many accounts and append dup with cap",
765 num_ix_accounts: MAX_ACCOUNTS_PER_INSTRUCTION,
766 append_dup_account: true,
767 expected_err: Some(InstructionError::MaxAccountsExceeded),
768 },
769 ] {
770 let program_id = solana_pubkey::new_rand();
771 let mut transaction_accounts = vec![(
772 program_id,
773 AccountSharedData::from(Account {
774 lamports: 0,
775 data: vec![],
776 owner: bpf_loader::id(),
777 executable: true,
778 rent_epoch: 0,
779 }),
780 )];
781 for _ in 0..num_ix_accounts {
782 transaction_accounts.push((
783 Pubkey::new_unique(),
784 AccountSharedData::from(Account {
785 lamports: 0,
786 data: vec![],
787 owner: program_id,
788 executable: false,
789 rent_epoch: 0,
790 }),
791 ));
792 }
793
794 let transaction_accounts_indexes: Vec<IndexOfAccount> =
795 (0..num_ix_accounts as u16).collect();
796 let mut instruction_accounts =
797 deduplicated_instruction_accounts(&transaction_accounts_indexes, |_| false);
798 if append_dup_account {
799 instruction_accounts.push(instruction_accounts.last().cloned().unwrap());
800 }
801 let instruction_data = vec![];
802
803 with_mock_invoke_context!(
804 invoke_context,
805 transaction_context,
806 transaction_accounts
807 );
808 if instruction_accounts.len() > MAX_ACCOUNTS_PER_INSTRUCTION {
809 let dedup_map = vec![u16::MAX; MAX_ACCOUNTS_PER_TRANSACTION];
813 invoke_context
814 .transaction_context
815 .configure_instruction_at_index(
816 0,
817 0,
818 instruction_accounts,
819 dedup_map,
820 Cow::Owned(instruction_data.clone()),
821 Some(0),
822 )
823 .unwrap();
824 } else {
825 invoke_context
826 .transaction_context
827 .configure_top_level_instruction_for_tests(
828 0,
829 instruction_accounts,
830 instruction_data.clone(),
831 )
832 .unwrap();
833 }
834 invoke_context.push().unwrap();
835 let instruction_context = invoke_context
836 .transaction_context
837 .get_current_instruction_context()
838 .unwrap();
839
840 let serialization_result = serialize_parameters(
841 &instruction_context,
842 virtual_address_space_adjustments,
843 false, direct_account_pointers_in_program_input,
845 );
846 assert_eq!(
847 serialization_result.as_ref().err(),
848 expected_err.as_ref(),
849 "{name} test case failed",
850 );
851 if expected_err.is_some() {
852 continue;
853 }
854
855 let (mut serialized, regions, _account_lengths, _instruction_data_offset) =
856 serialization_result.unwrap();
857 let mut serialized_regions = unsafe {
858 concat_regions(®ions)
860 };
861 let (de_program_id, de_accounts, de_instruction_data) = unsafe {
862 deserialize(
863 if !virtual_address_space_adjustments {
864 serialized.as_slice_mut()
865 } else {
866 serialized_regions.as_slice_mut()
867 }
868 .first_mut()
869 .unwrap() as *mut u8,
870 )
871 };
872 assert_eq!(de_program_id, &program_id);
873 assert_eq!(de_instruction_data, &instruction_data);
874 for account_info in de_accounts {
875 let index_in_transaction = invoke_context
876 .transaction_context
877 .find_index_of_account(account_info.key)
878 .unwrap();
879 let account = invoke_context
880 .transaction_context
881 .accounts()
882 .try_borrow(index_in_transaction)
883 .unwrap();
884 assert_eq!(account.lamports(), account_info.lamports());
885 assert_eq!(account.data(), &account_info.data.borrow()[..]);
886 assert_eq!(account.owner(), account_info.owner);
887 assert_eq!(account.executable(), account_info.executable);
888 #[allow(deprecated)]
889 {
890 assert_eq!(0, account_info._unused);
892 }
893 }
894 }
895 }
896 }
897
898 #[test_case(false; "direct_account_pointers_in_program_input disabled")]
899 #[test_case(true; "direct_account_pointers_in_program_input enabled")]
900 fn test_serialize_parameters(direct_account_pointers_in_program_input: bool) {
901 for virtual_address_space_adjustments in [false, true] {
902 let program_id = solana_pubkey::new_rand();
903 let transaction_accounts = vec![
904 (
905 program_id,
906 AccountSharedData::from(Account {
907 lamports: 0,
908 data: vec![],
909 owner: bpf_loader::id(),
910 executable: true,
911 rent_epoch: 0,
912 }),
913 ),
914 (
915 solana_pubkey::new_rand(),
916 AccountSharedData::from(Account {
917 lamports: 1,
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: 2,
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: 3,
938 data: vec![],
939 owner: bpf_loader::id(),
940 executable: false,
941 rent_epoch: 3100,
942 }),
943 ),
944 (
945 solana_pubkey::new_rand(),
946 AccountSharedData::from(Account {
947 lamports: 4,
948 data: vec![1u8, 2, 3, 4, 5],
949 owner: bpf_loader::id(),
950 executable: false,
951 rent_epoch: 100,
952 }),
953 ),
954 (
955 solana_pubkey::new_rand(),
956 AccountSharedData::from(Account {
957 lamports: 5,
958 data: vec![11u8, 12, 13, 14, 15, 16, 17, 18, 19],
959 owner: bpf_loader::id(),
960 executable: true,
961 rent_epoch: 200,
962 }),
963 ),
964 (
965 solana_pubkey::new_rand(),
966 AccountSharedData::from(Account {
967 lamports: 6,
968 data: vec![],
969 owner: bpf_loader::id(),
970 executable: false,
971 rent_epoch: 3100,
972 }),
973 ),
974 (
975 program_id,
976 AccountSharedData::from(Account {
977 lamports: 0,
978 data: vec![],
979 owner: bpf_loader_deprecated::id(),
980 executable: true,
981 rent_epoch: 0,
982 }),
983 ),
984 ];
985 let instruction_accounts =
986 deduplicated_instruction_accounts(&[1, 1, 2, 3, 4, 4, 5, 6], |index| index >= 4);
987 let instruction_data = vec![1u8, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11];
988 let original_accounts = transaction_accounts.clone();
989 with_mock_invoke_context!(invoke_context, transaction_context, transaction_accounts);
990 invoke_context
991 .transaction_context
992 .configure_top_level_instruction_for_tests(
993 0,
994 instruction_accounts.clone(),
995 instruction_data.clone(),
996 )
997 .unwrap();
998 invoke_context.push().unwrap();
999 let instruction_context = invoke_context
1000 .transaction_context
1001 .get_current_instruction_context()
1002 .unwrap();
1003
1004 let (mut serialized, regions, accounts_metadata, _instruction_data_offset) =
1006 serialize_parameters(
1007 &instruction_context,
1008 virtual_address_space_adjustments,
1009 false, direct_account_pointers_in_program_input,
1011 )
1012 .unwrap();
1013
1014 let mut serialized_regions = unsafe {
1015 concat_regions(®ions)
1017 };
1018 if !virtual_address_space_adjustments {
1019 assert_eq!(serialized.as_slice(), serialized_regions.as_slice());
1020 }
1021 let (de_program_id, de_accounts, de_instruction_data) = unsafe {
1022 deserialize(
1023 if !virtual_address_space_adjustments {
1024 serialized.as_slice_mut()
1025 } else {
1026 serialized_regions.as_slice_mut()
1027 }
1028 .first_mut()
1029 .unwrap() as *mut u8,
1030 )
1031 };
1032
1033 assert_eq!(&program_id, de_program_id);
1034 assert_eq!(instruction_data, de_instruction_data);
1035 assert_eq!(
1036 (de_instruction_data.first().unwrap() as *const u8).align_offset(BPF_ALIGN_OF_U128),
1037 0
1038 );
1039 for account_info in de_accounts {
1040 let index_in_transaction = invoke_context
1041 .transaction_context
1042 .find_index_of_account(account_info.key)
1043 .unwrap();
1044 let account = invoke_context
1045 .transaction_context
1046 .accounts()
1047 .try_borrow(index_in_transaction)
1048 .unwrap();
1049 assert_eq!(account.lamports(), account_info.lamports());
1050 assert_eq!(account.data(), &account_info.data.borrow()[..]);
1051 assert_eq!(account.owner(), account_info.owner);
1052 assert_eq!(account.executable(), account_info.executable);
1053 #[allow(deprecated)]
1054 {
1055 assert_eq!(0, account_info._unused);
1057 }
1058
1059 assert_eq!(
1060 (*account_info.lamports.borrow() as *const u64).align_offset(BPF_ALIGN_OF_U128),
1061 0
1062 );
1063 assert_eq!(
1064 account_info
1065 .data
1066 .borrow()
1067 .as_ptr()
1068 .align_offset(BPF_ALIGN_OF_U128),
1069 0
1070 );
1071 }
1072
1073 deserialize_parameters(
1074 &instruction_context,
1075 virtual_address_space_adjustments,
1076 false, serialized.as_slice(),
1078 &accounts_metadata,
1079 )
1080 .unwrap();
1081 for (index_in_transaction, (_key, original_account)) in
1082 original_accounts.iter().enumerate()
1083 {
1084 let account = invoke_context
1085 .transaction_context
1086 .accounts()
1087 .try_borrow(index_in_transaction as IndexOfAccount)
1088 .unwrap();
1089 assert_eq!(&*account, original_account);
1090 }
1091
1092 invoke_context.pop().unwrap();
1093 invoke_context
1095 .transaction_context
1096 .configure_top_level_instruction_for_tests(
1097 7,
1098 instruction_accounts,
1099 instruction_data.clone(),
1100 )
1101 .unwrap();
1102 invoke_context.push().unwrap();
1103 let instruction_context = invoke_context
1104 .transaction_context
1105 .get_current_instruction_context()
1106 .unwrap();
1107
1108 let (mut serialized, regions, account_lengths, _instruction_data_offset) =
1109 serialize_parameters(
1110 &instruction_context,
1111 virtual_address_space_adjustments,
1112 false, direct_account_pointers_in_program_input,
1114 )
1115 .unwrap();
1116 let mut serialized_regions = unsafe {
1117 concat_regions(®ions)
1119 };
1120
1121 let (de_program_id, de_accounts, de_instruction_data) = unsafe {
1122 deserialize_for_abiv0(
1123 if !virtual_address_space_adjustments {
1124 serialized.as_slice_mut()
1125 } else {
1126 serialized_regions.as_slice_mut()
1127 }
1128 .first_mut()
1129 .unwrap() as *mut u8,
1130 )
1131 };
1132 assert_eq!(&program_id, de_program_id);
1133 assert_eq!(instruction_data, de_instruction_data);
1134 for account_info in de_accounts {
1135 let index_in_transaction = invoke_context
1136 .transaction_context
1137 .find_index_of_account(account_info.key)
1138 .unwrap();
1139 let account = invoke_context
1140 .transaction_context
1141 .accounts()
1142 .try_borrow(index_in_transaction)
1143 .unwrap();
1144 assert_eq!(account.lamports(), account_info.lamports());
1145 assert_eq!(account.data(), &account_info.data.borrow()[..]);
1146 assert_eq!(account.owner(), account_info.owner);
1147 assert_eq!(account.executable(), account_info.executable);
1148 #[allow(deprecated)]
1149 {
1150 assert_eq!(u64::MAX, account_info._unused);
1151 }
1152 }
1153
1154 deserialize_parameters(
1155 &instruction_context,
1156 virtual_address_space_adjustments,
1157 false, serialized.as_slice(),
1159 &account_lengths,
1160 )
1161 .unwrap();
1162 for (index_in_transaction, (_key, original_account)) in
1163 original_accounts.iter().enumerate()
1164 {
1165 let account = invoke_context
1166 .transaction_context
1167 .accounts()
1168 .try_borrow(index_in_transaction as IndexOfAccount)
1169 .unwrap();
1170 assert_eq!(&*account, original_account);
1171 }
1172 }
1173 }
1174
1175 #[test_case(false; "direct_account_pointers_in_program_input disabled")]
1176 #[test_case(true; "direct_account_pointers_in_program_input enabled")]
1177 fn test_serialize_parameters_mask_out_rent_epoch_in_vm_serialization(
1178 direct_account_pointers_in_program_input: bool,
1179 ) {
1180 let transaction_accounts = vec![
1181 (
1182 solana_pubkey::new_rand(),
1183 AccountSharedData::from(Account {
1184 lamports: 0,
1185 data: vec![],
1186 owner: bpf_loader::id(),
1187 executable: true,
1188 rent_epoch: 0,
1189 }),
1190 ),
1191 (
1192 solana_pubkey::new_rand(),
1193 AccountSharedData::from(Account {
1194 lamports: 1,
1195 data: vec![1u8, 2, 3, 4, 5],
1196 owner: bpf_loader::id(),
1197 executable: false,
1198 rent_epoch: 100,
1199 }),
1200 ),
1201 (
1202 solana_pubkey::new_rand(),
1203 AccountSharedData::from(Account {
1204 lamports: 2,
1205 data: vec![11u8, 12, 13, 14, 15, 16, 17, 18, 19],
1206 owner: bpf_loader::id(),
1207 executable: true,
1208 rent_epoch: 200,
1209 }),
1210 ),
1211 (
1212 solana_pubkey::new_rand(),
1213 AccountSharedData::from(Account {
1214 lamports: 3,
1215 data: vec![],
1216 owner: bpf_loader::id(),
1217 executable: false,
1218 rent_epoch: 300,
1219 }),
1220 ),
1221 (
1222 solana_pubkey::new_rand(),
1223 AccountSharedData::from(Account {
1224 lamports: 4,
1225 data: vec![1u8, 2, 3, 4, 5],
1226 owner: bpf_loader::id(),
1227 executable: false,
1228 rent_epoch: 100,
1229 }),
1230 ),
1231 (
1232 solana_pubkey::new_rand(),
1233 AccountSharedData::from(Account {
1234 lamports: 5,
1235 data: vec![11u8, 12, 13, 14, 15, 16, 17, 18, 19],
1236 owner: bpf_loader::id(),
1237 executable: true,
1238 rent_epoch: 200,
1239 }),
1240 ),
1241 (
1242 solana_pubkey::new_rand(),
1243 AccountSharedData::from(Account {
1244 lamports: 6,
1245 data: vec![],
1246 owner: bpf_loader::id(),
1247 executable: false,
1248 rent_epoch: 3100,
1249 }),
1250 ),
1251 (
1252 solana_pubkey::new_rand(),
1253 AccountSharedData::from(Account {
1254 lamports: 0,
1255 data: vec![],
1256 owner: bpf_loader_deprecated::id(),
1257 executable: true,
1258 rent_epoch: 0,
1259 }),
1260 ),
1261 ];
1262 let instruction_accounts =
1263 deduplicated_instruction_accounts(&[1, 1, 2, 3, 4, 4, 5, 6], |index| index >= 4);
1264 with_mock_invoke_context!(invoke_context, transaction_context, transaction_accounts);
1265 invoke_context
1266 .transaction_context
1267 .configure_top_level_instruction_for_tests(0, instruction_accounts.clone(), vec![])
1268 .unwrap();
1269 invoke_context.push().unwrap();
1270 let instruction_context = invoke_context
1271 .transaction_context
1272 .get_current_instruction_context()
1273 .unwrap();
1274
1275 let (_serialized, regions, _accounts_metadata, _instruction_data_offset) =
1277 serialize_parameters(
1278 &instruction_context,
1279 true,
1280 false, direct_account_pointers_in_program_input,
1282 )
1283 .unwrap();
1284
1285 let mut serialized_regions = unsafe {
1286 concat_regions(®ions)
1288 };
1289 let (_de_program_id, de_accounts, _de_instruction_data) = unsafe {
1290 deserialize(serialized_regions.as_slice_mut().first_mut().unwrap() as *mut u8)
1291 };
1292
1293 for account_info in de_accounts {
1294 #[allow(deprecated)]
1296 {
1297 assert_eq!(0, account_info._unused);
1298 }
1299 }
1300
1301 invoke_context
1303 .transaction_context
1304 .configure_top_level_instruction_for_tests(7, instruction_accounts, vec![])
1305 .unwrap();
1306 invoke_context.push().unwrap();
1307 let instruction_context = invoke_context
1308 .transaction_context
1309 .get_current_instruction_context()
1310 .unwrap();
1311
1312 let (_serialized, regions, _account_lengths, _instruction_data_offset) =
1313 serialize_parameters(
1314 &instruction_context,
1315 true,
1316 false, direct_account_pointers_in_program_input,
1318 )
1319 .unwrap();
1320 let mut serialized_regions = unsafe {
1321 concat_regions(®ions)
1323 };
1324
1325 let (_de_program_id, de_accounts, _de_instruction_data) = unsafe {
1326 deserialize_for_abiv0(serialized_regions.as_slice_mut().first_mut().unwrap() as *mut u8)
1327 };
1328 for account_info in de_accounts {
1329 #[allow(deprecated)]
1330 {
1331 assert_eq!(account_info._unused, u64::MAX);
1332 }
1333 }
1334 }
1335
1336 #[deny(unsafe_op_in_unsafe_fn)]
1338 unsafe fn deserialize_for_abiv0<'a>(
1339 input: *mut u8,
1340 ) -> (&'a Pubkey, Vec<AccountInfo<'a>>, &'a [u8]) {
1341 struct Ptr<T>(std::marker::PhantomData<T>);
1343 impl<T> Ptr<T> {
1344 const COULD_BE_UNALIGNED: bool = std::mem::align_of::<T>() > 1;
1345
1346 #[inline(always)]
1347 fn read_possibly_unaligned(input: *mut u8, offset: usize) -> T {
1348 unsafe {
1349 let src = input.add(offset) as *const T;
1350 if Self::COULD_BE_UNALIGNED {
1351 src.read_unaligned()
1352 } else {
1353 src.read()
1354 }
1355 }
1356 }
1357
1358 #[inline(always)]
1367 fn ref_possibly_unaligned<'a>(input: *mut u8, offset: usize) -> &'a T {
1368 #[allow(clippy::transmute_ptr_to_ref)]
1369 unsafe {
1370 transmute(input.add(offset) as *const T)
1371 }
1372 }
1373
1374 #[inline(always)]
1376 fn mut_possibly_unaligned<'a>(input: *mut u8, offset: usize) -> &'a mut T {
1377 #[allow(clippy::transmute_ptr_to_ref)]
1378 unsafe {
1379 transmute(input.add(offset) as *mut T)
1380 }
1381 }
1382 }
1383
1384 let mut offset: usize = 0;
1385
1386 let num_accounts = Ptr::<u64>::read_possibly_unaligned(input, offset) as usize;
1389 offset += size_of::<u64>();
1390
1391 let mut accounts = Vec::with_capacity(num_accounts);
1394 for _ in 0..num_accounts {
1395 let dup_info = Ptr::<u8>::read_possibly_unaligned(input, offset);
1396 offset += size_of::<u8>();
1397 if dup_info == NON_DUP_MARKER {
1398 let is_signer = Ptr::<u8>::read_possibly_unaligned(input, offset) != 0;
1399 offset += size_of::<u8>();
1400
1401 let is_writable = Ptr::<u8>::read_possibly_unaligned(input, offset) != 0;
1402 offset += size_of::<u8>();
1403
1404 let key = Ptr::<Pubkey>::ref_possibly_unaligned(input, offset);
1405 offset += size_of::<Pubkey>();
1406
1407 let lamports = Rc::new(RefCell::new(Ptr::mut_possibly_unaligned(input, offset)));
1408 offset += size_of::<u64>();
1409
1410 let data_len = Ptr::<u64>::read_possibly_unaligned(input, offset) as usize;
1411 offset += size_of::<u64>();
1412
1413 let data = Rc::new(RefCell::new(unsafe {
1414 from_raw_parts_mut(input.add(offset), data_len)
1415 }));
1416 offset += data_len;
1417
1418 let owner: &Pubkey = Ptr::<Pubkey>::ref_possibly_unaligned(input, offset);
1419 offset += size_of::<Pubkey>();
1420
1421 let executable = Ptr::<u8>::read_possibly_unaligned(input, offset) != 0;
1422 offset += size_of::<u8>();
1423
1424 let unused = Ptr::<u64>::read_possibly_unaligned(input, offset);
1425 offset += size_of::<u64>();
1426
1427 #[allow(deprecated)]
1428 accounts.push(AccountInfo {
1429 key,
1430 is_signer,
1431 is_writable,
1432 lamports,
1433 data,
1434 owner,
1435 executable,
1436 _unused: unused,
1437 });
1438 } else {
1439 accounts.push(accounts.get(dup_info as usize).unwrap().clone());
1441 }
1442 }
1443
1444 let instruction_data_len = Ptr::<u64>::read_possibly_unaligned(input, offset) as usize;
1447 offset += size_of::<u64>();
1448
1449 let instruction_data = unsafe { from_raw_parts(input.add(offset), instruction_data_len) };
1450 offset += instruction_data_len;
1451
1452 let program_id = Ptr::<Pubkey>::ref_possibly_unaligned(input, offset);
1455
1456 (program_id, accounts, instruction_data)
1457 }
1458
1459 unsafe fn concat_regions(regions: &[MemoryRegion]) -> AlignedMemory<HOST_ALIGN> {
1463 let last_region = regions.last().unwrap();
1464 let last_region_vm_addr = last_region.vm_addr_range().start;
1465 let mut mem = AlignedMemory::zero_filled(
1466 (last_region_vm_addr - MM_INPUT_START + last_region.len() as u64) as usize,
1467 );
1468 for region in regions {
1469 let vm_start = region.vm_addr_range().start;
1470 let buffer = region.host_buffer().ptr();
1471 mem.as_slice_mut()[(vm_start - MM_INPUT_START) as usize..][..buffer.len()]
1472 .copy_from_slice(unsafe {
1473 buffer.as_ref_unchecked()
1478 })
1479 }
1480 mem
1481 }
1482
1483 #[test]
1484 fn test_access_violation_handler() {
1485 let program_id = Pubkey::new_unique();
1486 let shared_account = AccountSharedData::new(0, 4, &program_id);
1487 let mut transaction_context = TransactionContext::new(
1488 vec![
1489 (
1490 Pubkey::new_unique(),
1491 AccountSharedData::new(0, 4, &program_id),
1492 ), (Pubkey::new_unique(), shared_account.clone()), (
1495 Pubkey::new_unique(),
1496 AccountSharedData::new(0, 0, &program_id),
1497 ), (
1499 Pubkey::new_unique(),
1500 AccountSharedData::new(
1501 0,
1502 MAX_PERMITTED_DATA_LENGTH as usize - 0x100,
1503 &program_id,
1504 ),
1505 ), (
1507 Pubkey::new_unique(),
1508 AccountSharedData::new(0, 0, &program_id),
1509 ), (
1511 Pubkey::new_unique(),
1512 AccountSharedData::new(0, 0x3000, &program_id),
1513 ), (program_id, AccountSharedData::default()), ],
1516 Rent::default(),
1517 1,
1518 1,
1519 1,
1520 );
1521 let transaction_accounts_indexes = [0, 1, 2, 3, 4, 5];
1522 let instruction_accounts =
1523 deduplicated_instruction_accounts(&transaction_accounts_indexes, |index| index > 0);
1524 transaction_context
1525 .configure_top_level_instruction_for_tests(6, instruction_accounts, vec![])
1526 .unwrap();
1527 transaction_context.push().unwrap();
1528 let instruction_context = transaction_context
1529 .get_current_instruction_context()
1530 .unwrap();
1531 let account_start_offsets = [
1532 MM_INPUT_START,
1533 MM_INPUT_START + 4 + MAX_PERMITTED_DATA_INCREASE as u64,
1534 MM_INPUT_START + (4 + MAX_PERMITTED_DATA_INCREASE as u64) * 2,
1535 MM_INPUT_START + (4 + MAX_PERMITTED_DATA_INCREASE as u64) * 3,
1536 ];
1537 let regions = account_start_offsets
1538 .iter()
1539 .enumerate()
1540 .map(|(index_in_instruction, account_start_offset)| {
1541 create_memory_region_of_account(
1542 &mut instruction_context
1543 .try_borrow_instruction_account(index_in_instruction as IndexOfAccount)
1544 .unwrap(),
1545 *account_start_offset,
1546 )
1547 .unwrap()
1548 })
1549 .collect::<Vec<_>>();
1550 let config = Config {
1551 aligned_memory_mapping: false,
1552 ..Config::default()
1553 };
1554 let mut memory_mapping = unsafe {
1555 MemoryMapping::new_with_access_violation_handler(
1556 regions,
1557 &config,
1558 SBPFVersion::V3,
1559 transaction_context.access_violation_handler(true, true),
1560 )
1561 .unwrap()
1562 };
1563
1564 memory_mapping
1566 .load::<u32>(account_start_offsets[0])
1567 .unwrap();
1568
1569 memory_mapping
1571 .load::<u32>(account_start_offsets[1])
1572 .unwrap();
1573
1574 memory_mapping
1576 .load::<u32>(account_start_offsets[0] + 4)
1577 .unwrap_err();
1578
1579 memory_mapping
1581 .store::<u32>(0, account_start_offsets[0])
1582 .unwrap_err();
1583
1584 let _shared_account_ref = shared_account;
1588 assert!(
1589 transaction_context
1590 .accounts()
1591 .try_borrow_mut(1)
1592 .unwrap()
1593 .is_shared()
1594 );
1595 memory_mapping
1596 .store::<u32>(0, account_start_offsets[1])
1597 .unwrap();
1598 assert!(
1599 !transaction_context
1600 .accounts()
1601 .try_borrow_mut(1)
1602 .unwrap()
1603 .is_shared()
1604 );
1605 assert_eq!(
1606 transaction_context
1607 .accounts()
1608 .try_borrow(1)
1609 .unwrap()
1610 .data()
1611 .len(),
1612 4,
1613 );
1614
1615 memory_mapping
1617 .load::<u32>(account_start_offsets[1] + 4)
1618 .unwrap_err();
1619
1620 memory_mapping
1623 .store::<u32>(0, account_start_offsets[1] + 4)
1624 .unwrap();
1625 assert_eq!(
1626 transaction_context
1627 .accounts()
1628 .try_borrow(1)
1629 .unwrap()
1630 .data()
1631 .len(),
1632 4 + MAX_PERMITTED_DATA_INCREASE,
1633 );
1634 assert!(
1635 transaction_context
1636 .accounts()
1637 .try_borrow(1)
1638 .unwrap()
1639 .data()
1640 .len()
1641 < 0x3000
1642 );
1643
1644 memory_mapping
1647 .store::<u32>(0, account_start_offsets[3] + MAX_PERMITTED_DATA_LENGTH - 4)
1648 .unwrap();
1649 assert_eq!(
1650 transaction_context
1651 .accounts()
1652 .try_borrow(3)
1653 .unwrap()
1654 .data()
1655 .len(),
1656 MAX_PERMITTED_DATA_LENGTH as usize,
1657 );
1658
1659 memory_mapping
1662 .load::<u32>(account_start_offsets[3] + MAX_PERMITTED_DATA_LENGTH)
1663 .unwrap_err();
1664 memory_mapping
1665 .store::<u32>(0, account_start_offsets[3] + MAX_PERMITTED_DATA_LENGTH)
1666 .unwrap_err();
1667
1668 let remaining_allowed_growth: usize = 0x700;
1670 for index_in_instruction in 4..6 {
1671 let mut borrowed_account = instruction_context
1672 .try_borrow_instruction_account(index_in_instruction)
1673 .unwrap();
1674 borrowed_account
1675 .set_data_from_slice(&vec![0u8; MAX_PERMITTED_DATA_LENGTH as usize])
1676 .unwrap();
1677 }
1678 assert_eq!(
1679 transaction_context.accounts().resize_delta(),
1680 MAX_PERMITTED_ACCOUNTS_DATA_ALLOCATIONS_PER_TRANSACTION
1681 - remaining_allowed_growth as i64,
1682 );
1683
1684 memory_mapping
1687 .store::<u32>(0, account_start_offsets[2] + 0x500)
1688 .unwrap();
1689 assert_eq!(
1690 transaction_context
1691 .accounts()
1692 .try_borrow(2)
1693 .unwrap()
1694 .data()
1695 .len(),
1696 remaining_allowed_growth,
1697 );
1698 }
1699}