Skip to content

Commit e280afe

Browse files
L0STEfebo
authored andcommitted
A close macro implementation for AccountInfo (anza-xyz#42)
* 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
1 parent e1de31f commit e280afe

File tree

1 file changed

+49
-2
lines changed

1 file changed

+49
-2
lines changed

account-view/src/lib.rs

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
//! Data structures to represent account information.
2-
32
use core::{marker::PhantomData, mem::ManuallyDrop, ptr::NonNull, slice::from_raw_parts_mut};
43

5-
use crate::{program_error::ProgramError, pubkey::Pubkey, syscalls::sol_memset_};
4+
use crate::{program_error::ProgramError, pubkey::Pubkey, syscalls::sol_memset_, ProgramResult};
65

76
/// Maximum number of bytes a program may add to an account during a
87
/// single realloc.
@@ -379,6 +378,54 @@ impl AccountInfo {
379378
Ok(())
380379
}
381380

381+
/// Zero out the the account's data_len, lamports and owner fields, effectively
382+
/// closing the account.
383+
///
384+
/// This doesn't protect against future reinitialization of the account
385+
/// since the account_data will need to be zeroed out as well. Or if the attacker
386+
/// has access to the keypair of the account that we're trying to close, they can
387+
/// just add the lenght, lamports and owner back before the data is wiped out from
388+
/// the ledger.
389+
///
390+
/// This works because the 48 bytes before the account data are:
391+
/// - 8 bytes for the data_len
392+
/// - 8 bytes for the lamports
393+
/// - 32 bytes for the owner
394+
pub fn close(&self) -> ProgramResult {
395+
{
396+
let _ = self.try_borrow_mut_data()?;
397+
}
398+
399+
unsafe {
400+
self.close_unchecked();
401+
}
402+
403+
Ok(())
404+
}
405+
///
406+
/// # Safety
407+
///
408+
/// This method makes assumptions about the layout and location of memory
409+
/// referenced by `AccountInfo` fields. It should only be called for
410+
/// instances of `AccountInfo` that were created by the runtime and received
411+
/// in the `process_instruction` entrypoint of a program.
412+
///
413+
/// This method is unsafe because it does not check if the account data is already
414+
/// borrowed. It should only be called when the account is not being used.
415+
#[inline(always)]
416+
pub unsafe fn close_unchecked(&self) {
417+
// Zero out the account owner. While the field is a `Pubkey`, it is quicker
418+
// to zero the 32 bytes as 4 x `u64`s.
419+
*(self.data_ptr().sub(48) as *mut u64) = 0u64;
420+
*(self.data_ptr().sub(40) as *mut u64) = 0u64;
421+
*(self.data_ptr().sub(32) as *mut u64) = 0u64;
422+
*(self.data_ptr().sub(16) as *mut u64) = 0u64;
423+
// Zero the account lamports.
424+
(*self.raw).lamports = 0;
425+
// Zero the account data length.
426+
(*self.raw).data_len = 0;
427+
}
428+
382429
/// Returns the memory address of the account data.
383430
fn data_ptr(&self) -> *mut u8 {
384431
unsafe { (self.raw as *const _ as *mut u8).add(core::mem::size_of::<Account>()) }

0 commit comments

Comments
 (0)