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 b9878b968b..45b21e85ab 100644 --- a/programs/bpf/tests/programs.rs +++ b/programs/bpf/tests/programs.rs @@ -1387,7 +1387,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(), @@ -1405,7 +1412,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(), @@ -1433,7 +1447,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 ecabdc8d93..47fd25c23a 100644 --- a/programs/bpf_loader/src/lib.rs +++ b/programs/bpf_loader/src/lib.rs @@ -181,7 +181,7 @@ pub fn process_instruction( 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()? @@ -193,6 +193,7 @@ pub fn process_instruction( } ( programdata, + &keyed_accounts[1..], UpgradeableLoaderState::programdata_data_offset()?, ) } else { @@ -200,7 +201,7 @@ pub fn process_instruction( return Err(InstructionError::InvalidAccountData); } } else { - (first_account, 0) + (first_account, keyed_accounts, 0) }; if program.owner()? != *program_id { @@ -279,8 +280,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); } @@ -442,7 +446,8 @@ fn process_loader_upgradeable_instruction( invoke_context, )?; - // 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, @@ -451,6 +456,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 @@ -1464,6 +1474,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(