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