|
1 | 1 | //! Data structures to represent account information. |
2 | | -
|
3 | 2 | use core::{marker::PhantomData, mem::ManuallyDrop, ptr::NonNull, slice::from_raw_parts_mut}; |
4 | 3 |
|
5 | | -use crate::{program_error::ProgramError, pubkey::Pubkey, syscalls::sol_memset_}; |
| 4 | +use crate::{program_error::ProgramError, pubkey::Pubkey, syscalls::sol_memset_, ProgramResult}; |
6 | 5 |
|
7 | 6 | /// Maximum number of bytes a program may add to an account during a |
8 | 7 | /// single realloc. |
@@ -379,6 +378,54 @@ impl AccountInfo { |
379 | 378 | Ok(()) |
380 | 379 | } |
381 | 380 |
|
| 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 | + |
382 | 429 | /// Returns the memory address of the account data. |
383 | 430 | fn data_ptr(&self) -> *mut u8 { |
384 | 431 | unsafe { (self.raw as *const _ as *mut u8).add(core::mem::size_of::<Account>()) } |
|
0 commit comments