Skip to main content

pinocchio/entrypoint/
mod.rs

1//! Macros and functions for defining the program entrypoint and setting up
2//! global handlers.
3
4pub mod lazy;
5
6#[cfg(feature = "alloc")]
7pub use alloc::BumpAllocator;
8pub use lazy::{InstructionContext, MaybeAccount};
9use {
10    crate::{
11        account::{AccountView, RuntimeAccount, MAX_PERMITTED_DATA_INCREASE},
12        Address, ProgramResult, BPF_ALIGN_OF_U128, MAX_TX_ACCOUNTS, SUCCESS,
13    },
14    core::{
15        alloc::{GlobalAlloc, Layout},
16        cmp::min,
17        mem::{size_of, MaybeUninit},
18        ptr::with_exposed_provenance_mut,
19        slice::from_raw_parts,
20    },
21};
22
23/// Start address of the memory region used for program heap.
24pub const HEAP_START_ADDRESS: u64 = 0x300000000;
25
26/// Length of the heap memory region used for program heap.
27#[deprecated(since = "0.10.0", note = "Use `MAX_HEAP_LENGTH` instead")]
28pub const HEAP_LENGTH: usize = 32 * 1024;
29
30/// Maximum heap length in bytes that a program can request.
31pub const MAX_HEAP_LENGTH: u32 = 256 * 1024;
32
33/// Value used to indicate that a serialized account is not a duplicate.
34pub const NON_DUP_MARKER: u8 = u8::MAX;
35
36/// The "static" size of an account in the input buffer.
37///
38/// This is the size of the account header plus the maximum permitted data
39/// increase.
40const STATIC_ACCOUNT_DATA: usize = size_of::<RuntimeAccount>() + MAX_PERMITTED_DATA_INCREASE;
41
42/// Declare the program entrypoint and set up global handlers.
43///
44/// The main difference from the standard (SDK) [`entrypoint`] macro is that
45/// this macro represents an entrypoint that does not perform allocations or
46/// copies when reading the input buffer.
47///
48/// [`entrypoint`]: https://docs.rs/solana-program-entrypoint/latest/solana_program_entrypoint/macro.entrypoint.html
49///
50/// This macro emits the common boilerplate necessary to begin program
51/// execution, calling a provided function to process the program instruction
52/// supplied by the runtime, and reporting its result to the runtime.
53///
54/// It also sets up a [global allocator] and [panic handler], using the
55/// [`crate::default_allocator!`] and [`crate::default_panic_handler!`] macros.
56///
57/// The first argument is the name of a function with this type signature:
58///
59/// ```ignore
60/// fn process_instruction(
61///     program_id: &Address,      // Address of the account the program was loaded into
62///     accounts: &[AccountView], // All accounts required to process the instruction
63///     instruction_data: &[u8],  // Serialized instruction-specific data
64/// ) -> ProgramResult;
65/// ```
66/// The argument is defined as an `expr`, which allows the use of any function
67/// pointer not just identifiers in the current scope.
68///
69/// There is a second optional argument that allows to specify the maximum
70/// number of accounts expected by instructions of the program. This is useful
71/// to reduce the stack size requirement for the entrypoint, as the default is
72/// set to [`crate::MAX_TX_ACCOUNTS`]. If the program receives more accounts
73/// than the specified maximum, these accounts will be ignored.
74///
75/// [global allocator]: https://doc.rust-lang.org/stable/alloc/alloc/trait.GlobalAlloc.html
76/// [maximum number of accounts]: https://github.com/anza-xyz/agave/blob/ccabfcf84921977202fd06d3197cbcea83742133/runtime/src/bank.rs#L3207-L3219
77/// [panic handler]: https://doc.rust-lang.org/stable/core/panic/trait.PanicHandler.html
78///
79/// # Examples
80///
81/// Defining an entrypoint conditional on the `bpf-entrypoint` feature. Although
82/// the `entrypoint` module is written inline in this example, it is common to
83/// put it into its own file.
84///
85/// ```no_run
86/// #[cfg(feature = "bpf-entrypoint")]
87/// pub mod entrypoint {
88///
89///     use pinocchio::{
90///         AccountView,
91///         entrypoint,
92///         Address,
93///         ProgramResult
94///     };
95///
96///     entrypoint!(process_instruction);
97///
98///     pub fn process_instruction(
99///         program_id: &Address,
100///         accounts: &[AccountView],
101///         instruction_data: &[u8],
102///     ) -> ProgramResult {
103///         Ok(())
104///     }
105///
106/// }
107/// ```
108///
109/// # Important
110///
111/// The panic handler set up is different depending on whether the `std` library
112/// is available to the linker or not. The `entrypoint` macro will set up a
113/// default panic "hook", that works with the `#[panic_handler]` set by the
114/// `std`. Therefore, this macro should be used when the program or any of its
115/// dependencies are dependent on the `std` library.
116///
117/// When the program and all its dependencies are `no_std`, it is necessary to
118/// set a `#[panic_handler]` to handle panics. This is done by the
119/// [`crate::nostd_panic_handler`] macro. In this case, it is not possible to
120/// use the `entrypoint` macro. Use the [`crate::program_entrypoint!`] macro
121/// instead and set up the allocator and panic handler manually.
122///
123/// The compiler may inline the instruction handler (and its call tree) into the
124/// generated `entrypoint`, which can significantly increase the entrypoint
125/// stack frame. If your program has large instruction dispatch logic or builds
126/// sizable CPI account arrays, consider adding `#[inline(never)]` to the
127/// instruction handler to keep it out of the entrypoint stack frame and avoid
128/// BPF stack overflows.
129///
130/// [`crate::nostd_panic_handler`]: https://docs.rs/pinocchio/latest/pinocchio/macro.nostd_panic_handler.html
131#[cfg(feature = "alloc")]
132#[macro_export]
133macro_rules! entrypoint {
134    ( $process_instruction:expr ) => {
135        $crate::entrypoint!($process_instruction, { $crate::MAX_TX_ACCOUNTS });
136    };
137    ( $process_instruction:expr, $maximum:expr ) => {
138        $crate::program_entrypoint!($process_instruction, $maximum);
139        $crate::default_allocator!();
140        $crate::default_panic_handler!();
141    };
142}
143
144/// Declare the program entrypoint.
145///
146/// This macro is similar to the [`crate::entrypoint!`] macro, but it does not
147/// set up a global allocator nor a panic handler. This is useful when the
148/// program will set up its own allocator and panic handler.
149///
150/// The first argument is the name of a function with this type signature:
151///
152/// ```ignore
153/// fn process_instruction(
154///     program_id: &Address,     // Address of the account the program was loaded into
155///     accounts: &[AccountView], // All accounts required to process the instruction
156///     instruction_data: &[u8],  // Serialized instruction-specific data
157/// ) -> ProgramResult;
158/// ```
159/// The argument is defined as an `expr`, which allows the use of any function
160/// pointer not just identifiers in the current scope.
161///
162/// There is a second optional argument that allows to specify the maximum
163/// number of accounts expected by instructions of the program. This is useful
164/// to reduce the stack size requirement for the entrypoint, as the default is
165/// set to [`MAX_TX_ACCOUNTS`]. If the program receives more accounts than the
166/// specified maximum, these accounts will be ignored.
167#[macro_export]
168macro_rules! program_entrypoint {
169    ( $process_instruction:expr ) => {
170        $crate::program_entrypoint!($process_instruction, { $crate::MAX_TX_ACCOUNTS });
171    };
172    ( $process_instruction:expr, $maximum:expr ) => {
173        /// Program entrypoint.
174        #[no_mangle]
175        pub unsafe extern "C" fn entrypoint(input: *mut u8) -> u64 {
176            $crate::entrypoint::process_entrypoint::<$maximum>(input, $process_instruction)
177        }
178    };
179}
180
181/// Entrypoint deserialization.
182///
183/// This function inlines entrypoint deserialization for use in the
184/// `program_entrypoint!` macro.
185///
186/// # Safety
187///
188/// The caller must ensure that the `input` buffer is valid, i.e., it represents
189/// the program input parameters serialized by the SVM loader. Additionally, the
190/// `input` should last for the lifetime of the program execution since the
191/// returned values reference the `input`.
192#[inline(always)]
193pub unsafe fn process_entrypoint<const MAX_ACCOUNTS: usize>(
194    input: *mut u8,
195    process_instruction: fn(&Address, &[AccountView], &[u8]) -> ProgramResult,
196) -> u64 {
197    const UNINIT: MaybeUninit<AccountView> = MaybeUninit::<AccountView>::uninit();
198    // Create an array of uninitialized account views.
199    let mut accounts = [UNINIT; MAX_ACCOUNTS];
200
201    let (program_id, count, instruction_data) =
202        unsafe { deserialize::<MAX_ACCOUNTS>(input, &mut accounts) };
203
204    // Call the program's entrypoint passing `count` account views; we know that
205    // they are initialized so we cast the pointer to a slice of `[AccountView]`.
206    match process_instruction(
207        program_id,
208        unsafe { from_raw_parts(accounts.as_ptr() as _, count) },
209        instruction_data,
210    ) {
211        Ok(()) => SUCCESS,
212        Err(error) => error.into(),
213    }
214}
215
216/// Align a pointer to the BPF alignment of [`u128`].
217macro_rules! align_pointer {
218    ($ptr:ident) => {
219        // Integer-to-pointer cast: first compute the aligned address as a `usize`,
220        // since this is more CU-efficient than using `ptr::align_offset()` or the
221        // strict provenance API (e.g., `ptr::with_addr()`). Then cast the result
222        // back to a pointer. The resulting pointer is guaranteed to be valid
223        // because it follows the layout serialized by the runtime.
224        with_exposed_provenance_mut(
225            ($ptr.expose_provenance() + (BPF_ALIGN_OF_U128 - 1)) & !(BPF_ALIGN_OF_U128 - 1),
226        )
227    };
228}
229
230/// Advance the input pointer in relation to a non-duplicated account.
231///
232/// The macro will add `STATIC_ACCOUNT_DATA` and the account length to
233/// the input pointer and align its address using [`align_pointer`].
234macro_rules! advance_input_with_account {
235    ($input:ident, $account:expr) => {{
236        $input = $input.add(STATIC_ACCOUNT_DATA);
237        $input = $input.add((*$account).data_len as usize);
238        $input = align_pointer!($input);
239    }};
240}
241
242/// A macro to repeat a pattern to process an account `n` times, where `n` is
243/// the number of `_` tokens in the input.
244///
245/// The main advantage of this macro is to inline the code to process `n`
246/// accounts, which is useful to reduce the number of jumps required.  As a
247/// result, it reduces the number of CUs required to process each account.
248///
249/// Note that this macro emits code to update both the `input` and `accounts`
250/// pointers.
251macro_rules! process_n_accounts {
252    // Base case: no tokens left.
253    ( () => ( $input:ident, $accounts:ident, $accounts_slice:ident ) ) => {};
254
255    // Recursive case: one `_` token per repetition.
256    ( ( _ $($rest:tt)* ) => ( $input:ident, $accounts:ident, $accounts_slice:ident ) ) => {
257        process_n_accounts!(@process_account => ($input, $accounts, $accounts_slice));
258        process_n_accounts!(($($rest)*) => ($input, $accounts, $accounts_slice));
259    };
260
261    // Process one account.
262    ( @process_account => ( $input:ident, $accounts:ident, $accounts_slice:ident ) ) => {
263        // Increment the `accounts` pointer to the next account.
264        $accounts = $accounts.add(1);
265
266        // Read the next account.
267        let account: *mut RuntimeAccount = $input as *mut RuntimeAccount;
268        // Adds an 8-bytes offset for:
269        //   - rent epoch in case of a non-duplicated account
270        //   - duplicated marker + 7 bytes of padding in case of a duplicated account
271        $input = $input.add(size_of::<u64>());
272
273        if (*account).borrow_state != NON_DUP_MARKER {
274            clone_account_view($accounts, $accounts_slice, (*account).borrow_state);
275        } else {
276            $accounts.write(AccountView::new_unchecked(account));
277            advance_input_with_account!($input, account);
278        }
279    };
280}
281
282/// Convenience macro to transform the number of accounts to process into a
283/// pattern of `_` tokens for the [`process_n_accounts`] macro.
284macro_rules! process_accounts {
285    ( 1 => ( $input:ident, $accounts:ident, $accounts_slice:ident ) ) => {
286        process_n_accounts!( (_) => ( $input, $accounts, $accounts_slice ));
287    };
288    ( 2 => ( $input:ident, $accounts:ident, $accounts_slice:ident ) ) => {
289        process_n_accounts!( (_ _) => ( $input, $accounts, $accounts_slice ));
290    };
291    ( 3 => ( $input:ident, $accounts:ident, $accounts_slice:ident ) ) => {
292        process_n_accounts!( (_ _ _) => ( $input, $accounts, $accounts_slice ));
293    };
294    ( 4 => ( $input:ident, $accounts:ident, $accounts_slice:ident ) ) => {
295        process_n_accounts!( (_ _ _ _) => ( $input, $accounts, $accounts_slice ));
296    };
297    ( 5 => ( $input:ident, $accounts:ident, $accounts_slice:ident ) ) => {
298        process_n_accounts!( (_ _ _ _ _) => ( $input, $accounts, $accounts_slice ));
299    };
300}
301
302/// Create an [`AccountView`] referencing the same account referenced by the
303/// [`AccountView`] at the specified `index`.
304///
305/// # Safety
306///
307/// The caller must ensure that:
308///   - `accounts` pointer must point to an array of [`AccountView`]s where the
309///     new [`AccountView`] will be written.
310///   - `accounts_slice` pointer must point to a slice of [`AccountView`]s
311///     already initialized.
312///   - `index` is a valid index in the `accounts_slice`.
313//
314// Note: The function is marked as `cold` to stop the compiler from optimizing the parsing of
315// duplicated accounts, which leads to an overall increase in CU consumption.
316#[allow(clippy::clone_on_copy)]
317#[cold]
318#[inline(always)]
319unsafe fn clone_account_view(
320    accounts: *mut AccountView,
321    accounts_slice: *const AccountView,
322    index: u8,
323) {
324    accounts.write((*accounts_slice.add(index as usize)).clone());
325}
326
327/// Parse the arguments from the runtime input buffer.
328///
329/// This function parses the `accounts`, `instruction_data` and `program_id`
330/// from the input buffer. The `MAX_ACCOUNTS` constant defines the maximum
331/// number of accounts that can be parsed from the input buffer. If the number
332/// of accounts in the input buffer exceeds `MAX_ACCOUNTS`, the excess
333/// accounts will be skipped (ignored).
334///
335/// # Safety
336///
337/// The caller must ensure that the `input` buffer is valid, i.e., it represents
338/// the program input parameters serialized by the SVM loader. Additionally, the
339/// `input` should last for the lifetime of the program execution since the
340/// returned values reference the `input`.
341#[inline(always)]
342pub unsafe fn deserialize<const MAX_ACCOUNTS: usize>(
343    mut input: *mut u8,
344    accounts: &mut [MaybeUninit<AccountView>; MAX_ACCOUNTS],
345) -> (&'static Address, usize, &'static [u8]) {
346    // Ensure that MAX_ACCOUNTS is less than or equal to the maximum number of
347    // accounts (MAX_TX_ACCOUNTS) that can be processed in a transaction.
348    const {
349        assert!(
350            MAX_ACCOUNTS <= MAX_TX_ACCOUNTS,
351            "MAX_ACCOUNTS must be less than or equal to MAX_TX_ACCOUNTS"
352        );
353    }
354
355    // Number of accounts to process.
356    let mut processed = *(input as *const u64) as usize;
357    // Skip the number of accounts (8 bytes).
358    input = input.add(size_of::<u64>());
359
360    if processed > 0 {
361        let mut accounts = accounts.as_mut_ptr() as *mut AccountView;
362        // Represents the beginning of the accounts slice.
363        let accounts_slice = accounts;
364
365        // The first account is always non-duplicated, so process
366        // it directly as such.
367        let account: *mut RuntimeAccount = input as *mut RuntimeAccount;
368        accounts.write(AccountView::new_unchecked(account));
369
370        input = input.add(size_of::<u64>());
371        advance_input_with_account!(input, account);
372
373        if processed > 1 {
374            // The number of accounts to process (`to_process_plus_one`) is limited to
375            // `MAX_ACCOUNTS`, which is the capacity of the accounts array. When there are
376            // more accounts to process than the maximum, we still need to skip
377            // the remaining accounts (`to_skip`) to move the input pointer to
378            // the instruction data. At the end, we return the number of
379            // accounts processed (`processed`), which represents the accounts
380            // initialized in the `accounts` slice.
381            //
382            // Note that `to_process_plus_one` includes the first (already processed)
383            // account to avoid decrementing the value. The actual number of
384            // remaining accounts to process is `to_process_plus_one - 1`.
385            let mut to_process_plus_one = if MAX_ACCOUNTS < MAX_TX_ACCOUNTS {
386                min(processed, MAX_ACCOUNTS)
387            } else {
388                processed
389            };
390
391            let mut to_skip = processed - to_process_plus_one;
392            processed = to_process_plus_one;
393
394            // This is an optimization to reduce the number of jumps required to process the
395            // accounts. The macro `process_accounts` will generate inline code to process
396            // the specified number of accounts.
397            if to_process_plus_one == 2 {
398                process_accounts!(1 => (input, accounts, accounts_slice));
399            } else {
400                while to_process_plus_one > 5 {
401                    // Process 5 accounts at a time.
402                    process_accounts!(5 => (input, accounts, accounts_slice));
403                    to_process_plus_one -= 5;
404                }
405
406                // There might be remaining accounts to process.
407                match to_process_plus_one {
408                    5 => {
409                        process_accounts!(4 => (input, accounts, accounts_slice));
410                    }
411                    4 => {
412                        process_accounts!(3 => (input, accounts, accounts_slice));
413                    }
414                    3 => {
415                        process_accounts!(2 => (input, accounts, accounts_slice));
416                    }
417                    2 => {
418                        process_accounts!(1 => (input, accounts, accounts_slice));
419                    }
420                    1 => (),
421                    _ => {
422                        // SAFETY: `while` loop above makes sure that `to_process_plus_one`
423                        // has 1 to 5 entries left.
424                        unsafe { core::hint::unreachable_unchecked() }
425                    }
426                }
427            }
428
429            // Process any remaining accounts to move the offset to the instruction data
430            // (there is a duplication of logic but we avoid testing whether we
431            // have space for the account or not).
432            //
433            // There might be accounts to skip only when `MAX_ACCOUNTS < MAX_TX_ACCOUNTS` so
434            // this allows the compiler to optimize the code and avoid the loop
435            // when `MAX_ACCOUNTS == MAX_TX_ACCOUNTS`.
436            if MAX_ACCOUNTS < MAX_TX_ACCOUNTS {
437                while to_skip > 0 {
438                    // Marks the account as skipped.
439                    to_skip -= 1;
440
441                    // Read the next account.
442                    let account: *mut RuntimeAccount = input as *mut RuntimeAccount;
443                    // Adds an 8-bytes offset for:
444                    //   - rent epoch in case of a non-duplicated account
445                    //   - duplicated marker + 7 bytes of padding in case of a duplicated account
446                    input = input.add(size_of::<u64>());
447
448                    if (*account).borrow_state == NON_DUP_MARKER {
449                        advance_input_with_account!(input, account);
450                    }
451                }
452            }
453        }
454    }
455
456    // instruction data
457    let instruction_data_len = *(input as *const u64) as usize;
458    input = input.add(size_of::<u64>());
459
460    let instruction_data = { from_raw_parts(input, instruction_data_len) };
461    let input = input.add(instruction_data_len);
462
463    // program id
464    let program_id: &Address = &*(input as *const Address);
465
466    (program_id, processed, instruction_data)
467}
468
469/// Default panic hook.
470///
471/// This macro sets up a default panic hook that logs the file where the panic
472/// occurred. It acts as a hook after Rust runtime panics; syscall `abort()`
473/// will be called after it returns.
474#[macro_export]
475macro_rules! default_panic_handler {
476    () => {
477        /// Default panic handler.
478        #[cfg(any(target_os = "solana", target_arch = "bpf"))]
479        #[no_mangle]
480        fn custom_panic(info: &core::panic::PanicInfo<'_>) {
481            if let Some(location) = info.location() {
482                let location = location.file();
483                unsafe { $crate::syscalls::sol_log_(location.as_ptr(), location.len() as u64) };
484            }
485            // Panic reporting.
486            const PANICKED: &str = "** PANICKED **";
487            unsafe { $crate::syscalls::sol_log_(PANICKED.as_ptr(), PANICKED.len() as u64) };
488        }
489    };
490}
491
492/// A global `#[panic_handler]` for `no_std` programs.
493///
494/// This macro sets up a default panic handler that logs the location (file,
495/// line and column) where the panic occurred and then calls the syscall
496/// `abort()`.
497///
498/// This macro should be used when all crates are `no_std`.
499#[macro_export]
500macro_rules! nostd_panic_handler {
501    () => {
502        /// A panic handler for `no_std`.
503        #[cfg(any(target_os = "solana", target_arch = "bpf"))]
504        #[panic_handler]
505        fn handler(info: &core::panic::PanicInfo<'_>) -> ! {
506            if let Some(location) = info.location() {
507                unsafe {
508                    $crate::syscalls::sol_panic_(
509                        location.file().as_ptr(),
510                        location.file().len() as u64,
511                        location.line() as u64,
512                        location.column() as u64,
513                    )
514                }
515            } else {
516                // Panic reporting.
517                const PANICKED: &str = "** PANICKED **";
518                unsafe {
519                    $crate::syscalls::sol_log_(PANICKED.as_ptr(), PANICKED.len() as u64);
520                    $crate::syscalls::abort();
521                }
522            }
523        }
524
525        /// A panic handler for when the program is compiled on a target different than
526        /// `"solana"`.
527        ///
528        /// This links the `std` library, which will set up a default panic handler.
529        #[cfg(not(any(target_os = "solana", target_arch = "bpf")))]
530        mod __private_panic_handler {
531            extern crate std as __std;
532        }
533    };
534}
535
536/// Default global allocator.
537///
538/// This macro sets up a default global allocator that uses a bump allocator to
539/// allocate memory.
540#[cfg(feature = "alloc")]
541#[macro_export]
542macro_rules! default_allocator {
543    () => {
544        #[cfg(any(target_os = "solana", target_arch = "bpf"))]
545        #[global_allocator]
546        static A: $crate::entrypoint::BumpAllocator = unsafe {
547            $crate::entrypoint::BumpAllocator::new_unchecked(
548                $crate::entrypoint::HEAP_START_ADDRESS as usize,
549                // Use the maximum heap length allowed. Programs can request heap sizes up
550                // to this value using the `ComputeBudget`.
551                $crate::entrypoint::MAX_HEAP_LENGTH as usize,
552            )
553        };
554
555        /// A default allocator for when the program is compiled on a target different
556        /// than `"solana"`.
557        ///
558        /// This links the `std` library, which will set up a default global allocator.
559        #[cfg(not(any(target_os = "solana", target_arch = "bpf")))]
560        mod __private_alloc {
561            extern crate std as __std;
562        }
563    };
564}
565
566/// A global allocator that does not dynamically allocate memory.
567///
568/// This macro sets up a global allocator that denies all dynamic allocations,
569/// while allowing static ("manual") allocations. This is useful when the
570/// program does not need to dynamically allocate memory and manages their own
571/// allocations.
572///
573/// The program will panic if it tries to dynamically allocate memory.
574///
575/// This is used when the `"alloc"` feature is disabled.
576#[macro_export]
577macro_rules! no_allocator {
578    () => {
579        #[cfg(any(target_os = "solana", target_arch = "bpf"))]
580        #[global_allocator]
581        static A: $crate::entrypoint::NoAllocator = $crate::entrypoint::NoAllocator;
582
583        /// Allocates memory for the given type `T` at the specified offset in the heap
584        /// reserved address space.
585        ///
586        /// # Safety
587        ///
588        /// It is the caller's responsibility to ensure that the offset does not overlap
589        /// with previous allocations and that type `T` can hold the bit-pattern `0` as
590        /// a valid value.
591        ///
592        /// For types that cannot hold the bit-pattern `0` as a valid value, use
593        /// [`core::mem::MaybeUninit<T>`] to allocate memory for the type and initialize
594        /// it later.
595        //
596        // Make this `const` once `const_mut_refs` is stable for the platform-tools toolchain Rust
597        // version.
598        #[inline(always)]
599        pub unsafe fn allocate_unchecked<T: Sized>(offset: usize) -> &'static mut T {
600            // SAFETY: The pointer is within a valid range and aligned to `T`.
601            unsafe { &mut *(calculate_offset::<T>(offset) as *mut T) }
602        }
603
604        #[inline(always)]
605        const fn calculate_offset<T: Sized>(offset: usize) -> usize {
606            let start = $crate::entrypoint::HEAP_START_ADDRESS as usize + offset;
607            let end = start + core::mem::size_of::<T>();
608
609            // Assert if the allocation does not exceed the heap size.
610            assert!(
611                end <= $crate::entrypoint::HEAP_START_ADDRESS as usize
612                    + $crate::entrypoint::MAX_HEAP_LENGTH as usize,
613                "allocation exceeds heap size"
614            );
615
616            // Assert if the pointer is aligned to `T`.
617            assert!(
618                start % core::mem::align_of::<T>() == 0,
619                "offset is not aligned"
620            );
621
622            start
623        }
624
625        /// A default allocator for when the program is compiled on a target different
626        /// than `"solana"`.
627        ///
628        /// This links the `std` library, which will set up a default global allocator.
629        #[cfg(not(any(target_os = "solana", target_arch = "bpf")))]
630        mod __private_alloc {
631            extern crate std as __std;
632        }
633    };
634}
635
636/// An allocator that does not allocate memory.
637#[cfg_attr(feature = "copy", derive(Copy))]
638#[derive(Clone, Debug)]
639pub struct NoAllocator;
640
641unsafe impl GlobalAlloc for NoAllocator {
642    #[inline]
643    unsafe fn alloc(&self, _: Layout) -> *mut u8 {
644        panic!("** NoAllocator::alloc() does not allocate memory **");
645    }
646
647    #[inline]
648    unsafe fn dealloc(&self, _: *mut u8, _: Layout) {
649        // I deny all allocations, so I don't need to free.
650    }
651}
652
653#[cfg(feature = "alloc")]
654mod alloc {
655    use {
656        crate::{entrypoint::MAX_HEAP_LENGTH, hint::unlikely},
657        core::{
658            alloc::{GlobalAlloc, Layout},
659            mem::size_of,
660            ptr::null_mut,
661        },
662    };
663
664    /// The bump allocator used as the default Rust heap when running programs.
665    ///
666    /// The allocator uses a forward bump allocation strategy, where memory is
667    /// allocated by moving a pointer forward in a pre-allocated memory
668    /// region. The current position of the heap pointer is stored at the
669    /// start of the memory region.
670    ///
671    /// This implementation relies on the runtime to zero out memory and to
672    /// enforce the limit of the heap memory region. Use of memory outside
673    /// the allocated region will result in a runtime error.
674    #[cfg_attr(feature = "copy", derive(Copy))]
675    #[derive(Clone, Debug)]
676    pub struct BumpAllocator {
677        start: usize,
678        end: usize,
679    }
680
681    impl BumpAllocator {
682        /// Creates the allocator tied to specific range of addresses.
683        ///
684        /// # Safety
685        ///
686        /// This is unsafe in most situations, unless you are totally sure that
687        /// the provided start address and length can be written to by the
688        /// allocator, and that the memory will be usable for the
689        /// lifespan of the allocator. The start address must be aligned
690        /// to `usize` and the length must be
691        /// at least `size_of::<usize>()` bytes.
692        ///
693        /// For Solana on-chain programs, a certain address range is reserved,
694        /// so the allocator can be given those addresses. In general,
695        /// the `len` is set to the maximum heap length allowed by the
696        /// runtime. The runtime will enforce the actual heap size
697        /// requested by the program.
698        pub const unsafe fn new_unchecked(start: usize, len: usize) -> Self {
699            Self {
700                start,
701                end: start + len,
702            }
703        }
704    }
705
706    // Integer arithmetic in this global allocator implementation is safe when
707    // operating on the prescribed `BumpAllocator::start` and
708    // `BumpAllocator::end`. Any other use may overflow and is thus unsupported
709    // and at one's own risk.
710    #[allow(clippy::arithmetic_side_effects)]
711    unsafe impl GlobalAlloc for BumpAllocator {
712        /// Allocates memory as described by the given `layout` using a forward
713        /// bump allocator.
714        ///
715        /// Returns a pointer to newly-allocated memory, or `null` to indicate
716        /// allocation failure.
717        ///
718        /// # Safety
719        ///
720        /// `layout` must have non-zero size. Attempting to allocate for a
721        /// zero-sized layout will result in undefined behavior.
722        #[inline]
723        unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
724            // Reads the current position of the heap pointer.
725            //
726            // Integer-to-pointer cast: the caller guarantees that `self.start` is a valid
727            // address for the lifetime of the allocator and aligned to `usize`.
728            let pos_ptr = self.start as *mut usize;
729            let mut pos = *pos_ptr;
730
731            if unlikely(pos == 0) {
732                // First time, set starting position.
733                pos = self.start + size_of::<usize>();
734            }
735
736            // Determines the allocation address, adjusting the alignment for the
737            // type being allocated.
738            let allocation = (pos + layout.align() - 1) & !(layout.align() - 1);
739
740            if unlikely(layout.size() > MAX_HEAP_LENGTH as usize)
741                || unlikely(self.end < allocation + layout.size())
742            {
743                return null_mut();
744            }
745
746            // Updates the heap pointer.
747            *pos_ptr = allocation + layout.size();
748
749            allocation as *mut u8
750        }
751
752        /// Behaves like `alloc`, but also ensures that the contents are set to
753        /// zero before being returned.
754        ///
755        /// This method relies on the runtime to zero out the memory when
756        /// reserving the heap region, so it simply calls `alloc`.
757        #[inline]
758        unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
759            self.alloc(layout)
760        }
761
762        /// This method has no effect since the bump allocator does not free
763        /// memory.
764        #[inline]
765        unsafe fn dealloc(&self, _: *mut u8, _: Layout) {}
766    }
767}
768
769#[cfg(test)]
770mod tests {
771    use {
772        super::*,
773        ::alloc::{
774            alloc::{alloc, dealloc, handle_alloc_error},
775            vec,
776        },
777        core::{
778            alloc::Layout,
779            ptr::{copy_nonoverlapping, null_mut},
780        },
781    };
782
783    /// The mock program ID used for testing.
784    const MOCK_PROGRAM_ID: Address = Address::new_from_array([5u8; 32]);
785
786    /// An uninitialized account view.
787    const UNINIT: MaybeUninit<AccountView> = MaybeUninit::<AccountView>::uninit();
788
789    /// Struct representing a memory region with a specific alignment.
790    struct AlignedMemory {
791        ptr: *mut u8,
792        layout: Layout,
793    }
794
795    impl AlignedMemory {
796        pub fn new(len: usize) -> Self {
797            let layout = Layout::from_size_align(len, BPF_ALIGN_OF_U128).unwrap();
798            // SAFETY: `align` is set to `BPF_ALIGN_OF_U128`.
799            unsafe {
800                let ptr = alloc(layout);
801                if ptr.is_null() {
802                    handle_alloc_error(layout);
803                }
804                AlignedMemory { ptr, layout }
805            }
806        }
807
808        /// Write data to the memory region at the specified offset.
809        ///
810        /// # Safety
811        ///
812        /// The caller must ensure that the `data` length does not exceed the
813        /// remaining space in the memory region starting from the
814        /// `offset`.
815        pub unsafe fn write(&mut self, data: &[u8], offset: usize) {
816            copy_nonoverlapping(data.as_ptr(), self.ptr.add(offset), data.len());
817        }
818
819        /// Return a mutable pointer to the memory region.
820        pub fn as_mut_ptr(&mut self) -> *mut u8 {
821            self.ptr
822        }
823    }
824
825    impl Drop for AlignedMemory {
826        fn drop(&mut self) {
827            unsafe {
828                dealloc(self.ptr, self.layout);
829            }
830        }
831    }
832
833    /// Creates an input buffer with a specified number of accounts and
834    /// instruction data.
835    ///
836    /// This function mimics the input buffer created by the SVM loader.  Each
837    /// account created has zeroed data, apart from the `data_len` field,
838    /// which is set to the index of the account.
839    ///
840    /// # Safety
841    ///
842    /// The returned `AlignedMemory` should only be used within the test
843    /// context.
844    unsafe fn create_input(accounts: usize, instruction_data: &[u8]) -> AlignedMemory {
845        let mut input = AlignedMemory::new(1_000_000_000);
846        // Number of accounts.
847        input.write(&(accounts as u64).to_le_bytes(), 0);
848        let mut offset = size_of::<u64>();
849
850        for i in 0..accounts {
851            // Account data.
852            let mut account = [0u8; STATIC_ACCOUNT_DATA + size_of::<u64>()];
853            account[0] = NON_DUP_MARKER;
854            // Set the accounts data length. The actual account data is zeroed.
855            account[80..88].copy_from_slice(&i.to_le_bytes());
856            input.write(&account, offset);
857            offset += account.len();
858            // Padding for the account data to align to `BPF_ALIGN_OF_U128`.
859            let padding_for_data = (i + (BPF_ALIGN_OF_U128 - 1)) & !(BPF_ALIGN_OF_U128 - 1);
860            input.write(&vec![0u8; padding_for_data], offset);
861            offset += padding_for_data;
862        }
863
864        // Instruction data length.
865        input.write(&instruction_data.len().to_le_bytes(), offset);
866        offset += size_of::<u64>();
867        // Instruction data.
868        input.write(instruction_data, offset);
869        offset += instruction_data.len();
870        // Program ID (mock).
871        input.write(MOCK_PROGRAM_ID.as_array(), offset);
872
873        input
874    }
875
876    /// Creates an input buffer with a specified number of accounts, including
877    /// duplicated accounts, and instruction data.
878    ///
879    /// This function differs from `create_input` in that it creates accounts
880    /// with a marker indicating that they are duplicated. There will be
881    /// `accounts - duplicated` unique accounts, and the remaining
882    /// `duplicated` accounts will be duplicates of the last unique account.
883    ///
884    /// This function mimics the input buffer created by the SVM loader.  Each
885    /// account created has zeroed data, apart from the `data_len` field,
886    /// which is set to the index of the account.
887    ///
888    /// # Safety
889    ///
890    /// The returned `AlignedMemory` should only be used within the test
891    /// context.
892    unsafe fn create_input_with_duplicates(
893        accounts: usize,
894        instruction_data: &[u8],
895        duplicated: usize,
896    ) -> AlignedMemory {
897        let mut input = AlignedMemory::new(1_000_000_000);
898        // Number of accounts.
899        input.write(&(accounts as u64).to_le_bytes(), 0);
900        let mut offset = size_of::<u64>();
901
902        if accounts > 0 {
903            assert!(
904                duplicated < accounts,
905                "Duplicated accounts must be less than total accounts"
906            );
907            let unique = accounts - duplicated;
908
909            for i in 0..unique {
910                // Account data.
911                let mut account = [0u8; STATIC_ACCOUNT_DATA + size_of::<u64>()];
912                account[0] = NON_DUP_MARKER;
913                // Set the accounts data length. The actual account data is zeroed.
914                account[80..88].copy_from_slice(&i.to_le_bytes());
915                input.write(&account, offset);
916                offset += account.len();
917                // Padding for the account data to align to `BPF_ALIGN_OF_U128`.
918                let padding_for_data = (i + (BPF_ALIGN_OF_U128 - 1)) & !(BPF_ALIGN_OF_U128 - 1);
919                input.write(&vec![0u8; padding_for_data], offset);
920                offset += padding_for_data;
921            }
922
923            // Remaining accounts are duplicated of the last unique account.
924            for _ in unique..accounts {
925                input.write(&[(unique - 1) as u8, 0, 0, 0, 0, 0, 0, 0], offset);
926                offset += size_of::<u64>();
927            }
928        }
929
930        // Instruction data length.
931        input.write(&instruction_data.len().to_le_bytes(), offset);
932        offset += size_of::<u64>();
933        // Instruction data.
934        input.write(instruction_data, offset);
935        offset += instruction_data.len();
936        // Program ID (mock).
937        input.write(MOCK_PROGRAM_ID.as_array(), offset);
938
939        input
940    }
941
942    /// Asserts that the accounts slice contains the expected number of accounts
943    /// and that each account's data length matches its index.
944    fn assert_accounts(accounts: &[MaybeUninit<AccountView>]) {
945        for (i, account) in accounts.iter().enumerate() {
946            let account_view = unsafe { account.assume_init_ref() };
947            assert_eq!(account_view.data_len(), i);
948        }
949    }
950
951    /// Asserts that the accounts slice contains the expected number of accounts
952    /// and all accounts are duplicated, apart from the first one.
953    fn assert_duplicated_accounts(accounts: &[MaybeUninit<AccountView>], duplicated: usize) {
954        assert!(accounts.len() > duplicated);
955
956        let unique = accounts.len() - duplicated;
957
958        // Unique accounts should have `data_len` equal to their index.
959        for (i, account) in accounts[..unique].iter().enumerate() {
960            let account_view = unsafe { account.assume_init_ref() };
961            assert_eq!(account_view.data_len(), i);
962        }
963
964        // Last unique account.
965        let duplicated = unsafe { accounts[unique - 1].assume_init_ref() };
966        // No mutable borrow active at this point.
967        assert!(duplicated.try_borrow_mut().is_ok());
968
969        // Duplicated accounts should reference (share) the account pointer
970        // to the last unique account.
971        for account in accounts[unique..].iter() {
972            let account_view = unsafe { account.assume_init_ref() };
973
974            assert_eq!(account_view, duplicated);
975            assert_eq!(account_view.data_len(), duplicated.data_len());
976
977            let borrowed = account_view.try_borrow_mut().unwrap();
978            // Only one mutable borrow at the same time should be allowed
979            // on the duplicated account.
980            assert!(duplicated.try_borrow_mut().is_err());
981            drop(borrowed);
982        }
983
984        // There should not be any mutable borrow on the duplicated account
985        // at this point.
986        assert!(duplicated.try_borrow_mut().is_ok());
987    }
988
989    #[test]
990    fn test_deserialize() {
991        let ix_data = [3u8; 100];
992
993        // Input with 0 accounts.
994
995        let mut input = unsafe { create_input(0, &ix_data) };
996        let mut accounts = [UNINIT; 1];
997
998        let (program_id, count, parsed_ix_data) =
999            unsafe { deserialize(input.as_mut_ptr(), &mut accounts) };
1000
1001        assert_eq!(count, 0);
1002        assert!(program_id == &MOCK_PROGRAM_ID);
1003        assert_eq!(&ix_data, parsed_ix_data);
1004
1005        // Input with 3 accounts but the accounts array has only space
1006        // for 1.
1007
1008        let mut input = unsafe { create_input(3, &ix_data) };
1009        let mut accounts = [UNINIT; 1];
1010
1011        let (program_id, count, parsed_ix_data) =
1012            unsafe { deserialize(input.as_mut_ptr(), &mut accounts) };
1013
1014        assert_eq!(count, 1);
1015        assert!(program_id == &MOCK_PROGRAM_ID);
1016        assert_eq!(&ix_data, parsed_ix_data);
1017        assert_accounts(&accounts[..count]);
1018
1019        // Input with `MAX_TX_ACCOUNTS` accounts but accounts array has
1020        // only space for 64.
1021
1022        let mut input = unsafe { create_input(MAX_TX_ACCOUNTS, &ix_data) };
1023        let mut accounts = [UNINIT; 64];
1024
1025        let (program_id, count, parsed_ix_data) =
1026            unsafe { deserialize(input.as_mut_ptr(), &mut accounts) };
1027
1028        assert_eq!(count, 64);
1029        assert!(program_id == &MOCK_PROGRAM_ID);
1030        assert_eq!(&ix_data, parsed_ix_data);
1031        assert_accounts(&accounts);
1032    }
1033
1034    #[test]
1035    fn test_deserialize_duplicated() {
1036        let ix_data = [3u8; 100];
1037
1038        // Input with 0 accounts.
1039
1040        let mut input = unsafe { create_input_with_duplicates(0, &ix_data, 0) };
1041        let mut accounts = [UNINIT; 1];
1042
1043        let (program_id, count, parsed_ix_data) =
1044            unsafe { deserialize(input.as_mut_ptr(), &mut accounts) };
1045
1046        assert_eq!(count, 0);
1047        assert!(program_id == &MOCK_PROGRAM_ID);
1048        assert_eq!(&ix_data, parsed_ix_data);
1049
1050        // Input with 3 (1 + 2 duplicated) accounts but the accounts array has only
1051        // space for 2. The assert checks that the second account is a duplicate
1052        // of the first one and the first one is unique.
1053
1054        let mut input = unsafe { create_input_with_duplicates(3, &ix_data, 2) };
1055        let mut accounts = [UNINIT; 2];
1056
1057        let (program_id, count, parsed_ix_data) =
1058            unsafe { deserialize(input.as_mut_ptr(), &mut accounts) };
1059
1060        assert_eq!(count, 2);
1061        assert!(program_id == &MOCK_PROGRAM_ID);
1062        assert_eq!(&ix_data, parsed_ix_data);
1063        assert_duplicated_accounts(&accounts[..count], 1);
1064
1065        // Input with `MAX_TX_ACCOUNTS` accounts (only 32 unique ones) but accounts
1066        // array has only space for 64. The assert checks that the first 32
1067        // accounts are unique and the rest are duplicates of the account at
1068        // index 31.
1069
1070        let mut input = unsafe {
1071            create_input_with_duplicates(MAX_TX_ACCOUNTS, &ix_data, MAX_TX_ACCOUNTS - 32)
1072        };
1073        let mut accounts = [UNINIT; 64];
1074
1075        let (program_id, count, parsed_ix_data) =
1076            unsafe { deserialize(input.as_mut_ptr(), &mut accounts) };
1077
1078        assert_eq!(count, 64);
1079        assert!(program_id == &MOCK_PROGRAM_ID);
1080        assert_eq!(&ix_data, parsed_ix_data);
1081        assert_duplicated_accounts(&accounts, 32);
1082    }
1083
1084    #[test]
1085    fn test_bump_allocator() {
1086        // alloc the entire
1087        {
1088            let mut heap = AlignedMemory::new(128);
1089            unsafe { heap.write(&[0; 128], 0) };
1090
1091            let allocator = unsafe {
1092                BumpAllocator::new_unchecked(heap.as_mut_ptr() as usize, heap.layout.size())
1093            };
1094
1095            for i in 0..128 - size_of::<*mut u8>() {
1096                let ptr = unsafe {
1097                    allocator.alloc(Layout::from_size_align(1, size_of::<u8>()).unwrap())
1098                };
1099                assert_eq!(
1100                    ptr as usize,
1101                    heap.as_mut_ptr() as usize + size_of::<*mut u8>() + i
1102                );
1103            }
1104            assert_eq!(null_mut(), unsafe {
1105                allocator.alloc(Layout::from_size_align(1, size_of::<u8>()).unwrap())
1106            });
1107        }
1108        // check alignment
1109        {
1110            let mut heap = AlignedMemory::new(128);
1111            unsafe { heap.write(&[0; 128], 0) };
1112
1113            let allocator = unsafe {
1114                BumpAllocator::new_unchecked(heap.as_mut_ptr() as usize, heap.layout.size())
1115            };
1116            let ptr =
1117                unsafe { allocator.alloc(Layout::from_size_align(1, size_of::<u8>()).unwrap()) };
1118            assert_eq!(0, ptr.align_offset(size_of::<u8>()));
1119            let ptr =
1120                unsafe { allocator.alloc(Layout::from_size_align(1, size_of::<u16>()).unwrap()) };
1121            assert_eq!(0, ptr.align_offset(size_of::<u16>()));
1122            let ptr =
1123                unsafe { allocator.alloc(Layout::from_size_align(1, size_of::<u32>()).unwrap()) };
1124            assert_eq!(0, ptr.align_offset(size_of::<u32>()));
1125            let ptr =
1126                unsafe { allocator.alloc(Layout::from_size_align(1, size_of::<u64>()).unwrap()) };
1127            assert_eq!(0, ptr.align_offset(size_of::<u64>()));
1128            let ptr =
1129                unsafe { allocator.alloc(Layout::from_size_align(1, size_of::<u128>()).unwrap()) };
1130            assert_eq!(0, ptr.align_offset(size_of::<u128>()));
1131            let ptr = unsafe { allocator.alloc(Layout::from_size_align(1, 64).unwrap()) };
1132            assert_eq!(0, ptr.align_offset(64));
1133        }
1134        // alloc entire block (minus the pos ptr)
1135        {
1136            let mut heap = AlignedMemory::new(128);
1137            unsafe { heap.write(&[0; 128], 0) };
1138
1139            let allocator = unsafe {
1140                BumpAllocator::new_unchecked(heap.as_mut_ptr() as usize, heap.layout.size())
1141            };
1142            let ptr = unsafe {
1143                allocator.alloc(
1144                    Layout::from_size_align(
1145                        heap.layout.size() - size_of::<usize>(),
1146                        size_of::<u8>(),
1147                    )
1148                    .unwrap(),
1149                )
1150            };
1151            assert_ne!(ptr, null_mut());
1152            assert_eq!(0, ptr.align_offset(size_of::<u64>()));
1153        }
1154    }
1155}