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