clone_solana_bpf_loader_program/
serialization.rs

1#![allow(clippy::arithmetic_side_effects)]
2
3use {
4    clone_solana_instruction::error::InstructionError,
5    clone_solana_program_entrypoint::{
6        BPF_ALIGN_OF_U128, MAX_PERMITTED_DATA_INCREASE, NON_DUP_MARKER,
7    },
8    clone_solana_program_runtime::invoke_context::SerializedAccountMetadata,
9    clone_solana_pubkey::Pubkey,
10    clone_solana_sdk_ids::bpf_loader_deprecated,
11    clone_solana_system_interface::MAX_PERMITTED_DATA_LENGTH,
12    clone_solana_transaction_context::{
13        BorrowedAccount, IndexOfAccount, InstructionContext, TransactionContext,
14    },
15    solana_sbpf::{
16        aligned_memory::{AlignedMemory, Pod},
17        ebpf::{HOST_ALIGN, MM_INPUT_START},
18        memory_region::{MemoryRegion, MemoryState},
19    },
20    std::mem::{self, 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
27#[allow(dead_code)]
28enum SerializeAccount<'a> {
29    Account(IndexOfAccount, BorrowedAccount<'a>),
30    Duplicate(IndexOfAccount),
31}
32
33struct Serializer {
34    buffer: AlignedMemory<HOST_ALIGN>,
35    regions: Vec<MemoryRegion>,
36    vaddr: u64,
37    region_start: usize,
38    aligned: bool,
39    copy_account_data: bool,
40}
41
42impl Serializer {
43    fn new(size: usize, start_addr: u64, aligned: bool, copy_account_data: bool) -> Serializer {
44        Serializer {
45            buffer: AlignedMemory::with_capacity(size),
46            regions: Vec::new(),
47            region_start: 0,
48            vaddr: start_addr,
49            aligned,
50            copy_account_data,
51        }
52    }
53
54    fn fill_write(&mut self, num: usize, value: u8) -> std::io::Result<()> {
55        self.buffer.fill_write(num, value)
56    }
57
58    fn write<T: Pod>(&mut self, value: T) -> u64 {
59        self.debug_assert_alignment::<T>();
60        let vaddr = self
61            .vaddr
62            .saturating_add(self.buffer.len() as u64)
63            .saturating_sub(self.region_start as u64);
64        // Safety:
65        // in serialize_parameters_(aligned|unaligned) first we compute the
66        // required size then we write into the newly allocated buffer. There's
67        // no need to check bounds at every write.
68        //
69        // AlignedMemory::write_unchecked _does_ debug_assert!() that the capacity
70        // is enough, so in the unlikely case we introduce a bug in the size
71        // computation, tests will abort.
72        unsafe {
73            self.buffer.write_unchecked(value);
74        }
75
76        vaddr
77    }
78
79    fn write_all(&mut self, value: &[u8]) -> u64 {
80        let vaddr = self
81            .vaddr
82            .saturating_add(self.buffer.len() as u64)
83            .saturating_sub(self.region_start as u64);
84        // Safety:
85        // see write() - the buffer is guaranteed to be large enough
86        unsafe {
87            self.buffer.write_all_unchecked(value);
88        }
89
90        vaddr
91    }
92
93    fn write_account(
94        &mut self,
95        account: &mut BorrowedAccount<'_>,
96    ) -> Result<u64, InstructionError> {
97        let vm_data_addr = if self.copy_account_data {
98            let vm_data_addr = self.vaddr.saturating_add(self.buffer.len() as u64);
99            self.write_all(account.get_data());
100            vm_data_addr
101        } else {
102            self.push_region(true);
103            let vaddr = self.vaddr;
104            self.push_account_data_region(account)?;
105            vaddr
106        };
107
108        if self.aligned {
109            let align_offset =
110                (account.get_data().len() as *const u8).align_offset(BPF_ALIGN_OF_U128);
111            if self.copy_account_data {
112                self.fill_write(MAX_PERMITTED_DATA_INCREASE + align_offset, 0)
113                    .map_err(|_| InstructionError::InvalidArgument)?;
114            } else {
115                // The deserialization code is going to align the vm_addr to
116                // BPF_ALIGN_OF_U128. Always add one BPF_ALIGN_OF_U128 worth of
117                // padding and shift the start of the next region, so that once
118                // vm_addr is aligned, the corresponding host_addr is aligned
119                // too.
120                self.fill_write(MAX_PERMITTED_DATA_INCREASE + BPF_ALIGN_OF_U128, 0)
121                    .map_err(|_| InstructionError::InvalidArgument)?;
122                self.region_start += BPF_ALIGN_OF_U128.saturating_sub(align_offset);
123                // put the realloc padding in its own region
124                self.push_region(account.can_data_be_changed().is_ok());
125            }
126        }
127
128        Ok(vm_data_addr)
129    }
130
131    fn push_account_data_region(
132        &mut self,
133        account: &mut BorrowedAccount<'_>,
134    ) -> Result<(), InstructionError> {
135        if !account.get_data().is_empty() {
136            let region = match account_data_region_memory_state(account) {
137                MemoryState::Readable => MemoryRegion::new_readonly(account.get_data(), self.vaddr),
138                MemoryState::Writable => {
139                    MemoryRegion::new_writable(account.get_data_mut()?, self.vaddr)
140                }
141                MemoryState::Cow(index_in_transaction) => {
142                    MemoryRegion::new_cow(account.get_data(), self.vaddr, index_in_transaction)
143                }
144            };
145            self.vaddr += region.len;
146            self.regions.push(region);
147        }
148
149        Ok(())
150    }
151
152    fn push_region(&mut self, writable: bool) {
153        let range = self.region_start..self.buffer.len();
154        let region = if writable {
155            MemoryRegion::new_writable(
156                self.buffer.as_slice_mut().get_mut(range.clone()).unwrap(),
157                self.vaddr,
158            )
159        } else {
160            MemoryRegion::new_readonly(
161                self.buffer.as_slice().get(range.clone()).unwrap(),
162                self.vaddr,
163            )
164        };
165        self.regions.push(region);
166        self.region_start = range.end;
167        self.vaddr += range.len() as u64;
168    }
169
170    fn finish(mut self) -> (AlignedMemory<HOST_ALIGN>, Vec<MemoryRegion>) {
171        self.push_region(true);
172        debug_assert_eq!(self.region_start, self.buffer.len());
173        (self.buffer, self.regions)
174    }
175
176    fn debug_assert_alignment<T>(&self) {
177        debug_assert!(
178            !self.aligned
179                || self
180                    .buffer
181                    .as_slice()
182                    .as_ptr_range()
183                    .end
184                    .align_offset(mem::align_of::<T>())
185                    == 0
186        );
187    }
188}
189
190pub fn serialize_parameters(
191    transaction_context: &TransactionContext,
192    instruction_context: &InstructionContext,
193    copy_account_data: bool,
194    mask_out_rent_epoch_in_vm_serialization: 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        // fun fact: jemalloc is good at caching tiny allocations like this one,
232        // so collecting here is actually faster than passing the iterator
233        // around, since the iterator does the work to produce its items each
234        // time it's iterated on.
235        .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            mask_out_rent_epoch_in_vm_serialization,
244        )
245    } else {
246        serialize_parameters_aligned(
247            accounts,
248            instruction_context.get_instruction_data(),
249            &program_id,
250            copy_account_data,
251            mask_out_rent_epoch_in_vm_serialization,
252        )
253    }
254}
255
256pub(crate) fn deserialize_parameters(
257    transaction_context: &TransactionContext,
258    instruction_context: &InstructionContext,
259    copy_account_data: bool,
260    buffer: &[u8],
261    accounts_metadata: &[SerializedAccountMetadata],
262) -> Result<(), InstructionError> {
263    let is_loader_deprecated = *instruction_context
264        .try_borrow_last_program_account(transaction_context)?
265        .get_owner()
266        == bpf_loader_deprecated::id();
267    let account_lengths = accounts_metadata.iter().map(|a| a.original_data_len);
268    if is_loader_deprecated {
269        deserialize_parameters_unaligned(
270            transaction_context,
271            instruction_context,
272            copy_account_data,
273            buffer,
274            account_lengths,
275        )
276    } else {
277        deserialize_parameters_aligned(
278            transaction_context,
279            instruction_context,
280            copy_account_data,
281            buffer,
282            account_lengths,
283        )
284    }
285}
286
287fn serialize_parameters_unaligned(
288    accounts: Vec<SerializeAccount>,
289    instruction_data: &[u8],
290    program_id: &Pubkey,
291    copy_account_data: bool,
292    mask_out_rent_epoch_in_vm_serialization: bool,
293) -> Result<
294    (
295        AlignedMemory<HOST_ALIGN>,
296        Vec<MemoryRegion>,
297        Vec<SerializedAccountMetadata>,
298    ),
299    InstructionError,
300> {
301    // Calculate size in order to alloc once
302    let mut size = size_of::<u64>();
303    for account in &accounts {
304        size += 1; // dup
305        match account {
306            SerializeAccount::Duplicate(_) => {}
307            SerializeAccount::Account(_, account) => {
308                size += size_of::<u8>() // is_signer
309                + size_of::<u8>() // is_writable
310                + size_of::<Pubkey>() // key
311                + size_of::<u64>()  // lamports
312                + size_of::<u64>()  // data len
313                + size_of::<Pubkey>() // owner
314                + size_of::<u8>() // executable
315                + size_of::<u64>(); // rent_epoch
316                if copy_account_data {
317                    size += account.get_data().len();
318                }
319            }
320        }
321    }
322    size += size_of::<u64>() // instruction data len
323         + instruction_data.len() // instruction data
324         + size_of::<Pubkey>(); // program id
325
326    let mut s = Serializer::new(size, MM_INPUT_START, false, copy_account_data);
327
328    let mut accounts_metadata: Vec<SerializedAccountMetadata> = Vec::with_capacity(accounts.len());
329    s.write::<u64>((accounts.len() as u64).to_le());
330    for account in accounts {
331        match account {
332            SerializeAccount::Duplicate(position) => {
333                accounts_metadata.push(accounts_metadata.get(position as usize).unwrap().clone());
334                s.write(position as u8);
335            }
336            SerializeAccount::Account(_, mut account) => {
337                s.write::<u8>(NON_DUP_MARKER);
338                s.write::<u8>(account.is_signer() as u8);
339                s.write::<u8>(account.is_writable() as u8);
340                let vm_key_addr = s.write_all(account.get_key().as_ref());
341                let vm_lamports_addr = s.write::<u64>(account.get_lamports().to_le());
342                s.write::<u64>((account.get_data().len() as u64).to_le());
343                let vm_data_addr = s.write_account(&mut account)?;
344                let vm_owner_addr = s.write_all(account.get_owner().as_ref());
345                #[allow(deprecated)]
346                s.write::<u8>(account.is_executable() as u8);
347                let rent_epoch = if mask_out_rent_epoch_in_vm_serialization {
348                    u64::MAX
349                } else {
350                    account.get_rent_epoch()
351                };
352                s.write::<u64>(rent_epoch.to_le());
353                accounts_metadata.push(SerializedAccountMetadata {
354                    original_data_len: account.get_data().len(),
355                    vm_key_addr,
356                    vm_lamports_addr,
357                    vm_owner_addr,
358                    vm_data_addr,
359                });
360            }
361        };
362    }
363    s.write::<u64>((instruction_data.len() as u64).to_le());
364    s.write_all(instruction_data);
365    s.write_all(program_id.as_ref());
366
367    let (mem, regions) = s.finish();
368    Ok((mem, regions, accounts_metadata))
369}
370
371fn deserialize_parameters_unaligned<I: IntoIterator<Item = usize>>(
372    transaction_context: &TransactionContext,
373    instruction_context: &InstructionContext,
374    copy_account_data: bool,
375    buffer: &[u8],
376    account_lengths: I,
377) -> Result<(), InstructionError> {
378    let mut start = size_of::<u64>(); // number of accounts
379    for (instruction_account_index, pre_len) in (0..instruction_context
380        .get_number_of_instruction_accounts())
381        .zip(account_lengths.into_iter())
382    {
383        let duplicate =
384            instruction_context.is_instruction_account_duplicate(instruction_account_index)?;
385        start += 1; // is_dup
386        if duplicate.is_none() {
387            let mut borrowed_account = instruction_context
388                .try_borrow_instruction_account(transaction_context, instruction_account_index)?;
389            start += size_of::<u8>(); // is_signer
390            start += size_of::<u8>(); // is_writable
391            start += size_of::<Pubkey>(); // key
392            let lamports = buffer
393                .get(start..start.saturating_add(8))
394                .map(<[u8; 8]>::try_from)
395                .and_then(Result::ok)
396                .map(u64::from_le_bytes)
397                .ok_or(InstructionError::InvalidArgument)?;
398            if borrowed_account.get_lamports() != lamports {
399                borrowed_account.set_lamports(lamports)?;
400            }
401            start += size_of::<u64>() // lamports
402                + size_of::<u64>(); // data length
403            if copy_account_data {
404                let data = buffer
405                    .get(start..start + pre_len)
406                    .ok_or(InstructionError::InvalidArgument)?;
407                // The redundant check helps to avoid the expensive data comparison if we can
408                match borrowed_account
409                    .can_data_be_resized(data.len())
410                    .and_then(|_| borrowed_account.can_data_be_changed())
411                {
412                    Ok(()) => borrowed_account.set_data_from_slice(data)?,
413                    Err(err) if borrowed_account.get_data() != data => return Err(err),
414                    _ => {}
415                }
416                start += pre_len; // data
417            }
418            start += size_of::<Pubkey>() // owner
419                + size_of::<u8>() // executable
420                + size_of::<u64>(); // rent_epoch
421        }
422    }
423    Ok(())
424}
425
426fn serialize_parameters_aligned(
427    accounts: Vec<SerializeAccount>,
428    instruction_data: &[u8],
429    program_id: &Pubkey,
430    copy_account_data: bool,
431    mask_out_rent_epoch_in_vm_serialization: bool,
432) -> Result<
433    (
434        AlignedMemory<HOST_ALIGN>,
435        Vec<MemoryRegion>,
436        Vec<SerializedAccountMetadata>,
437    ),
438    InstructionError,
439> {
440    let mut accounts_metadata = Vec::with_capacity(accounts.len());
441    // Calculate size in order to alloc once
442    let mut size = size_of::<u64>();
443    for account in &accounts {
444        size += 1; // dup
445        match account {
446            SerializeAccount::Duplicate(_) => size += 7, // padding to 64-bit aligned
447            SerializeAccount::Account(_, account) => {
448                let data_len = account.get_data().len();
449                size += size_of::<u8>() // is_signer
450                + size_of::<u8>() // is_writable
451                + size_of::<u8>() // executable
452                + size_of::<u32>() // original_data_len
453                + size_of::<Pubkey>()  // key
454                + size_of::<Pubkey>() // owner
455                + size_of::<u64>()  // lamports
456                + size_of::<u64>()  // data len
457                + MAX_PERMITTED_DATA_INCREASE
458                + size_of::<u64>(); // rent epoch
459                if copy_account_data {
460                    size += data_len + (data_len as *const u8).align_offset(BPF_ALIGN_OF_U128);
461                } else {
462                    size += BPF_ALIGN_OF_U128;
463                }
464            }
465        }
466    }
467    size += size_of::<u64>() // data len
468    + instruction_data.len()
469    + size_of::<Pubkey>(); // program id;
470
471    let mut s = Serializer::new(size, MM_INPUT_START, true, copy_account_data);
472
473    // Serialize into the buffer
474    s.write::<u64>((accounts.len() as u64).to_le());
475    for account in accounts {
476        match account {
477            SerializeAccount::Account(_, mut borrowed_account) => {
478                s.write::<u8>(NON_DUP_MARKER);
479                s.write::<u8>(borrowed_account.is_signer() as u8);
480                s.write::<u8>(borrowed_account.is_writable() as u8);
481                #[allow(deprecated)]
482                s.write::<u8>(borrowed_account.is_executable() as u8);
483                s.write_all(&[0u8, 0, 0, 0]);
484                let vm_key_addr = s.write_all(borrowed_account.get_key().as_ref());
485                let vm_owner_addr = s.write_all(borrowed_account.get_owner().as_ref());
486                let vm_lamports_addr = s.write::<u64>(borrowed_account.get_lamports().to_le());
487                s.write::<u64>((borrowed_account.get_data().len() as u64).to_le());
488                let vm_data_addr = s.write_account(&mut borrowed_account)?;
489                let rent_epoch = if mask_out_rent_epoch_in_vm_serialization {
490                    u64::MAX
491                } else {
492                    borrowed_account.get_rent_epoch()
493                };
494                s.write::<u64>(rent_epoch.to_le());
495                accounts_metadata.push(SerializedAccountMetadata {
496                    original_data_len: borrowed_account.get_data().len(),
497                    vm_key_addr,
498                    vm_owner_addr,
499                    vm_lamports_addr,
500                    vm_data_addr,
501                });
502            }
503            SerializeAccount::Duplicate(position) => {
504                accounts_metadata.push(accounts_metadata.get(position as usize).unwrap().clone());
505                s.write::<u8>(position as u8);
506                s.write_all(&[0u8, 0, 0, 0, 0, 0, 0]);
507            }
508        };
509    }
510    s.write::<u64>((instruction_data.len() as u64).to_le());
511    s.write_all(instruction_data);
512    s.write_all(program_id.as_ref());
513
514    let (mem, regions) = s.finish();
515    Ok((mem, regions, accounts_metadata))
516}
517
518fn deserialize_parameters_aligned<I: IntoIterator<Item = usize>>(
519    transaction_context: &TransactionContext,
520    instruction_context: &InstructionContext,
521    copy_account_data: bool,
522    buffer: &[u8],
523    account_lengths: I,
524) -> Result<(), InstructionError> {
525    let mut start = size_of::<u64>(); // number of accounts
526    for (instruction_account_index, pre_len) in (0..instruction_context
527        .get_number_of_instruction_accounts())
528        .zip(account_lengths.into_iter())
529    {
530        let duplicate =
531            instruction_context.is_instruction_account_duplicate(instruction_account_index)?;
532        start += size_of::<u8>(); // position
533        if duplicate.is_some() {
534            start += 7; // padding to 64-bit aligned
535        } else {
536            let mut borrowed_account = instruction_context
537                .try_borrow_instruction_account(transaction_context, instruction_account_index)?;
538            start += size_of::<u8>() // is_signer
539                + size_of::<u8>() // is_writable
540                + size_of::<u8>() // executable
541                + size_of::<u32>() // original_data_len
542                + size_of::<Pubkey>(); // key
543            let owner = buffer
544                .get(start..start + size_of::<Pubkey>())
545                .ok_or(InstructionError::InvalidArgument)?;
546            start += size_of::<Pubkey>(); // owner
547            let lamports = buffer
548                .get(start..start.saturating_add(8))
549                .map(<[u8; 8]>::try_from)
550                .and_then(Result::ok)
551                .map(u64::from_le_bytes)
552                .ok_or(InstructionError::InvalidArgument)?;
553            if borrowed_account.get_lamports() != lamports {
554                borrowed_account.set_lamports(lamports)?;
555            }
556            start += size_of::<u64>(); // lamports
557            let post_len = buffer
558                .get(start..start.saturating_add(8))
559                .map(<[u8; 8]>::try_from)
560                .and_then(Result::ok)
561                .map(u64::from_le_bytes)
562                .ok_or(InstructionError::InvalidArgument)? as usize;
563            start += size_of::<u64>(); // data length
564            if post_len.saturating_sub(pre_len) > MAX_PERMITTED_DATA_INCREASE
565                || post_len > MAX_PERMITTED_DATA_LENGTH as usize
566            {
567                return Err(InstructionError::InvalidRealloc);
568            }
569            // The redundant check helps to avoid the expensive data comparison if we can
570            let alignment_offset = (pre_len as *const u8).align_offset(BPF_ALIGN_OF_U128);
571            if copy_account_data {
572                let data = buffer
573                    .get(start..start + post_len)
574                    .ok_or(InstructionError::InvalidArgument)?;
575                match borrowed_account
576                    .can_data_be_resized(post_len)
577                    .and_then(|_| borrowed_account.can_data_be_changed())
578                {
579                    Ok(()) => borrowed_account.set_data_from_slice(data)?,
580                    Err(err) if borrowed_account.get_data() != data => return Err(err),
581                    _ => {}
582                }
583                start += pre_len; // data
584            } else {
585                // See Serializer::write_account() as to why we have this
586                // padding before the realloc region here.
587                start += BPF_ALIGN_OF_U128.saturating_sub(alignment_offset);
588                let data = buffer
589                    .get(start..start + MAX_PERMITTED_DATA_INCREASE)
590                    .ok_or(InstructionError::InvalidArgument)?;
591                match borrowed_account
592                    .can_data_be_resized(post_len)
593                    .and_then(|_| borrowed_account.can_data_be_changed())
594                {
595                    Ok(()) => {
596                        borrowed_account.set_data_length(post_len)?;
597                        let allocated_bytes = post_len.saturating_sub(pre_len);
598                        if allocated_bytes > 0 {
599                            borrowed_account
600                                .get_data_mut()?
601                                .get_mut(pre_len..pre_len.saturating_add(allocated_bytes))
602                                .ok_or(InstructionError::InvalidArgument)?
603                                .copy_from_slice(
604                                    data.get(0..allocated_bytes)
605                                        .ok_or(InstructionError::InvalidArgument)?,
606                                );
607                        }
608                    }
609                    Err(err) if borrowed_account.get_data().len() != post_len => return Err(err),
610                    _ => {}
611                }
612            }
613            start += MAX_PERMITTED_DATA_INCREASE;
614            start += alignment_offset;
615            start += size_of::<u64>(); // rent_epoch
616            if borrowed_account.get_owner().to_bytes() != owner {
617                // Change the owner at the end so that we are allowed to change the lamports and data before
618                borrowed_account.set_owner(owner)?;
619            }
620        }
621    }
622    Ok(())
623}
624
625pub(crate) fn account_data_region_memory_state(account: &BorrowedAccount<'_>) -> MemoryState {
626    if account.can_data_be_changed().is_ok() {
627        if account.is_shared() {
628            MemoryState::Cow(account.get_index_in_transaction() as u64)
629        } else {
630            MemoryState::Writable
631        }
632    } else {
633        MemoryState::Readable
634    }
635}
636
637#[cfg(test)]
638#[allow(clippy::indexing_slicing)]
639mod tests {
640    use {
641        super::*,
642        clone_solana_account::{Account, AccountSharedData, WritableAccount},
643        clone_solana_account_info::AccountInfo,
644        clone_solana_program_entrypoint::deserialize,
645        clone_solana_program_runtime::with_mock_invoke_context,
646        clone_solana_sdk_ids::bpf_loader,
647        clone_solana_transaction_context::InstructionAccount,
648        std::{
649            cell::RefCell,
650            mem::transmute,
651            rc::Rc,
652            slice::{self, from_raw_parts, from_raw_parts_mut},
653        },
654    };
655
656    fn deduplicated_instruction_accounts(
657        transaction_indexes: &[IndexOfAccount],
658        is_writable: fn(usize) -> bool,
659    ) -> Vec<InstructionAccount> {
660        transaction_indexes
661            .iter()
662            .enumerate()
663            .map(|(index_in_instruction, index_in_transaction)| {
664                let index_in_callee = transaction_indexes
665                    .get(0..index_in_instruction)
666                    .unwrap()
667                    .iter()
668                    .position(|account_index| account_index == index_in_transaction)
669                    .unwrap_or(index_in_instruction);
670                InstructionAccount {
671                    index_in_transaction: *index_in_transaction,
672                    index_in_caller: *index_in_transaction,
673                    index_in_callee: index_in_callee as IndexOfAccount,
674                    is_signer: false,
675                    is_writable: is_writable(index_in_instruction),
676                }
677            })
678            .collect()
679    }
680
681    #[test]
682    fn test_serialize_parameters_with_many_accounts() {
683        struct TestCase {
684            num_ix_accounts: usize,
685            append_dup_account: bool,
686            expected_err: Option<InstructionError>,
687            name: &'static str,
688        }
689
690        for copy_account_data in [true] {
691            for TestCase {
692                num_ix_accounts,
693                append_dup_account,
694                expected_err,
695                name,
696            } in [
697                TestCase {
698                    name: "serialize max accounts with cap",
699                    num_ix_accounts: usize::from(MAX_INSTRUCTION_ACCOUNTS),
700                    append_dup_account: false,
701                    expected_err: None,
702                },
703                TestCase {
704                    name: "serialize too many accounts with cap",
705                    num_ix_accounts: usize::from(MAX_INSTRUCTION_ACCOUNTS) + 1,
706                    append_dup_account: false,
707                    expected_err: Some(InstructionError::MaxAccountsExceeded),
708                },
709                TestCase {
710                    name: "serialize too many accounts and append dup with cap",
711                    num_ix_accounts: usize::from(MAX_INSTRUCTION_ACCOUNTS),
712                    append_dup_account: true,
713                    expected_err: Some(InstructionError::MaxAccountsExceeded),
714                },
715            ] {
716                let program_id = clone_solana_pubkey::new_rand();
717                let mut transaction_accounts = vec![(
718                    program_id,
719                    AccountSharedData::from(Account {
720                        lamports: 0,
721                        data: vec![],
722                        owner: bpf_loader::id(),
723                        executable: true,
724                        rent_epoch: 0,
725                    }),
726                )];
727                for _ in 0..num_ix_accounts {
728                    transaction_accounts.push((
729                        Pubkey::new_unique(),
730                        AccountSharedData::from(Account {
731                            lamports: 0,
732                            data: vec![],
733                            owner: program_id,
734                            executable: false,
735                            rent_epoch: 0,
736                        }),
737                    ));
738                }
739
740                let transaction_accounts_indexes: Vec<IndexOfAccount> =
741                    (1..(num_ix_accounts + 1) as u16).collect();
742                let mut instruction_accounts =
743                    deduplicated_instruction_accounts(&transaction_accounts_indexes, |_| false);
744                if append_dup_account {
745                    instruction_accounts.push(instruction_accounts.last().cloned().unwrap());
746                }
747                let program_indices = [0];
748                let instruction_data = vec![];
749
750                with_mock_invoke_context!(
751                    invoke_context,
752                    transaction_context,
753                    transaction_accounts
754                );
755                invoke_context
756                    .transaction_context
757                    .get_next_instruction_context()
758                    .unwrap()
759                    .configure(&program_indices, &instruction_accounts, &instruction_data);
760                invoke_context.push().unwrap();
761                let instruction_context = invoke_context
762                    .transaction_context
763                    .get_current_instruction_context()
764                    .unwrap();
765
766                let serialization_result = serialize_parameters(
767                    invoke_context.transaction_context,
768                    instruction_context,
769                    copy_account_data,
770                    true, // mask_out_rent_epoch_in_vm_serialization
771                );
772                assert_eq!(
773                    serialization_result.as_ref().err(),
774                    expected_err.as_ref(),
775                    "{name} test case failed",
776                );
777                if expected_err.is_some() {
778                    continue;
779                }
780
781                let (mut serialized, regions, _account_lengths) = serialization_result.unwrap();
782                let mut serialized_regions = concat_regions(&regions);
783                let (de_program_id, de_accounts, de_instruction_data) = unsafe {
784                    deserialize(
785                        if copy_account_data {
786                            serialized.as_slice_mut()
787                        } else {
788                            serialized_regions.as_slice_mut()
789                        }
790                        .first_mut()
791                        .unwrap() as *mut u8,
792                    )
793                };
794                assert_eq!(de_program_id, &program_id);
795                assert_eq!(de_instruction_data, &instruction_data);
796                for account_info in de_accounts {
797                    let index_in_transaction = invoke_context
798                        .transaction_context
799                        .find_index_of_account(account_info.key)
800                        .unwrap();
801                    let account = invoke_context
802                        .transaction_context
803                        .accounts()
804                        .try_borrow(index_in_transaction)
805                        .unwrap();
806                    assert_eq!(account.lamports(), account_info.lamports());
807                    assert_eq!(account.data(), &account_info.data.borrow()[..]);
808                    assert_eq!(account.owner(), account_info.owner);
809                    assert_eq!(account.executable(), account_info.executable);
810                    assert_eq!(u64::MAX, account_info.rent_epoch);
811                }
812            }
813        }
814    }
815
816    #[test]
817    fn test_serialize_parameters() {
818        for copy_account_data in [false, true] {
819            let program_id = clone_solana_pubkey::new_rand();
820            let transaction_accounts = vec![
821                (
822                    program_id,
823                    AccountSharedData::from(Account {
824                        lamports: 0,
825                        data: vec![],
826                        owner: bpf_loader::id(),
827                        executable: true,
828                        rent_epoch: 0,
829                    }),
830                ),
831                (
832                    clone_solana_pubkey::new_rand(),
833                    AccountSharedData::from(Account {
834                        lamports: 1,
835                        data: vec![1u8, 2, 3, 4, 5],
836                        owner: bpf_loader::id(),
837                        executable: false,
838                        rent_epoch: 100,
839                    }),
840                ),
841                (
842                    clone_solana_pubkey::new_rand(),
843                    AccountSharedData::from(Account {
844                        lamports: 2,
845                        data: vec![11u8, 12, 13, 14, 15, 16, 17, 18, 19],
846                        owner: bpf_loader::id(),
847                        executable: true,
848                        rent_epoch: 200,
849                    }),
850                ),
851                (
852                    clone_solana_pubkey::new_rand(),
853                    AccountSharedData::from(Account {
854                        lamports: 3,
855                        data: vec![],
856                        owner: bpf_loader::id(),
857                        executable: false,
858                        rent_epoch: 3100,
859                    }),
860                ),
861                (
862                    clone_solana_pubkey::new_rand(),
863                    AccountSharedData::from(Account {
864                        lamports: 4,
865                        data: vec![1u8, 2, 3, 4, 5],
866                        owner: bpf_loader::id(),
867                        executable: false,
868                        rent_epoch: 100,
869                    }),
870                ),
871                (
872                    clone_solana_pubkey::new_rand(),
873                    AccountSharedData::from(Account {
874                        lamports: 5,
875                        data: vec![11u8, 12, 13, 14, 15, 16, 17, 18, 19],
876                        owner: bpf_loader::id(),
877                        executable: true,
878                        rent_epoch: 200,
879                    }),
880                ),
881                (
882                    clone_solana_pubkey::new_rand(),
883                    AccountSharedData::from(Account {
884                        lamports: 6,
885                        data: vec![],
886                        owner: bpf_loader::id(),
887                        executable: false,
888                        rent_epoch: 3100,
889                    }),
890                ),
891            ];
892            let instruction_accounts =
893                deduplicated_instruction_accounts(&[1, 1, 2, 3, 4, 4, 5, 6], |index| index >= 4);
894            let instruction_data = vec![1u8, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11];
895            let program_indices = [0];
896            let mut original_accounts = transaction_accounts.clone();
897            with_mock_invoke_context!(invoke_context, transaction_context, transaction_accounts);
898            invoke_context
899                .transaction_context
900                .get_next_instruction_context()
901                .unwrap()
902                .configure(&program_indices, &instruction_accounts, &instruction_data);
903            invoke_context.push().unwrap();
904            let instruction_context = invoke_context
905                .transaction_context
906                .get_current_instruction_context()
907                .unwrap();
908
909            // check serialize_parameters_aligned
910            let (mut serialized, regions, accounts_metadata) = serialize_parameters(
911                invoke_context.transaction_context,
912                instruction_context,
913                copy_account_data,
914                true, // mask_out_rent_epoch_in_vm_serialization
915            )
916            .unwrap();
917
918            let mut serialized_regions = concat_regions(&regions);
919            if copy_account_data {
920                assert_eq!(serialized.as_slice(), serialized_regions.as_slice());
921            }
922            let (de_program_id, de_accounts, de_instruction_data) = unsafe {
923                deserialize(
924                    if copy_account_data {
925                        serialized.as_slice_mut()
926                    } else {
927                        serialized_regions.as_slice_mut()
928                    }
929                    .first_mut()
930                    .unwrap() as *mut u8,
931                )
932            };
933
934            assert_eq!(&program_id, de_program_id);
935            assert_eq!(instruction_data, de_instruction_data);
936            assert_eq!(
937                (de_instruction_data.first().unwrap() as *const u8).align_offset(BPF_ALIGN_OF_U128),
938                0
939            );
940            for account_info in de_accounts {
941                let index_in_transaction = invoke_context
942                    .transaction_context
943                    .find_index_of_account(account_info.key)
944                    .unwrap();
945                let account = invoke_context
946                    .transaction_context
947                    .accounts()
948                    .try_borrow(index_in_transaction)
949                    .unwrap();
950                assert_eq!(account.lamports(), account_info.lamports());
951                assert_eq!(account.data(), &account_info.data.borrow()[..]);
952                assert_eq!(account.owner(), account_info.owner);
953                assert_eq!(account.executable(), account_info.executable);
954                assert_eq!(u64::MAX, account_info.rent_epoch);
955
956                assert_eq!(
957                    (*account_info.lamports.borrow() as *const u64).align_offset(BPF_ALIGN_OF_U128),
958                    0
959                );
960                assert_eq!(
961                    account_info
962                        .data
963                        .borrow()
964                        .as_ptr()
965                        .align_offset(BPF_ALIGN_OF_U128),
966                    0
967                );
968            }
969
970            deserialize_parameters(
971                invoke_context.transaction_context,
972                instruction_context,
973                copy_account_data,
974                serialized.as_slice(),
975                &accounts_metadata,
976            )
977            .unwrap();
978            for (index_in_transaction, (_key, original_account)) in
979                original_accounts.iter().enumerate()
980            {
981                let account = invoke_context
982                    .transaction_context
983                    .accounts()
984                    .try_borrow(index_in_transaction as IndexOfAccount)
985                    .unwrap();
986                assert_eq!(&*account, original_account);
987            }
988
989            // check serialize_parameters_unaligned
990            original_accounts
991                .first_mut()
992                .unwrap()
993                .1
994                .set_owner(bpf_loader_deprecated::id());
995            invoke_context
996                .transaction_context
997                .get_account_at_index(0)
998                .unwrap()
999                .try_borrow_mut()
1000                .unwrap()
1001                .set_owner(bpf_loader_deprecated::id());
1002
1003            let (mut serialized, regions, account_lengths) = serialize_parameters(
1004                invoke_context.transaction_context,
1005                instruction_context,
1006                copy_account_data,
1007                true, // mask_out_rent_epoch_in_vm_serialization
1008            )
1009            .unwrap();
1010            let mut serialized_regions = concat_regions(&regions);
1011
1012            let (de_program_id, de_accounts, de_instruction_data) = unsafe {
1013                deserialize_unaligned(
1014                    if copy_account_data {
1015                        serialized.as_slice_mut()
1016                    } else {
1017                        serialized_regions.as_slice_mut()
1018                    }
1019                    .first_mut()
1020                    .unwrap() as *mut u8,
1021                )
1022            };
1023            assert_eq!(&program_id, de_program_id);
1024            assert_eq!(instruction_data, de_instruction_data);
1025            for account_info in de_accounts {
1026                let index_in_transaction = invoke_context
1027                    .transaction_context
1028                    .find_index_of_account(account_info.key)
1029                    .unwrap();
1030                let account = invoke_context
1031                    .transaction_context
1032                    .accounts()
1033                    .try_borrow(index_in_transaction)
1034                    .unwrap();
1035                assert_eq!(account.lamports(), account_info.lamports());
1036                assert_eq!(account.data(), &account_info.data.borrow()[..]);
1037                assert_eq!(account.owner(), account_info.owner);
1038                assert_eq!(account.executable(), account_info.executable);
1039                assert_eq!(u64::MAX, account_info.rent_epoch);
1040            }
1041
1042            deserialize_parameters(
1043                invoke_context.transaction_context,
1044                instruction_context,
1045                copy_account_data,
1046                serialized.as_slice(),
1047                &account_lengths,
1048            )
1049            .unwrap();
1050            for (index_in_transaction, (_key, original_account)) in
1051                original_accounts.iter().enumerate()
1052            {
1053                let account = invoke_context
1054                    .transaction_context
1055                    .accounts()
1056                    .try_borrow(index_in_transaction as IndexOfAccount)
1057                    .unwrap();
1058                assert_eq!(&*account, original_account);
1059            }
1060        }
1061    }
1062
1063    #[test]
1064    fn test_serialize_parameters_mask_out_rent_epoch_in_vm_serialization() {
1065        for mask_out_rent_epoch_in_vm_serialization in [false, true] {
1066            let transaction_accounts = vec![
1067                (
1068                    clone_solana_pubkey::new_rand(),
1069                    AccountSharedData::from(Account {
1070                        lamports: 0,
1071                        data: vec![],
1072                        owner: bpf_loader::id(),
1073                        executable: true,
1074                        rent_epoch: 0,
1075                    }),
1076                ),
1077                (
1078                    clone_solana_pubkey::new_rand(),
1079                    AccountSharedData::from(Account {
1080                        lamports: 1,
1081                        data: vec![1u8, 2, 3, 4, 5],
1082                        owner: bpf_loader::id(),
1083                        executable: false,
1084                        rent_epoch: 100,
1085                    }),
1086                ),
1087                (
1088                    clone_solana_pubkey::new_rand(),
1089                    AccountSharedData::from(Account {
1090                        lamports: 2,
1091                        data: vec![11u8, 12, 13, 14, 15, 16, 17, 18, 19],
1092                        owner: bpf_loader::id(),
1093                        executable: true,
1094                        rent_epoch: 200,
1095                    }),
1096                ),
1097                (
1098                    clone_solana_pubkey::new_rand(),
1099                    AccountSharedData::from(Account {
1100                        lamports: 3,
1101                        data: vec![],
1102                        owner: bpf_loader::id(),
1103                        executable: false,
1104                        rent_epoch: 300,
1105                    }),
1106                ),
1107                (
1108                    clone_solana_pubkey::new_rand(),
1109                    AccountSharedData::from(Account {
1110                        lamports: 4,
1111                        data: vec![1u8, 2, 3, 4, 5],
1112                        owner: bpf_loader::id(),
1113                        executable: false,
1114                        rent_epoch: 100,
1115                    }),
1116                ),
1117                (
1118                    clone_solana_pubkey::new_rand(),
1119                    AccountSharedData::from(Account {
1120                        lamports: 5,
1121                        data: vec![11u8, 12, 13, 14, 15, 16, 17, 18, 19],
1122                        owner: bpf_loader::id(),
1123                        executable: true,
1124                        rent_epoch: 200,
1125                    }),
1126                ),
1127                (
1128                    clone_solana_pubkey::new_rand(),
1129                    AccountSharedData::from(Account {
1130                        lamports: 6,
1131                        data: vec![],
1132                        owner: bpf_loader::id(),
1133                        executable: false,
1134                        rent_epoch: 3100,
1135                    }),
1136                ),
1137            ];
1138            let instruction_accounts =
1139                deduplicated_instruction_accounts(&[1, 1, 2, 3, 4, 4, 5, 6], |index| index >= 4);
1140            let instruction_data = vec![];
1141            let program_indices = [0];
1142            let mut original_accounts = transaction_accounts.clone();
1143            with_mock_invoke_context!(invoke_context, transaction_context, transaction_accounts);
1144            invoke_context
1145                .transaction_context
1146                .get_next_instruction_context()
1147                .unwrap()
1148                .configure(&program_indices, &instruction_accounts, &instruction_data);
1149            invoke_context.push().unwrap();
1150            let instruction_context = invoke_context
1151                .transaction_context
1152                .get_current_instruction_context()
1153                .unwrap();
1154
1155            // check serialize_parameters_aligned
1156            let (_serialized, regions, _accounts_metadata) = serialize_parameters(
1157                invoke_context.transaction_context,
1158                instruction_context,
1159                true,
1160                mask_out_rent_epoch_in_vm_serialization,
1161            )
1162            .unwrap();
1163
1164            let mut serialized_regions = concat_regions(&regions);
1165            let (_de_program_id, de_accounts, _de_instruction_data) = unsafe {
1166                deserialize(serialized_regions.as_slice_mut().first_mut().unwrap() as *mut u8)
1167            };
1168
1169            for account_info in de_accounts {
1170                let index_in_transaction = invoke_context
1171                    .transaction_context
1172                    .find_index_of_account(account_info.key)
1173                    .unwrap();
1174                let account = invoke_context
1175                    .transaction_context
1176                    .accounts()
1177                    .try_borrow(index_in_transaction)
1178                    .unwrap();
1179                let expected_rent_epoch = if mask_out_rent_epoch_in_vm_serialization {
1180                    u64::MAX
1181                } else {
1182                    account.rent_epoch()
1183                };
1184                assert_eq!(expected_rent_epoch, account_info.rent_epoch);
1185            }
1186
1187            // check serialize_parameters_unaligned
1188            original_accounts
1189                .first_mut()
1190                .unwrap()
1191                .1
1192                .set_owner(bpf_loader_deprecated::id());
1193            invoke_context
1194                .transaction_context
1195                .get_account_at_index(0)
1196                .unwrap()
1197                .try_borrow_mut()
1198                .unwrap()
1199                .set_owner(bpf_loader_deprecated::id());
1200
1201            let (_serialized, regions, _account_lengths) = serialize_parameters(
1202                invoke_context.transaction_context,
1203                instruction_context,
1204                true,
1205                mask_out_rent_epoch_in_vm_serialization,
1206            )
1207            .unwrap();
1208            let mut serialized_regions = concat_regions(&regions);
1209
1210            let (_de_program_id, de_accounts, _de_instruction_data) = unsafe {
1211                deserialize_unaligned(
1212                    serialized_regions.as_slice_mut().first_mut().unwrap() as *mut u8
1213                )
1214            };
1215            for account_info in de_accounts {
1216                let index_in_transaction = invoke_context
1217                    .transaction_context
1218                    .find_index_of_account(account_info.key)
1219                    .unwrap();
1220                let account = invoke_context
1221                    .transaction_context
1222                    .accounts()
1223                    .try_borrow(index_in_transaction)
1224                    .unwrap();
1225                let expected_rent_epoch = if mask_out_rent_epoch_in_vm_serialization {
1226                    u64::MAX
1227                } else {
1228                    account.rent_epoch()
1229                };
1230                assert_eq!(expected_rent_epoch, account_info.rent_epoch);
1231            }
1232        }
1233    }
1234
1235    // the old bpf_loader in-program deserializer bpf_loader::id()
1236    #[deny(unsafe_op_in_unsafe_fn)]
1237    unsafe fn deserialize_unaligned<'a>(
1238        input: *mut u8,
1239    ) -> (&'a Pubkey, Vec<AccountInfo<'a>>, &'a [u8]) {
1240        // this boring boilerplate struct is needed until inline const...
1241        struct Ptr<T>(std::marker::PhantomData<T>);
1242        impl<T> Ptr<T> {
1243            const COULD_BE_UNALIGNED: bool = std::mem::align_of::<T>() > 1;
1244
1245            #[inline(always)]
1246            fn read_possibly_unaligned(input: *mut u8, offset: usize) -> T {
1247                unsafe {
1248                    let src = input.add(offset) as *const T;
1249                    if Self::COULD_BE_UNALIGNED {
1250                        src.read_unaligned()
1251                    } else {
1252                        src.read()
1253                    }
1254                }
1255            }
1256
1257            // rustc inserts debug_assert! for misaligned pointer dereferences when
1258            // deserializing, starting from [1]. so, use std::mem::transmute as the last resort
1259            // while preventing clippy from complaining to suggest not to use it.
1260            // [1]: https://github.com/rust-lang/rust/commit/22a7a19f9333bc1fcba97ce444a3515cb5fb33e6
1261            // as for the ub nature of the misaligned pointer dereference, this is
1262            // acceptable in this code, given that this is cfg(test) and it's cared only with
1263            // x86-64 and the target only incurs some performance penalty, not like segfaults
1264            // in other targets.
1265            #[inline(always)]
1266            fn ref_possibly_unaligned<'a>(input: *mut u8, offset: usize) -> &'a T {
1267                #[allow(clippy::transmute_ptr_to_ref)]
1268                unsafe {
1269                    transmute(input.add(offset) as *const T)
1270                }
1271            }
1272
1273            // See ref_possibly_unaligned's comment
1274            #[inline(always)]
1275            fn mut_possibly_unaligned<'a>(input: *mut u8, offset: usize) -> &'a mut T {
1276                #[allow(clippy::transmute_ptr_to_ref)]
1277                unsafe {
1278                    transmute(input.add(offset) as *mut T)
1279                }
1280            }
1281        }
1282
1283        let mut offset: usize = 0;
1284
1285        // number of accounts present
1286
1287        let num_accounts = Ptr::<u64>::read_possibly_unaligned(input, offset) as usize;
1288        offset += size_of::<u64>();
1289
1290        // account Infos
1291
1292        let mut accounts = Vec::with_capacity(num_accounts);
1293        for _ in 0..num_accounts {
1294            let dup_info = Ptr::<u8>::read_possibly_unaligned(input, offset);
1295            offset += size_of::<u8>();
1296            if dup_info == NON_DUP_MARKER {
1297                let is_signer = Ptr::<u8>::read_possibly_unaligned(input, offset) != 0;
1298                offset += size_of::<u8>();
1299
1300                let is_writable = Ptr::<u8>::read_possibly_unaligned(input, offset) != 0;
1301                offset += size_of::<u8>();
1302
1303                let key = Ptr::<Pubkey>::ref_possibly_unaligned(input, offset);
1304                offset += size_of::<Pubkey>();
1305
1306                let lamports = Rc::new(RefCell::new(Ptr::mut_possibly_unaligned(input, offset)));
1307                offset += size_of::<u64>();
1308
1309                let data_len = Ptr::<u64>::read_possibly_unaligned(input, offset) as usize;
1310                offset += size_of::<u64>();
1311
1312                let data = Rc::new(RefCell::new(unsafe {
1313                    from_raw_parts_mut(input.add(offset), data_len)
1314                }));
1315                offset += data_len;
1316
1317                let owner: &Pubkey = Ptr::<Pubkey>::ref_possibly_unaligned(input, offset);
1318                offset += size_of::<Pubkey>();
1319
1320                let executable = Ptr::<u8>::read_possibly_unaligned(input, offset) != 0;
1321                offset += size_of::<u8>();
1322
1323                let rent_epoch = Ptr::<u64>::read_possibly_unaligned(input, offset);
1324                offset += size_of::<u64>();
1325
1326                accounts.push(AccountInfo {
1327                    key,
1328                    is_signer,
1329                    is_writable,
1330                    lamports,
1331                    data,
1332                    owner,
1333                    executable,
1334                    rent_epoch,
1335                });
1336            } else {
1337                // duplicate account, clone the original
1338                accounts.push(accounts.get(dup_info as usize).unwrap().clone());
1339            }
1340        }
1341
1342        // instruction data
1343
1344        let instruction_data_len = Ptr::<u64>::read_possibly_unaligned(input, offset) as usize;
1345        offset += size_of::<u64>();
1346
1347        let instruction_data = unsafe { from_raw_parts(input.add(offset), instruction_data_len) };
1348        offset += instruction_data_len;
1349
1350        // program Id
1351
1352        let program_id = Ptr::<Pubkey>::ref_possibly_unaligned(input, offset);
1353
1354        (program_id, accounts, instruction_data)
1355    }
1356
1357    fn concat_regions(regions: &[MemoryRegion]) -> AlignedMemory<HOST_ALIGN> {
1358        let len = regions.iter().fold(0, |len, region| len + region.len) as usize;
1359        let mut mem = AlignedMemory::zero_filled(len);
1360        for region in regions {
1361            let host_slice = unsafe {
1362                slice::from_raw_parts(region.host_addr.get() as *const u8, region.len as usize)
1363            };
1364            mem.as_slice_mut()[(region.vm_addr - MM_INPUT_START) as usize..][..region.len as usize]
1365                .copy_from_slice(host_slice)
1366        }
1367        mem
1368    }
1369}