@ -57,6 +57,7 @@ impl UserDefinedError for BPFError {}
|
||||
|
||||
pub fn create_vm<'a>(
|
||||
prog: &'a [u8],
|
||||
parameter_accounts: &'a [KeyedAccount<'a>],
|
||||
invoke_context: &'a mut dyn InvokeContext,
|
||||
) -> Result<(EbpfVm<'a, BPFError>, MemoryRegion), EbpfError<BPFError>> {
|
||||
let mut vm = EbpfVm::new(None)?;
|
||||
@ -64,7 +65,7 @@ pub fn create_vm<'a>(
|
||||
vm.set_max_instruction_count(100_000)?;
|
||||
vm.set_elf(&prog)?;
|
||||
|
||||
let heap_region = syscalls::register_syscalls(&mut vm, invoke_context)?;
|
||||
let heap_region = syscalls::register_syscalls(&mut vm, parameter_accounts, invoke_context)?;
|
||||
|
||||
Ok((vm, heap_region))
|
||||
}
|
||||
@ -182,13 +183,14 @@ pub fn process_instruction(
|
||||
)?;
|
||||
{
|
||||
let program_account = program.try_account_ref_mut()?;
|
||||
let (mut vm, heap_region) = match create_vm(&program_account.data, invoke_context) {
|
||||
Ok(info) => info,
|
||||
Err(e) => {
|
||||
warn!("Failed to create BPF VM: {}", e);
|
||||
return Err(BPFLoaderError::VirtualMachineCreationFailed.into());
|
||||
}
|
||||
};
|
||||
let (mut vm, heap_region) =
|
||||
match create_vm(&program_account.data, ¶meter_accounts, invoke_context) {
|
||||
Ok(info) => info,
|
||||
Err(e) => {
|
||||
warn!("Failed to create BPF VM: {}", e);
|
||||
return Err(BPFLoaderError::VirtualMachineCreationFailed.into());
|
||||
}
|
||||
};
|
||||
|
||||
info!("Call BPF program {}", program.unsigned_key());
|
||||
match vm.execute_program(parameter_bytes.as_slice(), &[], &[heap_region]) {
|
||||
@ -274,7 +276,6 @@ mod tests {
|
||||
&mut self,
|
||||
_message: &Message,
|
||||
_instruction: &CompiledInstruction,
|
||||
_signers: &[Pubkey],
|
||||
_accounts: &[Rc<RefCell<Account>>],
|
||||
) -> Result<(), InstructionError> {
|
||||
Ok(())
|
||||
|
@ -9,6 +9,7 @@ use solana_rbpf::{
|
||||
use solana_runtime::{builtin_programs::get_builtin_programs, message_processor::MessageProcessor};
|
||||
use solana_sdk::{
|
||||
account::Account,
|
||||
account::KeyedAccount,
|
||||
account_info::AccountInfo,
|
||||
bpf_loader,
|
||||
entrypoint::SUCCESS,
|
||||
@ -48,6 +49,8 @@ pub enum SyscallError {
|
||||
ProgramNotSupported,
|
||||
#[error("{0}")]
|
||||
InstructionError(InstructionError),
|
||||
#[error("Cross-program invocation with unauthorized signer or writable account")]
|
||||
PrivilegeEscalation,
|
||||
}
|
||||
impl From<SyscallError> for EbpfError<BPFError> {
|
||||
fn from(error: SyscallError) -> Self {
|
||||
@ -69,6 +72,7 @@ const DEFAULT_HEAP_SIZE: usize = 32 * 1024;
|
||||
|
||||
pub fn register_syscalls<'a>(
|
||||
vm: &mut EbpfVm<'a, BPFError>,
|
||||
callers_keyed_accounts: &'a [KeyedAccount<'a>],
|
||||
invoke_context: &'a mut dyn InvokeContext,
|
||||
) -> Result<MemoryRegion, EbpfError<BPFError>> {
|
||||
// Syscall function common across languages
|
||||
@ -83,12 +87,14 @@ pub fn register_syscalls<'a>(
|
||||
vm.register_syscall_with_context_ex(
|
||||
"sol_invoke_signed_c",
|
||||
Box::new(SyscallProcessSolInstructionC {
|
||||
callers_keyed_accounts,
|
||||
invoke_context: invoke_context.clone(),
|
||||
}),
|
||||
)?;
|
||||
vm.register_syscall_with_context_ex(
|
||||
"sol_invoke_signed_rust",
|
||||
Box::new(SyscallProcessInstructionRust {
|
||||
callers_keyed_accounts,
|
||||
invoke_context: invoke_context.clone(),
|
||||
}),
|
||||
)?;
|
||||
@ -301,6 +307,7 @@ pub type TranslatedAccounts<'a> = (Vec<Rc<RefCell<Account>>>, Vec<(&'a mut u64,
|
||||
/// Implemented by language specific data structure translators
|
||||
trait SyscallProcessInstruction<'a> {
|
||||
fn get_context_mut(&self) -> Result<RefMut<&'a mut dyn InvokeContext>, EbpfError<BPFError>>;
|
||||
fn get_callers_keyed_accounts(&self) -> &'a [KeyedAccount<'a>];
|
||||
fn translate_instruction(
|
||||
&self,
|
||||
addr: u64,
|
||||
@ -325,6 +332,7 @@ trait SyscallProcessInstruction<'a> {
|
||||
|
||||
/// Cross-program invocation called from Rust
|
||||
pub struct SyscallProcessInstructionRust<'a> {
|
||||
callers_keyed_accounts: &'a [KeyedAccount<'a>],
|
||||
invoke_context: Rc<RefCell<&'a mut dyn InvokeContext>>,
|
||||
}
|
||||
impl<'a> SyscallProcessInstruction<'a> for SyscallProcessInstructionRust<'a> {
|
||||
@ -333,6 +341,9 @@ impl<'a> SyscallProcessInstruction<'a> for SyscallProcessInstructionRust<'a> {
|
||||
.try_borrow_mut()
|
||||
.map_err(|_| SyscallError::InvokeContextBorrowFailed.into())
|
||||
}
|
||||
fn get_callers_keyed_accounts(&self) -> &'a [KeyedAccount<'a>] {
|
||||
self.callers_keyed_accounts
|
||||
}
|
||||
fn translate_instruction(
|
||||
&self,
|
||||
addr: u64,
|
||||
@ -519,6 +530,7 @@ struct SolSignerSeedsC {
|
||||
|
||||
/// Cross-program invocation called from C
|
||||
pub struct SyscallProcessSolInstructionC<'a> {
|
||||
callers_keyed_accounts: &'a [KeyedAccount<'a>],
|
||||
invoke_context: Rc<RefCell<&'a mut dyn InvokeContext>>,
|
||||
}
|
||||
impl<'a> SyscallProcessInstruction<'a> for SyscallProcessSolInstructionC<'a> {
|
||||
@ -527,7 +539,9 @@ impl<'a> SyscallProcessInstruction<'a> for SyscallProcessSolInstructionC<'a> {
|
||||
.try_borrow_mut()
|
||||
.map_err(|_| SyscallError::InvokeContextBorrowFailed.into())
|
||||
}
|
||||
|
||||
fn get_callers_keyed_accounts(&self) -> &'a [KeyedAccount<'a>] {
|
||||
self.callers_keyed_accounts
|
||||
}
|
||||
fn translate_instruction(
|
||||
&self,
|
||||
addr: u64,
|
||||
@ -673,6 +687,44 @@ impl<'a> SyscallObject<BPFError> for SyscallProcessSolInstructionC<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
fn verify_instruction<'a>(
|
||||
syscall: &dyn SyscallProcessInstruction<'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::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::PrivilegeEscalation.into());
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Call process instruction, common to both Rust and C
|
||||
fn call<'a>(
|
||||
syscall: &mut dyn SyscallProcessInstruction<'a>,
|
||||
@ -689,12 +741,19 @@ fn call<'a>(
|
||||
// Translate data passed from the VM
|
||||
|
||||
let instruction = syscall.translate_instruction(instruction_addr, ro_regions)?;
|
||||
let message = Message::new_with_payer(&[instruction], None);
|
||||
let callee_program_id_index = message.instructions[0].program_id_index as usize;
|
||||
let callee_program_id = message.account_keys[callee_program_id_index];
|
||||
let caller_program_id = invoke_context
|
||||
.get_caller()
|
||||
.map_err(SyscallError::InstructionError)?;
|
||||
let signers = syscall.translate_signers(
|
||||
caller_program_id,
|
||||
signers_seeds_addr,
|
||||
signers_seeds_len as usize,
|
||||
ro_regions,
|
||||
)?;
|
||||
verify_instruction(syscall, &instruction, &signers)?;
|
||||
let message = Message::new_with_payer(&[instruction], None);
|
||||
let callee_program_id_index = message.instructions[0].program_id_index as usize;
|
||||
let callee_program_id = message.account_keys[callee_program_id_index];
|
||||
let (accounts, refs) = syscall.translate_accounts(
|
||||
&message,
|
||||
account_infos_addr,
|
||||
@ -702,12 +761,6 @@ fn call<'a>(
|
||||
ro_regions,
|
||||
rw_regions,
|
||||
)?;
|
||||
let signers = syscall.translate_signers(
|
||||
caller_program_id,
|
||||
signers_seeds_addr,
|
||||
signers_seeds_len as usize,
|
||||
ro_regions,
|
||||
)?;
|
||||
|
||||
// Process instruction
|
||||
|
||||
@ -725,7 +778,6 @@ fn call<'a>(
|
||||
&message,
|
||||
&executable_accounts,
|
||||
&accounts,
|
||||
&signers,
|
||||
*(&mut *invoke_context),
|
||||
) {
|
||||
Ok(()) => (),
|
||||
|
Reference in New Issue
Block a user