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 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
37pub 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(&raw mut account.get_data_mut()?[..], vaddr)
45 } else {
46 MemoryRegion::new(&raw const 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#[expect(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 virtual_address_space_adjustments: 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 virtual_address_space_adjustments: 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 virtual_address_space_adjustments,
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 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 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.virtual_address_space_adjustments {
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 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 let region_slice = self.buffer.as_slice_mut().get_mut(range.clone()).unwrap();
195 self.regions
196 .push(MemoryRegion::new(&raw mut region_slice[..], self.vaddr));
197 self.region_start = range.end;
198 self.vaddr += range.len() as u64;
199 }
200
201 fn finish(mut self) -> (AlignedMemory<HOST_ALIGN>, Vec<MemoryRegion>) {
202 self.push_region();
203 debug_assert_eq!(self.region_start, self.buffer.len());
204 (self.buffer, self.regions)
205 }
206
207 fn debug_assert_alignment<T>(&self) {
208 debug_assert!(
209 self.is_loader_v1
210 || self
211 .buffer
212 .as_slice()
213 .as_ptr_range()
214 .end
215 .align_offset(mem::align_of::<T>())
216 == 0
217 );
218 }
219}
220
221pub fn serialize_parameters(
222 instruction_context: &InstructionContext,
223 virtual_address_space_adjustments: bool,
224 account_data_direct_mapping: bool,
225 direct_account_pointers_in_program_input: bool,
226) -> Result<
227 (
228 AlignedMemory<HOST_ALIGN>,
229 Vec<MemoryRegion>,
230 Vec<SerializedAccountMetadata>,
231 usize,
232 ),
233 InstructionError,
234> {
235 let num_ix_accounts = instruction_context.get_number_of_instruction_accounts();
236 if num_ix_accounts > MAX_ACCOUNTS_PER_INSTRUCTION as IndexOfAccount {
237 return Err(InstructionError::MaxAccountsExceeded);
238 }
239
240 let program_id = *instruction_context.get_program_key()?;
241 let is_loader_deprecated =
242 instruction_context.get_program_owner()? == bpf_loader_deprecated::id();
243
244 let accounts = (0..instruction_context.get_number_of_instruction_accounts())
245 .map(|instruction_account_index| {
246 if let Some(index) = instruction_context
247 .is_instruction_account_duplicate(instruction_account_index)
248 .unwrap()
249 {
250 SerializeAccount::Duplicate(index)
251 } else {
252 let account = instruction_context
253 .try_borrow_instruction_account(instruction_account_index)
254 .unwrap();
255 SerializeAccount::Account(instruction_account_index, account)
256 }
257 })
258 .collect::<Vec<_>>();
263
264 if is_loader_deprecated {
265 serialize_parameters_for_abiv0(
267 accounts,
268 instruction_context.get_instruction_data(),
269 &program_id,
270 virtual_address_space_adjustments,
271 account_data_direct_mapping,
272 )
273 } else {
274 serialize_parameters_for_abiv1(
276 accounts,
277 instruction_context.get_instruction_data(),
278 &program_id,
279 virtual_address_space_adjustments,
280 account_data_direct_mapping,
281 direct_account_pointers_in_program_input,
283 )
284 }
285}
286
287pub fn deserialize_parameters(
288 instruction_context: &InstructionContext,
289 virtual_address_space_adjustments: bool,
290 account_data_direct_mapping: bool,
291 buffer: &[u8],
292 accounts_metadata: &[SerializedAccountMetadata],
293) -> Result<(), InstructionError> {
294 let is_loader_deprecated =
295 instruction_context.get_program_owner()? == bpf_loader_deprecated::id();
296 let account_lengths = accounts_metadata.iter().map(|a| a.original_data_len);
297 if is_loader_deprecated {
298 deserialize_parameters_for_abiv0(
300 instruction_context,
301 virtual_address_space_adjustments,
302 account_data_direct_mapping,
303 buffer,
304 account_lengths,
305 )
306 } else {
307 deserialize_parameters_for_abiv1(
309 instruction_context,
310 virtual_address_space_adjustments,
311 account_data_direct_mapping,
312 buffer,
313 account_lengths,
314 )
315 }
316}
317
318fn serialize_parameters_for_abiv0(
319 accounts: Vec<SerializeAccount>,
320 instruction_data: &[u8],
321 program_id: &Pubkey,
322 virtual_address_space_adjustments: bool,
323 account_data_direct_mapping: bool,
324) -> Result<
325 (
326 AlignedMemory<HOST_ALIGN>,
327 Vec<MemoryRegion>,
328 Vec<SerializedAccountMetadata>,
329 usize,
330 ),
331 InstructionError,
332> {
333 let mut size = size_of::<u64>();
335 for account in &accounts {
336 size += 1; match account {
338 SerializeAccount::Duplicate(_) => {}
339 SerializeAccount::Account(_, account) => {
340 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) {
349 size += account.get_data().len();
350 }
351 }
352 }
353 }
354 size += size_of::<u64>() + instruction_data.len() + size_of::<Pubkey>(); let mut s = Serializer::new(
359 size,
360 MM_INPUT_START,
361 true,
362 virtual_address_space_adjustments,
363 account_data_direct_mapping,
364 );
365
366 let mut accounts_metadata: Vec<SerializedAccountMetadata> = Vec::with_capacity(accounts.len());
367 s.write::<u64>((accounts.len() as u64).to_le());
368 for account in accounts {
369 match account {
370 SerializeAccount::Duplicate(position) => {
371 accounts_metadata.push(accounts_metadata.get(position as usize).unwrap().clone());
372 s.write(position as u8);
373 }
374 SerializeAccount::Account(_, mut account) => {
375 let vm_addr = s.write::<u8>(NON_DUP_MARKER);
376 s.write::<u8>(account.is_signer() as u8);
377 s.write::<u8>(account.is_writable() as u8);
378 let vm_key_addr = s.write_all(account.get_key().as_ref());
379 let vm_lamports_addr = s.write::<u64>(account.get_lamports().to_le());
380 s.write::<u64>((account.get_data().len() as u64).to_le());
381 let vm_data_addr = s.write_account(&mut account)?;
382 let vm_owner_addr = s.write_all(account.get_owner().as_ref());
383 #[expect(deprecated)]
384 s.write::<u8>(account.is_executable() as u8);
385 let rent_epoch = u64::MAX;
386 s.write::<u64>(rent_epoch.to_le());
387 accounts_metadata.push(SerializedAccountMetadata {
388 vm_addr,
389 original_data_len: account.get_data().len(),
390 vm_key_addr,
391 vm_lamports_addr,
392 vm_owner_addr,
393 vm_data_addr,
394 });
395 }
396 };
397 }
398 s.write::<u64>((instruction_data.len() as u64).to_le());
399 let instruction_data_offset = s.write_all(instruction_data);
400 s.write_all(program_id.as_ref());
401
402 let (mem, regions) = s.finish();
403 Ok((
404 mem,
405 regions,
406 accounts_metadata,
407 instruction_data_offset as usize,
408 ))
409}
410
411fn deserialize_parameters_for_abiv0<I: IntoIterator<Item = usize>>(
412 instruction_context: &InstructionContext,
413 virtual_address_space_adjustments: bool,
414 account_data_direct_mapping: bool,
415 buffer: &[u8],
416 account_lengths: I,
417) -> Result<(), InstructionError> {
418 let mut start = size_of::<u64>(); for (instruction_account_index, pre_len) in
420 (0..instruction_context.get_number_of_instruction_accounts()).zip(account_lengths)
421 {
422 let duplicate =
423 instruction_context.is_instruction_account_duplicate(instruction_account_index)?;
424 start += 1; if duplicate.is_none() {
426 let mut borrowed_account =
427 instruction_context.try_borrow_instruction_account(instruction_account_index)?;
428 start += size_of::<u8>(); start += size_of::<u8>(); start += size_of::<Pubkey>(); let lamports = buffer
432 .get(start..start.saturating_add(8))
433 .map(<[u8; 8]>::try_from)
434 .and_then(Result::ok)
435 .map(u64::from_le_bytes)
436 .ok_or(InstructionError::InvalidArgument)?;
437 if borrowed_account.get_lamports() != lamports {
438 borrowed_account.set_lamports(lamports)?;
439 }
440 start += size_of::<u64>() + size_of::<u64>(); if !virtual_address_space_adjustments {
443 let data = buffer
444 .get(start..start + pre_len)
445 .ok_or(InstructionError::InvalidArgument)?;
446 match borrowed_account.can_data_be_resized(pre_len) {
448 Ok(()) => borrowed_account.set_data_from_slice(data)?,
449 Err(err) if borrowed_account.get_data() != data => return Err(err),
450 _ => {}
451 }
452 } else if !account_data_direct_mapping && borrowed_account.can_data_be_changed().is_ok()
453 {
454 let data = buffer
455 .get(start..start + pre_len)
456 .ok_or(InstructionError::InvalidArgument)?;
457 borrowed_account.set_data_from_slice(data)?;
458 } else if borrowed_account.get_data().len() != pre_len {
459 borrowed_account.set_data_length(pre_len)?;
460 }
461 if !(virtual_address_space_adjustments && account_data_direct_mapping) {
462 start += pre_len; }
464 start += size_of::<Pubkey>() + size_of::<u8>() + size_of::<u64>(); }
468 }
469 Ok(())
470}
471
472fn serialize_parameters_for_abiv1(
473 accounts: Vec<SerializeAccount>,
474 instruction_data: &[u8],
475 program_id: &Pubkey,
476 virtual_address_space_adjustments: bool,
477 account_data_direct_mapping: bool,
478 direct_account_pointers_program_input: bool,
479) -> Result<
480 (
481 AlignedMemory<HOST_ALIGN>,
482 Vec<MemoryRegion>,
483 Vec<SerializedAccountMetadata>,
484 usize,
485 ),
486 InstructionError,
487> {
488 let mut accounts_metadata = Vec::with_capacity(accounts.len());
489 let mut size = size_of::<u64>();
491 for account in &accounts {
492 size += 1; match account {
494 SerializeAccount::Duplicate(_) => size += 7, SerializeAccount::Account(_, account) => {
496 let data_len = account.get_data().len();
497 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) {
507 size += data_len
508 + MAX_PERMITTED_DATA_INCREASE
509 + (data_len as *const u8).align_offset(BPF_ALIGN_OF_U128);
510 } else {
511 size += BPF_ALIGN_OF_U128;
512 }
513 }
514 }
515 }
516 size += size_of::<u64>() + instruction_data.len()
518 + size_of::<Pubkey>(); let account_pointers_offset = if direct_account_pointers_program_input {
522 let offset = (size as *const u8).align_offset(BPF_ALIGN_OF_U128);
523 size += offset + accounts.len() * size_of::<u64>();
524 Some(offset)
525 } else {
526 None
527 };
528
529 let mut s = Serializer::new(
530 size,
531 MM_INPUT_START,
532 false,
533 virtual_address_space_adjustments,
534 account_data_direct_mapping,
535 );
536
537 s.write::<u64>((accounts.len() as u64).to_le());
539 for account in accounts {
540 match account {
541 SerializeAccount::Account(_, mut borrowed_account) => {
542 let vm_addr = s.write::<u8>(NON_DUP_MARKER);
543 s.write::<u8>(borrowed_account.is_signer() as u8);
544 s.write::<u8>(borrowed_account.is_writable() as u8);
545 #[expect(deprecated)]
546 s.write::<u8>(borrowed_account.is_executable() as u8);
547 s.write_all(&[0u8, 0, 0, 0]);
548 let vm_key_addr = s.write_all(borrowed_account.get_key().as_ref());
549 let vm_owner_addr = s.write_all(borrowed_account.get_owner().as_ref());
550 let vm_lamports_addr = s.write::<u64>(borrowed_account.get_lamports().to_le());
551 s.write::<u64>((borrowed_account.get_data().len() as u64).to_le());
552 let vm_data_addr = s.write_account(&mut borrowed_account)?;
553 let rent_epoch = u64::MAX;
554 s.write::<u64>(rent_epoch.to_le());
555 accounts_metadata.push(SerializedAccountMetadata {
556 vm_addr,
557 original_data_len: borrowed_account.get_data().len(),
558 vm_key_addr,
559 vm_owner_addr,
560 vm_lamports_addr,
561 vm_data_addr,
562 });
563 }
564 SerializeAccount::Duplicate(position) => {
565 accounts_metadata.push(accounts_metadata.get(position as usize).unwrap().clone());
566 s.write::<u8>(position as u8);
567 s.write_all(&[0u8, 0, 0, 0, 0, 0, 0]);
568 }
569 };
570 }
571 s.write::<u64>((instruction_data.len() as u64).to_le());
572 let instruction_data_offset = s.write_all(instruction_data);
573 s.write_all(program_id.as_ref());
574
575 if let Some(offset) = account_pointers_offset {
576 s.fill_write(offset, 0)
579 .map_err(|_| InstructionError::InvalidArgument)?;
580 for entry in accounts_metadata.iter() {
581 s.write::<u64>(entry.vm_addr.to_le());
582 }
583 }
584
585 let (mem, regions) = s.finish();
586 Ok((
587 mem,
588 regions,
589 accounts_metadata,
590 instruction_data_offset as usize,
591 ))
592}
593
594fn deserialize_parameters_for_abiv1<I: IntoIterator<Item = usize>>(
595 instruction_context: &InstructionContext,
596 virtual_address_space_adjustments: bool,
597 account_data_direct_mapping: bool,
598 buffer: &[u8],
599 account_lengths: I,
600) -> Result<(), InstructionError> {
601 let mut start = size_of::<u64>(); for (instruction_account_index, pre_len) in
603 (0..instruction_context.get_number_of_instruction_accounts()).zip(account_lengths)
604 {
605 let duplicate =
606 instruction_context.is_instruction_account_duplicate(instruction_account_index)?;
607 start += size_of::<u8>(); if duplicate.is_some() {
609 start += 7; } else {
611 let mut borrowed_account =
612 instruction_context.try_borrow_instruction_account(instruction_account_index)?;
613 start += size_of::<u8>() + size_of::<u8>() + size_of::<u8>() + size_of::<u32>() + size_of::<Pubkey>(); let owner = buffer
619 .get(start..start + size_of::<Pubkey>())
620 .ok_or(InstructionError::InvalidArgument)?;
621 start += size_of::<Pubkey>(); let lamports = buffer
623 .get(start..start.saturating_add(8))
624 .map(<[u8; 8]>::try_from)
625 .and_then(Result::ok)
626 .map(u64::from_le_bytes)
627 .ok_or(InstructionError::InvalidArgument)?;
628 if borrowed_account.get_lamports() != lamports {
629 borrowed_account.set_lamports(lamports)?;
630 }
631 start += size_of::<u64>(); let post_len = buffer
633 .get(start..start.saturating_add(8))
634 .map(<[u8; 8]>::try_from)
635 .and_then(Result::ok)
636 .map(u64::from_le_bytes)
637 .ok_or(InstructionError::InvalidArgument)? as usize;
638 start += size_of::<u64>(); if post_len.saturating_sub(pre_len) > MAX_PERMITTED_DATA_INCREASE
640 || post_len > MAX_PERMITTED_DATA_LENGTH as usize
641 {
642 return Err(InstructionError::InvalidRealloc);
643 }
644 if !virtual_address_space_adjustments {
645 let data = buffer
646 .get(start..start + post_len)
647 .ok_or(InstructionError::InvalidArgument)?;
648 match borrowed_account.can_data_be_resized(post_len) {
650 Ok(()) => borrowed_account.set_data_from_slice(data)?,
651 Err(err) if borrowed_account.get_data() != data => return Err(err),
652 _ => {}
653 }
654 } else if !account_data_direct_mapping && borrowed_account.can_data_be_changed().is_ok()
655 {
656 let data = buffer
657 .get(start..start + post_len)
658 .ok_or(InstructionError::InvalidArgument)?;
659 borrowed_account.set_data_from_slice(data)?;
660 } else if borrowed_account.get_data().len() != post_len {
661 borrowed_account.set_data_length(post_len)?;
662 }
663 start += if !(virtual_address_space_adjustments && account_data_direct_mapping) {
664 let alignment_offset = (pre_len as *const u8).align_offset(BPF_ALIGN_OF_U128);
665 pre_len .saturating_add(MAX_PERMITTED_DATA_INCREASE) .saturating_add(alignment_offset)
668 } else {
669 BPF_ALIGN_OF_U128
671 };
672 start += size_of::<u64>(); if borrowed_account.get_owner().to_bytes() != owner {
674 borrowed_account.set_owner(owner)?;
676 }
677 }
678 }
679 Ok(())
680}
681
682#[cfg(test)]
683#[allow(clippy::indexing_slicing)]
684mod tests {
685 use {
686 super::*,
687 crate::with_mock_invoke_context,
688 solana_account::{Account, AccountSharedData, ReadableAccount},
689 solana_account_info::AccountInfo,
690 solana_program_entrypoint::deserialize,
691 solana_rent::Rent,
692 solana_sbpf::{memory_region::MemoryMapping, program::SBPFVersion, vm::Config},
693 solana_sdk_ids::bpf_loader,
694 solana_system_interface::MAX_PERMITTED_ACCOUNTS_DATA_ALLOCATIONS_PER_TRANSACTION,
695 solana_transaction_context::{
696 MAX_ACCOUNTS_PER_TRANSACTION, instruction_accounts::InstructionAccount,
697 transaction::TransactionContext,
698 },
699 std::{
700 borrow::Cow,
701 cell::RefCell,
702 mem::transmute,
703 rc::Rc,
704 slice::{self, from_raw_parts, from_raw_parts_mut},
705 },
706 test_case::test_case,
707 };
708
709 fn deduplicated_instruction_accounts(
710 transaction_indexes: &[IndexOfAccount],
711 is_writable: fn(usize) -> bool,
712 ) -> Vec<InstructionAccount> {
713 transaction_indexes
714 .iter()
715 .enumerate()
716 .map(|(index_in_instruction, index_in_transaction)| {
717 InstructionAccount::new(
718 *index_in_transaction,
719 false,
720 is_writable(index_in_instruction),
721 )
722 })
723 .collect()
724 }
725
726 #[test_case(false; "direct_account_pointers_in_program_input disabled")]
727 #[test_case(true; "direct_account_pointers_in_program_input enabled")]
728 fn test_serialize_parameters_with_many_accounts(
729 direct_account_pointers_in_program_input: bool,
730 ) {
731 struct TestCase {
732 num_ix_accounts: usize,
733 append_dup_account: bool,
734 expected_err: Option<InstructionError>,
735 name: &'static str,
736 }
737
738 for virtual_address_space_adjustments in [false, true] {
739 for TestCase {
740 num_ix_accounts,
741 append_dup_account,
742 expected_err,
743 name,
744 } in [
745 TestCase {
746 name: "serialize max accounts with cap",
747 num_ix_accounts: MAX_ACCOUNTS_PER_INSTRUCTION,
748 append_dup_account: false,
749 expected_err: None,
750 },
751 TestCase {
752 name: "serialize too many accounts with cap",
753 num_ix_accounts: MAX_ACCOUNTS_PER_INSTRUCTION + 1,
754 append_dup_account: false,
755 expected_err: Some(InstructionError::MaxAccountsExceeded),
756 },
757 TestCase {
758 name: "serialize too many accounts and append dup with cap",
759 num_ix_accounts: MAX_ACCOUNTS_PER_INSTRUCTION,
760 append_dup_account: true,
761 expected_err: Some(InstructionError::MaxAccountsExceeded),
762 },
763 ] {
764 let program_id = solana_pubkey::new_rand();
765 let mut transaction_accounts = vec![(
766 program_id,
767 AccountSharedData::from(Account {
768 lamports: 0,
769 data: vec![],
770 owner: bpf_loader::id(),
771 executable: true,
772 rent_epoch: 0,
773 }),
774 )];
775 for _ in 0..num_ix_accounts {
776 transaction_accounts.push((
777 Pubkey::new_unique(),
778 AccountSharedData::from(Account {
779 lamports: 0,
780 data: vec![],
781 owner: program_id,
782 executable: false,
783 rent_epoch: 0,
784 }),
785 ));
786 }
787
788 let transaction_accounts_indexes: Vec<IndexOfAccount> =
789 (0..num_ix_accounts as u16).collect();
790 let mut instruction_accounts =
791 deduplicated_instruction_accounts(&transaction_accounts_indexes, |_| false);
792 if append_dup_account {
793 instruction_accounts.push(instruction_accounts.last().cloned().unwrap());
794 }
795 let instruction_data = vec![];
796
797 with_mock_invoke_context!(
798 invoke_context,
799 transaction_context,
800 transaction_accounts
801 );
802 if instruction_accounts.len() > MAX_ACCOUNTS_PER_INSTRUCTION {
803 let dedup_map = vec![u16::MAX; MAX_ACCOUNTS_PER_TRANSACTION];
807 invoke_context
808 .transaction_context
809 .configure_instruction_at_index(
810 0,
811 0,
812 instruction_accounts,
813 dedup_map,
814 Cow::Owned(instruction_data.clone()),
815 Some(0),
816 )
817 .unwrap();
818 } else {
819 invoke_context
820 .transaction_context
821 .configure_top_level_instruction_for_tests(
822 0,
823 instruction_accounts,
824 instruction_data.clone(),
825 )
826 .unwrap();
827 }
828 invoke_context.push().unwrap();
829 let instruction_context = invoke_context
830 .transaction_context
831 .get_current_instruction_context()
832 .unwrap();
833
834 let serialization_result = serialize_parameters(
835 &instruction_context,
836 virtual_address_space_adjustments,
837 false, direct_account_pointers_in_program_input,
839 );
840 assert_eq!(
841 serialization_result.as_ref().err(),
842 expected_err.as_ref(),
843 "{name} test case failed",
844 );
845 if expected_err.is_some() {
846 continue;
847 }
848
849 let (mut serialized, regions, _account_lengths, _instruction_data_offset) =
850 serialization_result.unwrap();
851 let mut serialized_regions = concat_regions(®ions);
852 let (de_program_id, de_accounts, de_instruction_data) = unsafe {
853 deserialize(
854 if !virtual_address_space_adjustments {
855 serialized.as_slice_mut()
856 } else {
857 serialized_regions.as_slice_mut()
858 }
859 .first_mut()
860 .unwrap() as *mut u8,
861 )
862 };
863 assert_eq!(de_program_id, &program_id);
864 assert_eq!(de_instruction_data, &instruction_data);
865 for account_info in de_accounts {
866 let index_in_transaction = invoke_context
867 .transaction_context
868 .find_index_of_account(account_info.key)
869 .unwrap();
870 let account = invoke_context
871 .transaction_context
872 .accounts()
873 .try_borrow(index_in_transaction)
874 .unwrap();
875 assert_eq!(account.lamports(), account_info.lamports());
876 assert_eq!(account.data(), &account_info.data.borrow()[..]);
877 assert_eq!(account.owner(), account_info.owner);
878 assert_eq!(account.executable(), account_info.executable);
879 #[allow(deprecated)]
880 {
881 assert_eq!(0, account_info._unused);
883 }
884 }
885 }
886 }
887 }
888
889 #[test_case(false; "direct_account_pointers_in_program_input disabled")]
890 #[test_case(true; "direct_account_pointers_in_program_input enabled")]
891 fn test_serialize_parameters(direct_account_pointers_in_program_input: bool) {
892 for virtual_address_space_adjustments in [false, true] {
893 let program_id = solana_pubkey::new_rand();
894 let transaction_accounts = vec![
895 (
896 program_id,
897 AccountSharedData::from(Account {
898 lamports: 0,
899 data: vec![],
900 owner: bpf_loader::id(),
901 executable: true,
902 rent_epoch: 0,
903 }),
904 ),
905 (
906 solana_pubkey::new_rand(),
907 AccountSharedData::from(Account {
908 lamports: 1,
909 data: vec![1u8, 2, 3, 4, 5],
910 owner: bpf_loader::id(),
911 executable: false,
912 rent_epoch: 100,
913 }),
914 ),
915 (
916 solana_pubkey::new_rand(),
917 AccountSharedData::from(Account {
918 lamports: 2,
919 data: vec![11u8, 12, 13, 14, 15, 16, 17, 18, 19],
920 owner: bpf_loader::id(),
921 executable: true,
922 rent_epoch: 200,
923 }),
924 ),
925 (
926 solana_pubkey::new_rand(),
927 AccountSharedData::from(Account {
928 lamports: 3,
929 data: vec![],
930 owner: bpf_loader::id(),
931 executable: false,
932 rent_epoch: 3100,
933 }),
934 ),
935 (
936 solana_pubkey::new_rand(),
937 AccountSharedData::from(Account {
938 lamports: 4,
939 data: vec![1u8, 2, 3, 4, 5],
940 owner: bpf_loader::id(),
941 executable: false,
942 rent_epoch: 100,
943 }),
944 ),
945 (
946 solana_pubkey::new_rand(),
947 AccountSharedData::from(Account {
948 lamports: 5,
949 data: vec![11u8, 12, 13, 14, 15, 16, 17, 18, 19],
950 owner: bpf_loader::id(),
951 executable: true,
952 rent_epoch: 200,
953 }),
954 ),
955 (
956 solana_pubkey::new_rand(),
957 AccountSharedData::from(Account {
958 lamports: 6,
959 data: vec![],
960 owner: bpf_loader::id(),
961 executable: false,
962 rent_epoch: 3100,
963 }),
964 ),
965 (
966 program_id,
967 AccountSharedData::from(Account {
968 lamports: 0,
969 data: vec![],
970 owner: bpf_loader_deprecated::id(),
971 executable: true,
972 rent_epoch: 0,
973 }),
974 ),
975 ];
976 let instruction_accounts =
977 deduplicated_instruction_accounts(&[1, 1, 2, 3, 4, 4, 5, 6], |index| index >= 4);
978 let instruction_data = vec![1u8, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11];
979 let original_accounts = transaction_accounts.clone();
980 with_mock_invoke_context!(invoke_context, transaction_context, transaction_accounts);
981 invoke_context
982 .transaction_context
983 .configure_top_level_instruction_for_tests(
984 0,
985 instruction_accounts.clone(),
986 instruction_data.clone(),
987 )
988 .unwrap();
989 invoke_context.push().unwrap();
990 let instruction_context = invoke_context
991 .transaction_context
992 .get_current_instruction_context()
993 .unwrap();
994
995 let (mut serialized, regions, accounts_metadata, _instruction_data_offset) =
997 serialize_parameters(
998 &instruction_context,
999 virtual_address_space_adjustments,
1000 false, direct_account_pointers_in_program_input,
1002 )
1003 .unwrap();
1004
1005 let mut serialized_regions = concat_regions(®ions);
1006 if !virtual_address_space_adjustments {
1007 assert_eq!(serialized.as_slice(), serialized_regions.as_slice());
1008 }
1009 let (de_program_id, de_accounts, de_instruction_data) = unsafe {
1010 deserialize(
1011 if !virtual_address_space_adjustments {
1012 serialized.as_slice_mut()
1013 } else {
1014 serialized_regions.as_slice_mut()
1015 }
1016 .first_mut()
1017 .unwrap() as *mut u8,
1018 )
1019 };
1020
1021 assert_eq!(&program_id, de_program_id);
1022 assert_eq!(instruction_data, de_instruction_data);
1023 assert_eq!(
1024 (de_instruction_data.first().unwrap() as *const u8).align_offset(BPF_ALIGN_OF_U128),
1025 0
1026 );
1027 for account_info in de_accounts {
1028 let index_in_transaction = invoke_context
1029 .transaction_context
1030 .find_index_of_account(account_info.key)
1031 .unwrap();
1032 let account = invoke_context
1033 .transaction_context
1034 .accounts()
1035 .try_borrow(index_in_transaction)
1036 .unwrap();
1037 assert_eq!(account.lamports(), account_info.lamports());
1038 assert_eq!(account.data(), &account_info.data.borrow()[..]);
1039 assert_eq!(account.owner(), account_info.owner);
1040 assert_eq!(account.executable(), account_info.executable);
1041 #[allow(deprecated)]
1042 {
1043 assert_eq!(0, account_info._unused);
1045 }
1046
1047 assert_eq!(
1048 (*account_info.lamports.borrow() as *const u64).align_offset(BPF_ALIGN_OF_U128),
1049 0
1050 );
1051 assert_eq!(
1052 account_info
1053 .data
1054 .borrow()
1055 .as_ptr()
1056 .align_offset(BPF_ALIGN_OF_U128),
1057 0
1058 );
1059 }
1060
1061 deserialize_parameters(
1062 &instruction_context,
1063 virtual_address_space_adjustments,
1064 false, serialized.as_slice(),
1066 &accounts_metadata,
1067 )
1068 .unwrap();
1069 for (index_in_transaction, (_key, original_account)) in
1070 original_accounts.iter().enumerate()
1071 {
1072 let account = invoke_context
1073 .transaction_context
1074 .accounts()
1075 .try_borrow(index_in_transaction as IndexOfAccount)
1076 .unwrap();
1077 assert_eq!(&*account, original_account);
1078 }
1079
1080 invoke_context.pop().unwrap();
1081 invoke_context
1083 .transaction_context
1084 .configure_top_level_instruction_for_tests(
1085 7,
1086 instruction_accounts,
1087 instruction_data.clone(),
1088 )
1089 .unwrap();
1090 invoke_context.push().unwrap();
1091 let instruction_context = invoke_context
1092 .transaction_context
1093 .get_current_instruction_context()
1094 .unwrap();
1095
1096 let (mut serialized, regions, account_lengths, _instruction_data_offset) =
1097 serialize_parameters(
1098 &instruction_context,
1099 virtual_address_space_adjustments,
1100 false, direct_account_pointers_in_program_input,
1102 )
1103 .unwrap();
1104 let mut serialized_regions = concat_regions(®ions);
1105
1106 let (de_program_id, de_accounts, de_instruction_data) = unsafe {
1107 deserialize_for_abiv0(
1108 if !virtual_address_space_adjustments {
1109 serialized.as_slice_mut()
1110 } else {
1111 serialized_regions.as_slice_mut()
1112 }
1113 .first_mut()
1114 .unwrap() as *mut u8,
1115 )
1116 };
1117 assert_eq!(&program_id, de_program_id);
1118 assert_eq!(instruction_data, de_instruction_data);
1119 for account_info in de_accounts {
1120 let index_in_transaction = invoke_context
1121 .transaction_context
1122 .find_index_of_account(account_info.key)
1123 .unwrap();
1124 let account = invoke_context
1125 .transaction_context
1126 .accounts()
1127 .try_borrow(index_in_transaction)
1128 .unwrap();
1129 assert_eq!(account.lamports(), account_info.lamports());
1130 assert_eq!(account.data(), &account_info.data.borrow()[..]);
1131 assert_eq!(account.owner(), account_info.owner);
1132 assert_eq!(account.executable(), account_info.executable);
1133 #[allow(deprecated)]
1134 {
1135 assert_eq!(u64::MAX, account_info._unused);
1136 }
1137 }
1138
1139 deserialize_parameters(
1140 &instruction_context,
1141 virtual_address_space_adjustments,
1142 false, serialized.as_slice(),
1144 &account_lengths,
1145 )
1146 .unwrap();
1147 for (index_in_transaction, (_key, original_account)) in
1148 original_accounts.iter().enumerate()
1149 {
1150 let account = invoke_context
1151 .transaction_context
1152 .accounts()
1153 .try_borrow(index_in_transaction as IndexOfAccount)
1154 .unwrap();
1155 assert_eq!(&*account, original_account);
1156 }
1157 }
1158 }
1159
1160 #[test_case(false; "direct_account_pointers_in_program_input disabled")]
1161 #[test_case(true; "direct_account_pointers_in_program_input enabled")]
1162 fn test_serialize_parameters_mask_out_rent_epoch_in_vm_serialization(
1163 direct_account_pointers_in_program_input: bool,
1164 ) {
1165 let transaction_accounts = vec![
1166 (
1167 solana_pubkey::new_rand(),
1168 AccountSharedData::from(Account {
1169 lamports: 0,
1170 data: vec![],
1171 owner: bpf_loader::id(),
1172 executable: true,
1173 rent_epoch: 0,
1174 }),
1175 ),
1176 (
1177 solana_pubkey::new_rand(),
1178 AccountSharedData::from(Account {
1179 lamports: 1,
1180 data: vec![1u8, 2, 3, 4, 5],
1181 owner: bpf_loader::id(),
1182 executable: false,
1183 rent_epoch: 100,
1184 }),
1185 ),
1186 (
1187 solana_pubkey::new_rand(),
1188 AccountSharedData::from(Account {
1189 lamports: 2,
1190 data: vec![11u8, 12, 13, 14, 15, 16, 17, 18, 19],
1191 owner: bpf_loader::id(),
1192 executable: true,
1193 rent_epoch: 200,
1194 }),
1195 ),
1196 (
1197 solana_pubkey::new_rand(),
1198 AccountSharedData::from(Account {
1199 lamports: 3,
1200 data: vec![],
1201 owner: bpf_loader::id(),
1202 executable: false,
1203 rent_epoch: 300,
1204 }),
1205 ),
1206 (
1207 solana_pubkey::new_rand(),
1208 AccountSharedData::from(Account {
1209 lamports: 4,
1210 data: vec![1u8, 2, 3, 4, 5],
1211 owner: bpf_loader::id(),
1212 executable: false,
1213 rent_epoch: 100,
1214 }),
1215 ),
1216 (
1217 solana_pubkey::new_rand(),
1218 AccountSharedData::from(Account {
1219 lamports: 5,
1220 data: vec![11u8, 12, 13, 14, 15, 16, 17, 18, 19],
1221 owner: bpf_loader::id(),
1222 executable: true,
1223 rent_epoch: 200,
1224 }),
1225 ),
1226 (
1227 solana_pubkey::new_rand(),
1228 AccountSharedData::from(Account {
1229 lamports: 6,
1230 data: vec![],
1231 owner: bpf_loader::id(),
1232 executable: false,
1233 rent_epoch: 3100,
1234 }),
1235 ),
1236 (
1237 solana_pubkey::new_rand(),
1238 AccountSharedData::from(Account {
1239 lamports: 0,
1240 data: vec![],
1241 owner: bpf_loader_deprecated::id(),
1242 executable: true,
1243 rent_epoch: 0,
1244 }),
1245 ),
1246 ];
1247 let instruction_accounts =
1248 deduplicated_instruction_accounts(&[1, 1, 2, 3, 4, 4, 5, 6], |index| index >= 4);
1249 with_mock_invoke_context!(invoke_context, transaction_context, transaction_accounts);
1250 invoke_context
1251 .transaction_context
1252 .configure_top_level_instruction_for_tests(0, instruction_accounts.clone(), vec![])
1253 .unwrap();
1254 invoke_context.push().unwrap();
1255 let instruction_context = invoke_context
1256 .transaction_context
1257 .get_current_instruction_context()
1258 .unwrap();
1259
1260 let (_serialized, regions, _accounts_metadata, _instruction_data_offset) =
1262 serialize_parameters(
1263 &instruction_context,
1264 true,
1265 false, direct_account_pointers_in_program_input,
1267 )
1268 .unwrap();
1269
1270 let mut serialized_regions = concat_regions(®ions);
1271 let (_de_program_id, de_accounts, _de_instruction_data) = unsafe {
1272 deserialize(serialized_regions.as_slice_mut().first_mut().unwrap() as *mut u8)
1273 };
1274
1275 for account_info in de_accounts {
1276 #[allow(deprecated)]
1278 {
1279 assert_eq!(0, account_info._unused);
1280 }
1281 }
1282
1283 invoke_context
1285 .transaction_context
1286 .configure_top_level_instruction_for_tests(7, instruction_accounts, vec![])
1287 .unwrap();
1288 invoke_context.push().unwrap();
1289 let instruction_context = invoke_context
1290 .transaction_context
1291 .get_current_instruction_context()
1292 .unwrap();
1293
1294 let (_serialized, regions, _account_lengths, _instruction_data_offset) =
1295 serialize_parameters(
1296 &instruction_context,
1297 true,
1298 false, direct_account_pointers_in_program_input,
1300 )
1301 .unwrap();
1302 let mut serialized_regions = concat_regions(®ions);
1303
1304 let (_de_program_id, de_accounts, _de_instruction_data) = unsafe {
1305 deserialize_for_abiv0(serialized_regions.as_slice_mut().first_mut().unwrap() as *mut u8)
1306 };
1307 for account_info in de_accounts {
1308 #[allow(deprecated)]
1309 {
1310 assert_eq!(account_info._unused, u64::MAX);
1311 }
1312 }
1313 }
1314
1315 #[deny(unsafe_op_in_unsafe_fn)]
1317 unsafe fn deserialize_for_abiv0<'a>(
1318 input: *mut u8,
1319 ) -> (&'a Pubkey, Vec<AccountInfo<'a>>, &'a [u8]) {
1320 struct Ptr<T>(std::marker::PhantomData<T>);
1322 impl<T> Ptr<T> {
1323 const COULD_BE_UNALIGNED: bool = std::mem::align_of::<T>() > 1;
1324
1325 #[inline(always)]
1326 fn read_possibly_unaligned(input: *mut u8, offset: usize) -> T {
1327 unsafe {
1328 let src = input.add(offset) as *const T;
1329 if Self::COULD_BE_UNALIGNED {
1330 src.read_unaligned()
1331 } else {
1332 src.read()
1333 }
1334 }
1335 }
1336
1337 #[inline(always)]
1346 fn ref_possibly_unaligned<'a>(input: *mut u8, offset: usize) -> &'a T {
1347 #[allow(clippy::transmute_ptr_to_ref)]
1348 unsafe {
1349 transmute(input.add(offset) as *const T)
1350 }
1351 }
1352
1353 #[inline(always)]
1355 fn mut_possibly_unaligned<'a>(input: *mut u8, offset: usize) -> &'a mut T {
1356 #[allow(clippy::transmute_ptr_to_ref)]
1357 unsafe {
1358 transmute(input.add(offset) as *mut T)
1359 }
1360 }
1361 }
1362
1363 let mut offset: usize = 0;
1364
1365 let num_accounts = Ptr::<u64>::read_possibly_unaligned(input, offset) as usize;
1368 offset += size_of::<u64>();
1369
1370 let mut accounts = Vec::with_capacity(num_accounts);
1373 for _ in 0..num_accounts {
1374 let dup_info = Ptr::<u8>::read_possibly_unaligned(input, offset);
1375 offset += size_of::<u8>();
1376 if dup_info == NON_DUP_MARKER {
1377 let is_signer = Ptr::<u8>::read_possibly_unaligned(input, offset) != 0;
1378 offset += size_of::<u8>();
1379
1380 let is_writable = Ptr::<u8>::read_possibly_unaligned(input, offset) != 0;
1381 offset += size_of::<u8>();
1382
1383 let key = Ptr::<Pubkey>::ref_possibly_unaligned(input, offset);
1384 offset += size_of::<Pubkey>();
1385
1386 let lamports = Rc::new(RefCell::new(Ptr::mut_possibly_unaligned(input, offset)));
1387 offset += size_of::<u64>();
1388
1389 let data_len = Ptr::<u64>::read_possibly_unaligned(input, offset) as usize;
1390 offset += size_of::<u64>();
1391
1392 let data = Rc::new(RefCell::new(unsafe {
1393 from_raw_parts_mut(input.add(offset), data_len)
1394 }));
1395 offset += data_len;
1396
1397 let owner: &Pubkey = Ptr::<Pubkey>::ref_possibly_unaligned(input, offset);
1398 offset += size_of::<Pubkey>();
1399
1400 let executable = Ptr::<u8>::read_possibly_unaligned(input, offset) != 0;
1401 offset += size_of::<u8>();
1402
1403 let unused = Ptr::<u64>::read_possibly_unaligned(input, offset);
1404 offset += size_of::<u64>();
1405
1406 #[allow(deprecated)]
1407 accounts.push(AccountInfo {
1408 key,
1409 is_signer,
1410 is_writable,
1411 lamports,
1412 data,
1413 owner,
1414 executable,
1415 _unused: unused,
1416 });
1417 } else {
1418 accounts.push(accounts.get(dup_info as usize).unwrap().clone());
1420 }
1421 }
1422
1423 let instruction_data_len = Ptr::<u64>::read_possibly_unaligned(input, offset) as usize;
1426 offset += size_of::<u64>();
1427
1428 let instruction_data = unsafe { from_raw_parts(input.add(offset), instruction_data_len) };
1429 offset += instruction_data_len;
1430
1431 let program_id = Ptr::<Pubkey>::ref_possibly_unaligned(input, offset);
1434
1435 (program_id, accounts, instruction_data)
1436 }
1437
1438 fn concat_regions(regions: &[MemoryRegion]) -> AlignedMemory<HOST_ALIGN> {
1439 let last_region = regions.last().unwrap();
1440 let mut mem = AlignedMemory::zero_filled(
1441 (last_region.vm_addr - MM_INPUT_START + last_region.len) as usize,
1442 );
1443 for region in regions {
1444 let host_slice = unsafe {
1445 slice::from_raw_parts(region.host_addr as *const u8, region.len as usize)
1446 };
1447 mem.as_slice_mut()[(region.vm_addr - MM_INPUT_START) as usize..][..region.len as usize]
1448 .copy_from_slice(host_slice)
1449 }
1450 mem
1451 }
1452
1453 #[test]
1454 fn test_access_violation_handler() {
1455 let program_id = Pubkey::new_unique();
1456 let shared_account = AccountSharedData::new(0, 4, &program_id);
1457 let mut transaction_context = TransactionContext::new(
1458 vec![
1459 (
1460 Pubkey::new_unique(),
1461 AccountSharedData::new(0, 4, &program_id),
1462 ), (Pubkey::new_unique(), shared_account.clone()), (
1465 Pubkey::new_unique(),
1466 AccountSharedData::new(0, 0, &program_id),
1467 ), (
1469 Pubkey::new_unique(),
1470 AccountSharedData::new(
1471 0,
1472 MAX_PERMITTED_DATA_LENGTH as usize - 0x100,
1473 &program_id,
1474 ),
1475 ), (
1477 Pubkey::new_unique(),
1478 AccountSharedData::new(0, 0, &program_id),
1479 ), (
1481 Pubkey::new_unique(),
1482 AccountSharedData::new(0, 0x3000, &program_id),
1483 ), (program_id, AccountSharedData::default()), ],
1486 Rent::default(),
1487 1,
1488 1,
1489 1,
1490 );
1491 let transaction_accounts_indexes = [0, 1, 2, 3, 4, 5];
1492 let instruction_accounts =
1493 deduplicated_instruction_accounts(&transaction_accounts_indexes, |index| index > 0);
1494 transaction_context
1495 .configure_top_level_instruction_for_tests(6, instruction_accounts, vec![])
1496 .unwrap();
1497 transaction_context.push().unwrap();
1498 let instruction_context = transaction_context
1499 .get_current_instruction_context()
1500 .unwrap();
1501 let account_start_offsets = [
1502 MM_INPUT_START,
1503 MM_INPUT_START + 4 + MAX_PERMITTED_DATA_INCREASE as u64,
1504 MM_INPUT_START + (4 + MAX_PERMITTED_DATA_INCREASE as u64) * 2,
1505 MM_INPUT_START + (4 + MAX_PERMITTED_DATA_INCREASE as u64) * 3,
1506 ];
1507 let regions = account_start_offsets
1508 .iter()
1509 .enumerate()
1510 .map(|(index_in_instruction, account_start_offset)| {
1511 create_memory_region_of_account(
1512 &mut instruction_context
1513 .try_borrow_instruction_account(index_in_instruction as IndexOfAccount)
1514 .unwrap(),
1515 *account_start_offset,
1516 )
1517 .unwrap()
1518 })
1519 .collect::<Vec<_>>();
1520 let config = Config {
1521 aligned_memory_mapping: false,
1522 ..Config::default()
1523 };
1524 let mut memory_mapping = unsafe {
1525 MemoryMapping::new_with_access_violation_handler(
1526 regions,
1527 &config,
1528 SBPFVersion::V3,
1529 transaction_context.access_violation_handler(true, true),
1530 )
1531 .unwrap()
1532 };
1533
1534 memory_mapping
1536 .load::<u32>(account_start_offsets[0])
1537 .unwrap();
1538
1539 memory_mapping
1541 .load::<u32>(account_start_offsets[1])
1542 .unwrap();
1543
1544 memory_mapping
1546 .load::<u32>(account_start_offsets[0] + 4)
1547 .unwrap_err();
1548
1549 memory_mapping
1551 .store::<u32>(0, account_start_offsets[0])
1552 .unwrap_err();
1553
1554 let _shared_account_ref = shared_account;
1558 assert!(
1559 transaction_context
1560 .accounts()
1561 .try_borrow_mut(1)
1562 .unwrap()
1563 .is_shared()
1564 );
1565 memory_mapping
1566 .store::<u32>(0, account_start_offsets[1])
1567 .unwrap();
1568 assert!(
1569 !transaction_context
1570 .accounts()
1571 .try_borrow_mut(1)
1572 .unwrap()
1573 .is_shared()
1574 );
1575 assert_eq!(
1576 transaction_context
1577 .accounts()
1578 .try_borrow(1)
1579 .unwrap()
1580 .data()
1581 .len(),
1582 4,
1583 );
1584
1585 memory_mapping
1587 .load::<u32>(account_start_offsets[1] + 4)
1588 .unwrap_err();
1589
1590 memory_mapping
1593 .store::<u32>(0, account_start_offsets[1] + 4)
1594 .unwrap();
1595 assert_eq!(
1596 transaction_context
1597 .accounts()
1598 .try_borrow(1)
1599 .unwrap()
1600 .data()
1601 .len(),
1602 4 + MAX_PERMITTED_DATA_INCREASE,
1603 );
1604 assert!(
1605 transaction_context
1606 .accounts()
1607 .try_borrow(1)
1608 .unwrap()
1609 .data()
1610 .len()
1611 < 0x3000
1612 );
1613
1614 memory_mapping
1617 .store::<u32>(0, account_start_offsets[3] + MAX_PERMITTED_DATA_LENGTH - 4)
1618 .unwrap();
1619 assert_eq!(
1620 transaction_context
1621 .accounts()
1622 .try_borrow(3)
1623 .unwrap()
1624 .data()
1625 .len(),
1626 MAX_PERMITTED_DATA_LENGTH as usize,
1627 );
1628
1629 memory_mapping
1632 .load::<u32>(account_start_offsets[3] + MAX_PERMITTED_DATA_LENGTH)
1633 .unwrap_err();
1634 memory_mapping
1635 .store::<u32>(0, account_start_offsets[3] + MAX_PERMITTED_DATA_LENGTH)
1636 .unwrap_err();
1637
1638 let remaining_allowed_growth: usize = 0x700;
1640 for index_in_instruction in 4..6 {
1641 let mut borrowed_account = instruction_context
1642 .try_borrow_instruction_account(index_in_instruction)
1643 .unwrap();
1644 borrowed_account
1645 .set_data_from_slice(&vec![0u8; MAX_PERMITTED_DATA_LENGTH as usize])
1646 .unwrap();
1647 }
1648 assert_eq!(
1649 transaction_context.accounts().resize_delta(),
1650 MAX_PERMITTED_ACCOUNTS_DATA_ALLOCATIONS_PER_TRANSACTION
1651 - remaining_allowed_growth as i64,
1652 );
1653
1654 memory_mapping
1657 .store::<u32>(0, account_start_offsets[2] + 0x500)
1658 .unwrap();
1659 assert_eq!(
1660 transaction_context
1661 .accounts()
1662 .try_borrow(2)
1663 .unwrap()
1664 .data()
1665 .len(),
1666 remaining_allowed_growth,
1667 );
1668 }
1669}