solana_bpf_loader_program/
serialization.rs

1#![allow(clippy::integer_arithmetic)]
2
3use {
4    byteorder::{ByteOrder, LittleEndian},
5    solana_rbpf::{
6        aligned_memory::{AlignedMemory, Pod},
7        ebpf::{HOST_ALIGN, MM_INPUT_START},
8        memory_region::MemoryRegion,
9    },
10    solana_sdk::{
11        bpf_loader_deprecated,
12        entrypoint::{BPF_ALIGN_OF_U128, MAX_PERMITTED_DATA_INCREASE, NON_DUP_MARKER},
13        instruction::InstructionError,
14        pubkey::Pubkey,
15        system_instruction::MAX_PERMITTED_DATA_LENGTH,
16        transaction_context::{
17            BorrowedAccount, IndexOfAccount, InstructionContext, TransactionContext,
18        },
19    },
20    std::{mem, mem::size_of},
21};
22
23/// Maximum number of instruction accounts that can be serialized into the
24/// SBF VM.
25const MAX_INSTRUCTION_ACCOUNTS: u8 = NON_DUP_MARKER;
26
27enum SerializeAccount<'a> {
28    Account(IndexOfAccount, BorrowedAccount<'a>),
29    Duplicate(IndexOfAccount),
30}
31
32struct Serializer {
33    pub buffer: AlignedMemory<HOST_ALIGN>,
34    regions: Vec<MemoryRegion>,
35    vaddr: u64,
36    region_start: usize,
37    aligned: bool,
38}
39
40impl Serializer {
41    fn new(size: usize, start_addr: u64, aligned: 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        }
49    }
50
51    fn fill_write(&mut self, num: usize, value: u8) -> std::io::Result<()> {
52        self.buffer.fill_write(num, value)
53    }
54
55    pub fn write<T: Pod>(&mut self, value: T) {
56        self.debug_assert_alignment::<T>();
57        // Safety:
58        // in serialize_parameters_(aligned|unaligned) first we compute the
59        // required size then we write into the newly allocated buffer. There's
60        // no need to check bounds at every write.
61        //
62        // AlignedMemory::write_unchecked _does_ debug_assert!() that the capacity
63        // is enough, so in the unlikely case we introduce a bug in the size
64        // computation, tests will abort.
65        unsafe {
66            self.buffer.write_unchecked(value);
67        }
68    }
69
70    fn write_all(&mut self, value: &[u8]) {
71        // Safety:
72        // see write() - the buffer is guaranteed to be large enough
73        unsafe {
74            self.buffer.write_all_unchecked(value);
75        }
76    }
77
78    fn write_account(&mut self, account: &BorrowedAccount<'_>) -> Result<(), InstructionError> {
79        self.write_all(account.get_data());
80
81        if self.aligned {
82            let align_offset =
83                (account.get_data().len() as *const u8).align_offset(BPF_ALIGN_OF_U128);
84            self.fill_write(MAX_PERMITTED_DATA_INCREASE + align_offset, 0)
85                .map_err(|_| InstructionError::InvalidArgument)?;
86        }
87
88        Ok(())
89    }
90
91    fn push_region(&mut self) {
92        let range = self.region_start..self.buffer.len();
93        let region = MemoryRegion::new_writable(
94            self.buffer.as_slice_mut().get_mut(range.clone()).unwrap(),
95            self.vaddr,
96        );
97        self.regions.push(region);
98        self.region_start = range.end;
99        self.vaddr += range.len() as u64;
100    }
101
102    fn finish(mut self) -> (AlignedMemory<HOST_ALIGN>, Vec<MemoryRegion>) {
103        self.push_region();
104        debug_assert_eq!(self.region_start, self.buffer.len());
105        (self.buffer, self.regions)
106    }
107
108    fn debug_assert_alignment<T>(&self) {
109        debug_assert!(
110            !self.aligned
111                || self
112                    .buffer
113                    .as_slice()
114                    .as_ptr_range()
115                    .end
116                    .align_offset(mem::align_of::<T>())
117                    == 0
118        );
119    }
120}
121
122pub fn serialize_parameters(
123    transaction_context: &TransactionContext,
124    instruction_context: &InstructionContext,
125    should_cap_ix_accounts: bool,
126) -> Result<(AlignedMemory<HOST_ALIGN>, Vec<MemoryRegion>, Vec<usize>), InstructionError> {
127    let num_ix_accounts = instruction_context.get_number_of_instruction_accounts();
128    if should_cap_ix_accounts && num_ix_accounts > MAX_INSTRUCTION_ACCOUNTS as IndexOfAccount {
129        return Err(InstructionError::MaxAccountsExceeded);
130    }
131    let is_loader_deprecated = *instruction_context
132        .try_borrow_last_program_account(transaction_context)?
133        .get_owner()
134        == bpf_loader_deprecated::id();
135
136    let num_accounts = instruction_context.get_number_of_instruction_accounts() as usize;
137    let mut accounts = Vec::with_capacity(num_accounts);
138    let mut account_lengths: Vec<usize> = Vec::with_capacity(num_accounts);
139    for instruction_account_index in 0..instruction_context.get_number_of_instruction_accounts() {
140        if let Some(index) =
141            instruction_context.is_instruction_account_duplicate(instruction_account_index)?
142        {
143            accounts.push(SerializeAccount::Duplicate(index));
144            // unwrap here is safe: if an account is a duplicate, we must have
145            // seen the original already
146            account_lengths.push(*account_lengths.get(index as usize).unwrap());
147        } else {
148            let account = instruction_context
149                .try_borrow_instruction_account(transaction_context, instruction_account_index)?;
150            account_lengths.push(account.get_data().len());
151            accounts.push(SerializeAccount::Account(
152                instruction_account_index,
153                account,
154            ));
155        };
156    }
157
158    if is_loader_deprecated {
159        serialize_parameters_unaligned(transaction_context, instruction_context, accounts)
160    } else {
161        serialize_parameters_aligned(transaction_context, instruction_context, accounts)
162    }
163    .map(|(buffer, regions)| (buffer, regions, account_lengths))
164}
165
166pub fn deserialize_parameters(
167    transaction_context: &TransactionContext,
168    instruction_context: &InstructionContext,
169    buffer: &[u8],
170    account_lengths: &[usize],
171) -> Result<(), InstructionError> {
172    let is_loader_deprecated = *instruction_context
173        .try_borrow_last_program_account(transaction_context)?
174        .get_owner()
175        == bpf_loader_deprecated::id();
176    if is_loader_deprecated {
177        deserialize_parameters_unaligned(
178            transaction_context,
179            instruction_context,
180            buffer,
181            account_lengths,
182        )
183    } else {
184        deserialize_parameters_aligned(
185            transaction_context,
186            instruction_context,
187            buffer,
188            account_lengths,
189        )
190    }
191}
192
193fn serialize_parameters_unaligned(
194    transaction_context: &TransactionContext,
195    instruction_context: &InstructionContext,
196    accounts: Vec<SerializeAccount>,
197) -> Result<(AlignedMemory<HOST_ALIGN>, Vec<MemoryRegion>), InstructionError> {
198    // Calculate size in order to alloc once
199    let mut size = size_of::<u64>();
200    for account in &accounts {
201        size += 1; // dup
202        match account {
203            SerializeAccount::Duplicate(_) => {}
204            SerializeAccount::Account(_, account) => {
205                size += size_of::<u8>() // is_signer
206                + size_of::<u8>() // is_writable
207                + size_of::<Pubkey>() // key
208                + size_of::<u64>()  // lamports
209                + size_of::<u64>()  // data len
210                + account.get_data().len() // data
211                + size_of::<Pubkey>() // owner
212                + size_of::<u8>() // executable
213                + size_of::<u64>(); // rent_epoch
214            }
215        }
216    }
217    size += size_of::<u64>() // instruction data len
218         + instruction_context.get_instruction_data().len() // instruction data
219         + size_of::<Pubkey>(); // program id
220
221    let mut s = Serializer::new(size, MM_INPUT_START, false);
222
223    s.write::<u64>((accounts.len() as u64).to_le());
224    for account in accounts {
225        match account {
226            SerializeAccount::Duplicate(position) => s.write(position as u8),
227            SerializeAccount::Account(_, account) => {
228                s.write::<u8>(NON_DUP_MARKER);
229                s.write::<u8>(account.is_signer() as u8);
230                s.write::<u8>(account.is_writable() as u8);
231                s.write_all(account.get_key().as_ref());
232                s.write::<u64>(account.get_lamports().to_le());
233                s.write::<u64>((account.get_data().len() as u64).to_le());
234                s.write_account(&account)
235                    .map_err(|_| InstructionError::InvalidArgument)?;
236                s.write_all(account.get_owner().as_ref());
237                s.write::<u8>(account.is_executable() as u8);
238                s.write::<u64>((account.get_rent_epoch()).to_le());
239            }
240        };
241    }
242    s.write::<u64>((instruction_context.get_instruction_data().len() as u64).to_le());
243    s.write_all(instruction_context.get_instruction_data());
244    s.write_all(
245        instruction_context
246            .try_borrow_last_program_account(transaction_context)?
247            .get_key()
248            .as_ref(),
249    );
250
251    Ok(s.finish())
252}
253
254pub fn deserialize_parameters_unaligned(
255    transaction_context: &TransactionContext,
256    instruction_context: &InstructionContext,
257    buffer: &[u8],
258    account_lengths: &[usize],
259) -> Result<(), InstructionError> {
260    let mut start = size_of::<u64>(); // number of accounts
261    for (instruction_account_index, pre_len) in
262        (0..instruction_context.get_number_of_instruction_accounts()).zip(account_lengths.iter())
263    {
264        let duplicate =
265            instruction_context.is_instruction_account_duplicate(instruction_account_index)?;
266        start += 1; // is_dup
267        if duplicate.is_none() {
268            let mut borrowed_account = instruction_context
269                .try_borrow_instruction_account(transaction_context, instruction_account_index)?;
270            start += size_of::<u8>(); // is_signer
271            start += size_of::<u8>(); // is_writable
272            start += size_of::<Pubkey>(); // key
273            let lamports = LittleEndian::read_u64(
274                buffer
275                    .get(start..)
276                    .ok_or(InstructionError::InvalidArgument)?,
277            );
278            if borrowed_account.get_lamports() != lamports {
279                borrowed_account.set_lamports(lamports)?;
280            }
281            start += size_of::<u64>() // lamports
282                + size_of::<u64>(); // data length
283            let data = buffer
284                .get(start..start + pre_len)
285                .ok_or(InstructionError::InvalidArgument)?;
286            // The redundant check helps to avoid the expensive data comparison if we can
287            match borrowed_account
288                .can_data_be_resized(data.len())
289                .and_then(|_| borrowed_account.can_data_be_changed())
290            {
291                Ok(()) => borrowed_account.set_data_from_slice(data)?,
292                Err(err) if borrowed_account.get_data() != data => return Err(err),
293                _ => {}
294            }
295            start += pre_len // data
296                + size_of::<Pubkey>() // owner
297                + size_of::<u8>() // executable
298                + size_of::<u64>(); // rent_epoch
299        }
300    }
301    Ok(())
302}
303
304fn serialize_parameters_aligned(
305    transaction_context: &TransactionContext,
306    instruction_context: &InstructionContext,
307    accounts: Vec<SerializeAccount>,
308) -> Result<(AlignedMemory<HOST_ALIGN>, Vec<MemoryRegion>), InstructionError> {
309    // Calculate size in order to alloc once
310    let mut size = size_of::<u64>();
311    for account in &accounts {
312        size += 1; // dup
313        match account {
314            SerializeAccount::Duplicate(_) => size += 7, // padding to 64-bit aligned
315            SerializeAccount::Account(_, account) => {
316                let data_len = account.get_data().len();
317                size += size_of::<u8>() // is_signer
318                + size_of::<u8>() // is_writable
319                + size_of::<u8>() // executable
320                + size_of::<u32>() // original_data_len
321                + size_of::<Pubkey>()  // key
322                + size_of::<Pubkey>() // owner
323                + size_of::<u64>()  // lamports
324                + size_of::<u64>()  // data len
325                + data_len
326                + MAX_PERMITTED_DATA_INCREASE
327                + (data_len as *const u8).align_offset(BPF_ALIGN_OF_U128)
328                + size_of::<u64>(); // rent epoch
329            }
330        }
331    }
332    size += size_of::<u64>() // data len
333    + instruction_context.get_instruction_data().len()
334    + size_of::<Pubkey>(); // program id;
335
336    let mut s = Serializer::new(size, MM_INPUT_START, true);
337
338    // Serialize into the buffer
339    s.write::<u64>((accounts.len() as u64).to_le());
340    for account in accounts {
341        match account {
342            SerializeAccount::Account(_, borrowed_account) => {
343                s.write::<u8>(NON_DUP_MARKER);
344                s.write::<u8>(borrowed_account.is_signer() as u8);
345                s.write::<u8>(borrowed_account.is_writable() as u8);
346                s.write::<u8>(borrowed_account.is_executable() as u8);
347                s.write_all(&[0u8, 0, 0, 0]);
348                s.write_all(borrowed_account.get_key().as_ref());
349                s.write_all(borrowed_account.get_owner().as_ref());
350                s.write::<u64>(borrowed_account.get_lamports().to_le());
351                s.write::<u64>((borrowed_account.get_data().len() as u64).to_le());
352                s.write_account(&borrowed_account)
353                    .map_err(|_| InstructionError::InvalidArgument)?;
354                s.write::<u64>((borrowed_account.get_rent_epoch()).to_le());
355            }
356            SerializeAccount::Duplicate(position) => {
357                s.write::<u8>(position as u8);
358                s.write_all(&[0u8, 0, 0, 0, 0, 0, 0]);
359            }
360        };
361    }
362    s.write::<u64>((instruction_context.get_instruction_data().len() as u64).to_le());
363    s.write_all(instruction_context.get_instruction_data());
364    s.write_all(
365        instruction_context
366            .try_borrow_last_program_account(transaction_context)?
367            .get_key()
368            .as_ref(),
369    );
370
371    Ok(s.finish())
372}
373
374pub fn deserialize_parameters_aligned(
375    transaction_context: &TransactionContext,
376    instruction_context: &InstructionContext,
377    buffer: &[u8],
378    account_lengths: &[usize],
379) -> Result<(), InstructionError> {
380    let mut start = size_of::<u64>(); // number of accounts
381    for (instruction_account_index, pre_len) in
382        (0..instruction_context.get_number_of_instruction_accounts()).zip(account_lengths.iter())
383    {
384        let duplicate =
385            instruction_context.is_instruction_account_duplicate(instruction_account_index)?;
386        start += size_of::<u8>(); // position
387        if duplicate.is_some() {
388            start += 7; // padding to 64-bit aligned
389        } else {
390            let mut borrowed_account = instruction_context
391                .try_borrow_instruction_account(transaction_context, instruction_account_index)?;
392            start += size_of::<u8>() // is_signer
393                + size_of::<u8>() // is_writable
394                + size_of::<u8>() // executable
395                + size_of::<u32>() // original_data_len
396                + size_of::<Pubkey>(); // key
397            let owner = buffer
398                .get(start..start + size_of::<Pubkey>())
399                .ok_or(InstructionError::InvalidArgument)?;
400            start += size_of::<Pubkey>(); // owner
401            let lamports = LittleEndian::read_u64(
402                buffer
403                    .get(start..)
404                    .ok_or(InstructionError::InvalidArgument)?,
405            );
406            if borrowed_account.get_lamports() != lamports {
407                borrowed_account.set_lamports(lamports)?;
408            }
409            start += size_of::<u64>(); // lamports
410            let post_len = LittleEndian::read_u64(
411                buffer
412                    .get(start..)
413                    .ok_or(InstructionError::InvalidArgument)?,
414            ) as usize;
415            start += size_of::<u64>(); // data length
416            if post_len.saturating_sub(*pre_len) > MAX_PERMITTED_DATA_INCREASE
417                || post_len > MAX_PERMITTED_DATA_LENGTH as usize
418            {
419                return Err(InstructionError::InvalidRealloc);
420            }
421            let data_end = start + post_len;
422            let data = buffer
423                .get(start..data_end)
424                .ok_or(InstructionError::InvalidArgument)?;
425            // The redundant check helps to avoid the expensive data comparison if we can
426            match borrowed_account
427                .can_data_be_resized(data.len())
428                .and_then(|_| borrowed_account.can_data_be_changed())
429            {
430                Ok(()) => borrowed_account.set_data_from_slice(data)?,
431                Err(err) if borrowed_account.get_data() != data => return Err(err),
432                _ => {}
433            }
434            start += *pre_len + MAX_PERMITTED_DATA_INCREASE; // data
435            start += (start as *const u8).align_offset(BPF_ALIGN_OF_U128);
436            start += size_of::<u64>(); // rent_epoch
437            if borrowed_account.get_owner().to_bytes() != owner {
438                // Change the owner at the end so that we are allowed to change the lamports and data before
439                borrowed_account.set_owner(owner)?;
440            }
441        }
442    }
443    Ok(())
444}
445
446#[cfg(test)]
447mod tests {
448    use {
449        super::*,
450        solana_program_runtime::invoke_context::{prepare_mock_invoke_context, InvokeContext},
451        solana_sdk::{
452            account::{Account, AccountSharedData, ReadableAccount, WritableAccount},
453            account_info::AccountInfo,
454            bpf_loader,
455            entrypoint::deserialize,
456            instruction::AccountMeta,
457            sysvar::rent::Rent,
458        },
459        std::{
460            cell::RefCell,
461            rc::Rc,
462            slice::{from_raw_parts, from_raw_parts_mut},
463        },
464    };
465
466    #[test]
467    fn test_serialize_parameters_with_many_accounts() {
468        struct TestCase {
469            num_ix_accounts: usize,
470            append_dup_account: bool,
471            should_cap_ix_accounts: bool,
472            expected_err: Option<InstructionError>,
473            name: &'static str,
474        }
475
476        for TestCase {
477            num_ix_accounts,
478            append_dup_account,
479            should_cap_ix_accounts,
480            expected_err,
481            name,
482        } in [
483            TestCase {
484                name: "serialize max accounts without cap",
485                num_ix_accounts: usize::from(MAX_INSTRUCTION_ACCOUNTS),
486                should_cap_ix_accounts: false,
487                append_dup_account: false,
488                expected_err: None,
489            },
490            TestCase {
491                name: "serialize max accounts and append dup without cap",
492                num_ix_accounts: usize::from(MAX_INSTRUCTION_ACCOUNTS),
493                should_cap_ix_accounts: false,
494                append_dup_account: true,
495                expected_err: None,
496            },
497            TestCase {
498                name: "serialize max accounts with cap",
499                num_ix_accounts: usize::from(MAX_INSTRUCTION_ACCOUNTS),
500                should_cap_ix_accounts: true,
501                append_dup_account: false,
502                expected_err: None,
503            },
504            TestCase {
505                name: "serialize too many accounts with cap",
506                num_ix_accounts: usize::from(MAX_INSTRUCTION_ACCOUNTS) + 1,
507                should_cap_ix_accounts: true,
508                append_dup_account: false,
509                expected_err: Some(InstructionError::MaxAccountsExceeded),
510            },
511            TestCase {
512                name: "serialize too many accounts and append dup with cap",
513                num_ix_accounts: usize::from(MAX_INSTRUCTION_ACCOUNTS),
514                should_cap_ix_accounts: true,
515                append_dup_account: true,
516                expected_err: Some(InstructionError::MaxAccountsExceeded),
517            },
518            // This test case breaks parameter deserialization and can be cleaned up
519            // when should_cap_ix_accounts is enabled.
520            //
521            // TestCase {
522            //     name: "serialize too many accounts and append dup without cap",
523            //     num_ix_accounts: usize::from(MAX_INSTRUCTION_ACCOUNTS) + 1,
524            //     should_cap_ix_accounts: false,
525            //     append_dup_account: true,
526            //     expected_err: None,
527            // },
528        ] {
529            let program_id = solana_sdk::pubkey::new_rand();
530            let mut transaction_accounts = vec![(
531                program_id,
532                AccountSharedData::from(Account {
533                    lamports: 0,
534                    data: vec![],
535                    owner: bpf_loader::id(),
536                    executable: true,
537                    rent_epoch: 0,
538                }),
539            )];
540
541            let instruction_account_keys: Vec<Pubkey> =
542                (0..num_ix_accounts).map(|_| Pubkey::new_unique()).collect();
543
544            for key in &instruction_account_keys {
545                transaction_accounts.push((
546                    *key,
547                    AccountSharedData::from(Account {
548                        lamports: 0,
549                        data: vec![],
550                        owner: program_id,
551                        executable: false,
552                        rent_epoch: 0,
553                    }),
554                ));
555            }
556
557            let mut instruction_account_metas: Vec<_> = instruction_account_keys
558                .iter()
559                .map(|key| AccountMeta::new_readonly(*key, false))
560                .collect();
561            if append_dup_account {
562                instruction_account_metas.push(instruction_account_metas.last().cloned().unwrap());
563            }
564
565            let program_indices = [0];
566            let instruction_accounts = prepare_mock_invoke_context(
567                transaction_accounts.clone(),
568                instruction_account_metas,
569                &program_indices,
570            )
571            .instruction_accounts;
572            let instruction_data = vec![];
573
574            let mut transaction_context =
575                TransactionContext::new(transaction_accounts, Some(Rent::default()), 1, 1);
576            transaction_context
577                .get_next_instruction_context()
578                .unwrap()
579                .configure(&program_indices, &instruction_accounts, &instruction_data);
580            transaction_context.push().unwrap();
581            let instruction_context = transaction_context
582                .get_instruction_context_at_index_in_trace(0)
583                .unwrap();
584
585            let serialization_result = serialize_parameters(
586                &transaction_context,
587                instruction_context,
588                should_cap_ix_accounts,
589            );
590            assert_eq!(
591                serialization_result.as_ref().err(),
592                expected_err.as_ref(),
593                "{name} test case failed"
594            );
595            if expected_err.is_some() {
596                continue;
597            }
598
599            let (mut serialized, _regions, _account_lengths) = serialization_result.unwrap();
600            let (de_program_id, de_accounts, de_instruction_data) =
601                unsafe { deserialize(serialized.as_slice_mut().first_mut().unwrap() as *mut u8) };
602            assert_eq!(de_program_id, &program_id);
603            assert_eq!(de_instruction_data, &instruction_data);
604            for (index, account_info) in de_accounts.into_iter().enumerate() {
605                let ix_account = &instruction_accounts.get(index).unwrap();
606                assert_eq!(
607                    account_info.key,
608                    transaction_context
609                        .get_key_of_account_at_index(ix_account.index_in_transaction)
610                        .unwrap()
611                );
612                assert_eq!(account_info.owner, &program_id);
613                assert!(!account_info.executable);
614                assert!(account_info.data_is_empty());
615                assert!(!account_info.is_writable);
616                assert!(!account_info.is_signer);
617            }
618        }
619    }
620
621    #[test]
622    fn test_serialize_parameters() {
623        let program_id = solana_sdk::pubkey::new_rand();
624        let transaction_accounts = vec![
625            (
626                program_id,
627                AccountSharedData::from(Account {
628                    lamports: 0,
629                    data: vec![],
630                    owner: bpf_loader::id(),
631                    executable: true,
632                    rent_epoch: 0,
633                }),
634            ),
635            (
636                solana_sdk::pubkey::new_rand(),
637                AccountSharedData::from(Account {
638                    lamports: 1,
639                    data: vec![1u8, 2, 3, 4, 5],
640                    owner: bpf_loader::id(),
641                    executable: false,
642                    rent_epoch: 100,
643                }),
644            ),
645            (
646                solana_sdk::pubkey::new_rand(),
647                AccountSharedData::from(Account {
648                    lamports: 2,
649                    data: vec![11u8, 12, 13, 14, 15, 16, 17, 18, 19],
650                    owner: bpf_loader::id(),
651                    executable: true,
652                    rent_epoch: 200,
653                }),
654            ),
655            (
656                solana_sdk::pubkey::new_rand(),
657                AccountSharedData::from(Account {
658                    lamports: 3,
659                    data: vec![],
660                    owner: bpf_loader::id(),
661                    executable: false,
662                    rent_epoch: 3100,
663                }),
664            ),
665            (
666                solana_sdk::pubkey::new_rand(),
667                AccountSharedData::from(Account {
668                    lamports: 4,
669                    data: vec![1u8, 2, 3, 4, 5],
670                    owner: bpf_loader::id(),
671                    executable: false,
672                    rent_epoch: 100,
673                }),
674            ),
675            (
676                solana_sdk::pubkey::new_rand(),
677                AccountSharedData::from(Account {
678                    lamports: 5,
679                    data: vec![11u8, 12, 13, 14, 15, 16, 17, 18, 19],
680                    owner: bpf_loader::id(),
681                    executable: true,
682                    rent_epoch: 200,
683                }),
684            ),
685            (
686                solana_sdk::pubkey::new_rand(),
687                AccountSharedData::from(Account {
688                    lamports: 6,
689                    data: vec![],
690                    owner: bpf_loader::id(),
691                    executable: false,
692                    rent_epoch: 3100,
693                }),
694            ),
695        ];
696        let instruction_accounts = [1, 1, 2, 3, 4, 4, 5, 6]
697            .into_iter()
698            .enumerate()
699            .map(
700                |(instruction_account_index, index_in_transaction)| AccountMeta {
701                    pubkey: transaction_accounts.get(index_in_transaction).unwrap().0,
702                    is_signer: false,
703                    is_writable: instruction_account_index >= 4,
704                },
705            )
706            .collect();
707        let instruction_data = vec![1u8, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11];
708        let program_indices = [0];
709        let mut original_accounts = transaction_accounts.clone();
710        let preparation = prepare_mock_invoke_context(
711            transaction_accounts,
712            instruction_accounts,
713            &program_indices,
714        );
715        let mut transaction_context = TransactionContext::new(
716            preparation.transaction_accounts,
717            Some(Rent::default()),
718            1,
719            1,
720        );
721        let mut invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
722        invoke_context
723            .transaction_context
724            .get_next_instruction_context()
725            .unwrap()
726            .configure(
727                &program_indices,
728                &preparation.instruction_accounts,
729                &instruction_data,
730            );
731        invoke_context.push().unwrap();
732        let instruction_context = invoke_context
733            .transaction_context
734            .get_current_instruction_context()
735            .unwrap();
736
737        // check serialize_parameters_aligned
738
739        let (mut serialized, _regions, account_lengths) = serialize_parameters(
740            invoke_context.transaction_context,
741            instruction_context,
742            true,
743        )
744        .unwrap();
745
746        let (de_program_id, de_accounts, de_instruction_data) =
747            unsafe { deserialize(serialized.as_slice_mut().first_mut().unwrap() as *mut u8) };
748
749        assert_eq!(&program_id, de_program_id);
750        assert_eq!(instruction_data, de_instruction_data);
751        assert_eq!(
752            (de_instruction_data.first().unwrap() as *const u8).align_offset(BPF_ALIGN_OF_U128),
753            0
754        );
755        for account_info in de_accounts {
756            let index_in_transaction = invoke_context
757                .transaction_context
758                .find_index_of_account(account_info.key)
759                .unwrap();
760            let account = invoke_context
761                .transaction_context
762                .get_account_at_index(index_in_transaction)
763                .unwrap()
764                .borrow();
765            assert_eq!(account.lamports(), account_info.lamports());
766            assert_eq!(account.data(), &account_info.data.borrow()[..]);
767            assert_eq!(account.owner(), account_info.owner);
768            assert_eq!(account.executable(), account_info.executable);
769            assert_eq!(account.rent_epoch(), account_info.rent_epoch);
770
771            assert_eq!(
772                (*account_info.lamports.borrow() as *const u64).align_offset(BPF_ALIGN_OF_U128),
773                0
774            );
775            assert_eq!(
776                account_info
777                    .data
778                    .borrow()
779                    .as_ptr()
780                    .align_offset(BPF_ALIGN_OF_U128),
781                0
782            );
783        }
784
785        deserialize_parameters(
786            invoke_context.transaction_context,
787            instruction_context,
788            serialized.as_slice(),
789            &account_lengths,
790        )
791        .unwrap();
792        for (index_in_transaction, (_key, original_account)) in original_accounts.iter().enumerate()
793        {
794            let account = invoke_context
795                .transaction_context
796                .get_account_at_index(index_in_transaction as IndexOfAccount)
797                .unwrap()
798                .borrow();
799            assert_eq!(&*account, original_account);
800        }
801
802        // check serialize_parameters_unaligned
803        original_accounts
804            .first_mut()
805            .unwrap()
806            .1
807            .set_owner(bpf_loader_deprecated::id());
808        invoke_context
809            .transaction_context
810            .get_account_at_index(0)
811            .unwrap()
812            .borrow_mut()
813            .set_owner(bpf_loader_deprecated::id());
814
815        let (mut serialized, _regions, account_lengths) = serialize_parameters(
816            invoke_context.transaction_context,
817            instruction_context,
818            true,
819        )
820        .unwrap();
821
822        let (de_program_id, de_accounts, de_instruction_data) = unsafe {
823            deserialize_unaligned(serialized.as_slice_mut().first_mut().unwrap() as *mut u8)
824        };
825        assert_eq!(&program_id, de_program_id);
826        assert_eq!(instruction_data, de_instruction_data);
827        for account_info in de_accounts {
828            let index_in_transaction = invoke_context
829                .transaction_context
830                .find_index_of_account(account_info.key)
831                .unwrap();
832            let account = invoke_context
833                .transaction_context
834                .get_account_at_index(index_in_transaction)
835                .unwrap()
836                .borrow();
837            assert_eq!(account.lamports(), account_info.lamports());
838            assert_eq!(account.data(), &account_info.data.borrow()[..]);
839            assert_eq!(account.owner(), account_info.owner);
840            assert_eq!(account.executable(), account_info.executable);
841            assert_eq!(account.rent_epoch(), account_info.rent_epoch);
842        }
843
844        deserialize_parameters(
845            invoke_context.transaction_context,
846            instruction_context,
847            serialized.as_slice(),
848            &account_lengths,
849        )
850        .unwrap();
851        for (index_in_transaction, (_key, original_account)) in original_accounts.iter().enumerate()
852        {
853            let account = invoke_context
854                .transaction_context
855                .get_account_at_index(index_in_transaction as IndexOfAccount)
856                .unwrap()
857                .borrow();
858            assert_eq!(&*account, original_account);
859        }
860    }
861
862    // the old bpf_loader in-program deserializer bpf_loader::id()
863    #[allow(clippy::type_complexity)]
864    pub unsafe fn deserialize_unaligned<'a>(
865        input: *mut u8,
866    ) -> (&'a Pubkey, Vec<AccountInfo<'a>>, &'a [u8]) {
867        let mut offset: usize = 0;
868
869        // number of accounts present
870
871        #[allow(clippy::cast_ptr_alignment)]
872        let num_accounts = *(input.add(offset) as *const u64) as usize;
873        offset += size_of::<u64>();
874
875        // account Infos
876
877        let mut accounts = Vec::with_capacity(num_accounts);
878        for _ in 0..num_accounts {
879            let dup_info = *(input.add(offset) as *const u8);
880            offset += size_of::<u8>();
881            if dup_info == NON_DUP_MARKER {
882                #[allow(clippy::cast_ptr_alignment)]
883                let is_signer = *(input.add(offset) as *const u8) != 0;
884                offset += size_of::<u8>();
885
886                #[allow(clippy::cast_ptr_alignment)]
887                let is_writable = *(input.add(offset) as *const u8) != 0;
888                offset += size_of::<u8>();
889
890                let key: &Pubkey = &*(input.add(offset) as *const Pubkey);
891                offset += size_of::<Pubkey>();
892
893                #[allow(clippy::cast_ptr_alignment)]
894                let lamports = Rc::new(RefCell::new(&mut *(input.add(offset) as *mut u64)));
895                offset += size_of::<u64>();
896
897                #[allow(clippy::cast_ptr_alignment)]
898                let data_len = *(input.add(offset) as *const u64) as usize;
899                offset += size_of::<u64>();
900
901                let data = Rc::new(RefCell::new({
902                    from_raw_parts_mut(input.add(offset), data_len)
903                }));
904                offset += data_len;
905
906                let owner: &Pubkey = &*(input.add(offset) as *const Pubkey);
907                offset += size_of::<Pubkey>();
908
909                #[allow(clippy::cast_ptr_alignment)]
910                let executable = *(input.add(offset) as *const u8) != 0;
911                offset += size_of::<u8>();
912
913                #[allow(clippy::cast_ptr_alignment)]
914                let rent_epoch = *(input.add(offset) as *const u64);
915                offset += size_of::<u64>();
916
917                accounts.push(AccountInfo {
918                    key,
919                    is_signer,
920                    is_writable,
921                    lamports,
922                    data,
923                    owner,
924                    executable,
925                    rent_epoch,
926                });
927            } else {
928                // duplicate account, clone the original
929                accounts.push(accounts.get(dup_info as usize).unwrap().clone());
930            }
931        }
932
933        // instruction data
934
935        #[allow(clippy::cast_ptr_alignment)]
936        let instruction_data_len = *(input.add(offset) as *const u64) as usize;
937        offset += size_of::<u64>();
938
939        let instruction_data = { from_raw_parts(input.add(offset), instruction_data_len) };
940        offset += instruction_data_len;
941
942        // program Id
943
944        let program_id: &Pubkey = &*(input.add(offset) as *const Pubkey);
945
946        (program_id, accounts, instruction_data)
947    }
948}