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