Skip to content

Commit

Permalink
A close macro implementation for AccountInfo (#42)
Browse files Browse the repository at this point in the history
* Added close and based_close

* added docs comments + wrapped up and tested both function

* cargo clippy and fmt

* added the new close and changed the name for

* fixed and tested after comments
  • Loading branch information
L0STE authored Nov 20, 2024
1 parent cb7e5ab commit 74b5512
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 2 deletions.
51 changes: 49 additions & 2 deletions sdk/pinocchio/src/account_info.rs
Original file line number Diff line number Diff line change
@@ -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.
Expand Down Expand Up @@ -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::<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 74b5512

Please sign in to comment.