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 struct SerializationResult {
241 pub parameter_bytes: AlignedMemory<HOST_ALIGN>,
243
244 pub regions: Vec<MemoryRegion>,
246
247 pub accounts_metadata: Vec<SerializedAccountMetadata>,
249
250 pub instruction_data_offset: usize,
252
253 pub account_pointers_offset: Option<usize>,
257}
258
259pub fn serialize_parameters(
260 instruction_context: &InstructionContext,
261 virtual_address_space_adjustments: bool,
262 account_data_direct_mapping: bool,
263 direct_account_pointers_in_program_input: bool,
264) -> Result<SerializationResult, InstructionError> {
265 let num_ix_accounts = instruction_context.get_number_of_instruction_accounts();
266 if num_ix_accounts > MAX_ACCOUNTS_PER_INSTRUCTION as IndexOfAccount {
267 return Err(InstructionError::MaxAccountsExceeded);
268 }
269
270 let program_id = *instruction_context.get_program_key()?;
271 let is_loader_deprecated =
272 instruction_context.get_program_owner()? == bpf_loader_deprecated::id();
273
274 let accounts = (0..instruction_context.get_number_of_instruction_accounts())
275 .map(|instruction_account_index| {
276 if let Some(index) = instruction_context
277 .is_instruction_account_duplicate(instruction_account_index)
278 .unwrap()
279 {
280 SerializeAccount::Duplicate(index)
281 } else {
282 let account = instruction_context
283 .try_borrow_instruction_account(instruction_account_index)
284 .unwrap();
285 SerializeAccount::Account(instruction_account_index, account)
286 }
287 })
288 .collect::<Vec<_>>();
293
294 if is_loader_deprecated {
295 let (parameter_bytes, regions, accounts_metadata, instruction_data_offset) =
297 serialize_parameters_for_abiv0(
298 accounts,
299 instruction_context.get_instruction_data(),
300 &program_id,
301 virtual_address_space_adjustments,
302 account_data_direct_mapping,
303 )?;
304 Ok(SerializationResult {
305 parameter_bytes,
306 regions,
307 accounts_metadata,
308 instruction_data_offset,
309 account_pointers_offset: None,
310 })
311 } else {
312 serialize_parameters_for_abiv1(
314 accounts,
315 instruction_context.get_instruction_data(),
316 &program_id,
317 virtual_address_space_adjustments,
318 account_data_direct_mapping,
319 direct_account_pointers_in_program_input,
321 )
322 }
323}
324
325pub fn deserialize_parameters(
326 instruction_context: &InstructionContext,
327 virtual_address_space_adjustments: bool,
328 account_data_direct_mapping: bool,
329 buffer: &[u8],
330 accounts_metadata: &[SerializedAccountMetadata],
331) -> Result<(), InstructionError> {
332 let is_loader_deprecated =
333 instruction_context.get_program_owner()? == bpf_loader_deprecated::id();
334 let account_lengths = accounts_metadata.iter().map(|a| a.original_data_len);
335 if is_loader_deprecated {
336 deserialize_parameters_for_abiv0(
338 instruction_context,
339 virtual_address_space_adjustments,
340 account_data_direct_mapping,
341 buffer,
342 account_lengths,
343 )
344 } else {
345 deserialize_parameters_for_abiv1(
347 instruction_context,
348 virtual_address_space_adjustments,
349 account_data_direct_mapping,
350 buffer,
351 account_lengths,
352 )
353 }
354}
355
356fn serialize_parameters_for_abiv0(
357 accounts: Vec<SerializeAccount>,
358 instruction_data: &[u8],
359 program_id: &Pubkey,
360 virtual_address_space_adjustments: bool,
361 account_data_direct_mapping: bool,
362) -> Result<
363 (
364 AlignedMemory<HOST_ALIGN>,
365 Vec<MemoryRegion>,
366 Vec<SerializedAccountMetadata>,
367 usize,
368 ),
369 InstructionError,
370> {
371 let mut size = size_of::<u64>();
373 for account in &accounts {
374 size += 1; match account {
376 SerializeAccount::Duplicate(_) => {}
377 SerializeAccount::Account(_, account) => {
378 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) {
387 size += account.get_data().len();
388 }
389 }
390 }
391 }
392 size += size_of::<u64>() + instruction_data.len() + size_of::<Pubkey>(); let mut s = Serializer::new(
397 size,
398 MM_INPUT_START,
399 true,
400 virtual_address_space_adjustments,
401 account_data_direct_mapping,
402 );
403
404 let mut accounts_metadata: Vec<SerializedAccountMetadata> = Vec::with_capacity(accounts.len());
405 s.write::<u64>((accounts.len() as u64).to_le());
406 for account in accounts {
407 match account {
408 SerializeAccount::Duplicate(position) => {
409 accounts_metadata.push(accounts_metadata.get(position as usize).unwrap().clone());
410 s.write(position as u8);
411 }
412 SerializeAccount::Account(_, mut account) => {
413 let vm_addr = s.write::<u8>(NON_DUP_MARKER);
414 s.write::<u8>(account.is_signer() as u8);
415 s.write::<u8>(account.is_writable() as u8);
416 let vm_key_addr = s.write_all(account.get_key().as_ref());
417 let vm_lamports_addr = s.write::<u64>(account.get_lamports().to_le());
418 s.write::<u64>((account.get_data().len() as u64).to_le());
419 let vm_data_addr = s.write_account(&mut account)?;
420 let vm_owner_addr = s.write_all(account.get_owner().as_ref());
421 #[expect(deprecated)]
422 s.write::<u8>(account.is_executable() as u8);
423 let rent_epoch = u64::MAX;
424 s.write::<u64>(rent_epoch.to_le());
425 accounts_metadata.push(SerializedAccountMetadata {
426 vm_addr,
427 original_data_len: account.get_data().len(),
428 vm_key_addr,
429 vm_lamports_addr,
430 vm_owner_addr,
431 vm_data_addr,
432 });
433 }
434 };
435 }
436 s.write::<u64>((instruction_data.len() as u64).to_le());
437 let instruction_data_offset = s.write_all(instruction_data);
438 s.write_all(program_id.as_ref());
439
440 let (mem, regions) = s.finish();
441 Ok((
442 mem,
443 regions,
444 accounts_metadata,
445 instruction_data_offset as usize,
446 ))
447}
448
449fn deserialize_parameters_for_abiv0<I: IntoIterator<Item = usize>>(
450 instruction_context: &InstructionContext,
451 virtual_address_space_adjustments: bool,
452 account_data_direct_mapping: bool,
453 buffer: &[u8],
454 account_lengths: I,
455) -> Result<(), InstructionError> {
456 let mut start = size_of::<u64>(); for (instruction_account_index, pre_len) in
458 (0..instruction_context.get_number_of_instruction_accounts()).zip(account_lengths)
459 {
460 let duplicate =
461 instruction_context.is_instruction_account_duplicate(instruction_account_index)?;
462 start += 1; if duplicate.is_none() {
464 let mut borrowed_account =
465 instruction_context.try_borrow_instruction_account(instruction_account_index)?;
466 start += size_of::<u8>(); start += size_of::<u8>(); start += size_of::<Pubkey>(); let lamports = buffer
470 .get(start..start.saturating_add(8))
471 .map(<[u8; 8]>::try_from)
472 .and_then(Result::ok)
473 .map(u64::from_le_bytes)
474 .ok_or(InstructionError::InvalidArgument)?;
475 if borrowed_account.get_lamports() != lamports {
476 borrowed_account.set_lamports(lamports)?;
477 }
478 start += size_of::<u64>() + size_of::<u64>(); if !virtual_address_space_adjustments {
481 let data = buffer
482 .get(start..start + pre_len)
483 .ok_or(InstructionError::InvalidArgument)?;
484 match borrowed_account.can_data_be_resized(pre_len) {
486 Ok(()) => borrowed_account.set_data_from_slice(data)?,
487 Err(err) if borrowed_account.get_data() != data => return Err(err),
488 _ => {}
489 }
490 } else if !account_data_direct_mapping && borrowed_account.can_data_be_changed().is_ok()
491 {
492 let data = buffer
493 .get(start..start + pre_len)
494 .ok_or(InstructionError::InvalidArgument)?;
495 borrowed_account.set_data_from_slice(data)?;
496 } else if borrowed_account.get_data().len() != pre_len {
497 borrowed_account.set_data_length(pre_len)?;
498 }
499 if !(virtual_address_space_adjustments && account_data_direct_mapping) {
500 start += pre_len; }
502 start += size_of::<Pubkey>() + size_of::<u8>() + size_of::<u64>(); }
506 }
507 Ok(())
508}
509
510fn serialize_parameters_for_abiv1(
511 accounts: Vec<SerializeAccount>,
512 instruction_data: &[u8],
513 program_id: &Pubkey,
514 virtual_address_space_adjustments: bool,
515 account_data_direct_mapping: bool,
516 direct_account_pointers_program_input: bool,
517) -> Result<SerializationResult, InstructionError> {
518 let mut accounts_metadata = Vec::with_capacity(accounts.len());
519 let mut size = size_of::<u64>();
521 for account in &accounts {
522 size += 1; match account {
524 SerializeAccount::Duplicate(_) => size += 7, SerializeAccount::Account(_, account) => {
526 let data_len = account.get_data().len();
527 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) {
537 size += data_len
538 + MAX_PERMITTED_DATA_INCREASE
539 + (data_len as *const u8).align_offset(BPF_ALIGN_OF_U128);
540 } else {
541 size += BPF_ALIGN_OF_U128;
542 }
543 }
544 }
545 }
546 size += size_of::<u64>() + instruction_data.len()
548 + size_of::<Pubkey>(); let account_pointers_padding = if direct_account_pointers_program_input {
552 let padding = (size as *const u8).align_offset(BPF_ALIGN_OF_U128);
553 size += padding + accounts.len() * size_of::<u64>();
554 Some(padding)
555 } else {
556 None
557 };
558
559 let mut s = Serializer::new(
560 size,
561 MM_INPUT_START,
562 false,
563 virtual_address_space_adjustments,
564 account_data_direct_mapping,
565 );
566
567 s.write::<u64>((accounts.len() as u64).to_le());
569 for account in accounts {
570 match account {
571 SerializeAccount::Account(_, mut borrowed_account) => {
572 let vm_addr = s.write::<u8>(NON_DUP_MARKER);
573 s.write::<u8>(borrowed_account.is_signer() as u8);
574 s.write::<u8>(borrowed_account.is_writable() as u8);
575 #[expect(deprecated)]
576 s.write::<u8>(borrowed_account.is_executable() as u8);
577 s.write_all(&[0u8, 0, 0, 0]);
578 let vm_key_addr = s.write_all(borrowed_account.get_key().as_ref());
579 let vm_owner_addr = s.write_all(borrowed_account.get_owner().as_ref());
580 let vm_lamports_addr = s.write::<u64>(borrowed_account.get_lamports().to_le());
581 s.write::<u64>((borrowed_account.get_data().len() as u64).to_le());
582 let vm_data_addr = s.write_account(&mut borrowed_account)?;
583 let rent_epoch = u64::MAX;
584 s.write::<u64>(rent_epoch.to_le());
585 accounts_metadata.push(SerializedAccountMetadata {
586 vm_addr,
587 original_data_len: borrowed_account.get_data().len(),
588 vm_key_addr,
589 vm_owner_addr,
590 vm_lamports_addr,
591 vm_data_addr,
592 });
593 }
594 SerializeAccount::Duplicate(position) => {
595 accounts_metadata.push(accounts_metadata.get(position as usize).unwrap().clone());
596 s.write::<u8>(position as u8);
597 s.write_all(&[0u8, 0, 0, 0, 0, 0, 0]);
598 }
599 };
600 }
601 s.write::<u64>((instruction_data.len() as u64).to_le());
602 let instruction_data_offset = s.write_all(instruction_data);
603 let account_pointers_offset = {
606 let vaddr = s.write_all(program_id.as_ref()) as usize;
607
608 if let Some(padding) = account_pointers_padding {
609 s.fill_write(padding, 0)
612 .map_err(|_| InstructionError::InvalidArgument)?;
613 for entry in accounts_metadata.iter() {
614 s.write::<u64>(entry.vm_addr.to_le());
615 }
616 Some(vaddr + size_of::<Pubkey>() + padding)
618 } else {
619 None
620 }
621 };
622
623 let (mem, regions) = s.finish();
624 Ok(SerializationResult {
625 parameter_bytes: mem,
626 regions,
627 accounts_metadata,
628 instruction_data_offset: instruction_data_offset as usize,
629 account_pointers_offset,
630 })
631}
632
633fn deserialize_parameters_for_abiv1<I: IntoIterator<Item = usize>>(
634 instruction_context: &InstructionContext,
635 virtual_address_space_adjustments: bool,
636 account_data_direct_mapping: bool,
637 buffer: &[u8],
638 account_lengths: I,
639) -> Result<(), InstructionError> {
640 let mut start = size_of::<u64>(); for (instruction_account_index, pre_len) in
642 (0..instruction_context.get_number_of_instruction_accounts()).zip(account_lengths)
643 {
644 let duplicate =
645 instruction_context.is_instruction_account_duplicate(instruction_account_index)?;
646 start += size_of::<u8>(); if duplicate.is_some() {
648 start += 7; } else {
650 let mut borrowed_account =
651 instruction_context.try_borrow_instruction_account(instruction_account_index)?;
652 start += size_of::<u8>() + size_of::<u8>() + size_of::<u8>() + size_of::<u32>() + size_of::<Pubkey>(); let owner = buffer
658 .get(start..start + size_of::<Pubkey>())
659 .ok_or(InstructionError::InvalidArgument)?;
660 start += size_of::<Pubkey>(); let lamports = buffer
662 .get(start..start.saturating_add(8))
663 .map(<[u8; 8]>::try_from)
664 .and_then(Result::ok)
665 .map(u64::from_le_bytes)
666 .ok_or(InstructionError::InvalidArgument)?;
667 if borrowed_account.get_lamports() != lamports {
668 borrowed_account.set_lamports(lamports)?;
669 }
670 start += size_of::<u64>(); let post_len = buffer
672 .get(start..start.saturating_add(8))
673 .map(<[u8; 8]>::try_from)
674 .and_then(Result::ok)
675 .map(u64::from_le_bytes)
676 .ok_or(InstructionError::InvalidArgument)? as usize;
677 start += size_of::<u64>(); if post_len.saturating_sub(pre_len) > MAX_PERMITTED_DATA_INCREASE
679 || post_len > MAX_PERMITTED_DATA_LENGTH as usize
680 {
681 return Err(InstructionError::InvalidRealloc);
682 }
683 if !virtual_address_space_adjustments {
684 let data = buffer
685 .get(start..start + post_len)
686 .ok_or(InstructionError::InvalidArgument)?;
687 match borrowed_account.can_data_be_resized(post_len) {
689 Ok(()) => borrowed_account.set_data_from_slice(data)?,
690 Err(err) if borrowed_account.get_data() != data => return Err(err),
691 _ => {}
692 }
693 } else if !account_data_direct_mapping && borrowed_account.can_data_be_changed().is_ok()
694 {
695 let data = buffer
696 .get(start..start + post_len)
697 .ok_or(InstructionError::InvalidArgument)?;
698 borrowed_account.set_data_from_slice(data)?;
699 } else if borrowed_account.get_data().len() != post_len {
700 borrowed_account.set_data_length(post_len)?;
701 }
702 start += if !(virtual_address_space_adjustments && account_data_direct_mapping) {
703 let alignment_offset = (pre_len as *const u8).align_offset(BPF_ALIGN_OF_U128);
704 pre_len .saturating_add(MAX_PERMITTED_DATA_INCREASE) .saturating_add(alignment_offset)
707 } else {
708 BPF_ALIGN_OF_U128
710 };
711 start += size_of::<u64>(); if borrowed_account.get_owner().to_bytes() != owner {
713 borrowed_account.set_owner(owner)?;
715 }
716 }
717 }
718 Ok(())
719}
720
721#[cfg(test)]
722#[allow(clippy::indexing_slicing)]
723mod tests {
724 use {
725 super::*,
726 crate::with_mock_invoke_context,
727 solana_account::{Account, AccountSharedData, ReadableAccount},
728 solana_account_info::AccountInfo,
729 solana_program_entrypoint::deserialize,
730 solana_rent::Rent,
731 solana_sbpf::{memory_region::MemoryMapping, program::SBPFVersion, vm::Config},
732 solana_sdk_ids::{bpf_loader, bpf_loader_upgradeable},
733 solana_system_interface::MAX_PERMITTED_ACCOUNTS_DATA_ALLOCATIONS_PER_TRANSACTION,
734 solana_transaction_context::{
735 MAX_ACCOUNTS_PER_TRANSACTION, instruction_accounts::InstructionAccount,
736 transaction::TransactionContext,
737 },
738 std::{
739 borrow::Cow,
740 cell::RefCell,
741 mem::transmute,
742 rc::Rc,
743 slice::{from_raw_parts, from_raw_parts_mut},
744 },
745 test_case::test_case,
746 };
747
748 fn deduplicated_instruction_accounts(
749 transaction_indexes: &[IndexOfAccount],
750 is_writable: fn(usize) -> bool,
751 ) -> Vec<InstructionAccount> {
752 transaction_indexes
753 .iter()
754 .enumerate()
755 .map(|(index_in_instruction, index_in_transaction)| {
756 InstructionAccount::new(
757 *index_in_transaction,
758 false,
759 is_writable(index_in_instruction),
760 )
761 })
762 .collect()
763 }
764
765 #[test_case(false; "direct_account_pointers_in_program_input disabled")]
766 #[test_case(true; "direct_account_pointers_in_program_input enabled")]
767 fn test_serialize_parameters_with_many_accounts(
768 direct_account_pointers_in_program_input: bool,
769 ) {
770 struct TestCase {
771 num_ix_accounts: usize,
772 append_dup_account: bool,
773 expected_err: Option<InstructionError>,
774 name: &'static str,
775 }
776
777 for virtual_address_space_adjustments in [false, true] {
778 for TestCase {
779 num_ix_accounts,
780 append_dup_account,
781 expected_err,
782 name,
783 } in [
784 TestCase {
785 name: "serialize max accounts with cap",
786 num_ix_accounts: MAX_ACCOUNTS_PER_INSTRUCTION,
787 append_dup_account: false,
788 expected_err: None,
789 },
790 TestCase {
791 name: "serialize too many accounts with cap",
792 num_ix_accounts: MAX_ACCOUNTS_PER_INSTRUCTION + 1,
793 append_dup_account: false,
794 expected_err: Some(InstructionError::MaxAccountsExceeded),
795 },
796 TestCase {
797 name: "serialize too many accounts and append dup with cap",
798 num_ix_accounts: MAX_ACCOUNTS_PER_INSTRUCTION,
799 append_dup_account: true,
800 expected_err: Some(InstructionError::MaxAccountsExceeded),
801 },
802 ] {
803 let program_id = solana_pubkey::new_rand();
804 let mut transaction_accounts = vec![(
805 program_id,
806 AccountSharedData::from(Account {
807 lamports: 0,
808 data: vec![],
809 owner: bpf_loader::id(),
810 executable: true,
811 rent_epoch: 0,
812 }),
813 )];
814 for _ in 0..num_ix_accounts {
815 transaction_accounts.push((
816 Pubkey::new_unique(),
817 AccountSharedData::from(Account {
818 lamports: 0,
819 data: vec![],
820 owner: program_id,
821 executable: false,
822 rent_epoch: 0,
823 }),
824 ));
825 }
826
827 let transaction_accounts_indexes: Vec<IndexOfAccount> =
828 (0..num_ix_accounts as u16).collect();
829 let mut instruction_accounts =
830 deduplicated_instruction_accounts(&transaction_accounts_indexes, |_| false);
831 if append_dup_account {
832 instruction_accounts.push(instruction_accounts.last().cloned().unwrap());
833 }
834 let instruction_data = vec![];
835 let num_transaction_accounts =
836 transaction_accounts.len().min(MAX_ACCOUNTS_PER_TRANSACTION);
837
838 with_mock_invoke_context!(
839 invoke_context,
840 transaction_context,
841 transaction_accounts
842 );
843 if instruction_accounts.len() > MAX_ACCOUNTS_PER_INSTRUCTION {
844 let dedup_map = vec![u8::MAX; num_transaction_accounts];
846 assert_eq!(
847 invoke_context
848 .transaction_context
849 .configure_instruction_at_index(
850 0,
851 0,
852 instruction_accounts,
853 dedup_map,
854 Cow::Owned(instruction_data.clone()),
855 Some(0),
856 ),
857 Err(InstructionError::MaxAccountsExceeded),
858 "{name} test case failed",
859 );
860 continue;
861 }
862 invoke_context
863 .transaction_context
864 .configure_top_level_instruction_for_tests(
865 0,
866 instruction_accounts,
867 instruction_data.clone(),
868 )
869 .unwrap();
870 invoke_context.push().unwrap();
871 let instruction_context = invoke_context
872 .transaction_context
873 .get_current_instruction_context()
874 .unwrap();
875
876 let serialization_result = serialize_parameters(
877 &instruction_context,
878 virtual_address_space_adjustments,
879 false, direct_account_pointers_in_program_input,
881 );
882 assert_eq!(
883 serialization_result.as_ref().err(),
884 expected_err.as_ref(),
885 "{name} test case failed",
886 );
887 if expected_err.is_some() {
888 continue;
889 }
890
891 let SerializationResult {
892 parameter_bytes: mut serialized,
893 regions,
894 account_pointers_offset,
895 ..
896 } = serialization_result.unwrap();
897 let mut serialized_regions = unsafe {
898 concat_regions(®ions)
900 };
901 let (de_program_id, de_accounts, de_instruction_data) = unsafe {
902 deserialize(
903 if !virtual_address_space_adjustments {
904 serialized.as_slice_mut()
905 } else {
906 serialized_regions.as_slice_mut()
907 }
908 .first_mut()
909 .unwrap() as *mut u8,
910 )
911 };
912 assert_eq!(de_program_id, &program_id);
913 assert_eq!(de_instruction_data, &instruction_data);
914 assert_eq!(
915 direct_account_pointers_in_program_input,
916 account_pointers_offset.is_some()
917 );
918 for account_info in de_accounts {
919 let index_in_transaction = invoke_context
920 .transaction_context
921 .find_index_of_account(account_info.key)
922 .unwrap();
923 let account = invoke_context
924 .transaction_context
925 .accounts()
926 .try_borrow(index_in_transaction)
927 .unwrap();
928 assert_eq!(account.lamports(), account_info.lamports());
929 assert_eq!(account.data(), &account_info.data.borrow()[..]);
930 assert_eq!(account.owner(), account_info.owner);
931 assert_eq!(account.executable(), account_info.executable);
932 #[allow(deprecated)]
933 {
934 assert_eq!(0, account_info._unused);
936 }
937 }
938 }
939 }
940 }
941
942 #[test_case(false; "direct_account_pointers_in_program_input disabled")]
943 #[test_case(true; "direct_account_pointers_in_program_input enabled")]
944 fn test_serialize_parameters(direct_account_pointers_in_program_input: bool) {
945 for virtual_address_space_adjustments in [false, true] {
946 let program_id = solana_pubkey::new_rand();
947 let transaction_accounts = vec![
948 (
949 program_id,
950 AccountSharedData::from(Account {
951 lamports: 0,
952 data: vec![],
953 owner: bpf_loader::id(),
954 executable: true,
955 rent_epoch: 0,
956 }),
957 ),
958 (
959 solana_pubkey::new_rand(),
960 AccountSharedData::from(Account {
961 lamports: 1,
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: 2,
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: 3,
982 data: vec![],
983 owner: bpf_loader::id(),
984 executable: false,
985 rent_epoch: 3100,
986 }),
987 ),
988 (
989 solana_pubkey::new_rand(),
990 AccountSharedData::from(Account {
991 lamports: 4,
992 data: vec![1u8, 2, 3, 4, 5],
993 owner: bpf_loader::id(),
994 executable: false,
995 rent_epoch: 100,
996 }),
997 ),
998 (
999 solana_pubkey::new_rand(),
1000 AccountSharedData::from(Account {
1001 lamports: 5,
1002 data: vec![11u8, 12, 13, 14, 15, 16, 17, 18, 19],
1003 owner: bpf_loader::id(),
1004 executable: true,
1005 rent_epoch: 200,
1006 }),
1007 ),
1008 (
1009 solana_pubkey::new_rand(),
1010 AccountSharedData::from(Account {
1011 lamports: 6,
1012 data: vec![],
1013 owner: bpf_loader::id(),
1014 executable: false,
1015 rent_epoch: 3100,
1016 }),
1017 ),
1018 (
1019 program_id,
1020 AccountSharedData::from(Account {
1021 lamports: 0,
1022 data: vec![],
1023 owner: bpf_loader_deprecated::id(),
1024 executable: true,
1025 rent_epoch: 0,
1026 }),
1027 ),
1028 ];
1029 let instruction_accounts =
1030 deduplicated_instruction_accounts(&[1, 1, 2, 3, 4, 4, 5, 6], |index| index >= 4);
1031 let instruction_data = vec![1u8, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11];
1032 let original_accounts = transaction_accounts.clone();
1033 with_mock_invoke_context!(invoke_context, transaction_context, transaction_accounts);
1034 invoke_context
1035 .transaction_context
1036 .configure_top_level_instruction_for_tests(
1037 0,
1038 instruction_accounts.clone(),
1039 instruction_data.clone(),
1040 )
1041 .unwrap();
1042 invoke_context.push().unwrap();
1043 let instruction_context = invoke_context
1044 .transaction_context
1045 .get_current_instruction_context()
1046 .unwrap();
1047
1048 let SerializationResult {
1050 parameter_bytes: mut serialized,
1051 regions,
1052 accounts_metadata,
1053 account_pointers_offset,
1054 ..
1055 } = serialize_parameters(
1056 &instruction_context,
1057 virtual_address_space_adjustments,
1058 false, direct_account_pointers_in_program_input,
1060 )
1061 .unwrap();
1062
1063 let mut serialized_regions = unsafe {
1064 concat_regions(®ions)
1066 };
1067 if !virtual_address_space_adjustments {
1068 assert_eq!(serialized.as_slice(), serialized_regions.as_slice());
1069 }
1070 let (de_program_id, de_accounts, de_instruction_data) = unsafe {
1071 deserialize(
1072 if !virtual_address_space_adjustments {
1073 serialized.as_slice_mut()
1074 } else {
1075 serialized_regions.as_slice_mut()
1076 }
1077 .first_mut()
1078 .unwrap() as *mut u8,
1079 )
1080 };
1081
1082 assert_eq!(&program_id, de_program_id);
1083 assert_eq!(instruction_data, de_instruction_data);
1084 assert_eq!(
1085 (de_instruction_data.first().unwrap() as *const u8).align_offset(BPF_ALIGN_OF_U128),
1086 0
1087 );
1088 assert_eq!(
1089 direct_account_pointers_in_program_input,
1090 account_pointers_offset.is_some()
1091 );
1092 for account_info in de_accounts {
1093 let index_in_transaction = invoke_context
1094 .transaction_context
1095 .find_index_of_account(account_info.key)
1096 .unwrap();
1097 let account = invoke_context
1098 .transaction_context
1099 .accounts()
1100 .try_borrow(index_in_transaction)
1101 .unwrap();
1102 assert_eq!(account.lamports(), account_info.lamports());
1103 assert_eq!(account.data(), &account_info.data.borrow()[..]);
1104 assert_eq!(account.owner(), account_info.owner);
1105 assert_eq!(account.executable(), account_info.executable);
1106 #[allow(deprecated)]
1107 {
1108 assert_eq!(0, account_info._unused);
1110 }
1111
1112 assert_eq!(
1113 (*account_info.lamports.borrow() as *const u64).align_offset(BPF_ALIGN_OF_U128),
1114 0
1115 );
1116 assert_eq!(
1117 account_info
1118 .data
1119 .borrow()
1120 .as_ptr()
1121 .align_offset(BPF_ALIGN_OF_U128),
1122 0
1123 );
1124 }
1125
1126 deserialize_parameters(
1127 &instruction_context,
1128 virtual_address_space_adjustments,
1129 false, serialized.as_slice(),
1131 &accounts_metadata,
1132 )
1133 .unwrap();
1134 for (index_in_transaction, (_key, original_account)) in
1135 original_accounts.iter().enumerate()
1136 {
1137 let account = invoke_context
1138 .transaction_context
1139 .accounts()
1140 .try_borrow(index_in_transaction as IndexOfAccount)
1141 .unwrap();
1142 assert_eq!(&*account, original_account);
1143 }
1144
1145 invoke_context.pop().unwrap();
1146 invoke_context
1148 .transaction_context
1149 .configure_top_level_instruction_for_tests(
1150 7,
1151 instruction_accounts,
1152 instruction_data.clone(),
1153 )
1154 .unwrap();
1155 invoke_context.push().unwrap();
1156 let instruction_context = invoke_context
1157 .transaction_context
1158 .get_current_instruction_context()
1159 .unwrap();
1160
1161 let SerializationResult {
1162 parameter_bytes: mut serialized,
1163 regions,
1164 accounts_metadata: account_lengths,
1165 account_pointers_offset,
1166 ..
1167 } = serialize_parameters(
1168 &instruction_context,
1169 virtual_address_space_adjustments,
1170 false, direct_account_pointers_in_program_input,
1172 )
1173 .unwrap();
1174 let mut serialized_regions = unsafe {
1175 concat_regions(®ions)
1177 };
1178
1179 let (de_program_id, de_accounts, de_instruction_data) = unsafe {
1180 deserialize_for_abiv0(
1181 if !virtual_address_space_adjustments {
1182 serialized.as_slice_mut()
1183 } else {
1184 serialized_regions.as_slice_mut()
1185 }
1186 .first_mut()
1187 .unwrap() as *mut u8,
1188 )
1189 };
1190 assert_eq!(&program_id, de_program_id);
1191 assert_eq!(instruction_data, de_instruction_data);
1192 assert!(account_pointers_offset.is_none());
1194 for account_info in de_accounts {
1195 let index_in_transaction = invoke_context
1196 .transaction_context
1197 .find_index_of_account(account_info.key)
1198 .unwrap();
1199 let account = invoke_context
1200 .transaction_context
1201 .accounts()
1202 .try_borrow(index_in_transaction)
1203 .unwrap();
1204 assert_eq!(account.lamports(), account_info.lamports());
1205 assert_eq!(account.data(), &account_info.data.borrow()[..]);
1206 assert_eq!(account.owner(), account_info.owner);
1207 assert_eq!(account.executable(), account_info.executable);
1208 #[allow(deprecated)]
1209 {
1210 assert_eq!(u64::MAX, account_info._unused);
1211 }
1212 }
1213
1214 deserialize_parameters(
1215 &instruction_context,
1216 virtual_address_space_adjustments,
1217 false, serialized.as_slice(),
1219 &account_lengths,
1220 )
1221 .unwrap();
1222 for (index_in_transaction, (_key, original_account)) in
1223 original_accounts.iter().enumerate()
1224 {
1225 let account = invoke_context
1226 .transaction_context
1227 .accounts()
1228 .try_borrow(index_in_transaction as IndexOfAccount)
1229 .unwrap();
1230 assert_eq!(&*account, original_account);
1231 }
1232 }
1233 }
1234
1235 #[test_case(false; "direct_account_pointers_in_program_input disabled")]
1236 #[test_case(true; "direct_account_pointers_in_program_input enabled")]
1237 fn test_serialize_parameters_mask_out_rent_epoch_in_vm_serialization(
1238 direct_account_pointers_in_program_input: bool,
1239 ) {
1240 let transaction_accounts = vec![
1241 (
1242 solana_pubkey::new_rand(),
1243 AccountSharedData::from(Account {
1244 lamports: 0,
1245 data: vec![],
1246 owner: bpf_loader::id(),
1247 executable: true,
1248 rent_epoch: 0,
1249 }),
1250 ),
1251 (
1252 solana_pubkey::new_rand(),
1253 AccountSharedData::from(Account {
1254 lamports: 1,
1255 data: vec![1u8, 2, 3, 4, 5],
1256 owner: bpf_loader::id(),
1257 executable: false,
1258 rent_epoch: 100,
1259 }),
1260 ),
1261 (
1262 solana_pubkey::new_rand(),
1263 AccountSharedData::from(Account {
1264 lamports: 2,
1265 data: vec![11u8, 12, 13, 14, 15, 16, 17, 18, 19],
1266 owner: bpf_loader::id(),
1267 executable: true,
1268 rent_epoch: 200,
1269 }),
1270 ),
1271 (
1272 solana_pubkey::new_rand(),
1273 AccountSharedData::from(Account {
1274 lamports: 3,
1275 data: vec![],
1276 owner: bpf_loader::id(),
1277 executable: false,
1278 rent_epoch: 300,
1279 }),
1280 ),
1281 (
1282 solana_pubkey::new_rand(),
1283 AccountSharedData::from(Account {
1284 lamports: 4,
1285 data: vec![1u8, 2, 3, 4, 5],
1286 owner: bpf_loader::id(),
1287 executable: false,
1288 rent_epoch: 100,
1289 }),
1290 ),
1291 (
1292 solana_pubkey::new_rand(),
1293 AccountSharedData::from(Account {
1294 lamports: 5,
1295 data: vec![11u8, 12, 13, 14, 15, 16, 17, 18, 19],
1296 owner: bpf_loader::id(),
1297 executable: true,
1298 rent_epoch: 200,
1299 }),
1300 ),
1301 (
1302 solana_pubkey::new_rand(),
1303 AccountSharedData::from(Account {
1304 lamports: 6,
1305 data: vec![],
1306 owner: bpf_loader::id(),
1307 executable: false,
1308 rent_epoch: 3100,
1309 }),
1310 ),
1311 (
1312 solana_pubkey::new_rand(),
1313 AccountSharedData::from(Account {
1314 lamports: 0,
1315 data: vec![],
1316 owner: bpf_loader_deprecated::id(),
1317 executable: true,
1318 rent_epoch: 0,
1319 }),
1320 ),
1321 ];
1322 let instruction_accounts =
1323 deduplicated_instruction_accounts(&[1, 1, 2, 3, 4, 4, 5, 6], |index| index >= 4);
1324 with_mock_invoke_context!(invoke_context, transaction_context, transaction_accounts);
1325 invoke_context
1326 .transaction_context
1327 .configure_top_level_instruction_for_tests(0, instruction_accounts.clone(), vec![])
1328 .unwrap();
1329 invoke_context.push().unwrap();
1330 let instruction_context = invoke_context
1331 .transaction_context
1332 .get_current_instruction_context()
1333 .unwrap();
1334
1335 let SerializationResult {
1337 parameter_bytes: _serialized,
1338 regions,
1339 ..
1340 } = serialize_parameters(
1341 &instruction_context,
1342 true,
1343 false, direct_account_pointers_in_program_input,
1345 )
1346 .unwrap();
1347
1348 let mut serialized_regions = unsafe {
1349 concat_regions(®ions)
1351 };
1352 let (_de_program_id, de_accounts, _de_instruction_data) = unsafe {
1353 deserialize(serialized_regions.as_slice_mut().first_mut().unwrap() as *mut u8)
1354 };
1355
1356 for account_info in de_accounts {
1357 #[allow(deprecated)]
1359 {
1360 assert_eq!(0, account_info._unused);
1361 }
1362 }
1363
1364 invoke_context
1366 .transaction_context
1367 .configure_top_level_instruction_for_tests(7, instruction_accounts, vec![])
1368 .unwrap();
1369 invoke_context.push().unwrap();
1370 let instruction_context = invoke_context
1371 .transaction_context
1372 .get_current_instruction_context()
1373 .unwrap();
1374
1375 let SerializationResult {
1376 parameter_bytes: _serialized,
1377 regions,
1378 ..
1379 } = serialize_parameters(
1380 &instruction_context,
1381 true,
1382 false, direct_account_pointers_in_program_input,
1384 )
1385 .unwrap();
1386 let mut serialized_regions = unsafe {
1387 concat_regions(®ions)
1389 };
1390
1391 let (_de_program_id, de_accounts, _de_instruction_data) = unsafe {
1392 deserialize_for_abiv0(serialized_regions.as_slice_mut().first_mut().unwrap() as *mut u8)
1393 };
1394 for account_info in de_accounts {
1395 #[allow(deprecated)]
1396 {
1397 assert_eq!(account_info._unused, u64::MAX);
1398 }
1399 }
1400 }
1401
1402 #[deny(unsafe_op_in_unsafe_fn)]
1404 unsafe fn deserialize_for_abiv0<'a>(
1405 input: *mut u8,
1406 ) -> (&'a Pubkey, Vec<AccountInfo<'a>>, &'a [u8]) {
1407 struct Ptr<T>(std::marker::PhantomData<T>);
1409 impl<T> Ptr<T> {
1410 const COULD_BE_UNALIGNED: bool = std::mem::align_of::<T>() > 1;
1411
1412 #[inline(always)]
1413 fn read_possibly_unaligned(input: *mut u8, offset: usize) -> T {
1414 unsafe {
1415 let src = input.add(offset) as *const T;
1416 if Self::COULD_BE_UNALIGNED {
1417 src.read_unaligned()
1418 } else {
1419 src.read()
1420 }
1421 }
1422 }
1423
1424 #[inline(always)]
1433 fn ref_possibly_unaligned<'a>(input: *mut u8, offset: usize) -> &'a T {
1434 #[allow(clippy::transmute_ptr_to_ref)]
1435 unsafe {
1436 transmute(input.add(offset) as *const T)
1437 }
1438 }
1439
1440 #[inline(always)]
1442 fn mut_possibly_unaligned<'a>(input: *mut u8, offset: usize) -> &'a mut T {
1443 #[allow(clippy::transmute_ptr_to_ref)]
1444 unsafe {
1445 transmute(input.add(offset) as *mut T)
1446 }
1447 }
1448 }
1449
1450 let mut offset: usize = 0;
1451
1452 let num_accounts = Ptr::<u64>::read_possibly_unaligned(input, offset) as usize;
1455 offset += size_of::<u64>();
1456
1457 let mut accounts = Vec::with_capacity(num_accounts);
1460 for _ in 0..num_accounts {
1461 let dup_info = Ptr::<u8>::read_possibly_unaligned(input, offset);
1462 offset += size_of::<u8>();
1463 if dup_info == NON_DUP_MARKER {
1464 let is_signer = Ptr::<u8>::read_possibly_unaligned(input, offset) != 0;
1465 offset += size_of::<u8>();
1466
1467 let is_writable = Ptr::<u8>::read_possibly_unaligned(input, offset) != 0;
1468 offset += size_of::<u8>();
1469
1470 let key = Ptr::<Pubkey>::ref_possibly_unaligned(input, offset);
1471 offset += size_of::<Pubkey>();
1472
1473 let lamports = Rc::new(RefCell::new(Ptr::mut_possibly_unaligned(input, offset)));
1474 offset += size_of::<u64>();
1475
1476 let data_len = Ptr::<u64>::read_possibly_unaligned(input, offset) as usize;
1477 offset += size_of::<u64>();
1478
1479 let data = Rc::new(RefCell::new(unsafe {
1480 from_raw_parts_mut(input.add(offset), data_len)
1481 }));
1482 offset += data_len;
1483
1484 let owner: &Pubkey = Ptr::<Pubkey>::ref_possibly_unaligned(input, offset);
1485 offset += size_of::<Pubkey>();
1486
1487 let executable = Ptr::<u8>::read_possibly_unaligned(input, offset) != 0;
1488 offset += size_of::<u8>();
1489
1490 let unused = Ptr::<u64>::read_possibly_unaligned(input, offset);
1491 offset += size_of::<u64>();
1492
1493 #[allow(deprecated)]
1494 accounts.push(AccountInfo {
1495 key,
1496 is_signer,
1497 is_writable,
1498 lamports,
1499 data,
1500 owner,
1501 executable,
1502 _unused: unused,
1503 });
1504 } else {
1505 accounts.push(accounts.get(dup_info as usize).unwrap().clone());
1507 }
1508 }
1509
1510 let instruction_data_len = Ptr::<u64>::read_possibly_unaligned(input, offset) as usize;
1513 offset += size_of::<u64>();
1514
1515 let instruction_data = unsafe { from_raw_parts(input.add(offset), instruction_data_len) };
1516 offset += instruction_data_len;
1517
1518 let program_id = Ptr::<Pubkey>::ref_possibly_unaligned(input, offset);
1521
1522 (program_id, accounts, instruction_data)
1523 }
1524
1525 unsafe fn concat_regions(regions: &[MemoryRegion]) -> AlignedMemory<HOST_ALIGN> {
1529 let last_region = regions.last().unwrap();
1530 let last_region_vm_addr = last_region.vm_addr_range().start;
1531 let mut mem = AlignedMemory::zero_filled(
1532 (last_region_vm_addr - MM_INPUT_START + last_region.len() as u64) as usize,
1533 );
1534 for region in regions {
1535 let vm_start = region.vm_addr_range().start;
1536 let buffer = region.host_buffer().ptr();
1537 mem.as_slice_mut()[(vm_start - MM_INPUT_START) as usize..][..buffer.len()]
1538 .copy_from_slice(unsafe {
1539 buffer.as_ref_unchecked()
1544 })
1545 }
1546 mem
1547 }
1548
1549 #[test]
1550 fn test_access_violation_handler() {
1551 let program_id = Pubkey::new_unique();
1552 let shared_account = AccountSharedData::new(0, 4, &program_id);
1553 let mut transaction_context = TransactionContext::new(
1554 vec![
1555 (
1556 Pubkey::new_unique(),
1557 AccountSharedData::new(0, 4, &program_id),
1558 ), (Pubkey::new_unique(), shared_account.clone()), (
1561 Pubkey::new_unique(),
1562 AccountSharedData::new(0, 0, &program_id),
1563 ), (
1565 Pubkey::new_unique(),
1566 AccountSharedData::new(
1567 0,
1568 MAX_PERMITTED_DATA_LENGTH as usize - 0x100,
1569 &program_id,
1570 ),
1571 ), (
1573 Pubkey::new_unique(),
1574 AccountSharedData::new(0, 0, &program_id),
1575 ), (
1577 Pubkey::new_unique(),
1578 AccountSharedData::new(0, 0x3000, &program_id),
1579 ), (program_id, AccountSharedData::default()), ],
1582 Rent::default(),
1583 1,
1584 1,
1585 1,
1586 );
1587 let transaction_accounts_indexes = [0, 1, 2, 3, 4, 5];
1588 let instruction_accounts =
1589 deduplicated_instruction_accounts(&transaction_accounts_indexes, |index| index > 0);
1590 transaction_context
1591 .configure_top_level_instruction_for_tests(6, instruction_accounts, vec![])
1592 .unwrap();
1593 transaction_context.push(true).unwrap();
1594 let instruction_context = transaction_context
1595 .get_current_instruction_context()
1596 .unwrap();
1597 let account_start_offsets = [
1598 MM_INPUT_START,
1599 MM_INPUT_START + 4 + MAX_PERMITTED_DATA_INCREASE as u64,
1600 MM_INPUT_START + (4 + MAX_PERMITTED_DATA_INCREASE as u64) * 2,
1601 MM_INPUT_START + (4 + MAX_PERMITTED_DATA_INCREASE as u64) * 3,
1602 ];
1603 let regions = account_start_offsets
1604 .iter()
1605 .enumerate()
1606 .map(|(index_in_instruction, account_start_offset)| {
1607 create_memory_region_of_account(
1608 &mut instruction_context
1609 .try_borrow_instruction_account(index_in_instruction as IndexOfAccount)
1610 .unwrap(),
1611 *account_start_offset,
1612 )
1613 .unwrap()
1614 })
1615 .collect::<Vec<_>>();
1616 let config = Config {
1617 aligned_memory_mapping: false,
1618 ..Config::default()
1619 };
1620 let mut memory_mapping = unsafe {
1621 MemoryMapping::new_with_access_violation_handler(
1622 regions,
1623 &config,
1624 SBPFVersion::V3,
1625 transaction_context.access_violation_handler(true, true),
1626 )
1627 .unwrap()
1628 };
1629
1630 memory_mapping
1632 .load::<u32>(account_start_offsets[0])
1633 .unwrap();
1634
1635 memory_mapping
1637 .load::<u32>(account_start_offsets[1])
1638 .unwrap();
1639
1640 memory_mapping
1642 .load::<u32>(account_start_offsets[0] + 4)
1643 .unwrap_err();
1644
1645 memory_mapping
1647 .store::<u32>(0, account_start_offsets[0])
1648 .unwrap_err();
1649
1650 let _shared_account_ref = shared_account;
1654 assert!(
1655 transaction_context
1656 .accounts()
1657 .try_borrow_mut(1)
1658 .unwrap()
1659 .is_shared()
1660 );
1661 memory_mapping
1662 .store::<u32>(0, account_start_offsets[1])
1663 .unwrap();
1664 assert!(
1665 !transaction_context
1666 .accounts()
1667 .try_borrow_mut(1)
1668 .unwrap()
1669 .is_shared()
1670 );
1671 assert_eq!(
1672 transaction_context
1673 .accounts()
1674 .try_borrow(1)
1675 .unwrap()
1676 .data()
1677 .len(),
1678 4,
1679 );
1680
1681 memory_mapping
1683 .load::<u32>(account_start_offsets[1] + 4)
1684 .unwrap_err();
1685
1686 memory_mapping
1689 .store::<u32>(0, account_start_offsets[1] + 4)
1690 .unwrap();
1691 assert_eq!(
1692 transaction_context
1693 .accounts()
1694 .try_borrow(1)
1695 .unwrap()
1696 .data()
1697 .len(),
1698 4 + MAX_PERMITTED_DATA_INCREASE,
1699 );
1700 assert!(
1701 transaction_context
1702 .accounts()
1703 .try_borrow(1)
1704 .unwrap()
1705 .data()
1706 .len()
1707 < 0x3000
1708 );
1709
1710 memory_mapping
1713 .store::<u32>(0, account_start_offsets[3] + MAX_PERMITTED_DATA_LENGTH - 4)
1714 .unwrap();
1715 assert_eq!(
1716 transaction_context
1717 .accounts()
1718 .try_borrow(3)
1719 .unwrap()
1720 .data()
1721 .len(),
1722 MAX_PERMITTED_DATA_LENGTH as usize,
1723 );
1724
1725 memory_mapping
1728 .load::<u32>(account_start_offsets[3] + MAX_PERMITTED_DATA_LENGTH)
1729 .unwrap_err();
1730 memory_mapping
1731 .store::<u32>(0, account_start_offsets[3] + MAX_PERMITTED_DATA_LENGTH)
1732 .unwrap_err();
1733
1734 let remaining_allowed_growth: usize = 0x700;
1736 for index_in_instruction in 4..6 {
1737 let mut borrowed_account = instruction_context
1738 .try_borrow_instruction_account(index_in_instruction)
1739 .unwrap();
1740 borrowed_account
1741 .set_data_from_slice(&vec![0u8; MAX_PERMITTED_DATA_LENGTH as usize])
1742 .unwrap();
1743 }
1744 assert_eq!(
1745 transaction_context.accounts().resize_delta(),
1746 MAX_PERMITTED_ACCOUNTS_DATA_ALLOCATIONS_PER_TRANSACTION
1747 - remaining_allowed_growth as i64,
1748 );
1749
1750 memory_mapping
1753 .store::<u32>(0, account_start_offsets[2] + 0x500)
1754 .unwrap();
1755 assert_eq!(
1756 transaction_context
1757 .accounts()
1758 .try_borrow(2)
1759 .unwrap()
1760 .data()
1761 .len(),
1762 remaining_allowed_growth,
1763 );
1764 }
1765
1766 #[test]
1767 fn test_regression_initial_serialized_account_region_does_not_include_resize_affordance() {
1768 let program_id = Pubkey::new_unique();
1769 let transaction_accounts = vec![
1770 (
1771 Pubkey::new_unique(),
1772 AccountSharedData::new(0, 4, &program_id),
1773 ),
1774 (
1775 solana_pubkey::new_rand(),
1776 AccountSharedData::from(Account {
1777 lamports: 0,
1778 data: b"agave".into(),
1779 owner: bpf_loader_upgradeable::id(),
1780 executable: false,
1781 rent_epoch: 0,
1782 }),
1783 ),
1784 ];
1785 with_mock_invoke_context!(invoke_context, transaction_context, transaction_accounts);
1786 invoke_context
1787 .transaction_context
1788 .configure_top_level_instruction_for_tests(
1789 0,
1790 vec![InstructionAccount::new(1, false, true)],
1791 vec![],
1792 )
1793 .unwrap();
1794 invoke_context.push().unwrap();
1795 let instruction_context = invoke_context
1796 .transaction_context
1797 .get_current_instruction_context()
1798 .unwrap();
1799 let SerializationResult {
1800 regions,
1801 accounts_metadata,
1802 ..
1803 } = crate::serialization::serialize_parameters(
1804 &instruction_context,
1805 true, false, false, )
1809 .unwrap();
1810 let config = Config {
1811 aligned_memory_mapping: false,
1812 ..Config::default()
1813 };
1814 let memory_mapping =
1815 unsafe { MemoryMapping::new(regions, &config, SBPFVersion::V3).unwrap() };
1816 let account_metadata = &accounts_metadata[0];
1817 let vm_data_addr = account_metadata.vm_data_addr;
1818 let (_region_index, region) = memory_mapping.find_region(vm_data_addr).unwrap();
1819 assert_eq!(region.len(), 5);
1820 }
1821}