Skip to content
This repository was archived by the owner on Mar 11, 2025. It is now read-only.

Commit 76a92cd

Browse files
authored
Added checks if a token account is initialized when checking validity (#2986)
* Added checks if an account is initialized when checking validity
1 parent 221935b commit 76a92cd

File tree

2 files changed

+100
-16
lines changed

2 files changed

+100
-16
lines changed

token/program-2022/src/state.rs

Lines changed: 61 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -295,7 +295,13 @@ const ACCOUNTTYPE_ACCOUNT: u8 = AccountType::Account as u8;
295295
impl GenericTokenAccount for Account {
296296
fn valid_account_data(account_data: &[u8]) -> bool {
297297
spl_token::state::Account::valid_account_data(account_data)
298-
|| ACCOUNTTYPE_ACCOUNT == *account_data.get(Account::LEN).unwrap_or(&0)
298+
|| (account_data.len() >= Account::LEN
299+
&& account_data.len() != Multisig::LEN
300+
&& ACCOUNTTYPE_ACCOUNT
301+
== *account_data
302+
.get(spl_token::state::Account::get_packed_len())
303+
.unwrap_or(&(AccountType::Uninitialized as u8))
304+
&& spl_token::state::is_initialized_account(account_data))
299305
}
300306
}
301307

@@ -420,23 +426,45 @@ pub(crate) mod test {
420426
let result = Account::unpack_account_owner(&src);
421427
assert_eq!(result, Option::None);
422428

423-
// The right account data size, unpack will return some key
424-
let src: [u8; Account::LEN] = [0; Account::LEN];
429+
// The right account data size and initialized, unpack will return some key
430+
let mut src: [u8; Account::LEN] = [0; Account::LEN];
431+
src[spl_token::state::ACCOUNT_INITIALIZED_INDEX] = AccountState::Initialized as u8;
432+
let result = Account::unpack_account_owner(&src);
433+
assert!(result.is_some());
434+
435+
// The right account data size and frozen, unpack will return some key
436+
src[spl_token::state::ACCOUNT_INITIALIZED_INDEX] = AccountState::Frozen as u8;
425437
let result = Account::unpack_account_owner(&src);
426438
assert!(result.is_some());
427439

428440
// Account data length > account data size, but not a valid extension,
429441
// unpack will not return a key
430-
let src: [u8; Account::LEN + 5] = [0; Account::LEN + 5];
442+
let mut src: [u8; Account::LEN + 5] = [0; Account::LEN + 5];
443+
src[spl_token::state::ACCOUNT_INITIALIZED_INDEX] = AccountState::Initialized as u8;
431444
let result = Account::unpack_account_owner(&src);
432445
assert_eq!(result, Option::None);
433446

434-
// Account data length > account data size with a valid extension,
447+
// Account data length > account data size with a valid extension and initialized,
435448
// expect some key returned
436449
let mut src: [u8; Account::LEN + 5] = [0; Account::LEN + 5];
437-
src[Account::LEN] = 2;
450+
src[Account::LEN] = AccountType::Account as u8;
451+
src[spl_token::state::ACCOUNT_INITIALIZED_INDEX] = AccountState::Initialized as u8;
438452
let result = Account::unpack_account_owner(&src);
439453
assert!(result.is_some());
454+
455+
// Account data length > account data size with a valid extension but uninitialized,
456+
// expect None
457+
src[spl_token::state::ACCOUNT_INITIALIZED_INDEX] = AccountState::Uninitialized as u8;
458+
let result = Account::unpack_account_owner(&src);
459+
assert!(result.is_none());
460+
461+
// Account data length is multi-sig data size with a valid extension and initalized,
462+
// expect none
463+
let mut src: [u8; Multisig::LEN] = [0; Multisig::LEN];
464+
src[spl_token::state::ACCOUNT_INITIALIZED_INDEX] = AccountState::Initialized as u8;
465+
src[Account::LEN] = AccountType::Account as u8;
466+
let result = Account::unpack_account_owner(&src);
467+
assert!(result.is_none());
440468
}
441469

442470
#[test]
@@ -446,22 +474,44 @@ pub(crate) mod test {
446474
let result = Account::unpack_account_mint(&src);
447475
assert_eq!(result, Option::None);
448476

449-
// The right account data size, unpack will return some key
450-
let src: [u8; Account::LEN] = [0; Account::LEN];
477+
// The right account data size and initialized, unpack will return some key
478+
let mut src: [u8; Account::LEN] = [0; Account::LEN];
479+
src[spl_token::state::ACCOUNT_INITIALIZED_INDEX] = AccountState::Initialized as u8;
480+
let result = Account::unpack_account_mint(&src);
481+
assert!(result.is_some());
482+
483+
// The right account data size and frozen, unpack will return some key
484+
src[spl_token::state::ACCOUNT_INITIALIZED_INDEX] = AccountState::Frozen as u8;
451485
let result = Account::unpack_account_mint(&src);
452486
assert!(result.is_some());
453487

454488
// Account data length > account data size, but not a valid extension,
455489
// unpack will not return a key
456-
let src: [u8; Account::LEN + 5] = [0; Account::LEN + 5];
490+
let mut src: [u8; Account::LEN + 5] = [0; Account::LEN + 5];
491+
src[spl_token::state::ACCOUNT_INITIALIZED_INDEX] = AccountState::Initialized as u8;
457492
let result = Account::unpack_account_mint(&src);
458493
assert_eq!(result, Option::None);
459494

460-
// Account data length > account data size with a valid extension,
495+
// Account data length > account data size with a valid extension and initalized,
461496
// expect some key returned
462497
let mut src: [u8; Account::LEN + 5] = [0; Account::LEN + 5];
463-
src[Account::LEN] = 2;
498+
src[spl_token::state::ACCOUNT_INITIALIZED_INDEX] = AccountState::Initialized as u8;
499+
src[Account::LEN] = AccountType::Account as u8;
464500
let result = Account::unpack_account_mint(&src);
465501
assert!(result.is_some());
502+
503+
// Account data length > account data size with a valid extension but uninitalized,
504+
// expect none
505+
src[spl_token::state::ACCOUNT_INITIALIZED_INDEX] = AccountState::Uninitialized as u8;
506+
let result = Account::unpack_account_mint(&src);
507+
assert!(result.is_none());
508+
509+
// Account data length is multi-sig data size with a valid extension and initalized,
510+
// expect none
511+
let mut src: [u8; Multisig::LEN] = [0; Multisig::LEN];
512+
src[spl_token::state::ACCOUNT_INITIALIZED_INDEX] = AccountState::Initialized as u8;
513+
src[Account::LEN] = AccountType::Account as u8;
514+
let result = Account::unpack_account_mint(&src);
515+
assert!(result.is_none());
466516
}
467517
}

token/program/src/state.rs

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -331,9 +331,21 @@ pub trait GenericTokenAccount {
331331
}
332332
}
333333

334+
/// The offset of state field in Account's C representation
335+
pub const ACCOUNT_INITIALIZED_INDEX: usize = 108;
336+
337+
/// Check if the account data buffer represents an initialized account.
338+
/// This is checking the `state` (AccountState) field of an Account object.
339+
pub fn is_initialized_account(account_data: &[u8]) -> bool {
340+
*account_data
341+
.get(ACCOUNT_INITIALIZED_INDEX)
342+
.unwrap_or(&(AccountState::Uninitialized as u8))
343+
!= AccountState::Uninitialized as u8
344+
}
345+
334346
impl GenericTokenAccount for Account {
335347
fn valid_account_data(account_data: &[u8]) -> bool {
336-
account_data.len() == Account::LEN
348+
account_data.len() == Account::LEN && is_initialized_account(account_data)
337349
}
338350
}
339351

@@ -418,11 +430,22 @@ mod tests {
418430
let result = Account::unpack_account_owner(&src);
419431
assert_eq!(result, Option::None);
420432

421-
// The right account data size, unpack will return some key
422-
let src: [u8; Account::LEN] = [0; Account::LEN];
433+
// The right account data size and intialized, unpack will return some key
434+
let mut src: [u8; Account::LEN] = [0; Account::LEN];
435+
src[ACCOUNT_INITIALIZED_INDEX] = AccountState::Initialized as u8;
423436
let result = Account::unpack_account_owner(&src);
424437
assert!(result.is_some());
425438

439+
// The right account data size and frozen, unpack will return some key
440+
src[ACCOUNT_INITIALIZED_INDEX] = AccountState::Frozen as u8;
441+
let result = Account::unpack_account_owner(&src);
442+
assert!(result.is_some());
443+
444+
// The right account data size and uninitialized, unpack will return None
445+
src[ACCOUNT_INITIALIZED_INDEX] = AccountState::Uninitialized as u8;
446+
let result = Account::unpack_account_mint(&src);
447+
assert_eq!(result, Option::None);
448+
426449
// Account data length > account data size, unpack will not return a key
427450
let src: [u8; Account::LEN + 5] = [0; Account::LEN + 5];
428451
let result = Account::unpack_account_owner(&src);
@@ -436,11 +459,22 @@ mod tests {
436459
let result = Account::unpack_account_mint(&src);
437460
assert_eq!(result, Option::None);
438461

439-
// The right account data size, unpack will return some key
440-
let src: [u8; Account::LEN] = [0; Account::LEN];
462+
// The right account data size and initialized, unpack will return some key
463+
let mut src: [u8; Account::LEN] = [0; Account::LEN];
464+
src[ACCOUNT_INITIALIZED_INDEX] = AccountState::Initialized as u8;
465+
let result = Account::unpack_account_mint(&src);
466+
assert!(result.is_some());
467+
468+
// The right account data size and frozen, unpack will return some key
469+
src[ACCOUNT_INITIALIZED_INDEX] = AccountState::Frozen as u8;
441470
let result = Account::unpack_account_mint(&src);
442471
assert!(result.is_some());
443472

473+
// The right account data size and uninitialized, unpack will return None
474+
src[ACCOUNT_INITIALIZED_INDEX] = AccountState::Uninitialized as u8;
475+
let result = Account::unpack_account_mint(&src);
476+
assert_eq!(result, Option::None);
477+
444478
// Account data length > account data size, unpack will not return a key
445479
let src: [u8; Account::LEN + 5] = [0; Account::LEN + 5];
446480
let result = Account::unpack_account_mint(&src);

0 commit comments

Comments
 (0)