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 #[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_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_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_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_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_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_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_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_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 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}