Refactor: Make program_id always last in program chain (#20598)

* Replaces program_id field in InvokeContextStackFrame by index.

* Swaps order of program account and programdata account.

* Removes program_id parameter from InvokeContext::push().
This commit is contained in:
Alexander Meißner 2021-10-13 08:58:20 +02:00 committed by GitHub
parent c231cfe235
commit 1d813ea078
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 114 additions and 141 deletions

View File

@ -485,7 +485,7 @@ impl InstructionProcessor {
); );
return Err(InstructionError::AccountNotExecutable); return Err(InstructionError::AccountNotExecutable);
} }
let mut program_indices = vec![program_account_index]; let mut program_indices = vec![];
if program_account.borrow().owner() == &bpf_loader_upgradeable::id() { if program_account.borrow().owner() == &bpf_loader_upgradeable::id() {
if let UpgradeableLoaderState::Program { if let UpgradeableLoaderState::Program {
programdata_address, programdata_address,
@ -512,6 +512,7 @@ impl InstructionProcessor {
return Err(InstructionError::MissingAccount); return Err(InstructionError::MissingAccount);
} }
} }
program_indices.push(program_account_index);
Ok((message, caller_write_privileges, program_indices)) Ok((message, caller_write_privileges, program_indices))
} }
@ -593,8 +594,6 @@ impl InstructionProcessor {
.get(0) .get(0)
.ok_or(InstructionError::GenericError)?; .ok_or(InstructionError::GenericError)?;
let program_id = instruction.program_id(&message.account_keys);
// Verify the calling program hasn't misbehaved // Verify the calling program hasn't misbehaved
invoke_context.verify_and_update(instruction, account_indices, caller_write_privileges)?; invoke_context.verify_and_update(instruction, account_indices, caller_write_privileges)?;
@ -602,13 +601,7 @@ impl InstructionProcessor {
invoke_context.set_return_data(Vec::new())?; invoke_context.set_return_data(Vec::new())?;
// Invoke callee // Invoke callee
invoke_context.push( invoke_context.push(message, instruction, program_indices, Some(account_indices))?;
program_id,
message,
instruction,
program_indices,
Some(account_indices),
)?;
let mut instruction_processor = InstructionProcessor::default(); let mut instruction_processor = InstructionProcessor::default();
for (program_id, process_instruction) in invoke_context.get_programs().iter() { for (program_id, process_instruction) in invoke_context.get_programs().iter() {

View File

@ -199,72 +199,71 @@ fn process_instruction_common(
use_jit: bool, use_jit: bool,
) -> Result<(), InstructionError> { ) -> Result<(), InstructionError> {
let logger = invoke_context.get_logger(); let logger = invoke_context.get_logger();
let keyed_accounts = invoke_context.get_keyed_accounts()?; let program_id = invoke_context.get_caller()?;
let keyed_accounts = invoke_context.get_keyed_accounts()?;
let first_account = keyed_account_at_index(keyed_accounts, first_instruction_account)?; let first_account = keyed_account_at_index(keyed_accounts, first_instruction_account)?;
if first_account.executable()? { let second_account = keyed_account_at_index(keyed_accounts, first_instruction_account + 1);
let (program, next_first_instruction_account) = if first_account.unsigned_key() == program_id {
(first_account, first_instruction_account)
} else if second_account
.as_ref()
.map(|keyed_account| keyed_account.unsigned_key() == program_id)
.unwrap_or(false)
{
(second_account?, first_instruction_account + 1)
} else {
if first_account.executable()? {
ic_logger_msg!(logger, "BPF loader is executable");
return Err(InstructionError::IncorrectProgramId);
}
(first_account, first_instruction_account)
};
if program.executable()? {
debug_assert_eq!( debug_assert_eq!(
first_instruction_account, first_instruction_account,
1 - (invoke_context.invoke_depth() > 1) as usize, 1 - (invoke_context.invoke_depth() > 1) as usize,
); );
let program_id = invoke_context.get_caller()?; if !check_loader_id(&program.owner()?) {
if first_account.unsigned_key() != program_id {
ic_logger_msg!(logger, "Program id mismatch");
return Err(InstructionError::IncorrectProgramId);
}
let (first_instruction_account, program_data_offset) =
if bpf_loader_upgradeable::check_id(&first_account.owner()?) {
if let UpgradeableLoaderState::Program {
programdata_address,
} = first_account.state()?
{
let programdata =
keyed_account_at_index(keyed_accounts, first_instruction_account + 1)?;
if programdata_address != *programdata.unsigned_key() {
ic_logger_msg!(
logger,
"Wrong ProgramData account for this Program account"
);
return Err(InstructionError::InvalidArgument);
}
if !matches!(
programdata.state()?,
UpgradeableLoaderState::ProgramData {
slot: _,
upgrade_authority_address: _,
}
) {
ic_logger_msg!(logger, "Program has been closed");
return Err(InstructionError::InvalidAccountData);
}
(
first_instruction_account + 1,
UpgradeableLoaderState::programdata_data_offset()?,
)
} else {
ic_logger_msg!(logger, "Invalid Program account");
return Err(InstructionError::InvalidAccountData);
}
} else {
(first_instruction_account, 0)
};
let keyed_accounts = invoke_context.get_keyed_accounts()?;
let program = keyed_account_at_index(keyed_accounts, first_instruction_account)?;
let loader_id = &program.owner()?;
if !check_loader_id(loader_id) {
ic_logger_msg!(logger, "Executable account not owned by the BPF loader"); ic_logger_msg!(logger, "Executable account not owned by the BPF loader");
return Err(InstructionError::IncorrectProgramId); return Err(InstructionError::IncorrectProgramId);
} }
let program_data_offset = if bpf_loader_upgradeable::check_id(&program.owner()?) {
if let UpgradeableLoaderState::Program {
programdata_address,
} = program.state()?
{
if programdata_address != *first_account.unsigned_key() {
ic_logger_msg!(logger, "Wrong ProgramData account for this Program account");
return Err(InstructionError::InvalidArgument);
}
if !matches!(
first_account.state()?,
UpgradeableLoaderState::ProgramData {
slot: _,
upgrade_authority_address: _,
}
) {
ic_logger_msg!(logger, "Program has been closed");
return Err(InstructionError::InvalidAccountData);
}
UpgradeableLoaderState::programdata_data_offset()?
} else {
ic_logger_msg!(logger, "Invalid Program account");
return Err(InstructionError::InvalidAccountData);
}
} else {
0
};
let executor = match invoke_context.get_executor(program_id) { let executor = match invoke_context.get_executor(program_id) {
Some(executor) => executor, Some(executor) => executor,
None => { None => {
let executor = create_executor( let executor = create_executor(
first_instruction_account, next_first_instruction_account,
program_data_offset, program_data_offset,
invoke_context, invoke_context,
use_jit, use_jit,
@ -275,37 +274,32 @@ fn process_instruction_common(
} }
}; };
executor.execute( executor.execute(
first_instruction_account, next_first_instruction_account,
instruction_data, instruction_data,
invoke_context, invoke_context,
use_jit, use_jit,
)? )
} else { } else {
debug_assert_eq!(first_instruction_account, 1); debug_assert_eq!(first_instruction_account, 1);
let program_id = invoke_context.get_caller()?;
if !check_loader_id(program_id) {
ic_logger_msg!(logger, "Invalid BPF loader id");
return Err(InstructionError::IncorrectProgramId);
}
if bpf_loader_upgradeable::check_id(program_id) { if bpf_loader_upgradeable::check_id(program_id) {
process_loader_upgradeable_instruction( process_loader_upgradeable_instruction(
first_instruction_account, first_instruction_account,
instruction_data, instruction_data,
invoke_context, invoke_context,
use_jit, use_jit,
)?; )
} else { } else if bpf_loader::check_id(program_id) || bpf_loader_deprecated::check_id(program_id) {
process_loader_instruction( process_loader_instruction(
first_instruction_account, first_instruction_account,
instruction_data, instruction_data,
invoke_context, invoke_context,
use_jit, use_jit,
)?; )
} else {
ic_logger_msg!(logger, "Invalid BPF loader id");
Err(InstructionError::IncorrectProgramId)
} }
} }
Ok(())
} }
fn process_loader_upgradeable_instruction( fn process_loader_upgradeable_instruction(
@ -3374,8 +3368,8 @@ mod tests {
// Try to invoke closed account // Try to invoke closed account
let keyed_accounts: Vec<KeyedAccountTuple> = vec![ let keyed_accounts: Vec<KeyedAccountTuple> = vec![
(false, false, &program_address, &program_account),
(false, false, &programdata_address, &programdata_account), (false, false, &programdata_address, &programdata_account),
(false, false, &program_address, &program_account),
]; ];
assert_eq!( assert_eq!(
Err(InstructionError::InvalidAccountData), Err(InstructionError::InvalidAccountData),

View File

@ -429,7 +429,7 @@ impl Accounts {
// Add loader to chain // Add loader to chain
let program_owner = *program.owner(); let program_owner = *program.owner();
account_indices.insert(0, program_account_index);
if bpf_loader_upgradeable::check_id(&program_owner) { if bpf_loader_upgradeable::check_id(&program_owner) {
// The upgradeable loader requires the derived ProgramData account // The upgradeable loader requires the derived ProgramData account
if let Ok(UpgradeableLoaderState::Program { if let Ok(UpgradeableLoaderState::Program {
@ -457,7 +457,6 @@ impl Accounts {
} }
} }
account_indices.insert(0, program_account_index);
program_id = program_owner; program_id = program_owner;
} }
Ok(account_indices) Ok(account_indices)
@ -1923,8 +1922,8 @@ mod tests {
let result = loaded_accounts[0].0.as_ref().unwrap(); let result = loaded_accounts[0].0.as_ref().unwrap();
assert_eq!(result.accounts[..2], accounts[..2]); assert_eq!(result.accounts[..2], accounts[..2]);
assert_eq!(result.accounts[result.program_indices[0][0]], accounts[5]); assert_eq!(result.accounts[result.program_indices[0][0]], accounts[5]);
assert_eq!(result.accounts[result.program_indices[0][1]], accounts[3]); assert_eq!(result.accounts[result.program_indices[0][1]], accounts[4]);
assert_eq!(result.accounts[result.program_indices[0][2]], accounts[4]); assert_eq!(result.accounts[result.program_indices[0][2]], accounts[3]);
} }
#[test] #[test]

View File

@ -116,7 +116,6 @@ impl<'a> ThisInvokeContext<'a> {
impl<'a> InvokeContext for ThisInvokeContext<'a> { impl<'a> InvokeContext for ThisInvokeContext<'a> {
fn push( fn push(
&mut self, &mut self,
key: &Pubkey,
message: &Message, message: &Message,
instruction: &CompiledInstruction, instruction: &CompiledInstruction,
program_indices: &[usize], program_indices: &[usize],
@ -126,6 +125,23 @@ impl<'a> InvokeContext for ThisInvokeContext<'a> {
return Err(InstructionError::CallDepth); return Err(InstructionError::CallDepth);
} }
if let Some(index_of_program_id) = program_indices.last() {
let program_id = &self.accounts[*index_of_program_id].0;
let contains = self
.invoke_stack
.iter()
.any(|frame| frame.program_id() == Some(program_id));
let is_last = if let Some(last_frame) = self.invoke_stack.last() {
last_frame.program_id() == Some(program_id)
} else {
false
};
if contains && !is_last {
// Reentrancy not allowed unless caller is calling itself
return Err(InstructionError::ReentrancyNotAllowed);
}
}
if self.invoke_stack.is_empty() { if self.invoke_stack.is_empty() {
self.pre_accounts = Vec::with_capacity(instruction.accounts.len()); self.pre_accounts = Vec::with_capacity(instruction.accounts.len());
let mut work = |_unique_index: usize, account_index: usize| { let mut work = |_unique_index: usize, account_index: usize| {
@ -140,17 +156,6 @@ impl<'a> InvokeContext for ThisInvokeContext<'a> {
instruction.visit_each_account(&mut work)?; instruction.visit_each_account(&mut work)?;
} }
let contains = self.invoke_stack.iter().any(|frame| frame.key == *key);
let is_last = if let Some(last_frame) = self.invoke_stack.last() {
last_frame.key == *key
} else {
false
};
if contains && !is_last {
// Reentrancy not allowed unless caller is calling itself
return Err(InstructionError::ReentrancyNotAllowed);
}
// Create the KeyedAccounts that will be passed to the program // Create the KeyedAccounts that will be passed to the program
let demote_program_write_locks = self let demote_program_write_locks = self
.feature_set .feature_set
@ -180,8 +185,9 @@ impl<'a> InvokeContext for ThisInvokeContext<'a> {
) )
})) }))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
self.invoke_stack.push(InvokeContextStackFrame::new( self.invoke_stack.push(InvokeContextStackFrame::new(
*key, program_indices.len(),
create_keyed_accounts_unified(keyed_accounts.as_slice()), create_keyed_accounts_unified(keyed_accounts.as_slice()),
)); ));
Ok(()) Ok(())
@ -259,11 +265,11 @@ impl<'a> InvokeContext for ThisInvokeContext<'a> {
write_privileges: &[bool], write_privileges: &[bool],
) -> Result<(), InstructionError> { ) -> Result<(), InstructionError> {
let do_support_realloc = self.feature_set.is_active(&do_support_realloc::id()); let do_support_realloc = self.feature_set.is_active(&do_support_realloc::id());
let stack_frame = self let program_id = self
.invoke_stack .invoke_stack
.last() .last()
.and_then(|frame| frame.program_id())
.ok_or(InstructionError::CallDepth)?; .ok_or(InstructionError::CallDepth)?;
let program_id = &stack_frame.key;
let rent = &self.rent; let rent = &self.rent;
let logger = &self.logger; let logger = &self.logger;
let accounts = &self.accounts; let accounts = &self.accounts;
@ -325,7 +331,7 @@ impl<'a> InvokeContext for ThisInvokeContext<'a> {
fn get_caller(&self) -> Result<&Pubkey, InstructionError> { fn get_caller(&self) -> Result<&Pubkey, InstructionError> {
self.invoke_stack self.invoke_stack
.last() .last()
.map(|frame| &frame.key) .and_then(|frame| frame.program_id())
.ok_or(InstructionError::CallDepth) .ok_or(InstructionError::CallDepth)
} }
fn remove_first_keyed_account(&mut self) -> Result<(), InstructionError> { fn remove_first_keyed_account(&mut self) -> Result<(), InstructionError> {
@ -549,7 +555,7 @@ impl MessageProcessor {
invoke_context.set_instruction_index(instruction_index); invoke_context.set_instruction_index(instruction_index);
let result = invoke_context let result = invoke_context
.push(program_id, message, instruction, program_indices, None) .push(message, instruction, program_indices, None)
.and_then(|_| { .and_then(|_| {
instruction_processor instruction_processor
.process_instruction(&instruction.data, &mut invoke_context)?; .process_instruction(&instruction.data, &mut invoke_context)?;
@ -693,10 +699,9 @@ mod tests {
// Check call depth increases and has a limit // Check call depth increases and has a limit
let mut depth_reached = 0; let mut depth_reached = 0;
for program_id in invoke_stack.iter() { for _ in 0..invoke_stack.len() {
if Err(InstructionError::CallDepth) if Err(InstructionError::CallDepth)
== invoke_context.push( == invoke_context.push(
program_id,
&message, &message,
&message.instructions[0], &message.instructions[0],
&[MAX_DEPTH + depth_reached], &[MAX_DEPTH + depth_reached],
@ -796,13 +801,7 @@ mod tests {
&fee_calculator, &fee_calculator,
); );
invoke_context invoke_context
.push( .push(&message, &message.instructions[0], &[0], None)
&accounts[0].0,
&message,
&message.instructions[0],
&[0],
None,
)
.unwrap(); .unwrap();
assert!(invoke_context assert!(invoke_context
.verify(&message, &message.instructions[0], &[0]) .verify(&message, &message.instructions[0], &[0])
@ -1253,13 +1252,7 @@ mod tests {
&fee_calculator, &fee_calculator,
); );
invoke_context invoke_context
.push( .push(&message, &caller_instruction, &program_indices[..1], None)
&caller_program_id,
&message,
&caller_instruction,
&program_indices[..1],
None,
)
.unwrap(); .unwrap();
// not owned account modified by the caller (before the invoke) // not owned account modified by the caller (before the invoke)
@ -1317,13 +1310,7 @@ mod tests {
Instruction::new_with_bincode(callee_program_id, &case.0, metas.clone()); Instruction::new_with_bincode(callee_program_id, &case.0, metas.clone());
let message = Message::new(&[callee_instruction], None); let message = Message::new(&[callee_instruction], None);
invoke_context invoke_context
.push( .push(&message, &caller_instruction, &program_indices[..1], None)
&caller_program_id,
&message,
&caller_instruction,
&program_indices[..1],
None,
)
.unwrap(); .unwrap();
let caller_write_privileges = message let caller_write_privileges = message
.account_keys .account_keys
@ -1410,13 +1397,7 @@ mod tests {
&fee_calculator, &fee_calculator,
); );
invoke_context invoke_context
.push( .push(&message, &caller_instruction, &program_indices, None)
&caller_program_id,
&message,
&caller_instruction,
&program_indices,
None,
)
.unwrap(); .unwrap();
// not owned account modified by the invoker // not owned account modified by the invoker
@ -1469,13 +1450,7 @@ mod tests {
Instruction::new_with_bincode(callee_program_id, &case.0, metas.clone()); Instruction::new_with_bincode(callee_program_id, &case.0, metas.clone());
let message = Message::new(&[callee_instruction.clone()], None); let message = Message::new(&[callee_instruction.clone()], None);
invoke_context invoke_context
.push( .push(&message, &caller_instruction, &program_indices, None)
&caller_program_id,
&message,
&caller_instruction,
&program_indices,
None,
)
.unwrap(); .unwrap();
assert_eq!( assert_eq!(
InstructionProcessor::native_invoke( InstructionProcessor::native_invoke(

View File

@ -31,23 +31,29 @@ pub type ProcessInstructionWithContext =
fn(usize, &[u8], &mut dyn InvokeContext) -> Result<(), InstructionError>; fn(usize, &[u8], &mut dyn InvokeContext) -> Result<(), InstructionError>;
pub struct InvokeContextStackFrame<'a> { pub struct InvokeContextStackFrame<'a> {
pub key: Pubkey, pub number_of_program_accounts: usize,
pub keyed_accounts: Vec<KeyedAccount<'a>>, pub keyed_accounts: Vec<KeyedAccount<'a>>,
pub keyed_accounts_range: std::ops::Range<usize>, pub keyed_accounts_range: std::ops::Range<usize>,
} }
impl<'a> InvokeContextStackFrame<'a> { impl<'a> InvokeContextStackFrame<'a> {
pub fn new(key: Pubkey, keyed_accounts: Vec<KeyedAccount<'a>>) -> Self { pub fn new(number_of_program_accounts: usize, keyed_accounts: Vec<KeyedAccount<'a>>) -> Self {
let keyed_accounts_range = std::ops::Range { let keyed_accounts_range = std::ops::Range {
start: 0, start: 0,
end: keyed_accounts.len(), end: keyed_accounts.len(),
}; };
Self { Self {
key, number_of_program_accounts,
keyed_accounts, keyed_accounts,
keyed_accounts_range, keyed_accounts_range,
} }
} }
pub fn program_id(&self) -> Option<&Pubkey> {
self.keyed_accounts
.get(self.number_of_program_accounts.saturating_sub(1))
.map(|keyed_account| keyed_account.unsigned_key())
}
} }
/// Invocation context passed to loaders /// Invocation context passed to loaders
@ -55,7 +61,6 @@ pub trait InvokeContext {
/// Push a stack frame onto the invocation stack /// Push a stack frame onto the invocation stack
fn push( fn push(
&mut self, &mut self,
key: &Pubkey,
message: &Message, message: &Message,
instruction: &CompiledInstruction, instruction: &CompiledInstruction,
program_indices: &[usize], program_indices: &[usize],
@ -473,9 +478,17 @@ impl<'a> MockInvokeContext<'a> {
fee_calculator: FeeCalculator::default(), fee_calculator: FeeCalculator::default(),
return_data: (Pubkey::default(), Vec::new()), return_data: (Pubkey::default(), Vec::new()),
}; };
let number_of_program_accounts = keyed_accounts
.iter()
.position(|keyed_account| keyed_account.unsigned_key() == program_id)
.unwrap_or(0)
.saturating_add(1);
invoke_context invoke_context
.invoke_stack .invoke_stack
.push(InvokeContextStackFrame::new(*program_id, keyed_accounts)); .push(InvokeContextStackFrame::new(
number_of_program_accounts,
keyed_accounts,
));
invoke_context invoke_context
} }
} }
@ -498,14 +511,13 @@ pub fn mock_set_sysvar<T: Sysvar>(
impl<'a> InvokeContext for MockInvokeContext<'a> { impl<'a> InvokeContext for MockInvokeContext<'a> {
fn push( fn push(
&mut self, &mut self,
_key: &Pubkey,
_message: &Message, _message: &Message,
_instruction: &CompiledInstruction, _instruction: &CompiledInstruction,
_program_indices: &[usize], _program_indices: &[usize],
_account_indices: Option<&[usize]>, _account_indices: Option<&[usize]>,
) -> Result<(), InstructionError> { ) -> Result<(), InstructionError> {
self.invoke_stack.push(InvokeContextStackFrame::new( self.invoke_stack.push(InvokeContextStackFrame::new(
*_key, 0,
create_keyed_accounts_unified(&[]), create_keyed_accounts_unified(&[]),
)); ));
Ok(()) Ok(())
@ -535,7 +547,7 @@ impl<'a> InvokeContext for MockInvokeContext<'a> {
fn get_caller(&self) -> Result<&Pubkey, InstructionError> { fn get_caller(&self) -> Result<&Pubkey, InstructionError> {
self.invoke_stack self.invoke_stack
.last() .last()
.map(|frame| &frame.key) .and_then(|frame| frame.program_id())
.ok_or(InstructionError::CallDepth) .ok_or(InstructionError::CallDepth)
} }
fn remove_first_keyed_account(&mut self) -> Result<(), InstructionError> { fn remove_first_keyed_account(&mut self) -> Result<(), InstructionError> {