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