Clean invoke in prep for native invoke (#14055)
This commit is contained in:
parent
aa2751e614
commit
83fda2d972
|
@ -1245,62 +1245,6 @@ impl<'a> SyscallObject<BPFError> for SyscallInvokeSignedC<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn verify_instruction<'a>(
|
|
||||||
syscall: &dyn SyscallInvokeSigned<'a>,
|
|
||||||
instruction: &Instruction,
|
|
||||||
signers: &[Pubkey],
|
|
||||||
) -> Result<(), EbpfError<BPFError>> {
|
|
||||||
let callers_keyed_accounts = syscall.get_callers_keyed_accounts();
|
|
||||||
|
|
||||||
// Check for privilege escalation
|
|
||||||
for account in instruction.accounts.iter() {
|
|
||||||
let keyed_account = callers_keyed_accounts
|
|
||||||
.iter()
|
|
||||||
.find_map(|keyed_account| {
|
|
||||||
if &account.pubkey == keyed_account.unsigned_key() {
|
|
||||||
Some(keyed_account)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.ok_or(SyscallError::InstructionError(
|
|
||||||
InstructionError::MissingAccount,
|
|
||||||
))?;
|
|
||||||
// Readonly account cannot become writable
|
|
||||||
if account.is_writable && !keyed_account.is_writable() {
|
|
||||||
return Err(
|
|
||||||
SyscallError::InstructionError(InstructionError::PrivilegeEscalation).into(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if account.is_signer && // If message indicates account is signed
|
|
||||||
!( // one of the following needs to be true:
|
|
||||||
keyed_account.signer_key().is_some() // Signed in the parent instruction
|
|
||||||
|| signers.contains(&account.pubkey) // Signed by the program
|
|
||||||
) {
|
|
||||||
return Err(
|
|
||||||
SyscallError::InstructionError(InstructionError::PrivilegeEscalation).into(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// validate the caller has access to the program account
|
|
||||||
let _ = callers_keyed_accounts
|
|
||||||
.iter()
|
|
||||||
.find_map(|keyed_account| {
|
|
||||||
if &instruction.program_id == keyed_account.unsigned_key() {
|
|
||||||
Some(keyed_account)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.ok_or(SyscallError::InstructionError(
|
|
||||||
InstructionError::MissingAccount,
|
|
||||||
))?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Call process instruction, common to both Rust and C
|
/// Call process instruction, common to both Rust and C
|
||||||
fn call<'a>(
|
fn call<'a>(
|
||||||
syscall: &mut dyn SyscallInvokeSigned<'a>,
|
syscall: &mut dyn SyscallInvokeSigned<'a>,
|
||||||
|
@ -1316,7 +1260,7 @@ fn call<'a>(
|
||||||
.get_compute_meter()
|
.get_compute_meter()
|
||||||
.consume(invoke_context.get_bpf_compute_budget().invoke_units)?;
|
.consume(invoke_context.get_bpf_compute_budget().invoke_units)?;
|
||||||
|
|
||||||
// Translate data passed from the VM
|
// Translate and verify caller's data
|
||||||
|
|
||||||
let instruction = syscall.translate_instruction(instruction_addr, &memory_mapping)?;
|
let instruction = syscall.translate_instruction(instruction_addr, &memory_mapping)?;
|
||||||
let caller_program_id = invoke_context
|
let caller_program_id = invoke_context
|
||||||
|
@ -1328,10 +1272,13 @@ fn call<'a>(
|
||||||
signers_seeds_len,
|
signers_seeds_len,
|
||||||
memory_mapping,
|
memory_mapping,
|
||||||
)?;
|
)?;
|
||||||
verify_instruction(syscall, &instruction, &signers)?;
|
let keyed_account_refs = syscall
|
||||||
let message = Message::new(&[instruction.clone()], None);
|
.get_callers_keyed_accounts()
|
||||||
let callee_program_id_index = message.instructions[0].program_id_index as usize;
|
.iter()
|
||||||
let callee_program_id = message.account_keys[callee_program_id_index];
|
.collect::<Vec<&KeyedAccount>>();
|
||||||
|
let (message, callee_program_id, callee_program_id_index) =
|
||||||
|
MessageProcessor::create_message(&instruction, &keyed_account_refs, &signers)
|
||||||
|
.map_err(SyscallError::InstructionError)?;
|
||||||
let (accounts, account_refs) = syscall.translate_accounts(
|
let (accounts, account_refs) = syscall.translate_accounts(
|
||||||
&message,
|
&message,
|
||||||
account_infos_addr,
|
account_infos_addr,
|
||||||
|
@ -1339,11 +1286,16 @@ fn call<'a>(
|
||||||
memory_mapping,
|
memory_mapping,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
invoke_context.record_instruction(&instruction);
|
|
||||||
|
|
||||||
// Process instruction
|
// Process instruction
|
||||||
|
|
||||||
let program_account = (*accounts[callee_program_id_index]).clone();
|
invoke_context.record_instruction(&instruction);
|
||||||
|
let program_account =
|
||||||
|
(**accounts
|
||||||
|
.get(callee_program_id_index)
|
||||||
|
.ok_or(SyscallError::InstructionError(
|
||||||
|
InstructionError::MissingAccount,
|
||||||
|
))?)
|
||||||
|
.clone();
|
||||||
if !program_account.borrow().executable {
|
if !program_account.borrow().executable {
|
||||||
return Err(SyscallError::InstructionError(InstructionError::AccountNotExecutable).into());
|
return Err(SyscallError::InstructionError(InstructionError::AccountNotExecutable).into());
|
||||||
}
|
}
|
||||||
|
@ -1363,7 +1315,7 @@ fn call<'a>(
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy results back into caller's AccountInfos
|
// Copy results back to caller
|
||||||
|
|
||||||
for (i, (account, account_ref)) in accounts.iter().zip(account_refs).enumerate() {
|
for (i, (account, account_ref)) in accounts.iter().zip(account_refs).enumerate() {
|
||||||
let account = account.borrow();
|
let account = account.borrow();
|
||||||
|
@ -1375,7 +1327,7 @@ fn call<'a>(
|
||||||
*account_ref.serialized_len_ptr = account.data.len() as u64;
|
*account_ref.serialized_len_ptr = account.data.len() as u64;
|
||||||
if !account_ref.data.is_empty() {
|
if !account_ref.data.is_empty() {
|
||||||
// Only support for `CreateAccount` at this time.
|
// Only support for `CreateAccount` at this time.
|
||||||
// Need a way to limit total realloc size accross multiple CPI calls
|
// Need a way to limit total realloc size across multiple CPI calls
|
||||||
return Err(
|
return Err(
|
||||||
SyscallError::InstructionError(InstructionError::InvalidRealloc).into(),
|
SyscallError::InstructionError(InstructionError::InvalidRealloc).into(),
|
||||||
);
|
);
|
||||||
|
|
|
@ -490,6 +490,106 @@ impl MessageProcessor {
|
||||||
Err(InstructionError::UnsupportedProgramId)
|
Err(InstructionError::UnsupportedProgramId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Verify instruction against the caller
|
||||||
|
pub fn verify_instruction(
|
||||||
|
keyed_accounts: &[&KeyedAccount],
|
||||||
|
instruction: &Instruction,
|
||||||
|
signers: &[Pubkey],
|
||||||
|
) -> Result<(), InstructionError> {
|
||||||
|
// Check for privilege escalation
|
||||||
|
for account in instruction.accounts.iter() {
|
||||||
|
let keyed_account = keyed_accounts
|
||||||
|
.iter()
|
||||||
|
.find_map(|keyed_account| {
|
||||||
|
if &account.pubkey == keyed_account.unsigned_key() {
|
||||||
|
Some(keyed_account)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.ok_or(InstructionError::MissingAccount)?;
|
||||||
|
// Readonly account cannot become writable
|
||||||
|
if account.is_writable && !keyed_account.is_writable() {
|
||||||
|
return Err(InstructionError::PrivilegeEscalation);
|
||||||
|
}
|
||||||
|
|
||||||
|
if account.is_signer && // If message indicates account is signed
|
||||||
|
!( // one of the following needs to be true:
|
||||||
|
keyed_account.signer_key().is_some() // Signed in the parent instruction
|
||||||
|
|| signers.contains(&account.pubkey) // Signed by the program
|
||||||
|
) {
|
||||||
|
return Err(InstructionError::PrivilegeEscalation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// validate the caller has access to the program account
|
||||||
|
let _ = keyed_accounts
|
||||||
|
.iter()
|
||||||
|
.find_map(|keyed_account| {
|
||||||
|
if &instruction.program_id == keyed_account.unsigned_key() {
|
||||||
|
Some(keyed_account)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.ok_or(InstructionError::MissingAccount)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create_message(
|
||||||
|
instruction: &Instruction,
|
||||||
|
keyed_accounts: &[&KeyedAccount],
|
||||||
|
signers: &[Pubkey],
|
||||||
|
) -> Result<(Message, Pubkey, usize), InstructionError> {
|
||||||
|
// Check for privilege escalation
|
||||||
|
for account in instruction.accounts.iter() {
|
||||||
|
let keyed_account = keyed_accounts
|
||||||
|
.iter()
|
||||||
|
.find_map(|keyed_account| {
|
||||||
|
if &account.pubkey == keyed_account.unsigned_key() {
|
||||||
|
Some(keyed_account)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.ok_or(InstructionError::MissingAccount)?;
|
||||||
|
// Readonly account cannot become writable
|
||||||
|
if account.is_writable && !keyed_account.is_writable() {
|
||||||
|
return Err(InstructionError::PrivilegeEscalation);
|
||||||
|
}
|
||||||
|
|
||||||
|
if account.is_signer && // If message indicates account is signed
|
||||||
|
!( // one of the following needs to be true:
|
||||||
|
keyed_account.signer_key().is_some() // Signed in the parent instruction
|
||||||
|
|| signers.contains(&account.pubkey) // Signed by the program
|
||||||
|
) {
|
||||||
|
return Err(InstructionError::PrivilegeEscalation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// validate the caller has access to the program account
|
||||||
|
let _ = keyed_accounts
|
||||||
|
.iter()
|
||||||
|
.find_map(|keyed_account| {
|
||||||
|
if &instruction.program_id == keyed_account.unsigned_key() {
|
||||||
|
Some(keyed_account)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.ok_or(InstructionError::MissingAccount)?;
|
||||||
|
|
||||||
|
let message = Message::new(&[instruction.clone()], None);
|
||||||
|
let id = *message
|
||||||
|
.program_id(0)
|
||||||
|
.ok_or(InstructionError::MissingAccount)?;
|
||||||
|
let index = message
|
||||||
|
.program_index(0)
|
||||||
|
.ok_or(InstructionError::MissingAccount)?;
|
||||||
|
Ok((message, id, index))
|
||||||
|
}
|
||||||
|
|
||||||
/// Process a cross-program instruction
|
/// Process a cross-program instruction
|
||||||
/// This method calls the instruction's program entrypoint function
|
/// This method calls the instruction's program entrypoint function
|
||||||
pub fn process_cross_program_instruction(
|
pub fn process_cross_program_instruction(
|
||||||
|
|
Loading…
Reference in New Issue