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