Pipe FeatureSet though InvokeContext (#12536)

* Pipe FeatureSet though InvokeContext

* gate program size cap

* nit
This commit is contained in:
Jack May
2020-09-29 14:36:30 -07:00
committed by GitHub
parent ce98088457
commit 74fcb184b2
9 changed files with 124 additions and 180 deletions

View File

@@ -54,11 +54,11 @@ pub enum VerifierError {
InvalidRegister(usize),
}
fn check_prog_len(prog: &[u8]) -> Result<(), BPFError> {
fn check_prog_len(prog: &[u8], is_program_size_cap: bool) -> Result<(), BPFError> {
if prog.len() % ebpf::INSN_SIZE != 0 {
return Err(VerifierError::ProgramLengthNotMultiple.into());
}
if prog.len() > ebpf::PROG_MAX_SIZE {
if is_program_size_cap && prog.len() > ebpf::PROG_MAX_SIZE {
return Err(VerifierError::ProgramTooLarge(prog.len() / ebpf::INSN_SIZE).into());
}
@@ -139,8 +139,8 @@ fn check_imm_register(insn: &ebpf::Insn, insn_ptr: usize) -> Result<(), Verifier
}
#[rustfmt::skip]
pub fn check(prog: &[u8]) -> Result<(), BPFError> {
check_prog_len(prog)?;
pub fn check(prog: &[u8], is_program_size_cap: bool) -> Result<(), BPFError> {
check_prog_len(prog, is_program_size_cap)?;
let mut insn_ptr: usize = 0;
while insn_ptr * ebpf::INSN_SIZE < prog.len() {

View File

@@ -16,7 +16,10 @@ use solana_rbpf::{
memory_region::MemoryRegion,
vm::{EbpfVm, Executable, InstructionMeter},
};
use solana_runtime::process_instruction::{ComputeMeter, Executor, InvokeContext};
use solana_runtime::{
feature_set::compute_budget_config2,
process_instruction::{ComputeMeter, Executor, InvokeContext},
};
use solana_sdk::{
account::{is_executable, next_keyed_account, KeyedAccount},
bpf_loader, bpf_loader_deprecated,
@@ -78,19 +81,29 @@ macro_rules! log{
};
}
fn map_ebpf_error(
invoke_context: &mut dyn InvokeContext,
e: EbpfError<BPFError>,
) -> InstructionError {
let logger = invoke_context.get_logger();
log!(logger, "{}", e);
InstructionError::InvalidAccountData
}
pub fn create_and_cache_executor(
program: &KeyedAccount,
invoke_context: &mut dyn InvokeContext,
) -> Result<Arc<BPFExecutor>, InstructionError> {
let executable = EbpfVm::create_executable_from_elf(
&program.try_account_ref()?.data,
Some(bpf_verifier::check),
let executable = EbpfVm::create_executable_from_elf(&program.try_account_ref()?.data, None)
.map_err(|e| map_ebpf_error(invoke_context, e))?;
let (_, elf_bytes) = executable
.get_text_bytes()
.map_err(|e| map_ebpf_error(invoke_context, e))?;
bpf_verifier::check(
elf_bytes,
!invoke_context.is_feature_active(&compute_budget_config2::id()),
)
.map_err(|e| {
let logger = invoke_context.get_logger();
log!(logger, "{}", e);
InstructionError::InvalidAccountData
})?;
.map_err(|e| map_ebpf_error(invoke_context, EbpfError::UserError(e)))?;
let executor = Arc::new(BPFExecutor { executable });
invoke_context.add_executor(program.unsigned_key(), executor.clone());
Ok(executor)
@@ -271,6 +284,7 @@ mod tests {
use super::*;
use rand::Rng;
use solana_runtime::{
feature_set::FeatureSet,
message_processor::{Executors, ThisInvokeContext},
process_instruction::{ComputeBudget, Logger, ProcessInstruction},
};
@@ -313,6 +327,7 @@ mod tests {
pub struct MockInvokeContext {
pub key: Pubkey,
pub logger: MockLogger,
pub compute_budget: ComputeBudget,
pub compute_meter: MockComputeMeter,
}
impl Default for MockInvokeContext {
@@ -320,6 +335,7 @@ mod tests {
MockInvokeContext {
key: Pubkey::default(),
logger: MockLogger::default(),
compute_budget: ComputeBudget::default(),
compute_meter: MockComputeMeter {
remaining: std::u64::MAX,
},
@@ -348,11 +364,8 @@ mod tests {
fn get_logger(&self) -> Rc<RefCell<dyn Logger>> {
Rc::new(RefCell::new(self.logger.clone()))
}
fn is_cross_program_supported(&self) -> bool {
true
}
fn get_compute_budget(&self) -> ComputeBudget {
ComputeBudget::default()
fn get_compute_budget(&self) -> &ComputeBudget {
&self.compute_budget
}
fn get_compute_meter(&self) -> Rc<RefCell<dyn ComputeMeter>> {
Rc::new(RefCell::new(self.compute_meter.clone()))
@@ -362,6 +375,9 @@ mod tests {
None
}
fn record_instruction(&self, _instruction: &Instruction) {}
fn is_feature_active(&self, _feature_id: &Pubkey) -> bool {
true
}
}
struct TestInstructionMeter {
@@ -387,8 +403,7 @@ mod tests {
];
let input = &mut [0x00];
let executable =
EbpfVm::create_executable_from_text_bytes(program, Some(bpf_verifier::check)).unwrap();
let executable = EbpfVm::create_executable_from_text_bytes(program, None).unwrap();
let mut vm = EbpfVm::<BPFError>::new(executable.as_ref()).unwrap();
let instruction_meter = TestInstructionMeter { remaining: 10 };
vm.execute_program_metered(input, &[], &[], instruction_meter)
@@ -579,7 +594,6 @@ mod tests {
vec![],
vec![],
None,
true,
ComputeBudget {
max_units: 1,
log_units: 100,
@@ -590,6 +604,7 @@ mod tests {
},
Rc::new(RefCell::new(Executors::default())),
None,
Arc::new(FeatureSet::default()),
);
assert_eq!(
Err(InstructionError::Custom(194969602)),

View File

@@ -94,6 +94,7 @@ pub fn register_syscalls<'a>(
invoke_context: &'a mut dyn InvokeContext,
) -> Result<MemoryRegion, EbpfError<BPFError>> {
let compute_budget = invoke_context.get_compute_budget();
// Syscall functions common across languages
vm.register_syscall_ex("abort", syscall_abort)?;
@@ -115,36 +116,35 @@ pub fn register_syscalls<'a>(
logger: invoke_context.get_logger(),
}),
)?;
if invoke_context.is_cross_program_supported() {
vm.register_syscall_with_context_ex(
"sol_create_program_address",
Box::new(SyscallCreateProgramAddress {
cost: compute_budget.create_program_address_units,
compute_meter: invoke_context.get_compute_meter(),
loader_id,
}),
)?;
// Cross-program invocation syscalls
vm.register_syscall_with_context_ex(
"sol_create_program_address",
Box::new(SyscallCreateProgramAddress {
cost: compute_budget.create_program_address_units,
compute_meter: invoke_context.get_compute_meter(),
loader_id,
}),
)?;
let invoke_context = Rc::new(RefCell::new(invoke_context));
vm.register_syscall_with_context_ex(
"sol_invoke_signed_c",
Box::new(SyscallInvokeSignedC {
callers_keyed_accounts,
invoke_context: invoke_context.clone(),
loader_id,
}),
)?;
vm.register_syscall_with_context_ex(
"sol_invoke_signed_rust",
Box::new(SyscallInvokeSignedRust {
callers_keyed_accounts,
invoke_context: invoke_context.clone(),
loader_id,
}),
)?;
}
// Cross-program invocation syscalls
let invoke_context = Rc::new(RefCell::new(invoke_context));
vm.register_syscall_with_context_ex(
"sol_invoke_signed_c",
Box::new(SyscallInvokeSignedC {
callers_keyed_accounts,
invoke_context: invoke_context.clone(),
loader_id,
}),
)?;
vm.register_syscall_with_context_ex(
"sol_invoke_signed_rust",
Box::new(SyscallInvokeSignedRust {
callers_keyed_accounts,
invoke_context: invoke_context.clone(),
loader_id,
}),
)?;
// Memory allocator
let heap = vec![0_u8; DEFAULT_HEAP_SIZE];
@@ -1044,7 +1044,6 @@ fn call<'a>(
}
message_processor.add_loader(bpf_loader::id(), crate::process_instruction);
message_processor.add_loader(bpf_loader_deprecated::id(), crate::process_instruction);
message_processor.set_cross_program_support(invoke_context.is_cross_program_supported());
#[allow(clippy::deref_addrof)]
match message_processor.process_cross_program_instruction(