diff --git a/sdk/pinocchio/src/account_info.rs b/sdk/pinocchio/src/account_info.rs index cc662b7..8ca912a 100644 --- a/sdk/pinocchio/src/account_info.rs +++ b/sdk/pinocchio/src/account_info.rs @@ -1,8 +1,7 @@ //! Data structures to represent account information. - use core::{marker::PhantomData, mem::ManuallyDrop, ptr::NonNull, slice::from_raw_parts_mut}; -use crate::{program_error::ProgramError, pubkey::Pubkey, syscalls::sol_memset_}; +use crate::{program_error::ProgramError, pubkey::Pubkey, syscalls::sol_memset_, ProgramResult}; /// Maximum number of bytes a program may add to an account during a /// single realloc. @@ -379,6 +378,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::()) } diff --git a/sdk/pinocchio/src/lib.rs b/sdk/pinocchio/src/lib.rs index 230fc54..4cbceb1 100644 --- a/sdk/pinocchio/src/lib.rs +++ b/sdk/pinocchio/src/lib.rs @@ -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;