Skip to content

Commit d41d2d1

Browse files
committed
use Sysvar rent account
1 parent 34ce5b2 commit d41d2d1

File tree

1 file changed

+160
-38
lines changed
  • sdk/pinocchio/src/entrypoint

1 file changed

+160
-38
lines changed

sdk/pinocchio/src/entrypoint/mod.rs

Lines changed: 160 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,6 @@ use crate::{
1919
BPF_ALIGN_OF_U128, MAX_TX_ACCOUNTS, NON_DUP_MARKER,
2020
};
2121

22-
use core::mem::MaybeUninit;
23-
2422
/// Start address of the memory region used for program heap.
2523
pub const HEAP_START_ADDRESS: u64 = 0x300000000;
2624

@@ -501,6 +499,7 @@ macro_rules! default_allocator {
501499
#[global_allocator]
502500
static A: $crate::entrypoint::BumpAllocator = $crate::entrypoint::BumpAllocator {
503501
start: $crate::entrypoint::HEAP_START_ADDRESS as usize,
502+
len: $crate::entrypoint::HEAP_LENGTH,
504503
};
505504

506505
/// A default allocator for when the program is compiled on a target different than
@@ -519,7 +518,7 @@ macro_rules! default_allocator {
519518
/// Using this macro with the "`std`" feature enabled will result in a compile error.
520519
#[cfg(feature = "std")]
521520
#[macro_export]
522-
macro_rules! panic_on_alloc {
521+
macro_rules! no_allocator {
523522
() => {
524523
compile_error!("Feature 'std' cannot be enabled.");
525524
};
@@ -536,7 +535,7 @@ macro_rules! panic_on_alloc {
536535
/// This is used when the `"std"` feature is disabled.
537536
#[cfg(not(feature = "std"))]
538537
#[macro_export]
539-
macro_rules! panic_on_alloc {
538+
macro_rules! no_allocator {
540539
() => {
541540
#[cfg(target_os = "solana")]
542541
#[global_allocator]
@@ -558,14 +557,9 @@ macro_rules! panic_on_alloc {
558557
// Make this `const` once `const_mut_refs` is stable for the platform-tools
559558
// toolchain Rust version.
560559
#[inline(always)]
561-
pub unsafe fn allocate_unchecked<T: Sized>(offset: usize) -> &'static mut MaybeUninit<T> {
560+
pub unsafe fn allocate_unchecked<T: Sized>(offset: usize) -> &'static mut T {
562561
// SAFETY: The pointer is within a valid range and aligned to `T`.
563-
let start = HEAP_START_ADDRESS as usize + offset;
564-
let end = start + core::mem::size_of::<T>();
565-
const_assert!(core::mem::align_of::<T>() <= MAX_HEAP_ALIGNMENT);
566-
const_assert!(end <= (HEAP_START_ADDRESS as usize ) + HEAP_LENGTH);
567-
568-
unsafe { &mut *(calculate_offset::<T>(offset) as *mut MaybeUninit T) }
562+
unsafe { &mut *(calculate_offset::<T>(offset) as *mut T) }
569563
}
570564

571565
#[inline(always)]
@@ -605,12 +599,11 @@ mod alloc {
605599
//! The bump allocator used as the default rust heap when running programs.
606600
607601
extern crate alloc;
608-
use static_assertions::const_assert;
609-
use crate::entrypoint::{HEAP_START_ADDRESS, HEAP_LENGTH};
610602

611603
/// The bump allocator used as the default rust heap when running programs.
612604
pub struct BumpAllocator {
613605
pub start: usize,
606+
pub len: usize,
614607
}
615608

616609
/// Integer arithmetic in this global allocator implementation is safe when
@@ -626,7 +619,7 @@ mod alloc {
626619
let mut pos = *pos_ptr;
627620
if pos == 0 {
628621
// First time, set starting position.
629-
pos = self.start + HEAP_LENGTH;
622+
pos = self.start + self.len;
630623
}
631624
pos = pos.saturating_sub(layout.size());
632625
pos &= !(layout.align().wrapping_sub(1));
@@ -641,8 +634,6 @@ mod alloc {
641634
// I'm a bump allocator, I don't free.
642635
}
643636
}
644-
645-
646637
}
647638

648639
#[cfg(not(feature = "std"))]
@@ -662,30 +653,161 @@ unsafe impl core::alloc::GlobalAlloc for NoAllocator {
662653
}
663654
}
664655

665-
#[cfg(not(feature = "std"))]
666-
impl NoAllocator {
667-
#[inline(always)]
668-
pub unsafe fn allocate_unchecked<T: Sized>(offset: usize) -> &'static mut MaybeUninit<T> {
669-
// SAFETY: The pointer is within a valid range and aligned to `T`.
670-
let start = HEAP_START_ADDRESS as usize + offset;
671-
let end = start + core::mem::size_of::<T>();
672-
const_assert!(core::mem::align_of::<T>() <= MAX_HEAP_ALIGNMENT);
673-
const_assert!(end <= (HEAP_START_ADDRESS as usize ) + HEAP_LENGTH);
656+
#[cfg(all(test, not(target_os = "solana")))]
657+
mod tests {
658+
extern crate std;
659+
660+
use core::{alloc::Layout, ptr::copy_nonoverlapping};
661+
use std::{
662+
alloc::{alloc, dealloc},
663+
vec,
664+
};
665+
666+
use super::*;
674667

675-
unsafe { &mut *(calculate_offset::<T>(offset) as *mut MaybeUninit T) }
668+
/// The mock program ID used for testing.
669+
const MOCK_PROGRAM_ID: Pubkey = [5u8; 32];
670+
671+
/// An uninitialized account info.
672+
const UNINIT: MaybeUninit<AccountInfo> = MaybeUninit::<AccountInfo>::uninit();
673+
674+
/// Struct representing a memory region with a specific alignment.
675+
struct AlignedMemory {
676+
ptr: *mut u8,
677+
layout: Layout,
678+
}
679+
680+
impl AlignedMemory {
681+
pub fn new(len: usize) -> Self {
682+
let layout = Layout::from_size_align(len, BPF_ALIGN_OF_U128).unwrap();
683+
// SAFETY: `align` is set to `BPF_ALIGN_OF_U128`.
684+
unsafe {
685+
let ptr = alloc(layout);
686+
if ptr.is_null() {
687+
std::alloc::handle_alloc_error(layout);
688+
}
689+
AlignedMemory { ptr, layout }
690+
}
676691
}
677-
}
678692

679-
#[cfg(not(feature = "std"))]
680-
impl NoAllocator {
681-
#[inline(always)]
682-
pub unsafe fn allocate_unchecked<T: Sized>(offset: usize) -> &'static mut MaybeUninit<T> {
683-
// SAFETY: The pointer is within a valid range and aligned to `T`.
684-
let start = HEAP_START_ADDRESS as usize + offset;
685-
let end = start + core::mem::size_of::<T>();
686-
const_assert!(core::mem::align_of::<T>() <= MAX_HEAP_ALIGNMENT);
687-
const_assert!(end <= (HEAP_START_ADDRESS as usize ) + HEAP_LENGTH);
693+
/// Write data to the memory region at the specified offset.
694+
///
695+
/// # Safety
696+
///
697+
/// The caller must ensure that the `data` length does not exceed the
698+
/// remaining space in the memory region starting from the `offset`.
699+
pub unsafe fn write(&mut self, data: &[u8], offset: usize) {
700+
copy_nonoverlapping(data.as_ptr(), self.ptr.add(offset), data.len());
701+
}
688702

689-
unsafe { &mut *(calculate_offset::<T>(offset) as *mut MaybeUninit T) }
703+
/// Return a mutable pointer to the memory region.
704+
pub fn as_mut_ptr(&mut self) -> *mut u8 {
705+
self.ptr
690706
}
691-
}
707+
}
708+
709+
impl Drop for AlignedMemory {
710+
fn drop(&mut self) {
711+
unsafe {
712+
dealloc(self.ptr, self.layout);
713+
}
714+
}
715+
}
716+
717+
/// Creates an input buffer with a specified number of accounts and
718+
/// instruction data.
719+
///
720+
/// This function mimics the input buffer created by the SVM loader.
721+
/// Each account created has zeroed data, apart from the `data_len`
722+
/// field, which is set to the index of the account.
723+
///
724+
/// # Safety
725+
///
726+
/// The returned `AlignedMemory` should only be used within the test
727+
/// context.
728+
unsafe fn create_input(accounts: usize, instruction_data: &[u8]) -> AlignedMemory {
729+
let mut input = AlignedMemory::new(1_000_000_000);
730+
// Number of accounts.
731+
input.write(&(accounts as u64).to_le_bytes(), 0);
732+
let mut offset = size_of::<u64>();
733+
734+
for i in 0..accounts {
735+
// Account data.
736+
let mut account = [0u8; STATIC_ACCOUNT_DATA + size_of::<u64>()];
737+
account[0] = NON_DUP_MARKER;
738+
// Set the accounts data length. The actual account data is zeroed.
739+
account[80..88].copy_from_slice(&i.to_le_bytes());
740+
input.write(&account, offset);
741+
offset += account.len();
742+
// Padding for the account data to align to `BPF_ALIGN_OF_U128`.
743+
let padding_for_data = (i + (BPF_ALIGN_OF_U128 - 1)) & !(BPF_ALIGN_OF_U128 - 1);
744+
input.write(&vec![0u8; padding_for_data], offset);
745+
offset += padding_for_data;
746+
}
747+
748+
// Instruction data length.
749+
input.write(&instruction_data.len().to_le_bytes(), offset);
750+
offset += size_of::<u64>();
751+
// Instruction data.
752+
input.write(instruction_data, offset);
753+
offset += instruction_data.len();
754+
// Program ID (mock).
755+
input.write(&MOCK_PROGRAM_ID, offset);
756+
757+
input
758+
}
759+
760+
/// Asserts that the accounts slice contains the expected number of accounts
761+
/// and that each account's data length matches its index.
762+
fn assert_accounts(accounts: &[MaybeUninit<AccountInfo>]) {
763+
for (i, account) in accounts.iter().enumerate() {
764+
let account_info = unsafe { account.assume_init_ref() };
765+
assert_eq!(account_info.data_len(), i);
766+
}
767+
}
768+
769+
#[test]
770+
fn test_deserialize() {
771+
let ix_data = [3u8; 100];
772+
773+
// Input with 0 accounts.
774+
775+
let mut input = unsafe { create_input(0, &ix_data) };
776+
let mut accounts = [UNINIT; 1];
777+
778+
let (program_id, count, parsed_ix_data) =
779+
unsafe { deserialize(input.as_mut_ptr(), &mut accounts) };
780+
781+
assert_eq!(count, 0);
782+
assert_eq!(program_id, &MOCK_PROGRAM_ID);
783+
assert_eq!(&ix_data, parsed_ix_data);
784+
785+
// Input with 3 accounts but the accounts array has only space
786+
// for 1.
787+
788+
let mut input = unsafe { create_input(3, &ix_data) };
789+
let mut accounts = [UNINIT; 1];
790+
791+
let (program_id, count, parsed_ix_data) =
792+
unsafe { deserialize(input.as_mut_ptr(), &mut accounts) };
793+
794+
assert_eq!(count, 1);
795+
assert_eq!(program_id, &MOCK_PROGRAM_ID);
796+
assert_eq!(&ix_data, parsed_ix_data);
797+
assert_accounts(&accounts[..count]);
798+
799+
// Input with `MAX_TX_ACCOUNTS` accounts but accounts array has
800+
// only space for 64.
801+
802+
let mut input = unsafe { create_input(MAX_TX_ACCOUNTS, &ix_data) };
803+
let mut accounts = [UNINIT; 64];
804+
805+
let (program_id, count, parsed_ix_data) =
806+
unsafe { deserialize(input.as_mut_ptr(), &mut accounts) };
807+
808+
assert_eq!(count, 64);
809+
assert_eq!(program_id, &MOCK_PROGRAM_ID);
810+
assert_eq!(&ix_data, parsed_ix_data);
811+
assert_accounts(&accounts);
812+
}
813+
}

0 commit comments

Comments
 (0)