diff --git a/programs/stake/src/stake_instruction.rs b/programs/stake/src/stake_instruction.rs index 789b8d114..cfe41bae6 100644 --- a/programs/stake/src/stake_instruction.rs +++ b/programs/stake/src/stake_instruction.rs @@ -532,11 +532,20 @@ mod tests { sysvar::stake_history::StakeHistory, }; use std::cell::RefCell; + use std::str::FromStr; fn create_default_account() -> RefCell { RefCell::new(Account::default()) } + fn invalid_stake_state_pubkey() -> Pubkey { + Pubkey::from_str("BadStake11111111111111111111111111111111111").unwrap() + } + + fn invalid_vote_state_pubkey() -> Pubkey { + Pubkey::from_str("BadVote111111111111111111111111111111111111").unwrap() + } + fn process_instruction(instruction: &Instruction) -> Result<(), InstructionError> { let accounts: Vec<_> = instruction .accounts @@ -552,6 +561,14 @@ mod tests { config::create_account(0, &config::Config::default()) } else if sysvar::rent::check_id(&meta.pubkey) { account::create_account(&Rent::default(), 1) + } else if meta.pubkey == invalid_stake_state_pubkey() { + let mut account = Account::default(); + account.owner = id(); + account + } else if meta.pubkey == invalid_vote_state_pubkey() { + let mut account = Account::default(); + account.owner = solana_vote_program::id(); + account } else { Account::default() }) @@ -599,14 +616,18 @@ mod tests { &Pubkey::default(), &Pubkey::default(), 100, - &Pubkey::default() + &invalid_stake_state_pubkey(), )[1] ), Err(InstructionError::InvalidAccountData), ); assert_eq!( process_instruction( - &merge(&Pubkey::default(), &Pubkey::default(), &Pubkey::default(),)[0] + &merge( + &Pubkey::default(), + &invalid_stake_state_pubkey(), + &Pubkey::default(), + )[0] ), Err(InstructionError::InvalidAccountData), ); @@ -616,7 +637,7 @@ mod tests { &Pubkey::default(), &Pubkey::default(), 100, - &Pubkey::default(), + &invalid_stake_state_pubkey(), &Pubkey::default(), "seed" )[1] @@ -627,7 +648,7 @@ mod tests { process_instruction(&delegate_stake( &Pubkey::default(), &Pubkey::default(), - &Pubkey::default() + &invalid_vote_state_pubkey(), )), Err(InstructionError::InvalidAccountData), ); @@ -764,12 +785,14 @@ mod tests { ); // gets the check non-deserialize-able account in delegate_stake + let mut bad_vote_account = create_default_account(); + bad_vote_account.get_mut().owner = solana_vote_program::id(); assert_eq!( super::process_instruction( &Pubkey::default(), &[ KeyedAccount::new(&Pubkey::default(), true, &create_default_account()), - KeyedAccount::new(&Pubkey::default(), false, &create_default_account()), + KeyedAccount::new(&Pubkey::default(), false, &bad_vote_account), KeyedAccount::new( &sysvar::clock::id(), false, diff --git a/programs/stake/src/stake_state.rs b/programs/stake/src/stake_state.rs index 060a7176b..099a40411 100644 --- a/programs/stake/src/stake_state.rs +++ b/programs/stake/src/stake_state.rs @@ -821,6 +821,10 @@ impl<'a> StakeAccount for KeyedAccount<'a> { config: &Config, signers: &HashSet, ) -> Result<(), InstructionError> { + if vote_account.owner()? != solana_vote_program::id() { + return Err(InstructionError::IncorrectProgramId); + } + match self.state()? { StakeState::Initialized(meta) => { meta.authorized.check(signers, StakeAuthorize::Staker)?; @@ -882,6 +886,10 @@ impl<'a> StakeAccount for KeyedAccount<'a> { split: &KeyedAccount, signers: &HashSet, ) -> Result<(), InstructionError> { + if split.owner()? != id() { + return Err(InstructionError::IncorrectProgramId); + } + if let StakeState::Uninitialized = split.state()? { // verify enough account lamports if lamports > self.lamports()? { @@ -981,6 +989,10 @@ impl<'a> StakeAccount for KeyedAccount<'a> { stake_history: &StakeHistory, signers: &HashSet, ) -> Result<(), InstructionError> { + if source_stake.owner()? != id() { + return Err(InstructionError::IncorrectProgramId); + } + let meta = match self.state()? { StakeState::Stake(meta, stake) => { // stake must be fully de-activated @@ -1480,6 +1492,21 @@ mod tests { ) .is_ok()); + // signed but faked vote account + let faked_vote_account = vote_account.clone(); + faked_vote_account.borrow_mut().owner = solana_sdk::pubkey::new_rand(); + let faked_vote_keyed_account = KeyedAccount::new(&vote_pubkey, false, &faked_vote_account); + assert_eq!( + stake_keyed_account.delegate( + &faked_vote_keyed_account, + &clock, + &StakeHistory::default(), + &Config::default(), + &signers, + ), + Err(solana_sdk::instruction::InstructionError::IncorrectProgramId) + ); + // verify that delegate() looks right, compare against hand-rolled let stake = StakeState::stake_from(&stake_keyed_account.account.borrow()).unwrap(); assert_eq!( @@ -3558,6 +3585,40 @@ mod tests { } } + #[test] + fn test_split_fake_stake_dest() { + let stake_pubkey = solana_sdk::pubkey::new_rand(); + let stake_lamports = 42; + + let split_stake_pubkey = solana_sdk::pubkey::new_rand(); + let signers = vec![stake_pubkey].into_iter().collect(); + + let split_stake_account = Account::new_ref_data_with_space( + 0, + &StakeState::Uninitialized, + std::mem::size_of::(), + &solana_sdk::pubkey::new_rand(), + ) + .expect("stake_account"); + + let split_stake_keyed_account = + KeyedAccount::new(&split_stake_pubkey, true, &split_stake_account); + + let stake_account = Account::new_ref_data_with_space( + stake_lamports, + &StakeState::Stake(Meta::auto(&stake_pubkey), Stake::just_stake(stake_lamports)), + std::mem::size_of::(), + &id(), + ) + .expect("stake_account"); + let stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &stake_account); + + assert_eq!( + stake_keyed_account.split(stake_lamports / 2, &split_stake_keyed_account, &signers), + Err(InstructionError::IncorrectProgramId), + ); + } + #[test] fn test_split_to_account_with_rent_exempt_reserve() { let stake_pubkey = solana_sdk::pubkey::new_rand(); @@ -4421,6 +4482,51 @@ mod tests { } } + #[test] + fn test_merge_fake_stake_source() { + let stake_pubkey = solana_sdk::pubkey::new_rand(); + let source_stake_pubkey = solana_sdk::pubkey::new_rand(); + let authorized_pubkey = solana_sdk::pubkey::new_rand(); + let stake_lamports = 42; + + let signers = vec![authorized_pubkey].into_iter().collect(); + + let stake_account = Account::new_ref_data_with_space( + stake_lamports, + &StakeState::Stake( + Meta::auto(&authorized_pubkey), + Stake::just_stake(stake_lamports), + ), + std::mem::size_of::(), + &id(), + ) + .expect("stake_account"); + let stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &stake_account); + + let source_stake_account = Account::new_ref_data_with_space( + stake_lamports, + &StakeState::Stake( + Meta::auto(&authorized_pubkey), + Stake::just_stake(stake_lamports), + ), + std::mem::size_of::(), + &solana_sdk::pubkey::new_rand(), + ) + .expect("source_stake_account"); + let source_stake_keyed_account = + KeyedAccount::new(&source_stake_pubkey, true, &source_stake_account); + + assert_eq!( + stake_keyed_account.merge( + &source_stake_keyed_account, + &Clock::default(), + &StakeHistory::default(), + &signers + ), + Err(InstructionError::IncorrectProgramId) + ); + } + #[test] fn test_merge_active_stake() { let stake_pubkey = solana_sdk::pubkey::new_rand();