1#![allow(clippy::arithmetic_side_effects)]
2
3use {
4 solana_instruction::error::InstructionError,
5 solana_program_entrypoint::{BPF_ALIGN_OF_U128, MAX_PERMITTED_DATA_INCREASE, NON_DUP_MARKER},
6 solana_program_runtime::invoke_context::SerializedAccountMetadata,
7 solana_pubkey::Pubkey,
8 solana_sbpf::{
9 aligned_memory::{AlignedMemory, Pod},
10 ebpf::{HOST_ALIGN, MM_INPUT_START},
11 memory_region::{MemoryRegion, MemoryState},
12 },
13 solana_sdk_ids::bpf_loader_deprecated,
14 solana_system_interface::MAX_PERMITTED_DATA_LENGTH,
15 solana_transaction_context::{
16 BorrowedAccount, IndexOfAccount, InstructionContext, TransactionContext,
17 },
18 std::mem::{self, size_of},
19};
20
21const MAX_INSTRUCTION_ACCOUNTS: u8 = NON_DUP_MARKER;
24
25#[allow(dead_code)]
26enum SerializeAccount<'a> {
27 Account(IndexOfAccount, BorrowedAccount<'a>),
28 Duplicate(IndexOfAccount),
29}
30
31struct Serializer {
32 buffer: AlignedMemory<HOST_ALIGN>,
33 regions: Vec<MemoryRegion>,
34 vaddr: u64,
35 region_start: usize,
36 aligned: bool,
37 copy_account_data: bool,
38}
39
40impl Serializer {
41 fn new(size: usize, start_addr: u64, aligned: bool, copy_account_data: bool) -> Serializer {
42 Serializer {
43 buffer: AlignedMemory::with_capacity(size),
44 regions: Vec::new(),
45 region_start: 0,
46 vaddr: start_addr,
47 aligned,
48 copy_account_data,
49 }
50 }
51
52 fn fill_write(&mut self, num: usize, value: u8) -> std::io::Result<()> {
53 self.buffer.fill_write(num, value)
54 }
55
56 fn write<T: Pod>(&mut self, value: T) -> u64 {
57 self.debug_assert_alignment::<T>();
58 let vaddr = self
59 .vaddr
60 .saturating_add(self.buffer.len() as u64)
61 .saturating_sub(self.region_start as u64);
62 unsafe {
71 self.buffer.write_unchecked(value);
72 }
73
74 vaddr
75 }
76
77 fn write_all(&mut self, value: &[u8]) -> u64 {
78 let vaddr = self
79 .vaddr
80 .saturating_add(self.buffer.len() as u64)
81 .saturating_sub(self.region_start as u64);
82 unsafe {
85 self.buffer.write_all_unchecked(value);
86 }
87
88 vaddr
89 }
90
91 fn write_account(
92 &mut self,
93 account: &mut BorrowedAccount<'_>,
94 ) -> Result<u64, InstructionError> {
95 let vm_data_addr = if self.copy_account_data {
96 let vm_data_addr = self.vaddr.saturating_add(self.buffer.len() as u64);
97 self.write_all(account.get_data());
98 vm_data_addr
99 } else {
100 self.push_region(true);
101 let vaddr = self.vaddr;
102 self.push_account_data_region(account)?;
103 vaddr
104 };
105
106 if self.aligned {
107 let align_offset =
108 (account.get_data().len() as *const u8).align_offset(BPF_ALIGN_OF_U128);
109 if self.copy_account_data {
110 self.fill_write(MAX_PERMITTED_DATA_INCREASE + align_offset, 0)
111 .map_err(|_| InstructionError::InvalidArgument)?;
112 } else {
113 self.fill_write(MAX_PERMITTED_DATA_INCREASE + BPF_ALIGN_OF_U128, 0)
119 .map_err(|_| InstructionError::InvalidArgument)?;
120 self.region_start += BPF_ALIGN_OF_U128.saturating_sub(align_offset);
121 self.push_region(account.can_data_be_changed().is_ok());
123 }
124 }
125
126 Ok(vm_data_addr)
127 }
128
129 fn push_account_data_region(
130 &mut self,
131 account: &mut BorrowedAccount<'_>,
132 ) -> Result<(), InstructionError> {
133 if !account.get_data().is_empty() {
134 let region = match account_data_region_memory_state(account) {
135 MemoryState::Readable => MemoryRegion::new_readonly(account.get_data(), self.vaddr),
136 MemoryState::Writable => {
137 MemoryRegion::new_writable(account.get_data_mut()?, self.vaddr)
138 }
139 MemoryState::Cow(index_in_transaction) => {
140 MemoryRegion::new_cow(account.get_data(), self.vaddr, index_in_transaction)
141 }
142 };
143 self.vaddr += region.len;
144 self.regions.push(region);
145 }
146
147 Ok(())
148 }
149
150 fn push_region(&mut self, writable: bool) {
151 let range = self.region_start..self.buffer.len();
152 let region = if writable {
153 MemoryRegion::new_writable(
154 self.buffer.as_slice_mut().get_mut(range.clone()).unwrap(),
155 self.vaddr,
156 )
157 } else {
158 MemoryRegion::new_readonly(
159 self.buffer.as_slice().get(range.clone()).unwrap(),
160 self.vaddr,
161 )
162 };
163 self.regions.push(region);
164 self.region_start = range.end;
165 self.vaddr += range.len() as u64;
166 }
167
168 fn finish(mut self) -> (AlignedMemory<HOST_ALIGN>, Vec<MemoryRegion>) {
169 self.push_region(true);
170 debug_assert_eq!(self.region_start, self.buffer.len());
171 (self.buffer, self.regions)
172 }
173
174 fn debug_assert_alignment<T>(&self) {
175 debug_assert!(
176 !self.aligned
177 || self
178 .buffer
179 .as_slice()
180 .as_ptr_range()
181 .end
182 .align_offset(mem::align_of::<T>())
183 == 0
184 );
185 }
186}
187
188pub fn serialize_parameters(
189 transaction_context: &TransactionContext,
190 instruction_context: &InstructionContext,
191 copy_account_data: bool,
192) -> Result<
193 (
194 AlignedMemory<HOST_ALIGN>,
195 Vec<MemoryRegion>,
196 Vec<SerializedAccountMetadata>,
197 ),
198 InstructionError,
199> {
200 let num_ix_accounts = instruction_context.get_number_of_instruction_accounts();
201 if num_ix_accounts > MAX_INSTRUCTION_ACCOUNTS as IndexOfAccount {
202 return Err(InstructionError::MaxAccountsExceeded);
203 }
204
205 let (program_id, is_loader_deprecated) = {
206 let program_account =
207 instruction_context.try_borrow_last_program_account(transaction_context)?;
208 (
209 *program_account.get_key(),
210 *program_account.get_owner() == bpf_loader_deprecated::id(),
211 )
212 };
213
214 let accounts = (0..instruction_context.get_number_of_instruction_accounts())
215 .map(|instruction_account_index| {
216 if let Some(index) = instruction_context
217 .is_instruction_account_duplicate(instruction_account_index)
218 .unwrap()
219 {
220 SerializeAccount::Duplicate(index)
221 } else {
222 let account = instruction_context
223 .try_borrow_instruction_account(transaction_context, instruction_account_index)
224 .unwrap();
225 SerializeAccount::Account(instruction_account_index, account)
226 }
227 })
228 .collect::<Vec<_>>();
233
234 if is_loader_deprecated {
235 serialize_parameters_unaligned(
236 accounts,
237 instruction_context.get_instruction_data(),
238 &program_id,
239 copy_account_data,
240 )
241 } else {
242 serialize_parameters_aligned(
243 accounts,
244 instruction_context.get_instruction_data(),
245 &program_id,
246 copy_account_data,
247 )
248 }
249}
250
251pub(crate) fn deserialize_parameters(
252 transaction_context: &TransactionContext,
253 instruction_context: &InstructionContext,
254 copy_account_data: bool,
255 buffer: &[u8],
256 accounts_metadata: &[SerializedAccountMetadata],
257) -> Result<(), InstructionError> {
258 let is_loader_deprecated = *instruction_context
259 .try_borrow_last_program_account(transaction_context)?
260 .get_owner()
261 == bpf_loader_deprecated::id();
262 let account_lengths = accounts_metadata.iter().map(|a| a.original_data_len);
263 if is_loader_deprecated {
264 deserialize_parameters_unaligned(
265 transaction_context,
266 instruction_context,
267 copy_account_data,
268 buffer,
269 account_lengths,
270 )
271 } else {
272 deserialize_parameters_aligned(
273 transaction_context,
274 instruction_context,
275 copy_account_data,
276 buffer,
277 account_lengths,
278 )
279 }
280}
281
282fn serialize_parameters_unaligned(
283 accounts: Vec<SerializeAccount>,
284 instruction_data: &[u8],
285 program_id: &Pubkey,
286 copy_account_data: bool,
287) -> Result<
288 (
289 AlignedMemory<HOST_ALIGN>,
290 Vec<MemoryRegion>,
291 Vec<SerializedAccountMetadata>,
292 ),
293 InstructionError,
294> {
295 let mut size = size_of::<u64>();
297 for account in &accounts {
298 size += 1; match account {
300 SerializeAccount::Duplicate(_) => {}
301 SerializeAccount::Account(_, account) => {
302 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 copy_account_data {
311 size += account.get_data().len();
312 }
313 }
314 }
315 }
316 size += size_of::<u64>() + instruction_data.len() + size_of::<Pubkey>(); let mut s = Serializer::new(size, MM_INPUT_START, false, copy_account_data);
321
322 let mut accounts_metadata: Vec<SerializedAccountMetadata> = Vec::with_capacity(accounts.len());
323 s.write::<u64>((accounts.len() as u64).to_le());
324 for account in accounts {
325 match account {
326 SerializeAccount::Duplicate(position) => {
327 accounts_metadata.push(accounts_metadata.get(position as usize).unwrap().clone());
328 s.write(position as u8);
329 }
330 SerializeAccount::Account(_, mut account) => {
331 s.write::<u8>(NON_DUP_MARKER);
332 s.write::<u8>(account.is_signer() as u8);
333 s.write::<u8>(account.is_writable() as u8);
334 let vm_key_addr = s.write_all(account.get_key().as_ref());
335 let vm_lamports_addr = s.write::<u64>(account.get_lamports().to_le());
336 s.write::<u64>((account.get_data().len() as u64).to_le());
337 let vm_data_addr = s.write_account(&mut account)?;
338 let vm_owner_addr = s.write_all(account.get_owner().as_ref());
339 #[allow(deprecated)]
340 s.write::<u8>(account.is_executable() as u8);
341 s.write::<u64>((account.get_rent_epoch()).to_le());
342 accounts_metadata.push(SerializedAccountMetadata {
343 original_data_len: account.get_data().len(),
344 vm_key_addr,
345 vm_lamports_addr,
346 vm_owner_addr,
347 vm_data_addr,
348 });
349 }
350 };
351 }
352 s.write::<u64>((instruction_data.len() as u64).to_le());
353 s.write_all(instruction_data);
354 s.write_all(program_id.as_ref());
355
356 let (mem, regions) = s.finish();
357 Ok((mem, regions, accounts_metadata))
358}
359
360fn deserialize_parameters_unaligned<I: IntoIterator<Item = usize>>(
361 transaction_context: &TransactionContext,
362 instruction_context: &InstructionContext,
363 copy_account_data: bool,
364 buffer: &[u8],
365 account_lengths: I,
366) -> Result<(), InstructionError> {
367 let mut start = size_of::<u64>(); for (instruction_account_index, pre_len) in (0..instruction_context
369 .get_number_of_instruction_accounts())
370 .zip(account_lengths.into_iter())
371 {
372 let duplicate =
373 instruction_context.is_instruction_account_duplicate(instruction_account_index)?;
374 start += 1; if duplicate.is_none() {
376 let mut borrowed_account = instruction_context
377 .try_borrow_instruction_account(transaction_context, instruction_account_index)?;
378 start += size_of::<u8>(); start += size_of::<u8>(); start += size_of::<Pubkey>(); let lamports = buffer
382 .get(start..start.saturating_add(8))
383 .map(<[u8; 8]>::try_from)
384 .and_then(Result::ok)
385 .map(u64::from_le_bytes)
386 .ok_or(InstructionError::InvalidArgument)?;
387 if borrowed_account.get_lamports() != lamports {
388 borrowed_account.set_lamports(lamports)?;
389 }
390 start += size_of::<u64>() + size_of::<u64>(); if copy_account_data {
393 let data = buffer
394 .get(start..start + pre_len)
395 .ok_or(InstructionError::InvalidArgument)?;
396 match borrowed_account
398 .can_data_be_resized(data.len())
399 .and_then(|_| borrowed_account.can_data_be_changed())
400 {
401 Ok(()) => borrowed_account.set_data_from_slice(data)?,
402 Err(err) if borrowed_account.get_data() != data => return Err(err),
403 _ => {}
404 }
405 start += pre_len; }
407 start += size_of::<Pubkey>() + size_of::<u8>() + size_of::<u64>(); }
411 }
412 Ok(())
413}
414
415fn serialize_parameters_aligned(
416 accounts: Vec<SerializeAccount>,
417 instruction_data: &[u8],
418 program_id: &Pubkey,
419 copy_account_data: bool,
420) -> Result<
421 (
422 AlignedMemory<HOST_ALIGN>,
423 Vec<MemoryRegion>,
424 Vec<SerializedAccountMetadata>,
425 ),
426 InstructionError,
427> {
428 let mut accounts_metadata = Vec::with_capacity(accounts.len());
429 let mut size = size_of::<u64>();
431 for account in &accounts {
432 size += 1; match account {
434 SerializeAccount::Duplicate(_) => size += 7, SerializeAccount::Account(_, account) => {
436 let data_len = account.get_data().len();
437 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>() + MAX_PERMITTED_DATA_INCREASE
446 + size_of::<u64>(); if copy_account_data {
448 size += data_len + (data_len as *const u8).align_offset(BPF_ALIGN_OF_U128);
449 } else {
450 size += BPF_ALIGN_OF_U128;
451 }
452 }
453 }
454 }
455 size += size_of::<u64>() + instruction_data.len()
457 + size_of::<Pubkey>(); let mut s = Serializer::new(size, MM_INPUT_START, true, copy_account_data);
460
461 s.write::<u64>((accounts.len() as u64).to_le());
463 for account in accounts {
464 match account {
465 SerializeAccount::Account(_, mut borrowed_account) => {
466 s.write::<u8>(NON_DUP_MARKER);
467 s.write::<u8>(borrowed_account.is_signer() as u8);
468 s.write::<u8>(borrowed_account.is_writable() as u8);
469 #[allow(deprecated)]
470 s.write::<u8>(borrowed_account.is_executable() as u8);
471 s.write_all(&[0u8, 0, 0, 0]);
472 let vm_key_addr = s.write_all(borrowed_account.get_key().as_ref());
473 let vm_owner_addr = s.write_all(borrowed_account.get_owner().as_ref());
474 let vm_lamports_addr = s.write::<u64>(borrowed_account.get_lamports().to_le());
475 s.write::<u64>((borrowed_account.get_data().len() as u64).to_le());
476 let vm_data_addr = s.write_account(&mut borrowed_account)?;
477 s.write::<u64>((borrowed_account.get_rent_epoch()).to_le());
478 accounts_metadata.push(SerializedAccountMetadata {
479 original_data_len: borrowed_account.get_data().len(),
480 vm_key_addr,
481 vm_owner_addr,
482 vm_lamports_addr,
483 vm_data_addr,
484 });
485 }
486 SerializeAccount::Duplicate(position) => {
487 accounts_metadata.push(accounts_metadata.get(position as usize).unwrap().clone());
488 s.write::<u8>(position as u8);
489 s.write_all(&[0u8, 0, 0, 0, 0, 0, 0]);
490 }
491 };
492 }
493 s.write::<u64>((instruction_data.len() as u64).to_le());
494 s.write_all(instruction_data);
495 s.write_all(program_id.as_ref());
496
497 let (mem, regions) = s.finish();
498 Ok((mem, regions, accounts_metadata))
499}
500
501fn deserialize_parameters_aligned<I: IntoIterator<Item = usize>>(
502 transaction_context: &TransactionContext,
503 instruction_context: &InstructionContext,
504 copy_account_data: bool,
505 buffer: &[u8],
506 account_lengths: I,
507) -> Result<(), InstructionError> {
508 let mut start = size_of::<u64>(); for (instruction_account_index, pre_len) in (0..instruction_context
510 .get_number_of_instruction_accounts())
511 .zip(account_lengths.into_iter())
512 {
513 let duplicate =
514 instruction_context.is_instruction_account_duplicate(instruction_account_index)?;
515 start += size_of::<u8>(); if duplicate.is_some() {
517 start += 7; } else {
519 let mut borrowed_account = instruction_context
520 .try_borrow_instruction_account(transaction_context, instruction_account_index)?;
521 start += size_of::<u8>() + size_of::<u8>() + size_of::<u8>() + size_of::<u32>() + size_of::<Pubkey>(); let owner = buffer
527 .get(start..start + size_of::<Pubkey>())
528 .ok_or(InstructionError::InvalidArgument)?;
529 start += size_of::<Pubkey>(); let lamports = buffer
531 .get(start..start.saturating_add(8))
532 .map(<[u8; 8]>::try_from)
533 .and_then(Result::ok)
534 .map(u64::from_le_bytes)
535 .ok_or(InstructionError::InvalidArgument)?;
536 if borrowed_account.get_lamports() != lamports {
537 borrowed_account.set_lamports(lamports)?;
538 }
539 start += size_of::<u64>(); let post_len = buffer
541 .get(start..start.saturating_add(8))
542 .map(<[u8; 8]>::try_from)
543 .and_then(Result::ok)
544 .map(u64::from_le_bytes)
545 .ok_or(InstructionError::InvalidArgument)? as usize;
546 start += size_of::<u64>(); if post_len.saturating_sub(pre_len) > MAX_PERMITTED_DATA_INCREASE
548 || post_len > MAX_PERMITTED_DATA_LENGTH as usize
549 {
550 return Err(InstructionError::InvalidRealloc);
551 }
552 let alignment_offset = (pre_len as *const u8).align_offset(BPF_ALIGN_OF_U128);
554 if copy_account_data {
555 let data = buffer
556 .get(start..start + post_len)
557 .ok_or(InstructionError::InvalidArgument)?;
558 match borrowed_account
559 .can_data_be_resized(post_len)
560 .and_then(|_| borrowed_account.can_data_be_changed())
561 {
562 Ok(()) => borrowed_account.set_data_from_slice(data)?,
563 Err(err) if borrowed_account.get_data() != data => return Err(err),
564 _ => {}
565 }
566 start += pre_len; } else {
568 start += BPF_ALIGN_OF_U128.saturating_sub(alignment_offset);
571 let data = buffer
572 .get(start..start + MAX_PERMITTED_DATA_INCREASE)
573 .ok_or(InstructionError::InvalidArgument)?;
574 match borrowed_account
575 .can_data_be_resized(post_len)
576 .and_then(|_| borrowed_account.can_data_be_changed())
577 {
578 Ok(()) => {
579 borrowed_account.set_data_length(post_len)?;
580 let allocated_bytes = post_len.saturating_sub(pre_len);
581 if allocated_bytes > 0 {
582 borrowed_account
583 .get_data_mut()?
584 .get_mut(pre_len..pre_len.saturating_add(allocated_bytes))
585 .ok_or(InstructionError::InvalidArgument)?
586 .copy_from_slice(
587 data.get(0..allocated_bytes)
588 .ok_or(InstructionError::InvalidArgument)?,
589 );
590 }
591 }
592 Err(err) if borrowed_account.get_data().len() != post_len => return Err(err),
593 _ => {}
594 }
595 }
596 start += MAX_PERMITTED_DATA_INCREASE;
597 start += alignment_offset;
598 start += size_of::<u64>(); if borrowed_account.get_owner().to_bytes() != owner {
600 borrowed_account.set_owner(owner)?;
602 }
603 }
604 }
605 Ok(())
606}
607
608pub(crate) fn account_data_region_memory_state(account: &BorrowedAccount<'_>) -> MemoryState {
609 if account.can_data_be_changed().is_ok() {
610 if account.is_shared() {
611 MemoryState::Cow(account.get_index_in_transaction() as u64)
612 } else {
613 MemoryState::Writable
614 }
615 } else {
616 MemoryState::Readable
617 }
618}
619
620#[cfg(test)]
621#[allow(clippy::indexing_slicing)]
622mod tests {
623 use {
624 super::*,
625 solana_account::{Account, AccountSharedData, WritableAccount},
626 solana_account_info::AccountInfo,
627 solana_program_entrypoint::deserialize,
628 solana_program_runtime::with_mock_invoke_context,
629 solana_sdk_ids::bpf_loader,
630 solana_transaction_context::InstructionAccount,
631 std::{
632 cell::RefCell,
633 mem::transmute,
634 rc::Rc,
635 slice::{self, from_raw_parts, from_raw_parts_mut},
636 },
637 };
638
639 fn deduplicated_instruction_accounts(
640 transaction_indexes: &[IndexOfAccount],
641 is_writable: fn(usize) -> bool,
642 ) -> Vec<InstructionAccount> {
643 transaction_indexes
644 .iter()
645 .enumerate()
646 .map(|(index_in_instruction, index_in_transaction)| {
647 let index_in_callee = transaction_indexes
648 .get(0..index_in_instruction)
649 .unwrap()
650 .iter()
651 .position(|account_index| account_index == index_in_transaction)
652 .unwrap_or(index_in_instruction);
653 InstructionAccount {
654 index_in_transaction: *index_in_transaction,
655 index_in_caller: *index_in_transaction,
656 index_in_callee: index_in_callee as IndexOfAccount,
657 is_signer: false,
658 is_writable: is_writable(index_in_instruction),
659 }
660 })
661 .collect()
662 }
663
664 #[test]
665 fn test_serialize_parameters_with_many_accounts() {
666 struct TestCase {
667 num_ix_accounts: usize,
668 append_dup_account: bool,
669 expected_err: Option<InstructionError>,
670 name: &'static str,
671 }
672
673 for copy_account_data in [true] {
674 for TestCase {
675 num_ix_accounts,
676 append_dup_account,
677 expected_err,
678 name,
679 } in [
680 TestCase {
681 name: "serialize max accounts with cap",
682 num_ix_accounts: usize::from(MAX_INSTRUCTION_ACCOUNTS),
683 append_dup_account: false,
684 expected_err: None,
685 },
686 TestCase {
687 name: "serialize too many accounts with cap",
688 num_ix_accounts: usize::from(MAX_INSTRUCTION_ACCOUNTS) + 1,
689 append_dup_account: false,
690 expected_err: Some(InstructionError::MaxAccountsExceeded),
691 },
692 TestCase {
693 name: "serialize too many accounts and append dup with cap",
694 num_ix_accounts: usize::from(MAX_INSTRUCTION_ACCOUNTS),
695 append_dup_account: true,
696 expected_err: Some(InstructionError::MaxAccountsExceeded),
697 },
698 ] {
699 let program_id = solana_pubkey::new_rand();
700 let mut transaction_accounts = vec![(
701 program_id,
702 AccountSharedData::from(Account {
703 lamports: 0,
704 data: vec![],
705 owner: bpf_loader::id(),
706 executable: true,
707 rent_epoch: 0,
708 }),
709 )];
710 for _ in 0..num_ix_accounts {
711 transaction_accounts.push((
712 Pubkey::new_unique(),
713 AccountSharedData::from(Account {
714 lamports: 0,
715 data: vec![],
716 owner: program_id,
717 executable: false,
718 rent_epoch: 0,
719 }),
720 ));
721 }
722
723 let transaction_accounts_indexes: Vec<IndexOfAccount> =
724 (1..(num_ix_accounts + 1) as u16).collect();
725 let mut instruction_accounts =
726 deduplicated_instruction_accounts(&transaction_accounts_indexes, |_| false);
727 if append_dup_account {
728 instruction_accounts.push(instruction_accounts.last().cloned().unwrap());
729 }
730 let program_indices = [0];
731 let instruction_data = vec![];
732
733 with_mock_invoke_context!(
734 invoke_context,
735 transaction_context,
736 transaction_accounts
737 );
738 invoke_context
739 .transaction_context
740 .get_next_instruction_context()
741 .unwrap()
742 .configure(&program_indices, &instruction_accounts, &instruction_data);
743 invoke_context.push().unwrap();
744 let instruction_context = invoke_context
745 .transaction_context
746 .get_current_instruction_context()
747 .unwrap();
748
749 let serialization_result = serialize_parameters(
750 invoke_context.transaction_context,
751 instruction_context,
752 copy_account_data,
753 );
754 assert_eq!(
755 serialization_result.as_ref().err(),
756 expected_err.as_ref(),
757 "{name} test case failed",
758 );
759 if expected_err.is_some() {
760 continue;
761 }
762
763 let (mut serialized, regions, _account_lengths) = serialization_result.unwrap();
764 let mut serialized_regions = concat_regions(®ions);
765 let (de_program_id, de_accounts, de_instruction_data) = unsafe {
766 deserialize(
767 if copy_account_data {
768 serialized.as_slice_mut()
769 } else {
770 serialized_regions.as_slice_mut()
771 }
772 .first_mut()
773 .unwrap() as *mut u8,
774 )
775 };
776 assert_eq!(de_program_id, &program_id);
777 assert_eq!(de_instruction_data, &instruction_data);
778 for account_info in de_accounts {
779 let index_in_transaction = invoke_context
780 .transaction_context
781 .find_index_of_account(account_info.key)
782 .unwrap();
783 let account = invoke_context
784 .transaction_context
785 .get_account_at_index(index_in_transaction)
786 .unwrap()
787 .borrow();
788 assert_eq!(account.lamports(), account_info.lamports());
789 assert_eq!(account.data(), &account_info.data.borrow()[..]);
790 assert_eq!(account.owner(), account_info.owner);
791 assert_eq!(account.executable(), account_info.executable);
792 assert_eq!(account.rent_epoch(), account_info.rent_epoch);
793 }
794 }
795 }
796 }
797
798 #[test]
799 fn test_serialize_parameters() {
800 for copy_account_data in [false, true] {
801 let program_id = solana_pubkey::new_rand();
802 let transaction_accounts = vec![
803 (
804 program_id,
805 AccountSharedData::from(Account {
806 lamports: 0,
807 data: vec![],
808 owner: bpf_loader::id(),
809 executable: true,
810 rent_epoch: 0,
811 }),
812 ),
813 (
814 solana_pubkey::new_rand(),
815 AccountSharedData::from(Account {
816 lamports: 1,
817 data: vec![1u8, 2, 3, 4, 5],
818 owner: bpf_loader::id(),
819 executable: false,
820 rent_epoch: 100,
821 }),
822 ),
823 (
824 solana_pubkey::new_rand(),
825 AccountSharedData::from(Account {
826 lamports: 2,
827 data: vec![11u8, 12, 13, 14, 15, 16, 17, 18, 19],
828 owner: bpf_loader::id(),
829 executable: true,
830 rent_epoch: 200,
831 }),
832 ),
833 (
834 solana_pubkey::new_rand(),
835 AccountSharedData::from(Account {
836 lamports: 3,
837 data: vec![],
838 owner: bpf_loader::id(),
839 executable: false,
840 rent_epoch: 3100,
841 }),
842 ),
843 (
844 solana_pubkey::new_rand(),
845 AccountSharedData::from(Account {
846 lamports: 4,
847 data: vec![1u8, 2, 3, 4, 5],
848 owner: bpf_loader::id(),
849 executable: false,
850 rent_epoch: 100,
851 }),
852 ),
853 (
854 solana_pubkey::new_rand(),
855 AccountSharedData::from(Account {
856 lamports: 5,
857 data: vec![11u8, 12, 13, 14, 15, 16, 17, 18, 19],
858 owner: bpf_loader::id(),
859 executable: true,
860 rent_epoch: 200,
861 }),
862 ),
863 (
864 solana_pubkey::new_rand(),
865 AccountSharedData::from(Account {
866 lamports: 6,
867 data: vec![],
868 owner: bpf_loader::id(),
869 executable: false,
870 rent_epoch: 3100,
871 }),
872 ),
873 ];
874 let instruction_accounts =
875 deduplicated_instruction_accounts(&[1, 1, 2, 3, 4, 4, 5, 6], |index| index >= 4);
876 let instruction_data = vec![1u8, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11];
877 let program_indices = [0];
878 let mut original_accounts = transaction_accounts.clone();
879 with_mock_invoke_context!(invoke_context, transaction_context, transaction_accounts);
880 invoke_context
881 .transaction_context
882 .get_next_instruction_context()
883 .unwrap()
884 .configure(&program_indices, &instruction_accounts, &instruction_data);
885 invoke_context.push().unwrap();
886 let instruction_context = invoke_context
887 .transaction_context
888 .get_current_instruction_context()
889 .unwrap();
890
891 let (mut serialized, regions, accounts_metadata) = serialize_parameters(
893 invoke_context.transaction_context,
894 instruction_context,
895 copy_account_data,
896 )
897 .unwrap();
898
899 let mut serialized_regions = concat_regions(®ions);
900 if copy_account_data {
901 assert_eq!(serialized.as_slice(), serialized_regions.as_slice());
902 }
903 let (de_program_id, de_accounts, de_instruction_data) = unsafe {
904 deserialize(
905 if copy_account_data {
906 serialized.as_slice_mut()
907 } else {
908 serialized_regions.as_slice_mut()
909 }
910 .first_mut()
911 .unwrap() as *mut u8,
912 )
913 };
914
915 assert_eq!(&program_id, de_program_id);
916 assert_eq!(instruction_data, de_instruction_data);
917 assert_eq!(
918 (de_instruction_data.first().unwrap() as *const u8).align_offset(BPF_ALIGN_OF_U128),
919 0
920 );
921 for account_info in de_accounts {
922 let index_in_transaction = invoke_context
923 .transaction_context
924 .find_index_of_account(account_info.key)
925 .unwrap();
926 let account = invoke_context
927 .transaction_context
928 .get_account_at_index(index_in_transaction)
929 .unwrap()
930 .borrow();
931 assert_eq!(account.lamports(), account_info.lamports());
932 assert_eq!(account.data(), &account_info.data.borrow()[..]);
933 assert_eq!(account.owner(), account_info.owner);
934 assert_eq!(account.executable(), account_info.executable);
935 assert_eq!(account.rent_epoch(), account_info.rent_epoch);
936
937 assert_eq!(
938 (*account_info.lamports.borrow() as *const u64).align_offset(BPF_ALIGN_OF_U128),
939 0
940 );
941 assert_eq!(
942 account_info
943 .data
944 .borrow()
945 .as_ptr()
946 .align_offset(BPF_ALIGN_OF_U128),
947 0
948 );
949 }
950
951 deserialize_parameters(
952 invoke_context.transaction_context,
953 instruction_context,
954 copy_account_data,
955 serialized.as_slice(),
956 &accounts_metadata,
957 )
958 .unwrap();
959 for (index_in_transaction, (_key, original_account)) in
960 original_accounts.iter().enumerate()
961 {
962 let account = invoke_context
963 .transaction_context
964 .get_account_at_index(index_in_transaction as IndexOfAccount)
965 .unwrap()
966 .borrow();
967 assert_eq!(&*account, original_account);
968 }
969
970 original_accounts
972 .first_mut()
973 .unwrap()
974 .1
975 .set_owner(bpf_loader_deprecated::id());
976 invoke_context
977 .transaction_context
978 .get_account_at_index(0)
979 .unwrap()
980 .borrow_mut()
981 .set_owner(bpf_loader_deprecated::id());
982
983 let (mut serialized, regions, account_lengths) = serialize_parameters(
984 invoke_context.transaction_context,
985 instruction_context,
986 copy_account_data,
987 )
988 .unwrap();
989 let mut serialized_regions = concat_regions(®ions);
990
991 let (de_program_id, de_accounts, de_instruction_data) = unsafe {
992 deserialize_unaligned(
993 if copy_account_data {
994 serialized.as_slice_mut()
995 } else {
996 serialized_regions.as_slice_mut()
997 }
998 .first_mut()
999 .unwrap() as *mut u8,
1000 )
1001 };
1002 assert_eq!(&program_id, de_program_id);
1003 assert_eq!(instruction_data, de_instruction_data);
1004 for account_info in de_accounts {
1005 let index_in_transaction = invoke_context
1006 .transaction_context
1007 .find_index_of_account(account_info.key)
1008 .unwrap();
1009 let account = invoke_context
1010 .transaction_context
1011 .get_account_at_index(index_in_transaction)
1012 .unwrap()
1013 .borrow();
1014 assert_eq!(account.lamports(), account_info.lamports());
1015 assert_eq!(account.data(), &account_info.data.borrow()[..]);
1016 assert_eq!(account.owner(), account_info.owner);
1017 assert_eq!(account.executable(), account_info.executable);
1018 assert_eq!(account.rent_epoch(), account_info.rent_epoch);
1019 }
1020
1021 deserialize_parameters(
1022 invoke_context.transaction_context,
1023 instruction_context,
1024 copy_account_data,
1025 serialized.as_slice(),
1026 &account_lengths,
1027 )
1028 .unwrap();
1029 for (index_in_transaction, (_key, original_account)) in
1030 original_accounts.iter().enumerate()
1031 {
1032 let account = invoke_context
1033 .transaction_context
1034 .get_account_at_index(index_in_transaction as IndexOfAccount)
1035 .unwrap()
1036 .borrow();
1037 assert_eq!(&*account, original_account);
1038 }
1039 }
1040 }
1041
1042 #[deny(unsafe_op_in_unsafe_fn)]
1044 unsafe fn deserialize_unaligned<'a>(
1045 input: *mut u8,
1046 ) -> (&'a Pubkey, Vec<AccountInfo<'a>>, &'a [u8]) {
1047 struct Ptr<T>(std::marker::PhantomData<T>);
1049 impl<T> Ptr<T> {
1050 const COULD_BE_UNALIGNED: bool = std::mem::align_of::<T>() > 1;
1051
1052 #[inline(always)]
1053 fn read_possibly_unaligned(input: *mut u8, offset: usize) -> T {
1054 unsafe {
1055 let src = input.add(offset) as *const T;
1056 if Self::COULD_BE_UNALIGNED {
1057 src.read_unaligned()
1058 } else {
1059 src.read()
1060 }
1061 }
1062 }
1063
1064 #[inline(always)]
1073 fn ref_possibly_unaligned<'a>(input: *mut u8, offset: usize) -> &'a T {
1074 #[allow(clippy::transmute_ptr_to_ref)]
1075 unsafe {
1076 transmute(input.add(offset) as *const T)
1077 }
1078 }
1079
1080 #[inline(always)]
1082 fn mut_possibly_unaligned<'a>(input: *mut u8, offset: usize) -> &'a mut T {
1083 #[allow(clippy::transmute_ptr_to_ref)]
1084 unsafe {
1085 transmute(input.add(offset) as *mut T)
1086 }
1087 }
1088 }
1089
1090 let mut offset: usize = 0;
1091
1092 let num_accounts = Ptr::<u64>::read_possibly_unaligned(input, offset) as usize;
1095 offset += size_of::<u64>();
1096
1097 let mut accounts = Vec::with_capacity(num_accounts);
1100 for _ in 0..num_accounts {
1101 let dup_info = Ptr::<u8>::read_possibly_unaligned(input, offset);
1102 offset += size_of::<u8>();
1103 if dup_info == NON_DUP_MARKER {
1104 let is_signer = Ptr::<u8>::read_possibly_unaligned(input, offset) != 0;
1105 offset += size_of::<u8>();
1106
1107 let is_writable = Ptr::<u8>::read_possibly_unaligned(input, offset) != 0;
1108 offset += size_of::<u8>();
1109
1110 let key = Ptr::<Pubkey>::ref_possibly_unaligned(input, offset);
1111 offset += size_of::<Pubkey>();
1112
1113 let lamports = Rc::new(RefCell::new(Ptr::mut_possibly_unaligned(input, offset)));
1114 offset += size_of::<u64>();
1115
1116 let data_len = Ptr::<u64>::read_possibly_unaligned(input, offset) as usize;
1117 offset += size_of::<u64>();
1118
1119 let data = Rc::new(RefCell::new(unsafe {
1120 from_raw_parts_mut(input.add(offset), data_len)
1121 }));
1122 offset += data_len;
1123
1124 let owner: &Pubkey = Ptr::<Pubkey>::ref_possibly_unaligned(input, offset);
1125 offset += size_of::<Pubkey>();
1126
1127 let executable = Ptr::<u8>::read_possibly_unaligned(input, offset) != 0;
1128 offset += size_of::<u8>();
1129
1130 let rent_epoch = Ptr::<u64>::read_possibly_unaligned(input, offset);
1131 offset += size_of::<u64>();
1132
1133 accounts.push(AccountInfo {
1134 key,
1135 is_signer,
1136 is_writable,
1137 lamports,
1138 data,
1139 owner,
1140 executable,
1141 rent_epoch,
1142 });
1143 } else {
1144 accounts.push(accounts.get(dup_info as usize).unwrap().clone());
1146 }
1147 }
1148
1149 let instruction_data_len = Ptr::<u64>::read_possibly_unaligned(input, offset) as usize;
1152 offset += size_of::<u64>();
1153
1154 let instruction_data = unsafe { from_raw_parts(input.add(offset), instruction_data_len) };
1155 offset += instruction_data_len;
1156
1157 let program_id = Ptr::<Pubkey>::ref_possibly_unaligned(input, offset);
1160
1161 (program_id, accounts, instruction_data)
1162 }
1163
1164 fn concat_regions(regions: &[MemoryRegion]) -> AlignedMemory<HOST_ALIGN> {
1165 let len = regions.iter().fold(0, |len, region| len + region.len) as usize;
1166 let mut mem = AlignedMemory::zero_filled(len);
1167 for region in regions {
1168 let host_slice = unsafe {
1169 slice::from_raw_parts(region.host_addr.get() as *const u8, region.len as usize)
1170 };
1171 mem.as_slice_mut()[(region.vm_addr - MM_INPUT_START) as usize..][..region.len as usize]
1172 .copy_from_slice(host_slice)
1173 }
1174 mem
1175 }
1176}