@@ -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.
2523pub 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