From ff728e5e563f7aa7459f744cd330e62ea9f84964 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 17 Dec 2020 03:46:43 -0800 Subject: [PATCH] Fix program account rent exemption (#14176) (#14180) (cherry picked from commit 593ad809541336da5790bfed9d5899a3bcc08e8f) Co-authored-by: Jack May --- programs/bpf/rust/upgradeable/src/lib.rs | 3 +- programs/bpf/rust/upgraded/src/lib.rs | 3 +- programs/bpf/tests/programs.rs | 27 +++++++- programs/bpf_loader/src/lib.rs | 80 ++++++++++++++++++++++-- 4 files changed, 103 insertions(+), 10 deletions(-) diff --git a/programs/bpf/rust/upgradeable/src/lib.rs b/programs/bpf/rust/upgradeable/src/lib.rs index c693151943..2649e84975 100644 --- a/programs/bpf/rust/upgradeable/src/lib.rs +++ b/programs/bpf/rust/upgradeable/src/lib.rs @@ -8,9 +8,10 @@ use solana_program::{ entrypoint!(process_instruction); fn process_instruction( _program_id: &Pubkey, - _accounts: &[AccountInfo], + accounts: &[AccountInfo], _instruction_data: &[u8], ) -> ProgramResult { msg!("Upgradeable program"); + assert_eq!(accounts.len(), 2); Err(42.into()) } diff --git a/programs/bpf/rust/upgraded/src/lib.rs b/programs/bpf/rust/upgraded/src/lib.rs index 3fcf2cae0f..d03f31200a 100644 --- a/programs/bpf/rust/upgraded/src/lib.rs +++ b/programs/bpf/rust/upgraded/src/lib.rs @@ -8,9 +8,10 @@ use solana_program::{ entrypoint!(process_instruction); fn process_instruction( _program_id: &Pubkey, - _accounts: &[AccountInfo], + accounts: &[AccountInfo], _instruction_data: &[u8], ) -> ProgramResult { msg!("Upgraded program"); + assert_eq!(accounts.len(), 2); Err(43.into()) } diff --git a/programs/bpf/tests/programs.rs b/programs/bpf/tests/programs.rs index acc72dce54..f56cc255a6 100644 --- a/programs/bpf/tests/programs.rs +++ b/programs/bpf/tests/programs.rs @@ -1483,7 +1483,14 @@ fn test_program_bpf_upgrade() { // call upgrade program nonce += 1; - let instruction = Instruction::new(program_id, &[nonce], vec![]); + let instruction = Instruction::new( + program_id, + &[nonce], + vec![ + AccountMeta::new(clock::id(), false), + AccountMeta::new(fees::id(), false), + ], + ); let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction); assert_eq!( result.unwrap_err().unwrap(), @@ -1501,7 +1508,14 @@ fn test_program_bpf_upgrade() { // call upgraded program nonce += 1; - let instruction = Instruction::new(program_id, &[nonce], vec![]); + let instruction = Instruction::new( + program_id, + &[nonce], + vec![ + AccountMeta::new(clock::id(), false), + AccountMeta::new(fees::id(), false), + ], + ); let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction); assert_eq!( result.unwrap_err().unwrap(), @@ -1529,7 +1543,14 @@ fn test_program_bpf_upgrade() { // call original program nonce += 1; - let instruction = Instruction::new(program_id, &[nonce], vec![]); + let instruction = Instruction::new( + program_id, + &[nonce], + vec![ + AccountMeta::new(clock::id(), false), + AccountMeta::new(fees::id(), false), + ], + ); let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction); assert_eq!( result.unwrap_err().unwrap(), diff --git a/programs/bpf_loader/src/lib.rs b/programs/bpf_loader/src/lib.rs index 6e9c52455d..10d54e1ef2 100644 --- a/programs/bpf_loader/src/lib.rs +++ b/programs/bpf_loader/src/lib.rs @@ -228,7 +228,7 @@ fn process_instruction_common( let account_iter = &mut keyed_accounts.iter(); let first_account = next_keyed_account(account_iter)?; if first_account.executable()? { - let (program, offset) = if bpf_loader_upgradeable::check_id(program_id) { + let (program, keyed_accounts, offset) = if bpf_loader_upgradeable::check_id(program_id) { if let UpgradeableLoaderState::Program { programdata_address, } = first_account.state()? @@ -240,6 +240,7 @@ fn process_instruction_common( } ( programdata, + &keyed_accounts[1..], UpgradeableLoaderState::programdata_data_offset()?, ) } else { @@ -247,7 +248,7 @@ fn process_instruction_common( return Err(InstructionError::InvalidAccountData); } } else { - (first_account, 0) + (first_account, keyed_accounts, 0) }; if program.owner()? != *program_id { @@ -341,8 +342,11 @@ fn process_loader_upgradeable_instruction( log!(logger, "Program account already initialized"); return Err(InstructionError::AccountAlreadyInitialized); } - - if program.lamports()? < rent.minimum_balance(UpgradeableLoaderState::program_len()?) { + if program.data_len()? < UpgradeableLoaderState::program_len()? { + log!(logger, "Program account too small"); + return Err(InstructionError::AccountDataTooSmall); + } + if program.lamports()? < rent.minimum_balance(program.data_len()?) { log!(logger, "Program account not rent-exempt"); return Err(InstructionError::ExecutableAccountNotRentExempt); } @@ -506,7 +510,8 @@ fn process_loader_upgradeable_instruction( use_jit, )?; - // Update the ProgramData account and record the upgraded data + // Update the ProgramData account, record the upgraded data, and zero + // the rest programdata.set_state(&UpgradeableLoaderState::ProgramData { slot: clock.slot, @@ -515,6 +520,11 @@ fn process_loader_upgradeable_instruction( programdata.try_account_ref_mut()?.data [programdata_data_offset..programdata_data_offset + buffer_data_len] .copy_from_slice(&buffer.try_account_ref()?.data[buffer_data_offset..]); + for i in &mut programdata.try_account_ref_mut()?.data + [programdata_data_offset + buffer_data_len..] + { + *i = 0 + } // Fund ProgramData to rent-exemption, spill the rest @@ -1541,6 +1551,66 @@ mod tests { .unwrap() ); + // Test program account not rent exempt because data is larger than needed + bank.clear_signatures(); + bank.store_account(&buffer_address, &buffer_account); + bank.store_account(&program_keypair.pubkey(), &Account::default()); + bank.store_account(&programdata_address, &Account::default()); + let mut instructions = bpf_loader_upgradeable::deploy_with_max_program_len( + &mint_keypair.pubkey(), + &program_keypair.pubkey(), + &buffer_address, + None, + min_program_balance, + elf.len(), + ) + .unwrap(); + instructions[0] = system_instruction::create_account( + &mint_keypair.pubkey(), + &program_keypair.pubkey(), + min_program_balance, + UpgradeableLoaderState::program_len().unwrap() as u64 + 1, + &id(), + ); + let message = Message::new(&instructions, Some(&mint_keypair.pubkey())); + assert_eq!( + TransactionError::InstructionError(1, InstructionError::ExecutableAccountNotRentExempt), + bank_client + .send_and_confirm_message(&[&mint_keypair, &program_keypair], message) + .unwrap_err() + .unwrap() + ); + + // Test program account too small + bank.clear_signatures(); + bank.store_account(&buffer_address, &buffer_account); + bank.store_account(&program_keypair.pubkey(), &Account::default()); + bank.store_account(&programdata_address, &Account::default()); + let mut instructions = bpf_loader_upgradeable::deploy_with_max_program_len( + &mint_keypair.pubkey(), + &program_keypair.pubkey(), + &buffer_address, + None, + min_program_balance, + elf.len(), + ) + .unwrap(); + instructions[0] = system_instruction::create_account( + &mint_keypair.pubkey(), + &program_keypair.pubkey(), + min_program_balance, + UpgradeableLoaderState::program_len().unwrap() as u64 - 1, + &id(), + ); + let message = Message::new(&instructions, Some(&mint_keypair.pubkey())); + assert_eq!( + TransactionError::InstructionError(1, InstructionError::AccountDataTooSmall), + bank_client + .send_and_confirm_message(&[&mint_keypair, &program_keypair], message) + .unwrap_err() + .unwrap() + ); + // Test Insufficient payer funds bank.clear_signatures(); bank.store_account(