Skip to content

Commit

Permalink
Merge branch 'realloc-fix' of https://github.com/L0STE/pinocchio into…
Browse files Browse the repository at this point in the history
… realloc-fix
  • Loading branch information
L0STE committed Nov 20, 2024
2 parents f79149d + 8075448 commit b986ec5
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 1 deletion.
50 changes: 49 additions & 1 deletion sdk/pinocchio/src/account_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use core::{marker::PhantomData, mem::ManuallyDrop, ptr::NonNull, slice::from_raw
#[cfg(target_os = "solana")]
use crate::syscalls::sol_memset_;

use crate::{program_error::ProgramError, pubkey::Pubkey};
use crate::{program_error::ProgramError, pubkey::Pubkey, ProgramResult};

/// Maximum number of bytes a program may add to an account during a
/// single realloc.
Expand Down Expand Up @@ -384,6 +384,54 @@ impl AccountInfo {
Ok(())
}

/// Zero out the the account's data_len, lamports and owner fields, effectively
/// closing the account.
///
/// This doesn't protect against future reinitialization of the account
/// since the account_data will need to be zeroed out as well. Or if the attacker
/// has access to the keypair of the account that we're trying to close, they can
/// just add the lenght, lamports and owner back before the data is wiped out from
/// the ledger.
///
/// This works because the 48 bytes before the account data are:
/// - 8 bytes for the data_len
/// - 8 bytes for the lamports
/// - 32 bytes for the owner
pub fn close(&self) -> ProgramResult {
{
let _ = self.try_borrow_mut_data()?;
}

unsafe {
self.close_unchecked();
}

Ok(())
}
///
/// # Safety
///
/// This method makes assumptions about the layout and location of memory
/// referenced by `AccountInfo` fields. It should only be called for
/// instances of `AccountInfo` that were created by the runtime and received
/// in the `process_instruction` entrypoint of a program.
///
/// This method is unsafe because it does not check if the account data is already
/// borrowed. It should only be called when the account is not being used.
#[inline(always)]
pub unsafe fn close_unchecked(&self) {
// Zero out the account owner. While the field is a `Pubkey`, it is quicker
// to zero the 32 bytes as 4 x `u64`s.
*(self.data_ptr().sub(48) as *mut u64) = 0u64;
*(self.data_ptr().sub(40) as *mut u64) = 0u64;
*(self.data_ptr().sub(32) as *mut u64) = 0u64;
*(self.data_ptr().sub(16) as *mut u64) = 0u64;
// Zero the account lamports.
(*self.raw).lamports = 0;
// Zero the account data length.
(*self.raw).data_len = 0;
}

/// Returns the memory address of the account data.
fn data_ptr(&self) -> *mut u8 {
unsafe { (self.raw as *const _ as *mut u8).add(core::mem::size_of::<Account>()) }
Expand Down
1 change: 1 addition & 0 deletions sdk/pinocchio/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
//! [`solana-program`]: https://docs.rs/solana-program/latest/solana_program/
#![no_std]
#![cfg_attr(target_os = "solana", feature(asm_experimental_arch, asm_const))]

pub mod account_info;
pub mod entrypoint;
Expand Down

0 comments on commit b986ec5

Please sign in to comment.