Remove `KeyedAccount` in builtin program "BPF loader" (#24381)

* Uses transaction_context.get_key_of_account_at_index() when only the key is needed.

* Uses "!instruction_context.is_signer()" instead of ".signer_key().is_none()".

* Uses instruction_context.get_index_in_transaction() to detect account aliasing.

* Makes sure that there is only one KeyedAccount at a time.
Everywhere except for DeployWithMaxDataLen and Upgrade.

* Makes sure that there is only one KeyedAccount at a time.
In DeployWithMaxDataLen and Upgrade.

* Replaces KeyedAccount by BorrowedAccount.

* Removes unused code.
This commit is contained in:
Alexander Meißner 2022-04-19 11:35:40 +02:00 committed by GitHub
parent 697c9e1969
commit 998cdd1c29
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 234 additions and 232 deletions

View File

@ -36,8 +36,6 @@ use {
vm::{Config, EbpfVm, InstructionMeter}, vm::{Config, EbpfVm, InstructionMeter},
}, },
solana_sdk::{ solana_sdk::{
account::{ReadableAccount, WritableAccount},
account_utils::State,
bpf_loader, bpf_loader_deprecated, bpf_loader, bpf_loader_deprecated,
bpf_loader_upgradeable::{self, UpgradeableLoaderState}, bpf_loader_upgradeable::{self, UpgradeableLoaderState},
entrypoint::{HEAP_LENGTH, SUCCESS}, entrypoint::{HEAP_LENGTH, SUCCESS},
@ -48,7 +46,6 @@ use {
reduce_required_deploy_balance, reject_callx_r10, requestable_heap_size, reduce_required_deploy_balance, reject_callx_r10, requestable_heap_size,
}, },
instruction::{AccountMeta, InstructionError}, instruction::{AccountMeta, InstructionError},
keyed_account::keyed_account_at_index,
loader_instruction::LoaderInstruction, loader_instruction::LoaderInstruction,
loader_upgradeable_instruction::UpgradeableLoaderInstruction, loader_upgradeable_instruction::UpgradeableLoaderInstruction,
program_error::MAX_ACCOUNTS_DATA_SIZE_EXCEEDED, program_error::MAX_ACCOUNTS_DATA_SIZE_EXCEEDED,
@ -430,45 +427,50 @@ fn process_loader_upgradeable_instruction(
let instruction_context = transaction_context.get_current_instruction_context()?; let instruction_context = transaction_context.get_current_instruction_context()?;
let instruction_data = instruction_context.get_instruction_data(); let instruction_data = instruction_context.get_instruction_data();
let program_id = instruction_context.get_program_key(transaction_context)?; let program_id = instruction_context.get_program_key(transaction_context)?;
let keyed_accounts = invoke_context.get_keyed_accounts()?;
match limited_deserialize(instruction_data)? { match limited_deserialize(instruction_data)? {
UpgradeableLoaderInstruction::InitializeBuffer => { UpgradeableLoaderInstruction::InitializeBuffer => {
instruction_context.check_number_of_instruction_accounts(2)?; instruction_context.check_number_of_instruction_accounts(2)?;
let buffer = keyed_account_at_index(keyed_accounts, first_instruction_account)?; let mut buffer =
instruction_context.try_borrow_instruction_account(transaction_context, 0)?;
if UpgradeableLoaderState::Uninitialized != buffer.state()? { if UpgradeableLoaderState::Uninitialized != buffer.get_state()? {
ic_logger_msg!(log_collector, "Buffer account already initialized"); ic_logger_msg!(log_collector, "Buffer account already initialized");
return Err(InstructionError::AccountAlreadyInitialized); return Err(InstructionError::AccountAlreadyInitialized);
} }
let authority = keyed_account_at_index( let authority_key = Some(
keyed_accounts, *transaction_context.get_key_of_account_at_index(
first_instruction_account.saturating_add(1), instruction_context
)?; .get_index_in_transaction(first_instruction_account.saturating_add(1))?,
)?,
);
buffer.set_state(&UpgradeableLoaderState::Buffer { buffer.set_state(&UpgradeableLoaderState::Buffer {
authority_address: Some(*authority.unsigned_key()), authority_address: authority_key,
})?; })?;
} }
UpgradeableLoaderInstruction::Write { offset, bytes } => { UpgradeableLoaderInstruction::Write { offset, bytes } => {
instruction_context.check_number_of_instruction_accounts(2)?; instruction_context.check_number_of_instruction_accounts(2)?;
let buffer = keyed_account_at_index(keyed_accounts, first_instruction_account)?; let buffer =
let authority = keyed_account_at_index( instruction_context.try_borrow_instruction_account(transaction_context, 0)?;
keyed_accounts,
first_instruction_account.saturating_add(1),
)?;
if let UpgradeableLoaderState::Buffer { authority_address } = buffer.state()? { if let UpgradeableLoaderState::Buffer { authority_address } = buffer.get_state()? {
if authority_address.is_none() { if authority_address.is_none() {
ic_logger_msg!(log_collector, "Buffer is immutable"); ic_logger_msg!(log_collector, "Buffer is immutable");
return Err(InstructionError::Immutable); // TODO better error code return Err(InstructionError::Immutable); // TODO better error code
} }
if authority_address != Some(*authority.unsigned_key()) { let authority_key =
Some(*transaction_context.get_key_of_account_at_index(
instruction_context.get_index_in_transaction(
first_instruction_account.saturating_add(1),
)?,
)?);
if authority_address != authority_key {
ic_logger_msg!(log_collector, "Incorrect buffer authority provided"); ic_logger_msg!(log_collector, "Incorrect buffer authority provided");
return Err(InstructionError::IncorrectAuthority); return Err(InstructionError::IncorrectAuthority);
} }
if authority.signer_key().is_none() { if !instruction_context.is_signer(first_instruction_account.saturating_add(1))? {
ic_logger_msg!(log_collector, "Buffer authority did not sign"); ic_logger_msg!(log_collector, "Buffer authority did not sign");
return Err(InstructionError::MissingRequiredSignature); return Err(InstructionError::MissingRequiredSignature);
} }
@ -476,6 +478,7 @@ fn process_loader_upgradeable_instruction(
ic_logger_msg!(log_collector, "Invalid Buffer account"); ic_logger_msg!(log_collector, "Invalid Buffer account");
return Err(InstructionError::InvalidAccountData); return Err(InstructionError::InvalidAccountData);
} }
drop(buffer);
write_program_data( write_program_data(
first_instruction_account, first_instruction_account,
UpgradeableLoaderState::buffer_data_offset()?.saturating_add(offset as usize), UpgradeableLoaderState::buffer_data_offset()?.saturating_add(offset as usize),
@ -485,55 +488,53 @@ fn process_loader_upgradeable_instruction(
} }
UpgradeableLoaderInstruction::DeployWithMaxDataLen { max_data_len } => { UpgradeableLoaderInstruction::DeployWithMaxDataLen { max_data_len } => {
instruction_context.check_number_of_instruction_accounts(4)?; instruction_context.check_number_of_instruction_accounts(4)?;
let payer = keyed_account_at_index(keyed_accounts, first_instruction_account)?; let payer_key = *transaction_context.get_key_of_account_at_index(
let programdata = keyed_account_at_index( instruction_context.get_index_in_transaction(first_instruction_account)?,
keyed_accounts,
first_instruction_account.saturating_add(1),
)?; )?;
let program = keyed_account_at_index( let programdata_key = *transaction_context.get_key_of_account_at_index(
keyed_accounts, instruction_context
first_instruction_account.saturating_add(2), .get_index_in_transaction(first_instruction_account.saturating_add(1))?,
)?;
let buffer = keyed_account_at_index(
keyed_accounts,
first_instruction_account.saturating_add(3),
)?; )?;
let rent = get_sysvar_with_account_check::rent(invoke_context, instruction_context, 4)?; let rent = get_sysvar_with_account_check::rent(invoke_context, instruction_context, 4)?;
let clock = let clock =
get_sysvar_with_account_check::clock(invoke_context, instruction_context, 5)?; get_sysvar_with_account_check::clock(invoke_context, instruction_context, 5)?;
instruction_context.check_number_of_instruction_accounts(8)?; instruction_context.check_number_of_instruction_accounts(8)?;
let authority = keyed_account_at_index( let authority_key = Some(
keyed_accounts, *transaction_context.get_key_of_account_at_index(
first_instruction_account.saturating_add(7), instruction_context
)?; .get_index_in_transaction(first_instruction_account.saturating_add(7))?,
let upgrade_authority_address = Some(*authority.unsigned_key()); )?,
let upgrade_authority_signer = authority.signer_key().is_none(); );
// Verify Program account // Verify Program account
if UpgradeableLoaderState::Uninitialized != program.state()? { let program =
instruction_context.try_borrow_instruction_account(transaction_context, 2)?;
if UpgradeableLoaderState::Uninitialized != program.get_state()? {
ic_logger_msg!(log_collector, "Program account already initialized"); ic_logger_msg!(log_collector, "Program account already initialized");
return Err(InstructionError::AccountAlreadyInitialized); return Err(InstructionError::AccountAlreadyInitialized);
} }
if program.data_len()? < UpgradeableLoaderState::program_len()? { if program.get_data().len() < UpgradeableLoaderState::program_len()? {
ic_logger_msg!(log_collector, "Program account too small"); ic_logger_msg!(log_collector, "Program account too small");
return Err(InstructionError::AccountDataTooSmall); return Err(InstructionError::AccountDataTooSmall);
} }
if program.lamports()? < rent.minimum_balance(program.data_len()?) { if program.get_lamports() < rent.minimum_balance(program.get_data().len()) {
ic_logger_msg!(log_collector, "Program account not rent-exempt"); ic_logger_msg!(log_collector, "Program account not rent-exempt");
return Err(InstructionError::ExecutableAccountNotRentExempt); return Err(InstructionError::ExecutableAccountNotRentExempt);
} }
let new_program_id = *program.get_key();
let new_program_id = *program.unsigned_key(); drop(program);
// Verify Buffer account // Verify Buffer account
if let UpgradeableLoaderState::Buffer { authority_address } = buffer.state()? { let buffer =
if authority_address != upgrade_authority_address { instruction_context.try_borrow_instruction_account(transaction_context, 3)?;
if let UpgradeableLoaderState::Buffer { authority_address } = buffer.get_state()? {
if authority_address != authority_key {
ic_logger_msg!(log_collector, "Buffer and upgrade authority don't match"); ic_logger_msg!(log_collector, "Buffer and upgrade authority don't match");
return Err(InstructionError::IncorrectAuthority); return Err(InstructionError::IncorrectAuthority);
} }
if upgrade_authority_signer { if !instruction_context.is_signer(first_instruction_account.saturating_add(7))? {
ic_logger_msg!(log_collector, "Upgrade authority did not sign"); ic_logger_msg!(log_collector, "Upgrade authority did not sign");
return Err(InstructionError::MissingRequiredSignature); return Err(InstructionError::MissingRequiredSignature);
} }
@ -541,18 +542,19 @@ fn process_loader_upgradeable_instruction(
ic_logger_msg!(log_collector, "Invalid Buffer account"); ic_logger_msg!(log_collector, "Invalid Buffer account");
return Err(InstructionError::InvalidArgument); return Err(InstructionError::InvalidArgument);
} }
let buffer_key = *buffer.get_key();
let buffer_lamports = buffer.get_lamports();
let buffer_data_offset = UpgradeableLoaderState::buffer_data_offset()?; let buffer_data_offset = UpgradeableLoaderState::buffer_data_offset()?;
let buffer_data_len = buffer.data_len()?.saturating_sub(buffer_data_offset); let buffer_data_len = buffer.get_data().len().saturating_sub(buffer_data_offset);
let programdata_data_offset = UpgradeableLoaderState::programdata_data_offset()?; let programdata_data_offset = UpgradeableLoaderState::programdata_data_offset()?;
let programdata_len = UpgradeableLoaderState::programdata_len(max_data_len)?; let programdata_len = UpgradeableLoaderState::programdata_len(max_data_len)?;
if buffer.get_data().len() < UpgradeableLoaderState::buffer_data_offset()?
if buffer.data_len()? < UpgradeableLoaderState::buffer_data_offset()?
|| buffer_data_len == 0 || buffer_data_len == 0
{ {
ic_logger_msg!(log_collector, "Buffer account too small"); ic_logger_msg!(log_collector, "Buffer account too small");
return Err(InstructionError::InvalidAccountData); return Err(InstructionError::InvalidAccountData);
} }
drop(buffer);
if max_data_len < buffer_data_len { if max_data_len < buffer_data_len {
ic_logger_msg!( ic_logger_msg!(
log_collector, log_collector,
@ -569,7 +571,7 @@ fn process_loader_upgradeable_instruction(
let (derived_address, bump_seed) = let (derived_address, bump_seed) =
Pubkey::find_program_address(&[new_program_id.as_ref()], program_id); Pubkey::find_program_address(&[new_program_id.as_ref()], program_id);
if derived_address != *programdata.unsigned_key() { if derived_address != programdata_key {
ic_logger_msg!(log_collector, "ProgramData address is not derived"); ic_logger_msg!(log_collector, "ProgramData address is not derived");
return Err(InstructionError::InvalidArgument); return Err(InstructionError::InvalidArgument);
} }
@ -579,15 +581,18 @@ fn process_loader_upgradeable_instruction(
.is_active(&reduce_required_deploy_balance::id()); .is_active(&reduce_required_deploy_balance::id());
if predrain_buffer { if predrain_buffer {
// Drain the Buffer account to payer before paying for programdata account // Drain the Buffer account to payer before paying for programdata account
payer let mut payer =
.try_account_ref_mut()? instruction_context.try_borrow_instruction_account(transaction_context, 0)?;
.checked_add_lamports(buffer.lamports()?)?; payer.checked_add_lamports(buffer_lamports)?;
buffer.try_account_ref_mut()?.set_lamports(0); drop(payer);
let mut buffer =
instruction_context.try_borrow_instruction_account(transaction_context, 3)?;
buffer.set_lamports(0);
} }
let mut instruction = system_instruction::create_account( let mut instruction = system_instruction::create_account(
payer.unsigned_key(), &payer_key,
programdata.unsigned_key(), &programdata_key,
1.max(rent.minimum_balance(programdata_len)), 1.max(rent.minimum_balance(programdata_len)),
programdata_len as u64, programdata_len as u64,
program_id, program_id,
@ -596,7 +601,7 @@ fn process_loader_upgradeable_instruction(
// pass an extra account to avoid the overly strict UnbalancedInstruction error // pass an extra account to avoid the overly strict UnbalancedInstruction error
instruction instruction
.accounts .accounts
.push(AccountMeta::new(*buffer.unsigned_key(), false)); .push(AccountMeta::new(buffer_key, false));
let transaction_context = &invoke_context.transaction_context; let transaction_context = &invoke_context.transaction_context;
let instruction_context = transaction_context.get_current_instruction_context()?; let instruction_context = transaction_context.get_current_instruction_context()?;
@ -617,81 +622,76 @@ fn process_loader_upgradeable_instruction(
)?; )?;
invoke_context.update_executor(&new_program_id, executor); invoke_context.update_executor(&new_program_id, executor);
let keyed_accounts = invoke_context.get_keyed_accounts()?; let transaction_context = &invoke_context.transaction_context;
let payer = keyed_account_at_index(keyed_accounts, first_instruction_account)?; let instruction_context = transaction_context.get_current_instruction_context()?;
let programdata = keyed_account_at_index(
keyed_accounts,
first_instruction_account.saturating_add(1),
)?;
let program = keyed_account_at_index(
keyed_accounts,
first_instruction_account.saturating_add(2),
)?;
let buffer = keyed_account_at_index(
keyed_accounts,
first_instruction_account.saturating_add(3),
)?;
// Update the ProgramData account and record the program bits // Update the ProgramData account and record the program bits
{
let mut programdata =
instruction_context.try_borrow_instruction_account(transaction_context, 1)?;
programdata.set_state(&UpgradeableLoaderState::ProgramData { programdata.set_state(&UpgradeableLoaderState::ProgramData {
slot: clock.slot, slot: clock.slot,
upgrade_authority_address, upgrade_authority_address: authority_key,
})?; })?;
programdata let dst_slice = programdata
.try_account_ref_mut()? .get_data_mut()
.data_as_mut_slice()
.get_mut( .get_mut(
programdata_data_offset programdata_data_offset
..programdata_data_offset.saturating_add(buffer_data_len), ..programdata_data_offset.saturating_add(buffer_data_len),
) )
.ok_or(InstructionError::AccountDataTooSmall)? .ok_or(InstructionError::AccountDataTooSmall)?;
.copy_from_slice( let buffer =
buffer instruction_context.try_borrow_instruction_account(transaction_context, 3)?;
.try_account_ref()? let src_slice = buffer
.data() .get_data()
.get(buffer_data_offset..) .get(buffer_data_offset..)
.ok_or(InstructionError::AccountDataTooSmall)?, .ok_or(InstructionError::AccountDataTooSmall)?;
); dst_slice.copy_from_slice(src_slice);
}
// Update the Program account // Update the Program account
let mut program =
instruction_context.try_borrow_instruction_account(transaction_context, 2)?;
program.set_state(&UpgradeableLoaderState::Program { program.set_state(&UpgradeableLoaderState::Program {
programdata_address: *programdata.unsigned_key(), programdata_address: programdata_key,
})?; })?;
program.try_account_ref_mut()?.set_executable(true); program.set_executable(true);
drop(program);
if !predrain_buffer { if !predrain_buffer {
// Drain the Buffer account back to the payer // Drain the Buffer account back to the payer
payer let mut payer =
.try_account_ref_mut()? instruction_context.try_borrow_instruction_account(transaction_context, 0)?;
.checked_add_lamports(buffer.lamports()?)?; payer.checked_add_lamports(buffer_lamports)?;
buffer.try_account_ref_mut()?.set_lamports(0); drop(payer);
let mut buffer =
instruction_context.try_borrow_instruction_account(transaction_context, 3)?;
buffer.set_lamports(0);
} }
ic_logger_msg!(log_collector, "Deployed program {:?}", new_program_id); ic_logger_msg!(log_collector, "Deployed program {:?}", new_program_id);
} }
UpgradeableLoaderInstruction::Upgrade => { UpgradeableLoaderInstruction::Upgrade => {
instruction_context.check_number_of_instruction_accounts(3)?; instruction_context.check_number_of_instruction_accounts(3)?;
let programdata = keyed_account_at_index(keyed_accounts, first_instruction_account)?; let programdata_key = *transaction_context.get_key_of_account_at_index(
let program = keyed_account_at_index( instruction_context.get_index_in_transaction(first_instruction_account)?,
keyed_accounts,
first_instruction_account.saturating_add(1),
)?;
let buffer = keyed_account_at_index(
keyed_accounts,
first_instruction_account.saturating_add(2),
)?; )?;
let rent = get_sysvar_with_account_check::rent(invoke_context, instruction_context, 4)?; let rent = get_sysvar_with_account_check::rent(invoke_context, instruction_context, 4)?;
let clock = let clock =
get_sysvar_with_account_check::clock(invoke_context, instruction_context, 5)?; get_sysvar_with_account_check::clock(invoke_context, instruction_context, 5)?;
instruction_context.check_number_of_instruction_accounts(7)?; instruction_context.check_number_of_instruction_accounts(7)?;
let authority = keyed_account_at_index( let authority_key = Some(
keyed_accounts, *transaction_context.get_key_of_account_at_index(
first_instruction_account.saturating_add(6), instruction_context
)?; .get_index_in_transaction(first_instruction_account.saturating_add(6))?,
)?,
);
// Verify Program account // Verify Program account
if !program.executable()? { let program =
instruction_context.try_borrow_instruction_account(transaction_context, 1)?;
if !program.is_executable() {
ic_logger_msg!(log_collector, "Program account not executable"); ic_logger_msg!(log_collector, "Program account not executable");
return Err(InstructionError::AccountNotExecutable); return Err(InstructionError::AccountNotExecutable);
} }
@ -699,15 +699,15 @@ fn process_loader_upgradeable_instruction(
ic_logger_msg!(log_collector, "Program account not writeable"); ic_logger_msg!(log_collector, "Program account not writeable");
return Err(InstructionError::InvalidArgument); return Err(InstructionError::InvalidArgument);
} }
if &program.owner()? != program_id { if program.get_owner() != program_id {
ic_logger_msg!(log_collector, "Program account not owned by loader"); ic_logger_msg!(log_collector, "Program account not owned by loader");
return Err(InstructionError::IncorrectProgramId); return Err(InstructionError::IncorrectProgramId);
} }
if let UpgradeableLoaderState::Program { if let UpgradeableLoaderState::Program {
programdata_address, programdata_address,
} = program.state()? } = program.get_state()?
{ {
if programdata_address != *programdata.unsigned_key() { if programdata_address != programdata_key {
ic_logger_msg!(log_collector, "Program and ProgramData account mismatch"); ic_logger_msg!(log_collector, "Program and ProgramData account mismatch");
return Err(InstructionError::InvalidArgument); return Err(InstructionError::InvalidArgument);
} }
@ -715,17 +715,19 @@ fn process_loader_upgradeable_instruction(
ic_logger_msg!(log_collector, "Invalid Program account"); ic_logger_msg!(log_collector, "Invalid Program account");
return Err(InstructionError::InvalidAccountData); return Err(InstructionError::InvalidAccountData);
} }
let new_program_id = *program.get_key();
let new_program_id = *program.unsigned_key(); drop(program);
// Verify Buffer account // Verify Buffer account
if let UpgradeableLoaderState::Buffer { authority_address } = buffer.state()? { let buffer =
if authority_address != Some(*authority.unsigned_key()) { instruction_context.try_borrow_instruction_account(transaction_context, 2)?;
if let UpgradeableLoaderState::Buffer { authority_address } = buffer.get_state()? {
if authority_address != authority_key {
ic_logger_msg!(log_collector, "Buffer and upgrade authority don't match"); ic_logger_msg!(log_collector, "Buffer and upgrade authority don't match");
return Err(InstructionError::IncorrectAuthority); return Err(InstructionError::IncorrectAuthority);
} }
if authority.signer_key().is_none() { if !instruction_context.is_signer(first_instruction_account.saturating_add(6))? {
ic_logger_msg!(log_collector, "Upgrade authority did not sign"); ic_logger_msg!(log_collector, "Upgrade authority did not sign");
return Err(InstructionError::MissingRequiredSignature); return Err(InstructionError::MissingRequiredSignature);
} }
@ -733,26 +735,31 @@ fn process_loader_upgradeable_instruction(
ic_logger_msg!(log_collector, "Invalid Buffer account"); ic_logger_msg!(log_collector, "Invalid Buffer account");
return Err(InstructionError::InvalidArgument); return Err(InstructionError::InvalidArgument);
} }
let buffer_lamports = buffer.get_lamports();
let buffer_data_offset = UpgradeableLoaderState::buffer_data_offset()?; let buffer_data_offset = UpgradeableLoaderState::buffer_data_offset()?;
let buffer_data_len = buffer.data_len()?.saturating_sub(buffer_data_offset); let buffer_data_len = buffer.get_data().len().saturating_sub(buffer_data_offset);
let programdata_data_offset = UpgradeableLoaderState::programdata_data_offset()?; if buffer.get_data().len() < UpgradeableLoaderState::buffer_data_offset()?
let programdata_balance_required = 1.max(rent.minimum_balance(programdata.data_len()?));
if buffer.data_len()? < UpgradeableLoaderState::buffer_data_offset()?
|| buffer_data_len == 0 || buffer_data_len == 0
{ {
ic_logger_msg!(log_collector, "Buffer account too small"); ic_logger_msg!(log_collector, "Buffer account too small");
return Err(InstructionError::InvalidAccountData); return Err(InstructionError::InvalidAccountData);
} }
drop(buffer);
// Verify ProgramData account // Verify ProgramData account
if programdata.data_len()? < UpgradeableLoaderState::programdata_len(buffer_data_len)? { let programdata =
instruction_context.try_borrow_instruction_account(transaction_context, 0)?;
let programdata_data_offset = UpgradeableLoaderState::programdata_data_offset()?;
let programdata_balance_required =
1.max(rent.minimum_balance(programdata.get_data().len()));
if programdata.get_data().len()
< UpgradeableLoaderState::programdata_len(buffer_data_len)?
{
ic_logger_msg!(log_collector, "ProgramData account not large enough"); ic_logger_msg!(log_collector, "ProgramData account not large enough");
return Err(InstructionError::AccountDataTooSmall); return Err(InstructionError::AccountDataTooSmall);
} }
if programdata.lamports()?.saturating_add(buffer.lamports()?) if programdata.get_lamports().saturating_add(buffer_lamports)
< programdata_balance_required < programdata_balance_required
{ {
ic_logger_msg!( ic_logger_msg!(
@ -764,17 +771,17 @@ fn process_loader_upgradeable_instruction(
if let UpgradeableLoaderState::ProgramData { if let UpgradeableLoaderState::ProgramData {
slot: _, slot: _,
upgrade_authority_address, upgrade_authority_address,
} = programdata.state()? } = programdata.get_state()?
{ {
if upgrade_authority_address.is_none() { if upgrade_authority_address.is_none() {
ic_logger_msg!(log_collector, "Program not upgradeable"); ic_logger_msg!(log_collector, "Program not upgradeable");
return Err(InstructionError::Immutable); return Err(InstructionError::Immutable);
} }
if upgrade_authority_address != Some(*authority.unsigned_key()) { if upgrade_authority_address != authority_key {
ic_logger_msg!(log_collector, "Incorrect upgrade authority provided"); ic_logger_msg!(log_collector, "Incorrect upgrade authority provided");
return Err(InstructionError::IncorrectAuthority); return Err(InstructionError::IncorrectAuthority);
} }
if authority.signer_key().is_none() { if !instruction_context.is_signer(first_instruction_account.saturating_add(6))? {
ic_logger_msg!(log_collector, "Upgrade authority did not sign"); ic_logger_msg!(log_collector, "Upgrade authority did not sign");
return Err(InstructionError::MissingRequiredSignature); return Err(InstructionError::MissingRequiredSignature);
} }
@ -782,6 +789,7 @@ fn process_loader_upgradeable_instruction(
ic_logger_msg!(log_collector, "Invalid ProgramData account"); ic_logger_msg!(log_collector, "Invalid ProgramData account");
return Err(InstructionError::InvalidAccountData); return Err(InstructionError::InvalidAccountData);
} }
drop(programdata);
// Load and verify the program bits // Load and verify the program bits
let executor = create_executor( let executor = create_executor(
@ -793,77 +801,76 @@ fn process_loader_upgradeable_instruction(
)?; )?;
invoke_context.update_executor(&new_program_id, executor); invoke_context.update_executor(&new_program_id, executor);
let keyed_accounts = invoke_context.get_keyed_accounts()?; let transaction_context = &invoke_context.transaction_context;
let programdata = keyed_account_at_index(keyed_accounts, first_instruction_account)?; let instruction_context = transaction_context.get_current_instruction_context()?;
let buffer = keyed_account_at_index(
keyed_accounts,
first_instruction_account.saturating_add(2),
)?;
let spill = keyed_account_at_index(
keyed_accounts,
first_instruction_account.saturating_add(3),
)?;
let authority = keyed_account_at_index(
keyed_accounts,
first_instruction_account.saturating_add(6),
)?;
// Update the ProgramData account, record the upgraded data, and zero // Update the ProgramData account, record the upgraded data, and zero
// the rest // the rest
let mut programdata =
instruction_context.try_borrow_instruction_account(transaction_context, 0)?;
{
programdata.set_state(&UpgradeableLoaderState::ProgramData { programdata.set_state(&UpgradeableLoaderState::ProgramData {
slot: clock.slot, slot: clock.slot,
upgrade_authority_address: Some(*authority.unsigned_key()), upgrade_authority_address: authority_key,
})?; })?;
programdata let dst_slice = programdata
.try_account_ref_mut()? .get_data_mut()
.data_as_mut_slice()
.get_mut( .get_mut(
programdata_data_offset programdata_data_offset
..programdata_data_offset.saturating_add(buffer_data_len), ..programdata_data_offset.saturating_add(buffer_data_len),
) )
.ok_or(InstructionError::AccountDataTooSmall)? .ok_or(InstructionError::AccountDataTooSmall)?;
.copy_from_slice( let buffer =
buffer instruction_context.try_borrow_instruction_account(transaction_context, 2)?;
.try_account_ref()? let src_slice = buffer
.data() .get_data()
.get(buffer_data_offset..) .get(buffer_data_offset..)
.ok_or(InstructionError::AccountDataTooSmall)?, .ok_or(InstructionError::AccountDataTooSmall)?;
); dst_slice.copy_from_slice(src_slice);
}
programdata programdata
.try_account_ref_mut()? .get_data_mut()
.data_as_mut_slice()
.get_mut(programdata_data_offset.saturating_add(buffer_data_len)..) .get_mut(programdata_data_offset.saturating_add(buffer_data_len)..)
.ok_or(InstructionError::AccountDataTooSmall)? .ok_or(InstructionError::AccountDataTooSmall)?
.fill(0); .fill(0);
// Fund ProgramData to rent-exemption, spill the rest // Fund ProgramData to rent-exemption, spill the rest
spill.try_account_ref_mut()?.checked_add_lamports( let programdata_lamports = programdata.get_lamports();
programdata programdata.set_lamports(programdata_balance_required);
.lamports()? drop(programdata);
.saturating_add(buffer.lamports()?)
let mut buffer =
instruction_context.try_borrow_instruction_account(transaction_context, 2)?;
buffer.set_lamports(0);
drop(buffer);
let mut spill =
instruction_context.try_borrow_instruction_account(transaction_context, 3)?;
spill.checked_add_lamports(
programdata_lamports
.saturating_add(buffer_lamports)
.saturating_sub(programdata_balance_required), .saturating_sub(programdata_balance_required),
)?; )?;
buffer.try_account_ref_mut()?.set_lamports(0);
programdata
.try_account_ref_mut()?
.set_lamports(programdata_balance_required);
ic_logger_msg!(log_collector, "Upgraded program {:?}", new_program_id); ic_logger_msg!(log_collector, "Upgraded program {:?}", new_program_id);
} }
UpgradeableLoaderInstruction::SetAuthority => { UpgradeableLoaderInstruction::SetAuthority => {
instruction_context.check_number_of_instruction_accounts(2)?; instruction_context.check_number_of_instruction_accounts(2)?;
let account = keyed_account_at_index(keyed_accounts, first_instruction_account)?; let mut account =
let present_authority = keyed_account_at_index( instruction_context.try_borrow_instruction_account(transaction_context, 0)?;
keyed_accounts, let present_authority_key = transaction_context.get_key_of_account_at_index(
first_instruction_account.saturating_add(1), instruction_context
.get_index_in_transaction(first_instruction_account.saturating_add(1))?,
)?; )?;
let new_authority = let new_authority = instruction_context
keyed_account_at_index(keyed_accounts, first_instruction_account.saturating_add(2)) .get_index_in_transaction(first_instruction_account.saturating_add(2))
.ok() .and_then(|index_in_transaction| {
.map(|account| account.unsigned_key()); transaction_context.get_key_of_account_at_index(index_in_transaction)
})
.ok();
match account.state()? { match account.get_state()? {
UpgradeableLoaderState::Buffer { authority_address } => { UpgradeableLoaderState::Buffer { authority_address } => {
if new_authority.is_none() { if new_authority.is_none() {
ic_logger_msg!(log_collector, "Buffer authority is not optional"); ic_logger_msg!(log_collector, "Buffer authority is not optional");
@ -873,11 +880,13 @@ fn process_loader_upgradeable_instruction(
ic_logger_msg!(log_collector, "Buffer is immutable"); ic_logger_msg!(log_collector, "Buffer is immutable");
return Err(InstructionError::Immutable); return Err(InstructionError::Immutable);
} }
if authority_address != Some(*present_authority.unsigned_key()) { if authority_address != Some(*present_authority_key) {
ic_logger_msg!(log_collector, "Incorrect buffer authority provided"); ic_logger_msg!(log_collector, "Incorrect buffer authority provided");
return Err(InstructionError::IncorrectAuthority); return Err(InstructionError::IncorrectAuthority);
} }
if present_authority.signer_key().is_none() { if !instruction_context
.is_signer(first_instruction_account.saturating_add(1))?
{
ic_logger_msg!(log_collector, "Buffer authority did not sign"); ic_logger_msg!(log_collector, "Buffer authority did not sign");
return Err(InstructionError::MissingRequiredSignature); return Err(InstructionError::MissingRequiredSignature);
} }
@ -893,11 +902,13 @@ fn process_loader_upgradeable_instruction(
ic_logger_msg!(log_collector, "Program not upgradeable"); ic_logger_msg!(log_collector, "Program not upgradeable");
return Err(InstructionError::Immutable); return Err(InstructionError::Immutable);
} }
if upgrade_authority_address != Some(*present_authority.unsigned_key()) { if upgrade_authority_address != Some(*present_authority_key) {
ic_logger_msg!(log_collector, "Incorrect upgrade authority provided"); ic_logger_msg!(log_collector, "Incorrect upgrade authority provided");
return Err(InstructionError::IncorrectAuthority); return Err(InstructionError::IncorrectAuthority);
} }
if present_authority.signer_key().is_none() { if !instruction_context
.is_signer(first_instruction_account.saturating_add(1))?
{
ic_logger_msg!(log_collector, "Upgrade authority did not sign"); ic_logger_msg!(log_collector, "Upgrade authority did not sign");
return Err(InstructionError::MissingRequiredSignature); return Err(InstructionError::MissingRequiredSignature);
} }
@ -916,34 +927,33 @@ fn process_loader_upgradeable_instruction(
} }
UpgradeableLoaderInstruction::Close => { UpgradeableLoaderInstruction::Close => {
instruction_context.check_number_of_instruction_accounts(2)?; instruction_context.check_number_of_instruction_accounts(2)?;
let close_account = keyed_account_at_index(keyed_accounts, first_instruction_account)?; if instruction_context.get_index_in_transaction(first_instruction_account)?
let recipient_account = keyed_account_at_index( == instruction_context
keyed_accounts, .get_index_in_transaction(first_instruction_account.saturating_add(1))?
first_instruction_account.saturating_add(1), {
)?;
if close_account.unsigned_key() == recipient_account.unsigned_key() {
ic_logger_msg!( ic_logger_msg!(
log_collector, log_collector,
"Recipient is the same as the account being closed" "Recipient is the same as the account being closed"
); );
return Err(InstructionError::InvalidArgument); return Err(InstructionError::InvalidArgument);
} }
let mut close_account =
match close_account.state()? { instruction_context.try_borrow_instruction_account(transaction_context, 0)?;
let close_key = *close_account.get_key();
match close_account.get_state()? {
UpgradeableLoaderState::Uninitialized => { UpgradeableLoaderState::Uninitialized => {
recipient_account let close_lamports = close_account.get_lamports();
.try_account_ref_mut()? close_account.set_lamports(0);
.checked_add_lamports(close_account.lamports()?)?; drop(close_account);
close_account.try_account_ref_mut()?.set_lamports(0); let mut recipient_account = instruction_context
.try_borrow_instruction_account(transaction_context, 1)?;
recipient_account.checked_add_lamports(close_lamports)?;
ic_logger_msg!( ic_logger_msg!(log_collector, "Closed Uninitialized {}", close_key);
log_collector,
"Closed Uninitialized {}",
close_account.unsigned_key()
);
} }
UpgradeableLoaderState::Buffer { authority_address } => { UpgradeableLoaderState::Buffer { authority_address } => {
instruction_context.check_number_of_instruction_accounts(3)?; instruction_context.check_number_of_instruction_accounts(3)?;
drop(close_account);
common_close_account( common_close_account(
&authority_address, &authority_address,
transaction_context, transaction_context,
@ -951,36 +961,32 @@ fn process_loader_upgradeable_instruction(
&log_collector, &log_collector,
)?; )?;
ic_logger_msg!( ic_logger_msg!(log_collector, "Closed Buffer {}", close_key);
log_collector,
"Closed Buffer {}",
close_account.unsigned_key()
);
} }
UpgradeableLoaderState::ProgramData { UpgradeableLoaderState::ProgramData {
slot: _, slot: _,
upgrade_authority_address: authority_address, upgrade_authority_address: authority_address,
} => { } => {
instruction_context.check_number_of_instruction_accounts(4)?; instruction_context.check_number_of_instruction_accounts(4)?;
let program_account = keyed_account_at_index( drop(close_account);
keyed_accounts, let program_account = instruction_context
first_instruction_account.saturating_add(3), .try_borrow_instruction_account(transaction_context, 3)?;
)?; let program_key = *program_account.get_key();
if !program_account.is_writable() { if !program_account.is_writable() {
ic_logger_msg!(log_collector, "Program account is not writable"); ic_logger_msg!(log_collector, "Program account is not writable");
return Err(InstructionError::InvalidArgument); return Err(InstructionError::InvalidArgument);
} }
if &program_account.owner()? != program_id { if program_account.get_owner() != program_id {
ic_logger_msg!(log_collector, "Program account not owned by loader"); ic_logger_msg!(log_collector, "Program account not owned by loader");
return Err(InstructionError::IncorrectProgramId); return Err(InstructionError::IncorrectProgramId);
} }
match program_account.state()? { match program_account.get_state()? {
UpgradeableLoaderState::Program { UpgradeableLoaderState::Program {
programdata_address, programdata_address,
} => { } => {
if programdata_address != *close_account.unsigned_key() { if programdata_address != close_key {
ic_logger_msg!( ic_logger_msg!(
log_collector, log_collector,
"ProgramData account does not match ProgramData account" "ProgramData account does not match ProgramData account"
@ -988,6 +994,7 @@ fn process_loader_upgradeable_instruction(
return Err(InstructionError::InvalidArgument); return Err(InstructionError::InvalidArgument);
} }
drop(program_account);
common_close_account( common_close_account(
&authority_address, &authority_address,
transaction_context, transaction_context,
@ -1001,11 +1008,7 @@ fn process_loader_upgradeable_instruction(
} }
} }
ic_logger_msg!( ic_logger_msg!(log_collector, "Closed Program {}", program_key);
log_collector,
"Closed Program {}",
program_account.unsigned_key()
);
} }
_ => { _ => {
ic_logger_msg!(log_collector, "Account does not support closing"); ic_logger_msg!(log_collector, "Account does not support closing");
@ -1062,18 +1065,19 @@ fn process_loader_instruction(
let instruction_context = transaction_context.get_current_instruction_context()?; let instruction_context = transaction_context.get_current_instruction_context()?;
let instruction_data = instruction_context.get_instruction_data(); let instruction_data = instruction_context.get_instruction_data();
let program_id = instruction_context.get_program_key(transaction_context)?; let program_id = instruction_context.get_program_key(transaction_context)?;
let keyed_accounts = invoke_context.get_keyed_accounts()?; let program = instruction_context.try_borrow_instruction_account(transaction_context, 0)?;
let program = keyed_account_at_index(keyed_accounts, first_instruction_account)?; if program.get_owner() != program_id {
if program.owner()? != *program_id {
ic_msg!( ic_msg!(
invoke_context, invoke_context,
"Executable account not owned by the BPF loader" "Executable account not owned by the BPF loader"
); );
return Err(InstructionError::IncorrectProgramId); return Err(InstructionError::IncorrectProgramId);
} }
let is_program_signer = program.is_signer();
drop(program);
match limited_deserialize(instruction_data)? { match limited_deserialize(instruction_data)? {
LoaderInstruction::Write { offset, bytes } => { LoaderInstruction::Write { offset, bytes } => {
if program.signer_key().is_none() { if !is_program_signer {
ic_msg!(invoke_context, "Program account did not sign"); ic_msg!(invoke_context, "Program account did not sign");
return Err(InstructionError::MissingRequiredSignature); return Err(InstructionError::MissingRequiredSignature);
} }
@ -1085,22 +1089,19 @@ fn process_loader_instruction(
)?; )?;
} }
LoaderInstruction::Finalize => { LoaderInstruction::Finalize => {
if program.signer_key().is_none() { if !is_program_signer {
ic_msg!(invoke_context, "key[0] did not sign the transaction"); ic_msg!(invoke_context, "key[0] did not sign the transaction");
return Err(InstructionError::MissingRequiredSignature); return Err(InstructionError::MissingRequiredSignature);
} }
let executor = let executor =
create_executor(first_instruction_account, 0, invoke_context, use_jit, true)?; create_executor(first_instruction_account, 0, invoke_context, use_jit, true)?;
let keyed_accounts = invoke_context.get_keyed_accounts()?; let transaction_context = &invoke_context.transaction_context;
let program = keyed_account_at_index(keyed_accounts, first_instruction_account)?; let instruction_context = transaction_context.get_current_instruction_context()?;
invoke_context.update_executor(program.unsigned_key(), executor); let mut program =
program.try_account_ref_mut()?.set_executable(true); instruction_context.try_borrow_instruction_account(transaction_context, 0)?;
ic_msg!( invoke_context.update_executor(program.get_key(), executor);
invoke_context, program.set_executable(true);
"Finalized account {:?}", ic_msg!(invoke_context, "Finalized account {:?}", program.get_key());
program.unsigned_key()
);
} }
} }
@ -1281,6 +1282,7 @@ mod tests {
solana_sdk::{ solana_sdk::{
account::{ account::{
create_account_shared_data_for_test as create_account_for_test, AccountSharedData, create_account_shared_data_for_test as create_account_for_test, AccountSharedData,
ReadableAccount, WritableAccount,
}, },
account_utils::StateMut, account_utils::StateMut,
client::SyncClient, client::SyncClient,