diff --git a/benches/elf_loader.rs b/benches/elf_loader.rs index 77e075fd..a9564827 100644 --- a/benches/elf_loader.rs +++ b/benches/elf_loader.rs @@ -14,6 +14,7 @@ use solana_rbpf::{ elf::Executable, syscalls::{BpfSyscallContext, BpfSyscallU64}, user_error::UserError, + verifier::TautologyVerifier, vm::{Config, SyscallObject, SyscallRegistry, TestInstructionMeter}, }; use std::{fs::File, io::Read}; @@ -37,9 +38,8 @@ fn bench_load_elf(bencher: &mut Bencher) { let mut elf = Vec::new(); file.read_to_end(&mut elf).unwrap(); bencher.iter(|| { - Executable::::from_elf( + Executable::::from_elf::( &elf, - None, Config::default(), syscall_registry(), ) @@ -53,13 +53,13 @@ fn bench_load_elf_without_syscall(bencher: &mut Bencher) { let mut elf = Vec::new(); file.read_to_end(&mut elf).unwrap(); bencher.iter(|| { - let executable = Executable::::from_elf( - &elf, - None, - Config::default(), - syscall_registry(), - ) - .unwrap(); + let executable = + Executable::::from_elf::( + &elf, + Config::default(), + syscall_registry(), + ) + .unwrap(); executable }); } @@ -70,13 +70,13 @@ fn bench_load_elf_with_syscall(bencher: &mut Bencher) { let mut elf = Vec::new(); file.read_to_end(&mut elf).unwrap(); bencher.iter(|| { - let executable = Executable::::from_elf( - &elf, - None, - Config::default(), - syscall_registry(), - ) - .unwrap(); + let executable = + Executable::::from_elf::( + &elf, + Config::default(), + syscall_registry(), + ) + .unwrap(); executable }); } diff --git a/benches/jit_compile.rs b/benches/jit_compile.rs index d6a1cfce..815706f1 100644 --- a/benches/jit_compile.rs +++ b/benches/jit_compile.rs @@ -12,6 +12,7 @@ extern crate test; use solana_rbpf::{ elf::Executable, user_error::UserError, + verifier::TautologyVerifier, vm::{Config, EbpfVm, SyscallRegistry, TestInstructionMeter}, }; use std::{fs::File, io::Read}; @@ -22,9 +23,8 @@ fn bench_init_vm(bencher: &mut Bencher) { let mut file = File::open("tests/elfs/pass_stack_reference.so").unwrap(); let mut elf = Vec::new(); file.read_to_end(&mut elf).unwrap(); - let executable = Executable::::from_elf( + let executable = Executable::::from_elf::( &elf, - None, Config::default(), SyscallRegistry::default(), ) @@ -40,13 +40,13 @@ fn bench_jit_compile(bencher: &mut Bencher) { let mut file = File::open("tests/elfs/pass_stack_reference.so").unwrap(); let mut elf = Vec::new(); file.read_to_end(&mut elf).unwrap(); - let mut executable = Executable::::from_elf( - &elf, - None, - Config::default(), - SyscallRegistry::default(), - ) - .unwrap(); + let mut executable = + Executable::::from_elf::( + &elf, + Config::default(), + SyscallRegistry::default(), + ) + .unwrap(); bencher.iter(|| { Executable::::jit_compile(&mut executable).unwrap() }); diff --git a/benches/vm_execution.rs b/benches/vm_execution.rs index b29cae30..69a84765 100644 --- a/benches/vm_execution.rs +++ b/benches/vm_execution.rs @@ -14,6 +14,7 @@ use solana_rbpf::{ elf::Executable, memory_region::MemoryRegion, user_error::UserError, + verifier::TautologyVerifier, vm::{Config, EbpfVm, SyscallRegistry, TestInstructionMeter}, }; use std::{fs::File, io::Read}; @@ -24,9 +25,8 @@ fn bench_init_interpreter_execution(bencher: &mut Bencher) { let mut file = File::open("tests/elfs/pass_stack_reference.so").unwrap(); let mut elf = Vec::new(); file.read_to_end(&mut elf).unwrap(); - let executable = Executable::::from_elf( + let executable = Executable::::from_elf::( &elf, - None, Config::default(), SyscallRegistry::default(), ) @@ -45,13 +45,13 @@ fn bench_init_jit_execution(bencher: &mut Bencher) { let mut file = File::open("tests/elfs/pass_stack_reference.so").unwrap(); let mut elf = Vec::new(); file.read_to_end(&mut elf).unwrap(); - let mut executable = Executable::::from_elf( - &elf, - None, - Config::default(), - SyscallRegistry::default(), - ) - .unwrap(); + let mut executable = + Executable::::from_elf::( + &elf, + Config::default(), + SyscallRegistry::default(), + ) + .unwrap(); Executable::::jit_compile(&mut executable).unwrap(); let mut vm = EbpfVm::::new(&executable, &mut [], Vec::new()).unwrap(); @@ -69,12 +69,11 @@ fn bench_jit_vs_interpreter( instruction_meter: u64, mem: &mut [u8], ) { - let mut executable = solana_rbpf::assembler::assemble::( - assembly, - None, - config, - SyscallRegistry::default(), - ) + let mut executable = solana_rbpf::assembler::assemble::< + TautologyVerifier, + UserError, + TestInstructionMeter, + >(assembly, config, SyscallRegistry::default()) .unwrap(); Executable::::jit_compile(&mut executable).unwrap(); let mem_region = MemoryRegion::new_writable(mem, ebpf::MM_INPUT_START); diff --git a/cli/src/main.rs b/cli/src/main.rs index 87e1eb6d..e84a84fe 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -7,7 +7,7 @@ use solana_rbpf::{ static_analysis::Analysis, syscalls::Result, user_error::UserError, - verifier::check, + verifier::SbfVerifier, vm::{Config, DynamicAnalysis, EbpfVm, SyscallObject, SyscallRegistry, TestInstructionMeter}, }; use std::{fs::File, io::Read, path::Path}; @@ -106,12 +106,6 @@ fn main() { .short('p') .long("prof"), ) - .arg( - Arg::new("verify") - .about("Run the verifier before execution or disassembly") - .short('v') - .long("veri"), - ) .get_matches(); let config = Config { @@ -119,21 +113,14 @@ fn main() { enable_symbol_and_section_labels: true, ..Config::default() }; - let verifier: Option fn(&'r [u8], &Config) -> std::result::Result<_, _>> = - if matches.is_present("verify") { - Some(check) - } else { - None - }; let syscall_registry = SyscallRegistry::default(); let mut executable = match matches.value_of("assembler") { Some(asm_file_name) => { let mut file = File::open(&Path::new(asm_file_name)).unwrap(); let mut source = Vec::new(); file.read_to_end(&mut source).unwrap(); - assemble::( + assemble::( std::str::from_utf8(source.as_slice()).unwrap(), - verifier, config, syscall_registry, ) @@ -142,9 +129,8 @@ fn main() { let mut file = File::open(&Path::new(matches.value_of("elf").unwrap())).unwrap(); let mut elf = Vec::new(); file.read_to_end(&mut elf).unwrap(); - Executable::::from_elf( + Executable::::from_elf::( &elf, - verifier, config, syscall_registry, ) diff --git a/examples/disassemble.rs b/examples/disassemble.rs index b3992215..4026ba0a 100644 --- a/examples/disassemble.rs +++ b/examples/disassemble.rs @@ -9,7 +9,7 @@ use solana_rbpf::{ elf::Executable, static_analysis::Analysis, user_error::UserError, - verifier::check, + verifier::SbfVerifier, vm::{Config, SyscallRegistry, TestInstructionMeter}, }; use std::collections::BTreeMap; @@ -32,9 +32,8 @@ fn main() { 0x00, 0x00, 0x00, 0x00, 0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ]; - let executable = Executable::::from_text_bytes( + let executable = Executable::::from_text_bytes::( &program, - Some(check), Config::default(), SyscallRegistry::default(), BTreeMap::default(), diff --git a/examples/to_json.rs b/examples/to_json.rs index badf8861..0006b492 100644 --- a/examples/to_json.rs +++ b/examples/to_json.rs @@ -16,7 +16,7 @@ use solana_rbpf::{ elf::Executable, static_analysis::Analysis, user_error::UserError, - verifier::check, + verifier::SbfVerifier, vm::{Config, SyscallRegistry, TestInstructionMeter}, }; use std::collections::BTreeMap; @@ -30,9 +30,8 @@ use std::collections::BTreeMap; // * Print integers as integers, and not as strings containing their hexadecimal representation // (just replace the relevant `format!()` calls by the commented values. fn to_json(program: &[u8]) -> String { - let executable = Executable::::from_text_bytes( + let executable = Executable::::from_text_bytes::( &program, - Some(check), Config::default(), SyscallRegistry::default(), BTreeMap::default(), diff --git a/fuzz/fuzz_targets/dumb.rs b/fuzz/fuzz_targets/dumb.rs index 7d6d7dc3..5bdb6342 100644 --- a/fuzz/fuzz_targets/dumb.rs +++ b/fuzz/fuzz_targets/dumb.rs @@ -11,7 +11,7 @@ use solana_rbpf::{ elf::{register_bpf_function, Executable}, memory_region::MemoryRegion, user_error::UserError, - verifier::check, + verifier::{SbfVerifier, TautologyVerifier, Verifier}, vm::{EbpfVm, SyscallRegistry, TestInstructionMeter}, }; @@ -29,7 +29,7 @@ struct DumbFuzzData { fuzz_target!(|data: DumbFuzzData| { let prog = data.prog; let config = data.template.into(); - if check(&prog, &config).is_err() { + if SbfVerifier::verify(&prog, &config).is_err() { // verify please return; } @@ -37,9 +37,8 @@ fuzz_target!(|data: DumbFuzzData| { let registry = SyscallRegistry::default(); let mut bpf_functions = BTreeMap::new(); register_bpf_function(&config, &mut bpf_functions, ®istry, 0, "entrypoint").unwrap(); - let executable = Executable::::from_text_bytes( + let executable = Executable::::from_text_bytes::( &prog, - None, config, SyscallRegistry::default(), bpf_functions, diff --git a/fuzz/fuzz_targets/smart.rs b/fuzz/fuzz_targets/smart.rs index 5e8a0702..409b25bd 100644 --- a/fuzz/fuzz_targets/smart.rs +++ b/fuzz/fuzz_targets/smart.rs @@ -13,7 +13,7 @@ use solana_rbpf::{ insn_builder::{Arch, IntoBytes}, memory_region::MemoryRegion, user_error::UserError, - verifier::check, + verifier::{SbfVerifier, TautologyVerifier, Verifier}, vm::{EbpfVm, SyscallRegistry, TestInstructionMeter}, }; @@ -33,7 +33,7 @@ struct FuzzData { fuzz_target!(|data: FuzzData| { let prog = make_program(&data.prog, data.arch); let config = data.template.into(); - if check(prog.into_bytes(), &config).is_err() { + if SbfVerifier::verify(prog.into_bytes(), &config).is_err() { // verify please return; } @@ -41,9 +41,8 @@ fuzz_target!(|data: FuzzData| { let registry = SyscallRegistry::default(); let mut bpf_functions = BTreeMap::new(); register_bpf_function(&config, &mut bpf_functions, ®istry, 0, "entrypoint").unwrap(); - let executable = Executable::::from_text_bytes( + let executable = Executable::::from_text_bytes::( prog.into_bytes(), - None, config, SyscallRegistry::default(), bpf_functions, diff --git a/fuzz/fuzz_targets/smart_jit_diff.rs b/fuzz/fuzz_targets/smart_jit_diff.rs index 863cf8a3..177c1cc3 100644 --- a/fuzz/fuzz_targets/smart_jit_diff.rs +++ b/fuzz/fuzz_targets/smart_jit_diff.rs @@ -11,7 +11,7 @@ use solana_rbpf::{ insn_builder::{Arch, Instruction, IntoBytes}, memory_region::MemoryRegion, user_error::UserError, - verifier::check, + verifier::{SbfVerifier, TautologyVerifier, Verifier}, vm::{EbpfVm, SyscallRegistry, TestInstructionMeter}, }; @@ -40,7 +40,7 @@ fuzz_target!(|data: FuzzData| { .set_imm(data.exit_imm) .push(); let config = data.template.into(); - if check(prog.into_bytes(), &config).is_err() { + if SbfVerifier::verify(prog.into_bytes(), &config).is_err() { // verify please return; } @@ -49,9 +49,8 @@ fuzz_target!(|data: FuzzData| { let registry = SyscallRegistry::default(); let mut bpf_functions = BTreeMap::new(); register_bpf_function(&config, &mut bpf_functions, ®istry, 0, "entrypoint").unwrap(); - let mut executable = Executable::::from_text_bytes( + let mut executable = Executable::::from_text_bytes::( prog.into_bytes(), - None, config, SyscallRegistry::default(), bpf_functions, diff --git a/fuzz/fuzz_targets/smarter_jit_diff.rs b/fuzz/fuzz_targets/smarter_jit_diff.rs index 750d92b1..36d84e4d 100644 --- a/fuzz/fuzz_targets/smarter_jit_diff.rs +++ b/fuzz/fuzz_targets/smarter_jit_diff.rs @@ -13,7 +13,7 @@ use solana_rbpf::{ memory_region::MemoryRegion, static_analysis::Analysis, user_error::UserError, - verifier::check, + verifier::{SbfVerifier, TautologyVerifier, Verifier}, vm::{EbpfVm, InstructionMeter, SyscallRegistry, TestInstructionMeter}, }; @@ -38,7 +38,7 @@ fn dump_insns(executable: &Executable< fuzz_target!(|data: FuzzData| { let prog = make_program(&data.prog); let config = data.template.into(); - if check(prog.into_bytes(), &config).is_err() { + if SbfVerifier::verify(prog.into_bytes(), &config).is_err() { // verify please return; } @@ -47,9 +47,8 @@ fuzz_target!(|data: FuzzData| { let registry = SyscallRegistry::default(); let mut bpf_functions = BTreeMap::new(); register_bpf_function(&config, &mut bpf_functions, ®istry, 0, "entrypoint").unwrap(); - let mut executable = Executable::::from_text_bytes( + let mut executable = Executable::::from_text_bytes::( prog.into_bytes(), - None, config, SyscallRegistry::default(), bpf_functions, diff --git a/fuzz/fuzz_targets/verify_semantic_aware.rs b/fuzz/fuzz_targets/verify_semantic_aware.rs index f20b81ed..82d0adb7 100644 --- a/fuzz/fuzz_targets/verify_semantic_aware.rs +++ b/fuzz/fuzz_targets/verify_semantic_aware.rs @@ -4,7 +4,7 @@ use libfuzzer_sys::fuzz_target; use semantic_aware::*; use solana_rbpf::insn_builder::IntoBytes; -use solana_rbpf::verifier::check; +use solana_rbpf::verifier::{SbfVerifier, Verifier}; use crate::common::ConfigTemplate; @@ -20,5 +20,5 @@ struct FuzzData { fuzz_target!(|data: FuzzData| { let prog = make_program(&data.prog); let config = data.template.into(); - check(prog.into_bytes(), &config).unwrap(); + SbfVerifier::verify(prog.into_bytes(), &config).unwrap(); }); diff --git a/src/assembler.rs b/src/assembler.rs index 70622cf0..abfe8a76 100644 --- a/src/assembler.rs +++ b/src/assembler.rs @@ -20,7 +20,8 @@ use crate::{ ebpf::{self, Insn}, elf::{register_bpf_function, Executable}, error::UserDefinedError, - vm::{Config, InstructionMeter, SyscallRegistry, Verifier}, + verifier::Verifier, + vm::{Config, InstructionMeter, SyscallRegistry}, }; use std::{ collections::{BTreeMap, HashMap}, @@ -186,15 +187,14 @@ fn insn(opc: u8, dst: i64, src: i64, off: i64, imm: i64) -> Result /// # Examples /// /// ``` -/// use solana_rbpf::{assembler::assemble, user_error::UserError, vm::{Config, TestInstructionMeter, SyscallRegistry}, verifier::check}; -/// let executable = assemble::( +/// use solana_rbpf::{assembler::assemble, user_error::UserError, vm::{Config, TestInstructionMeter, SyscallRegistry}, verifier::SbfVerifier}; +/// let executable = assemble::( /// "add64 r1, 0x605 /// mov64 r2, 0x32 /// mov64 r1, r0 /// be16 r0 /// neg64 r2 /// exit", -/// Some(check), /// Config::default(), /// SyscallRegistry::default(), /// ).unwrap(); @@ -219,9 +219,8 @@ fn insn(opc: u8, dst: i64, src: i64, off: i64, imm: i64) -> Result /// 0x87, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] /// ``` -pub fn assemble( +pub fn assemble( src: &str, - verifier: Option, config: Config, syscall_registry: SyscallRegistry, ) -> Result>>, String> { @@ -389,6 +388,6 @@ pub fn assemble( .iter() .flat_map(|insn| insn.to_vec()) .collect::>(); - Executable::::from_text_bytes(&program, verifier, config, syscall_registry, bpf_functions) + Executable::::from_text_bytes::(&program, config, syscall_registry, bpf_functions) .map_err(|err| format!("Executable constructor {:?}", err)) } diff --git a/src/elf.rs b/src/elf.rs index 2ded30d8..db4d20e8 100644 --- a/src/elf.rs +++ b/src/elf.rs @@ -2086,9 +2086,8 @@ mod test { let mut elf_bytes = Vec::new(); file.read_to_end(&mut elf_bytes) .expect("failed to read elf file"); - let mut executable = ElfExecutable::from_elf( + let mut executable = ElfExecutable::from_elf::( &elf_bytes, - Some(crate::verifier::check), Config::default(), syscall_registry(), ) diff --git a/src/jit.rs b/src/jit.rs index a3c69421..a1db2cdb 100644 --- a/src/jit.rs +++ b/src/jit.rs @@ -1792,7 +1792,7 @@ impl JitCompiler { #[cfg(all(test, target_arch = "x86_64", not(target_os = "windows")))] mod tests { use super::*; - use crate::{syscalls, vm::{SyscallRegistry, SyscallObject, TestInstructionMeter}, elf::register_bpf_function}; + use crate::{syscalls, vm::{SyscallRegistry, SyscallObject, TestInstructionMeter}, elf::register_bpf_function, verifier::TautologyVerifier}; use std::collections::BTreeMap; use byteorder::{LittleEndian, ByteOrder}; @@ -1819,9 +1819,8 @@ mod tests { ) .unwrap(); bpf_functions.insert(0xFFFFFFFF, (8, "foo".to_string())); - Executable::::from_text_bytes( + Executable::::from_text_bytes::( program, - None, config, syscall_registry, bpf_functions, diff --git a/src/verifier.rs b/src/verifier.rs index 7952b22c..3ee66974 100644 --- a/src/verifier.rs +++ b/src/verifier.rs @@ -79,6 +79,21 @@ pub enum VerifierError { InvalidRegister(usize), } +/// Sanity checks for programs before they are compiled or executed +pub trait Verifier { + /// Checks the BPF code in `prog` according to the rules from `config` + fn verify(prog: &[u8], config: &Config) -> Result<(), VerifierError>; +} + +/// A `Verifier` that never rejects any program, used for testing only +pub struct TautologyVerifier {} + +impl Verifier for TautologyVerifier { + fn verify(_prog: &[u8], _config: &Config) -> Result<(), VerifierError> { + Ok(()) + } +} + fn adj_insn_ptr(insn_ptr: usize) -> usize { insn_ptr + ebpf::ELF_INSN_DUMP_OFFSET } @@ -190,163 +205,167 @@ fn check_imm_register( Ok(()) } -/// Check the program against the verifier's rules -#[rustfmt::skip] -pub fn check(prog: &[u8], config: &Config) -> Result<(), VerifierError> { - check_prog_len(prog)?; - - let mut insn_ptr: usize = 0; - while (insn_ptr + 1) * ebpf::INSN_SIZE <= prog.len() { - let insn = ebpf::get_insn(prog, insn_ptr); - let mut store = false; - - match insn.opc { - ebpf::LD_ABS_B - | ebpf::LD_ABS_H - | ebpf::LD_ABS_W - | ebpf::LD_ABS_DW - | ebpf::LD_IND_B - | ebpf::LD_IND_H - | ebpf::LD_IND_W - | ebpf::LD_IND_DW if config.disable_deprecated_load_instructions => { - return Err(VerifierError::UnknownOpCode(insn.opc, adj_insn_ptr(insn_ptr))); - }, - - // BPF_LD class - ebpf::LD_ABS_B => {}, - ebpf::LD_ABS_H => {}, - ebpf::LD_ABS_W => {}, - ebpf::LD_ABS_DW => {}, - ebpf::LD_IND_B => {}, - ebpf::LD_IND_H => {}, - ebpf::LD_IND_W => {}, - ebpf::LD_IND_DW => {}, - - ebpf::LD_DW_IMM => { - check_load_dw(prog, insn_ptr)?; - insn_ptr += 1; - }, - - // BPF_LDX class - ebpf::LD_B_REG => {}, - ebpf::LD_H_REG => {}, - ebpf::LD_W_REG => {}, - ebpf::LD_DW_REG => {}, - - // BPF_ST class - ebpf::ST_B_IMM => store = true, - ebpf::ST_H_IMM => store = true, - ebpf::ST_W_IMM => store = true, - ebpf::ST_DW_IMM => store = true, - - // BPF_STX class - ebpf::ST_B_REG => store = true, - ebpf::ST_H_REG => store = true, - ebpf::ST_W_REG => store = true, - ebpf::ST_DW_REG => store = true, - - // BPF_ALU class - ebpf::ADD32_IMM => {}, - ebpf::ADD32_REG => {}, - ebpf::SUB32_IMM => {}, - ebpf::SUB32_REG => {}, - ebpf::MUL32_IMM => {}, - ebpf::MUL32_REG => {}, - ebpf::DIV32_IMM => { check_imm_nonzero(&insn, insn_ptr)?; }, - ebpf::DIV32_REG => {}, - ebpf::SDIV32_IMM if config.enable_sdiv => { check_imm_nonzero(&insn, insn_ptr)?; }, - ebpf::SDIV32_REG if config.enable_sdiv => {}, - ebpf::OR32_IMM => {}, - ebpf::OR32_REG => {}, - ebpf::AND32_IMM => {}, - ebpf::AND32_REG => {}, - ebpf::LSH32_IMM => { check_imm_shift(&insn, insn_ptr, 32)?; }, - ebpf::LSH32_REG => {}, - ebpf::RSH32_IMM => { check_imm_shift(&insn, insn_ptr, 32)?; }, - ebpf::RSH32_REG => {}, - ebpf::NEG32 => {}, - ebpf::MOD32_IMM => { check_imm_nonzero(&insn, insn_ptr)?; }, - ebpf::MOD32_REG => {}, - ebpf::XOR32_IMM => {}, - ebpf::XOR32_REG => {}, - ebpf::MOV32_IMM => {}, - ebpf::MOV32_REG => {}, - ebpf::ARSH32_IMM => { check_imm_shift(&insn, insn_ptr, 32)?; }, - ebpf::ARSH32_REG => {}, - ebpf::LE => { check_imm_endian(&insn, insn_ptr)?; }, - ebpf::BE => { check_imm_endian(&insn, insn_ptr)?; }, - - // BPF_ALU64 class - ebpf::ADD64_IMM => {}, - ebpf::ADD64_REG => {}, - ebpf::SUB64_IMM => {}, - ebpf::SUB64_REG => {}, - ebpf::MUL64_IMM => {}, - ebpf::MUL64_REG => {}, - ebpf::DIV64_IMM => { check_imm_nonzero(&insn, insn_ptr)?; }, - ebpf::DIV64_REG => {}, - ebpf::SDIV64_IMM if config.enable_sdiv => { check_imm_nonzero(&insn, insn_ptr)?; }, - ebpf::SDIV64_REG if config.enable_sdiv => {}, - ebpf::OR64_IMM => {}, - ebpf::OR64_REG => {}, - ebpf::AND64_IMM => {}, - ebpf::AND64_REG => {}, - ebpf::LSH64_IMM => { check_imm_shift(&insn, insn_ptr, 64)?; }, - ebpf::LSH64_REG => {}, - ebpf::RSH64_IMM => { check_imm_shift(&insn, insn_ptr, 64)?; }, - ebpf::RSH64_REG => {}, - ebpf::NEG64 => {}, - ebpf::MOD64_IMM => { check_imm_nonzero(&insn, insn_ptr)?; }, - ebpf::MOD64_REG => {}, - ebpf::XOR64_IMM => {}, - ebpf::XOR64_REG => {}, - ebpf::MOV64_IMM => {}, - ebpf::MOV64_REG => {}, - ebpf::ARSH64_IMM => { check_imm_shift(&insn, insn_ptr, 64)?; }, - ebpf::ARSH64_REG => {}, - - // BPF_JMP class - ebpf::JA => { check_jmp_offset(prog, insn_ptr)?; }, - ebpf::JEQ_IMM => { check_jmp_offset(prog, insn_ptr)?; }, - ebpf::JEQ_REG => { check_jmp_offset(prog, insn_ptr)?; }, - ebpf::JGT_IMM => { check_jmp_offset(prog, insn_ptr)?; }, - ebpf::JGT_REG => { check_jmp_offset(prog, insn_ptr)?; }, - ebpf::JGE_IMM => { check_jmp_offset(prog, insn_ptr)?; }, - ebpf::JGE_REG => { check_jmp_offset(prog, insn_ptr)?; }, - ebpf::JLT_IMM => { check_jmp_offset(prog, insn_ptr)?; }, - ebpf::JLT_REG => { check_jmp_offset(prog, insn_ptr)?; }, - ebpf::JLE_IMM => { check_jmp_offset(prog, insn_ptr)?; }, - ebpf::JLE_REG => { check_jmp_offset(prog, insn_ptr)?; }, - ebpf::JSET_IMM => { check_jmp_offset(prog, insn_ptr)?; }, - ebpf::JSET_REG => { check_jmp_offset(prog, insn_ptr)?; }, - ebpf::JNE_IMM => { check_jmp_offset(prog, insn_ptr)?; }, - ebpf::JNE_REG => { check_jmp_offset(prog, insn_ptr)?; }, - ebpf::JSGT_IMM => { check_jmp_offset(prog, insn_ptr)?; }, - ebpf::JSGT_REG => { check_jmp_offset(prog, insn_ptr)?; }, - ebpf::JSGE_IMM => { check_jmp_offset(prog, insn_ptr)?; }, - ebpf::JSGE_REG => { check_jmp_offset(prog, insn_ptr)?; }, - ebpf::JSLT_IMM => { check_jmp_offset(prog, insn_ptr)?; }, - ebpf::JSLT_REG => { check_jmp_offset(prog, insn_ptr)?; }, - ebpf::JSLE_IMM => { check_jmp_offset(prog, insn_ptr)?; }, - ebpf::JSLE_REG => { check_jmp_offset(prog, insn_ptr)?; }, - ebpf::CALL_IMM => {}, - ebpf::CALL_REG => { check_imm_register(&insn, insn_ptr, config)?; }, - ebpf::EXIT => {}, - - _ => { - return Err(VerifierError::UnknownOpCode(insn.opc, adj_insn_ptr(insn_ptr))); +/// The solana binary format verifier +pub struct SbfVerifier {} + +impl Verifier for SbfVerifier { + #[rustfmt::skip] + fn verify(prog: &[u8], config: &Config) -> Result<(), VerifierError> { + check_prog_len(prog)?; + + let mut insn_ptr: usize = 0; + while (insn_ptr + 1) * ebpf::INSN_SIZE <= prog.len() { + let insn = ebpf::get_insn(prog, insn_ptr); + let mut store = false; + + match insn.opc { + ebpf::LD_ABS_B + | ebpf::LD_ABS_H + | ebpf::LD_ABS_W + | ebpf::LD_ABS_DW + | ebpf::LD_IND_B + | ebpf::LD_IND_H + | ebpf::LD_IND_W + | ebpf::LD_IND_DW if config.disable_deprecated_load_instructions => { + return Err(VerifierError::UnknownOpCode(insn.opc, adj_insn_ptr(insn_ptr))); + }, + + // BPF_LD class + ebpf::LD_ABS_B => {}, + ebpf::LD_ABS_H => {}, + ebpf::LD_ABS_W => {}, + ebpf::LD_ABS_DW => {}, + ebpf::LD_IND_B => {}, + ebpf::LD_IND_H => {}, + ebpf::LD_IND_W => {}, + ebpf::LD_IND_DW => {}, + + ebpf::LD_DW_IMM => { + check_load_dw(prog, insn_ptr)?; + insn_ptr += 1; + }, + + // BPF_LDX class + ebpf::LD_B_REG => {}, + ebpf::LD_H_REG => {}, + ebpf::LD_W_REG => {}, + ebpf::LD_DW_REG => {}, + + // BPF_ST class + ebpf::ST_B_IMM => store = true, + ebpf::ST_H_IMM => store = true, + ebpf::ST_W_IMM => store = true, + ebpf::ST_DW_IMM => store = true, + + // BPF_STX class + ebpf::ST_B_REG => store = true, + ebpf::ST_H_REG => store = true, + ebpf::ST_W_REG => store = true, + ebpf::ST_DW_REG => store = true, + + // BPF_ALU class + ebpf::ADD32_IMM => {}, + ebpf::ADD32_REG => {}, + ebpf::SUB32_IMM => {}, + ebpf::SUB32_REG => {}, + ebpf::MUL32_IMM => {}, + ebpf::MUL32_REG => {}, + ebpf::DIV32_IMM => { check_imm_nonzero(&insn, insn_ptr)?; }, + ebpf::DIV32_REG => {}, + ebpf::SDIV32_IMM if config.enable_sdiv => { check_imm_nonzero(&insn, insn_ptr)?; }, + ebpf::SDIV32_REG if config.enable_sdiv => {}, + ebpf::OR32_IMM => {}, + ebpf::OR32_REG => {}, + ebpf::AND32_IMM => {}, + ebpf::AND32_REG => {}, + ebpf::LSH32_IMM => { check_imm_shift(&insn, insn_ptr, 32)?; }, + ebpf::LSH32_REG => {}, + ebpf::RSH32_IMM => { check_imm_shift(&insn, insn_ptr, 32)?; }, + ebpf::RSH32_REG => {}, + ebpf::NEG32 => {}, + ebpf::MOD32_IMM => { check_imm_nonzero(&insn, insn_ptr)?; }, + ebpf::MOD32_REG => {}, + ebpf::XOR32_IMM => {}, + ebpf::XOR32_REG => {}, + ebpf::MOV32_IMM => {}, + ebpf::MOV32_REG => {}, + ebpf::ARSH32_IMM => { check_imm_shift(&insn, insn_ptr, 32)?; }, + ebpf::ARSH32_REG => {}, + ebpf::LE => { check_imm_endian(&insn, insn_ptr)?; }, + ebpf::BE => { check_imm_endian(&insn, insn_ptr)?; }, + + // BPF_ALU64 class + ebpf::ADD64_IMM => {}, + ebpf::ADD64_REG => {}, + ebpf::SUB64_IMM => {}, + ebpf::SUB64_REG => {}, + ebpf::MUL64_IMM => {}, + ebpf::MUL64_REG => {}, + ebpf::DIV64_IMM => { check_imm_nonzero(&insn, insn_ptr)?; }, + ebpf::DIV64_REG => {}, + ebpf::SDIV64_IMM if config.enable_sdiv => { check_imm_nonzero(&insn, insn_ptr)?; }, + ebpf::SDIV64_REG if config.enable_sdiv => {}, + ebpf::OR64_IMM => {}, + ebpf::OR64_REG => {}, + ebpf::AND64_IMM => {}, + ebpf::AND64_REG => {}, + ebpf::LSH64_IMM => { check_imm_shift(&insn, insn_ptr, 64)?; }, + ebpf::LSH64_REG => {}, + ebpf::RSH64_IMM => { check_imm_shift(&insn, insn_ptr, 64)?; }, + ebpf::RSH64_REG => {}, + ebpf::NEG64 => {}, + ebpf::MOD64_IMM => { check_imm_nonzero(&insn, insn_ptr)?; }, + ebpf::MOD64_REG => {}, + ebpf::XOR64_IMM => {}, + ebpf::XOR64_REG => {}, + ebpf::MOV64_IMM => {}, + ebpf::MOV64_REG => {}, + ebpf::ARSH64_IMM => { check_imm_shift(&insn, insn_ptr, 64)?; }, + ebpf::ARSH64_REG => {}, + + // BPF_JMP class + ebpf::JA => { check_jmp_offset(prog, insn_ptr)?; }, + ebpf::JEQ_IMM => { check_jmp_offset(prog, insn_ptr)?; }, + ebpf::JEQ_REG => { check_jmp_offset(prog, insn_ptr)?; }, + ebpf::JGT_IMM => { check_jmp_offset(prog, insn_ptr)?; }, + ebpf::JGT_REG => { check_jmp_offset(prog, insn_ptr)?; }, + ebpf::JGE_IMM => { check_jmp_offset(prog, insn_ptr)?; }, + ebpf::JGE_REG => { check_jmp_offset(prog, insn_ptr)?; }, + ebpf::JLT_IMM => { check_jmp_offset(prog, insn_ptr)?; }, + ebpf::JLT_REG => { check_jmp_offset(prog, insn_ptr)?; }, + ebpf::JLE_IMM => { check_jmp_offset(prog, insn_ptr)?; }, + ebpf::JLE_REG => { check_jmp_offset(prog, insn_ptr)?; }, + ebpf::JSET_IMM => { check_jmp_offset(prog, insn_ptr)?; }, + ebpf::JSET_REG => { check_jmp_offset(prog, insn_ptr)?; }, + ebpf::JNE_IMM => { check_jmp_offset(prog, insn_ptr)?; }, + ebpf::JNE_REG => { check_jmp_offset(prog, insn_ptr)?; }, + ebpf::JSGT_IMM => { check_jmp_offset(prog, insn_ptr)?; }, + ebpf::JSGT_REG => { check_jmp_offset(prog, insn_ptr)?; }, + ebpf::JSGE_IMM => { check_jmp_offset(prog, insn_ptr)?; }, + ebpf::JSGE_REG => { check_jmp_offset(prog, insn_ptr)?; }, + ebpf::JSLT_IMM => { check_jmp_offset(prog, insn_ptr)?; }, + ebpf::JSLT_REG => { check_jmp_offset(prog, insn_ptr)?; }, + ebpf::JSLE_IMM => { check_jmp_offset(prog, insn_ptr)?; }, + ebpf::JSLE_REG => { check_jmp_offset(prog, insn_ptr)?; }, + ebpf::CALL_IMM => {}, + ebpf::CALL_REG => { check_imm_register(&insn, insn_ptr, config)?; }, + ebpf::EXIT => {}, + + _ => { + return Err(VerifierError::UnknownOpCode(insn.opc, adj_insn_ptr(insn_ptr))); + } } - } - check_registers(&insn, store, insn_ptr, config.dynamic_stack_frames)?; + check_registers(&insn, store, insn_ptr, config.dynamic_stack_frames)?; - insn_ptr += 1; - } + insn_ptr += 1; + } - // insn_ptr should now be equal to number of instructions. - if insn_ptr != prog.len() / ebpf::INSN_SIZE { - return Err(VerifierError::JumpOutOfCode(adj_insn_ptr(insn_ptr), adj_insn_ptr(insn_ptr))); - } + // insn_ptr should now be equal to number of instructions. + if insn_ptr != prog.len() / ebpf::INSN_SIZE { + return Err(VerifierError::JumpOutOfCode(adj_insn_ptr(insn_ptr), adj_insn_ptr(insn_ptr))); + } - Ok(()) + Ok(()) + } } diff --git a/src/vm.rs b/src/vm.rs index c9041e93..d67ee2cc 100644 --- a/src/vm.rs +++ b/src/vm.rs @@ -22,7 +22,7 @@ use crate::{ jit::JitProgramArgument, memory_region::{MemoryMapping, MemoryRegion}, static_analysis::Analysis, - verifier::VerifierError, + verifier::Verifier, }; use std::{ collections::{BTreeMap, HashMap}, @@ -32,16 +32,6 @@ use std::{ u32, }; -/// eBPF verification function that returns an error if the program does not meet its requirements. -/// -/// Some examples of things the verifier may reject the program for: -/// -/// - Program does not terminate. -/// - Unknown instructions. -/// - Bad formed instruction. -/// - Unknown eBPF syscall index. -pub type Verifier = fn(prog: &[u8], config: &Config) -> Result<(), VerifierError>; - /// Return value of programs and syscalls pub type ProgramResult = Result>; @@ -277,35 +267,27 @@ pub const SYSCALL_CONTEXT_OBJECTS_OFFSET: usize = 4; /// Static constructors for Executable impl Executable { /// Creates a verified executable from an ELF file - pub fn from_elf( + pub fn from_elf( elf_bytes: &[u8], - verifier: Option, config: Config, syscall_registry: SyscallRegistry, ) -> Result>, EbpfError> { let executable = Executable::load(config, elf_bytes, syscall_registry)?; - if let Some(verifier) = verifier { - verifier(executable.get_text_bytes().1, executable.get_config())?; - } + ::verify(executable.get_text_bytes().1, executable.get_config())?; Ok(Pin::new(Box::new(executable))) } + /// Creates a verified executable from machine code - pub fn from_text_bytes( + pub fn from_text_bytes( text_bytes: &[u8], - verifier: Option, config: Config, syscall_registry: SyscallRegistry, bpf_functions: BTreeMap, ) -> Result>, EbpfError> { - if let Some(verifier) = verifier { - verifier(text_bytes, &config).map_err(EbpfError::VerifierError)?; - } - Ok(Pin::new(Box::new(Executable::new_from_text_bytes( - config, - text_bytes, - syscall_registry, - bpf_functions, - )))) + let executable = + Executable::new_from_text_bytes(config, text_bytes, syscall_registry, bpf_functions); + ::verify(executable.get_text_bytes().1, executable.get_config())?; + Ok(Pin::new(Box::new(executable))) } } @@ -437,7 +419,7 @@ impl Tracer { /// # Examples /// /// ``` -/// use solana_rbpf::{ebpf, elf::{Executable, register_bpf_function}, memory_region::MemoryRegion, vm::{Config, EbpfVm, TestInstructionMeter, SyscallRegistry}, verifier::check, user_error::UserError}; +/// use solana_rbpf::{ebpf, elf::{Executable, register_bpf_function}, memory_region::MemoryRegion, vm::{Config, EbpfVm, TestInstructionMeter, SyscallRegistry}, verifier::SbfVerifier, user_error::UserError}; /// /// let prog = &[ /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit @@ -451,7 +433,7 @@ impl Tracer { /// let mut bpf_functions = std::collections::BTreeMap::new(); /// let syscall_registry = SyscallRegistry::default(); /// register_bpf_function(&config, &mut bpf_functions, &syscall_registry, 0, "entrypoint").unwrap(); -/// let mut executable = Executable::::from_text_bytes(prog, Some(check), config, syscall_registry, bpf_functions).unwrap(); +/// let mut executable = Executable::::from_text_bytes::(prog, config, syscall_registry, bpf_functions).unwrap(); /// let mem_region = MemoryRegion::new_writable(mem, ebpf::MM_INPUT_START); /// let mut vm = EbpfVm::::new(&executable, &mut [], vec![mem_region]).unwrap(); /// @@ -478,7 +460,7 @@ impl<'a, E: UserDefinedError, I: InstructionMeter> EbpfVm<'a, E, I> { /// # Examples /// /// ``` - /// use solana_rbpf::{ebpf, elf::{Executable, register_bpf_function}, vm::{Config, EbpfVm, TestInstructionMeter, SyscallRegistry}, verifier::check, user_error::UserError}; + /// use solana_rbpf::{ebpf, elf::{Executable, register_bpf_function}, vm::{Config, EbpfVm, TestInstructionMeter, SyscallRegistry}, verifier::SbfVerifier, user_error::UserError}; /// /// let prog = &[ /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit @@ -489,7 +471,7 @@ impl<'a, E: UserDefinedError, I: InstructionMeter> EbpfVm<'a, E, I> { /// let mut bpf_functions = std::collections::BTreeMap::new(); /// let syscall_registry = SyscallRegistry::default(); /// register_bpf_function(&config, &mut bpf_functions, &syscall_registry, 0, "entrypoint").unwrap(); - /// let mut executable = Executable::::from_text_bytes(prog, Some(check), config, syscall_registry, bpf_functions).unwrap(); + /// let mut executable = Executable::::from_text_bytes::(prog, config, syscall_registry, bpf_functions).unwrap(); /// let mut vm = EbpfVm::::new(&executable, &mut [], Vec::new()).unwrap(); /// ``` pub fn new( @@ -554,7 +536,7 @@ impl<'a, E: UserDefinedError, I: InstructionMeter> EbpfVm<'a, E, I> { /// # Examples /// /// ``` - /// use solana_rbpf::{ebpf, elf::{Executable, register_bpf_function}, vm::{Config, EbpfVm, SyscallObject, SyscallRegistry, TestInstructionMeter}, verifier::check, syscalls::BpfTracePrintf, user_error::UserError}; + /// use solana_rbpf::{ebpf, elf::{Executable, register_bpf_function}, vm::{Config, EbpfVm, SyscallObject, SyscallRegistry, TestInstructionMeter}, verifier::SbfVerifier, syscalls::BpfTracePrintf, user_error::UserError}; /// /// // This program was compiled with clang, from a C program containing the following single /// // instruction: `return bpf_trace_printk("foo %c %c %c\n", 10, 1, 2, 3);` @@ -580,7 +562,7 @@ impl<'a, E: UserDefinedError, I: InstructionMeter> EbpfVm<'a, E, I> { /// let config = Config::default(); /// let mut bpf_functions = std::collections::BTreeMap::new(); /// register_bpf_function(&config, &mut bpf_functions, &syscall_registry, 0, "entrypoint").unwrap(); - /// let mut executable = Executable::::from_text_bytes(prog, Some(check), config, syscall_registry, bpf_functions).unwrap(); + /// let mut executable = Executable::::from_text_bytes::(prog, config, syscall_registry, bpf_functions).unwrap(); /// let mut vm = EbpfVm::::new(&executable, &mut [], Vec::new()).unwrap(); /// // Bind a context object instance to the previously registered syscall /// vm.bind_syscall_context_objects(0); @@ -632,7 +614,7 @@ impl<'a, E: UserDefinedError, I: InstructionMeter> EbpfVm<'a, E, I> { /// # Examples /// /// ``` - /// use solana_rbpf::{ebpf, elf::{Executable, register_bpf_function}, memory_region::MemoryRegion, vm::{Config, EbpfVm, TestInstructionMeter, SyscallRegistry}, verifier::check, user_error::UserError}; + /// use solana_rbpf::{ebpf, elf::{Executable, register_bpf_function}, memory_region::MemoryRegion, vm::{Config, EbpfVm, TestInstructionMeter, SyscallRegistry}, verifier::SbfVerifier, user_error::UserError}; /// /// let prog = &[ /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit @@ -646,7 +628,7 @@ impl<'a, E: UserDefinedError, I: InstructionMeter> EbpfVm<'a, E, I> { /// let mut bpf_functions = std::collections::BTreeMap::new(); /// let syscall_registry = SyscallRegistry::default(); /// register_bpf_function(&config, &mut bpf_functions, &syscall_registry, 0, "entrypoint").unwrap(); - /// let mut executable = Executable::::from_text_bytes(prog, Some(check), config, syscall_registry, bpf_functions).unwrap(); + /// let mut executable = Executable::::from_text_bytes::(prog, config, syscall_registry, bpf_functions).unwrap(); /// let mem_region = MemoryRegion::new_writable(mem, ebpf::MM_INPUT_START); /// let mut vm = EbpfVm::::new(&executable, &mut [], vec![mem_region]).unwrap(); /// diff --git a/tests/assembler.rs b/tests/assembler.rs index 1ad63c49..7cfba73c 100644 --- a/tests/assembler.rs +++ b/tests/assembler.rs @@ -12,15 +12,14 @@ use solana_rbpf::{ assembler::assemble, ebpf, user_error::UserError, - verifier::check, + verifier::{SbfVerifier, TautologyVerifier}, vm::{Config, SyscallRegistry, TestInstructionMeter}, }; use test_utils::{TCP_SACK_ASM, TCP_SACK_BIN}; fn asm(src: &str) -> Result, String> { - let executable = assemble::( + let executable = assemble::( src, - None, Config::default(), SyscallRegistry::default(), )?; @@ -576,9 +575,8 @@ fn test_large_immediate() { #[test] fn test_tcp_sack() { - let executable = assemble::( + let executable = assemble::( TCP_SACK_ASM, - Some(check), Config::default(), SyscallRegistry::default(), ) diff --git a/tests/disassembler.rs b/tests/disassembler.rs index 208047ea..a90c721f 100644 --- a/tests/disassembler.rs +++ b/tests/disassembler.rs @@ -11,6 +11,7 @@ use solana_rbpf::{ assembler::assemble, static_analysis::Analysis, user_error::UserError, + verifier::TautologyVerifier, vm::{Config, SyscallRegistry, TestInstructionMeter}, }; @@ -22,9 +23,8 @@ macro_rules! disasm { enable_symbol_and_section_labels: true, ..Config::default() }; - let executable = assemble::( + let executable = assemble::( src, - None, config, SyscallRegistry::default(), ) diff --git a/tests/misc.rs b/tests/misc.rs index 8b2b3831..a0fa4c7a 100644 --- a/tests/misc.rs +++ b/tests/misc.rs @@ -26,7 +26,7 @@ use solana_rbpf::{ fuzz::fuzz, syscalls::{BpfSyscallContext, BpfSyscallString, BpfSyscallU64}, user_error::UserError, - verifier::check, + verifier::SbfVerifier, vm::{Config, EbpfVm, SyscallObject, SyscallRegistry, TestInstructionMeter}, }; use std::{fs::File, io::Read}; @@ -127,12 +127,10 @@ fn test_fuzz_execute() { BpfSyscallU64::call, ) .unwrap(); - if let Ok(executable) = Executable::::from_elf( - bytes, - Some(check), - Config::default(), - syscall_registry, - ) { + if let Ok(executable) = Executable::::from_elf::< + SbfVerifier, + >(bytes, Config::default(), syscall_registry) + { let mut vm = EbpfVm::::new( &executable, &mut [], diff --git a/tests/ubpf_execution.rs b/tests/ubpf_execution.rs index e536c2d3..ad2b6d77 100644 --- a/tests/ubpf_execution.rs +++ b/tests/ubpf_execution.rs @@ -22,7 +22,7 @@ use solana_rbpf::{ memory_region::{AccessType, MemoryMapping, MemoryRegion}, syscalls::{self, BpfSyscallContext, Result}, user_error::UserError, - verifier::check, + verifier::SbfVerifier, vm::{Config, EbpfVm, SyscallObject, SyscallRegistry, TestInstructionMeter}, }; use std::{collections::BTreeMap, fs::File, io::Read}; @@ -100,7 +100,7 @@ macro_rules! test_interpreter_and_jit_asm { { let mut syscall_registry = SyscallRegistry::default(); $(test_interpreter_and_jit!(register, syscall_registry, $location => $syscall_init; $syscall_function);)* - let mut executable = assemble($source, Some(check), $config, syscall_registry).unwrap(); + let mut executable = assemble::($source, $config, syscall_registry).unwrap(); test_interpreter_and_jit!(executable, $mem, $syscall_context, $check, $expected_instruction_count); } }; @@ -126,7 +126,7 @@ macro_rules! test_interpreter_and_jit_elf { { let mut syscall_registry = SyscallRegistry::default(); $(test_interpreter_and_jit!(register, syscall_registry, $location => $syscall_init; $syscall_function);)* - let mut executable = Executable::::from_elf(&elf, Some(check), $config, syscall_registry).unwrap(); + let mut executable = Executable::::from_elf::(&elf, $config, syscall_registry).unwrap(); test_interpreter_and_jit!(executable, $mem, $syscall_context, $check, $expected_instruction_count); } }; @@ -2889,13 +2889,9 @@ fn test_err_mem_access_out_of_bound() { ) .unwrap(); #[allow(unused_mut)] - let mut executable = Executable::::from_text_bytes( - &prog, - Some(check), - config, - syscall_registry, - bpf_functions, - ) + let mut executable = Executable::::from_text_bytes::< + SbfVerifier, + >(&prog, config, syscall_registry, bpf_functions) .unwrap(); test_interpreter_and_jit!( executable, @@ -3410,13 +3406,12 @@ impl SyscallObject for NestedVmSyscall { ) .unwrap(); let mem = [depth as u8 - 1, throw as u8]; - let mut executable = assemble::( + let mut executable = assemble::( " ldxb r2, [r1+1] ldxb r1, [r1] syscall NestedVmSyscall exit", - Some(check), Config::default(), syscall_registry, ) @@ -3552,9 +3547,8 @@ fn test_custom_entrypoint() { let mut syscall_registry = SyscallRegistry::default(); test_interpreter_and_jit!(register, syscall_registry, b"log_64" => syscalls::BpfSyscallU64::init::; syscalls::BpfSyscallU64::call); #[allow(unused_mut)] - let mut executable = Executable::::from_elf( + let mut executable = Executable::::from_elf::( &elf, - Some(check), config, syscall_registry, ) @@ -3981,7 +3975,7 @@ fn test_err_unresolved_elf() { ..Config::default() }; assert!( - matches!(Executable::::from_elf(&elf, Some(check), config, syscall_registry), Err(EbpfError::ElfError(ElfError::UnresolvedSymbol(symbol, pc, offset))) if symbol == "log_64" && pc == 550 && offset == 4168) + matches!(Executable::::from_elf::(&elf, config, syscall_registry), Err(EbpfError::ElfError(ElfError::UnresolvedSymbol(symbol, pc, offset))) if symbol == "log_64" && pc == 550 && offset == 4168) ); } @@ -4380,9 +4374,8 @@ fn execute_generated_program(prog: &[u8]) -> bool { "entrypoint", ) .unwrap(); - let executable = Executable::::from_text_bytes( + let executable = Executable::::from_text_bytes::( prog, - Some(check), config, syscall_registry, bpf_functions, diff --git a/tests/ubpf_verifier.rs b/tests/ubpf_verifier.rs index 15aeb59e..b9a1097e 100644 --- a/tests/ubpf_verifier.rs +++ b/tests/ubpf_verifier.rs @@ -26,29 +26,18 @@ use solana_rbpf::{ assembler::assemble, ebpf, elf::Executable, - error::UserDefinedError, user_error::UserError, - verifier::{check, VerifierError}, + verifier::{SbfVerifier, TautologyVerifier, Verifier, VerifierError}, vm::{Config, EbpfVm, SyscallRegistry, TestInstructionMeter}, }; use std::collections::BTreeMap; -use thiserror::Error; - -/// Error definitions -#[derive(Debug, Error)] -pub enum VerifierTestError { - #[error("{0}")] - Rejected(String), -} -impl UserDefinedError for VerifierTestError {} #[test] fn test_verifier_success() { - let executable = assemble::( + let executable = assemble::( " mov32 r0, 0xBEE exit", - Some(|_prog: &[u8], _config: &Config| Ok(())), Config::default(), SyscallRegistry::default(), ) @@ -60,14 +49,16 @@ fn test_verifier_success() { #[test] #[should_panic(expected = "NoProgram")] fn test_verifier_fail() { - fn verifier_fail(_prog: &[u8], _config: &Config) -> Result<(), VerifierError> { - Err(VerifierError::NoProgram) + struct FailingVerifier {} + impl Verifier for FailingVerifier { + fn verify(_prog: &[u8], _config: &Config) -> Result<(), VerifierError> { + Err(VerifierError::NoProgram) + } } - let _executable = assemble::( + let _executable = assemble::( " mov32 r0, 0xBEE exit", - Some(verifier_fail), Config::default(), SyscallRegistry::default(), ) @@ -77,12 +68,11 @@ fn test_verifier_fail() { #[test] #[should_panic(expected = "DivisionByZero(30)")] fn test_verifier_err_div_by_zero_imm() { - let _executable = assemble::( + let _executable = assemble::( " mov32 r0, 1 div32 r0, 0 exit", - Some(check), Config::default(), SyscallRegistry::default(), ) @@ -97,9 +87,8 @@ fn test_verifier_err_endian_size() { 0xb7, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ]; - let _ = Executable::::from_text_bytes( + let _ = Executable::::from_text_bytes::( prog, - Some(check), Config::default(), SyscallRegistry::default(), BTreeMap::default(), @@ -115,9 +104,8 @@ fn test_verifier_err_incomplete_lddw() { 0x18, 0x00, 0x00, 0x00, 0x88, 0x77, 0x66, 0x55, // 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ]; - let _ = Executable::::from_text_bytes( + let _ = Executable::::from_text_bytes::( prog, - Some(check), Config::default(), SyscallRegistry::default(), BTreeMap::default(), @@ -131,11 +119,10 @@ fn test_verifier_err_invalid_reg_dst() { // allowed when dynamic_stack_frames=true for dynamic_stack_frames in [false, true] { assert_eq!( - assemble::( + assemble::( " mov r11, 1 exit", - Some(check), Config { dynamic_stack_frames, ..Config::default() @@ -154,11 +141,10 @@ fn test_verifier_err_invalid_reg_src() { // allowed when dynamic_stack_frames=true for dynamic_stack_frames in [false, true] { assert_eq!( - assemble::( + assemble::( " mov r0, r11 exit", - Some(check), Config { dynamic_stack_frames, ..Config::default() @@ -173,12 +159,11 @@ fn test_verifier_err_invalid_reg_src() { #[test] fn test_verifier_resize_stack_ptr_success() { - let _executable = assemble::( + let _executable = assemble::( " sub r11, 1 add r11, 1 exit", - Some(check), Config { dynamic_stack_frames: true, enable_stack_frame_gaps: false, @@ -192,12 +177,11 @@ fn test_verifier_resize_stack_ptr_success() { #[test] #[should_panic(expected = "JumpToMiddleOfLDDW(2, 29)")] fn test_verifier_err_jmp_lddw() { - let _executable = assemble::( + let _executable = assemble::( " ja +1 lddw r0, 0x1122334455667788 exit", - Some(check), Config::default(), SyscallRegistry::default(), ) @@ -207,11 +191,10 @@ fn test_verifier_err_jmp_lddw() { #[test] #[should_panic(expected = "JumpOutOfCode(3, 29)")] fn test_verifier_err_jmp_out_end() { - let _executable = assemble::( + let _executable = assemble::( " ja +2 exit", - Some(check), Config::default(), SyscallRegistry::default(), ) @@ -221,11 +204,10 @@ fn test_verifier_err_jmp_out_end() { #[test] #[should_panic(expected = "JumpOutOfCode(18446744073709551615, 29)")] fn test_verifier_err_jmp_out_start() { - let _executable = assemble::( + let _executable = assemble::( " ja -2 exit", - Some(check), Config::default(), SyscallRegistry::default(), ) @@ -239,9 +221,8 @@ fn test_verifier_err_unknown_opcode() { 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ]; - let _ = Executable::::from_text_bytes( + let _ = Executable::::from_text_bytes::( prog, - Some(check), Config::default(), SyscallRegistry::default(), BTreeMap::default(), @@ -252,11 +233,10 @@ fn test_verifier_err_unknown_opcode() { #[test] #[should_panic(expected = "CannotWriteR10(29)")] fn test_verifier_err_write_r10() { - let _executable = assemble::( + let _executable = assemble::( " mov r10, 1 exit", - Some(check), Config::default(), SyscallRegistry::default(), ) @@ -291,9 +271,8 @@ fn test_verifier_err_all_shift_overflows() { for (overflowing_instruction, expected) in testcases { let assembly = format!("\n{}\nexit", overflowing_instruction); - let result = assemble::( + let result = assemble::( &assembly, - Some(check), Config::default(), SyscallRegistry::default(), ); @@ -326,9 +305,8 @@ fn test_verifier_err_ldabs_ldind_disabled() { for (opc, instruction) in instructions { for disable_deprecated_load_instructions in [true, false] { let assembly = format!("\n{}\nexit", instruction); - let result = assemble::( + let result = assemble::( &assembly, - Some(check), Config { disable_deprecated_load_instructions, ..Config::default() @@ -364,9 +342,8 @@ fn test_sdiv_disabled() { for (opc, instruction) in instructions { for enable_sdiv in [true, false] { let assembly = format!("\n{}\nexit", instruction); - let result = assemble::( + let result = assemble::( &assembly, - Some(check), Config { enable_sdiv, ..Config::default()