diff --git a/benches/elf_loader.rs b/benches/elf_loader.rs index a9564827..77e075fd 100644 --- a/benches/elf_loader.rs +++ b/benches/elf_loader.rs @@ -14,7 +14,6 @@ use solana_rbpf::{ elf::Executable, syscalls::{BpfSyscallContext, BpfSyscallU64}, user_error::UserError, - verifier::TautologyVerifier, vm::{Config, SyscallObject, SyscallRegistry, TestInstructionMeter}, }; use std::{fs::File, io::Read}; @@ -38,8 +37,9 @@ 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, - Config::default(), - syscall_registry(), - ) - .unwrap(); + let executable = Executable::::from_elf( + &elf, + None, + 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, - Config::default(), - syscall_registry(), - ) - .unwrap(); + let executable = Executable::::from_elf( + &elf, + None, + Config::default(), + syscall_registry(), + ) + .unwrap(); executable }); } diff --git a/benches/jit_compile.rs b/benches/jit_compile.rs index 815706f1..d6a1cfce 100644 --- a/benches/jit_compile.rs +++ b/benches/jit_compile.rs @@ -12,7 +12,6 @@ extern crate test; use solana_rbpf::{ elf::Executable, user_error::UserError, - verifier::TautologyVerifier, vm::{Config, EbpfVm, SyscallRegistry, TestInstructionMeter}, }; use std::{fs::File, io::Read}; @@ -23,8 +22,9 @@ 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, - Config::default(), - SyscallRegistry::default(), - ) - .unwrap(); + let mut executable = Executable::::from_elf( + &elf, + None, + 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 69a84765..b29cae30 100644 --- a/benches/vm_execution.rs +++ b/benches/vm_execution.rs @@ -14,7 +14,6 @@ use solana_rbpf::{ elf::Executable, memory_region::MemoryRegion, user_error::UserError, - verifier::TautologyVerifier, vm::{Config, EbpfVm, SyscallRegistry, TestInstructionMeter}, }; use std::{fs::File, io::Read}; @@ -25,8 +24,9 @@ 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, - Config::default(), - SyscallRegistry::default(), - ) - .unwrap(); + let mut executable = Executable::::from_elf( + &elf, + None, + Config::default(), + SyscallRegistry::default(), + ) + .unwrap(); Executable::::jit_compile(&mut executable).unwrap(); let mut vm = EbpfVm::::new(&executable, &mut [], Vec::new()).unwrap(); @@ -69,11 +69,12 @@ fn bench_jit_vs_interpreter( instruction_meter: u64, mem: &mut [u8], ) { - let mut executable = solana_rbpf::assembler::assemble::< - TautologyVerifier, - UserError, - TestInstructionMeter, - >(assembly, config, SyscallRegistry::default()) + let mut executable = solana_rbpf::assembler::assemble::( + assembly, + None, + 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 e84a84fe..87e1eb6d 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::SbfVerifier, + verifier::check, vm::{Config, DynamicAnalysis, EbpfVm, SyscallObject, SyscallRegistry, TestInstructionMeter}, }; use std::{fs::File, io::Read, path::Path}; @@ -106,6 +106,12 @@ 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 { @@ -113,14 +119,21 @@ 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, ) @@ -129,8 +142,9 @@ 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 4026ba0a..b3992215 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::SbfVerifier, + verifier::check, vm::{Config, SyscallRegistry, TestInstructionMeter}, }; use std::collections::BTreeMap; @@ -32,8 +32,9 @@ 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 0006b492..badf8861 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::SbfVerifier, + verifier::check, vm::{Config, SyscallRegistry, TestInstructionMeter}, }; use std::collections::BTreeMap; @@ -30,8 +30,9 @@ 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 5bdb6342..7d6d7dc3 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::{SbfVerifier, TautologyVerifier, Verifier}, + verifier::check, vm::{EbpfVm, SyscallRegistry, TestInstructionMeter}, }; @@ -29,7 +29,7 @@ struct DumbFuzzData { fuzz_target!(|data: DumbFuzzData| { let prog = data.prog; let config = data.template.into(); - if SbfVerifier::verify(&prog, &config).is_err() { + if check(&prog, &config).is_err() { // verify please return; } @@ -37,8 +37,9 @@ 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 409b25bd..5e8a0702 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::{SbfVerifier, TautologyVerifier, Verifier}, + verifier::check, 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 SbfVerifier::verify(prog.into_bytes(), &config).is_err() { + if check(prog.into_bytes(), &config).is_err() { // verify please return; } @@ -41,8 +41,9 @@ 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 177c1cc3..863cf8a3 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::{SbfVerifier, TautologyVerifier, Verifier}, + verifier::check, vm::{EbpfVm, SyscallRegistry, TestInstructionMeter}, }; @@ -40,7 +40,7 @@ fuzz_target!(|data: FuzzData| { .set_imm(data.exit_imm) .push(); let config = data.template.into(); - if SbfVerifier::verify(prog.into_bytes(), &config).is_err() { + if check(prog.into_bytes(), &config).is_err() { // verify please return; } @@ -49,8 +49,9 @@ 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 36d84e4d..750d92b1 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::{SbfVerifier, TautologyVerifier, Verifier}, + verifier::check, 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 SbfVerifier::verify(prog.into_bytes(), &config).is_err() { + if check(prog.into_bytes(), &config).is_err() { // verify please return; } @@ -47,8 +47,9 @@ 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 82d0adb7..f20b81ed 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::{SbfVerifier, Verifier}; +use solana_rbpf::verifier::check; 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(); - SbfVerifier::verify(prog.into_bytes(), &config).unwrap(); + check(prog.into_bytes(), &config).unwrap(); }); diff --git a/src/assembler.rs b/src/assembler.rs index abfe8a76..70622cf0 100644 --- a/src/assembler.rs +++ b/src/assembler.rs @@ -20,8 +20,7 @@ use crate::{ ebpf::{self, Insn}, elf::{register_bpf_function, Executable}, error::UserDefinedError, - verifier::Verifier, - vm::{Config, InstructionMeter, SyscallRegistry}, + vm::{Config, InstructionMeter, SyscallRegistry, Verifier}, }; use std::{ collections::{BTreeMap, HashMap}, @@ -187,14 +186,15 @@ 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::SbfVerifier}; -/// let executable = assemble::( +/// use solana_rbpf::{assembler::assemble, user_error::UserError, vm::{Config, TestInstructionMeter, SyscallRegistry}, verifier::check}; +/// 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,8 +219,9 @@ 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> { @@ -388,6 +389,6 @@ pub fn assemble .iter() .flat_map(|insn| insn.to_vec()) .collect::>(); - Executable::::from_text_bytes::(&program, config, syscall_registry, bpf_functions) + Executable::::from_text_bytes(&program, verifier, config, syscall_registry, bpf_functions) .map_err(|err| format!("Executable constructor {:?}", err)) } diff --git a/src/elf.rs b/src/elf.rs index db4d20e8..2ded30d8 100644 --- a/src/elf.rs +++ b/src/elf.rs @@ -2086,8 +2086,9 @@ 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 a1db2cdb..a3c69421 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, verifier::TautologyVerifier}; + use crate::{syscalls, vm::{SyscallRegistry, SyscallObject, TestInstructionMeter}, elf::register_bpf_function}; use std::collections::BTreeMap; use byteorder::{LittleEndian, ByteOrder}; @@ -1819,8 +1819,9 @@ 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 3ee66974..7952b22c 100644 --- a/src/verifier.rs +++ b/src/verifier.rs @@ -79,21 +79,6 @@ 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 } @@ -205,167 +190,163 @@ fn check_imm_register( Ok(()) } -/// 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 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))); } - - check_registers(&insn, store, insn_ptr, config.dynamic_stack_frames)?; - - 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))); - } + check_registers(&insn, store, insn_ptr, config.dynamic_stack_frames)?; + + insn_ptr += 1; + } - Ok(()) + // 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(()) } diff --git a/src/vm.rs b/src/vm.rs index d67ee2cc..c9041e93 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::Verifier, + verifier::VerifierError, }; use std::{ collections::{BTreeMap, HashMap}, @@ -32,6 +32,16 @@ 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>; @@ -267,27 +277,35 @@ 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)?; - ::verify(executable.get_text_bytes().1, executable.get_config())?; + if let Some(verifier) = verifier { + verifier(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> { - 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))) + 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, + )))) } } @@ -419,7 +437,7 @@ impl Tracer { /// # Examples /// /// ``` -/// use solana_rbpf::{ebpf, elf::{Executable, register_bpf_function}, memory_region::MemoryRegion, vm::{Config, EbpfVm, TestInstructionMeter, SyscallRegistry}, verifier::SbfVerifier, user_error::UserError}; +/// use solana_rbpf::{ebpf, elf::{Executable, register_bpf_function}, memory_region::MemoryRegion, vm::{Config, EbpfVm, TestInstructionMeter, SyscallRegistry}, verifier::check, user_error::UserError}; /// /// let prog = &[ /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit @@ -433,7 +451,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, config, syscall_registry, bpf_functions).unwrap(); +/// let mut executable = Executable::::from_text_bytes(prog, Some(check), 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(); /// @@ -460,7 +478,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::SbfVerifier, user_error::UserError}; + /// use solana_rbpf::{ebpf, elf::{Executable, register_bpf_function}, vm::{Config, EbpfVm, TestInstructionMeter, SyscallRegistry}, verifier::check, user_error::UserError}; /// /// let prog = &[ /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit @@ -471,7 +489,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, config, syscall_registry, bpf_functions).unwrap(); + /// let mut executable = Executable::::from_text_bytes(prog, Some(check), config, syscall_registry, bpf_functions).unwrap(); /// let mut vm = EbpfVm::::new(&executable, &mut [], Vec::new()).unwrap(); /// ``` pub fn new( @@ -536,7 +554,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::SbfVerifier, syscalls::BpfTracePrintf, user_error::UserError}; + /// use solana_rbpf::{ebpf, elf::{Executable, register_bpf_function}, vm::{Config, EbpfVm, SyscallObject, SyscallRegistry, TestInstructionMeter}, verifier::check, 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);` @@ -562,7 +580,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, config, syscall_registry, bpf_functions).unwrap(); + /// let mut executable = Executable::::from_text_bytes(prog, Some(check), 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); @@ -614,7 +632,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::SbfVerifier, user_error::UserError}; + /// use solana_rbpf::{ebpf, elf::{Executable, register_bpf_function}, memory_region::MemoryRegion, vm::{Config, EbpfVm, TestInstructionMeter, SyscallRegistry}, verifier::check, user_error::UserError}; /// /// let prog = &[ /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit @@ -628,7 +646,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, config, syscall_registry, bpf_functions).unwrap(); + /// let mut executable = Executable::::from_text_bytes(prog, Some(check), 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 7cfba73c..1ad63c49 100644 --- a/tests/assembler.rs +++ b/tests/assembler.rs @@ -12,14 +12,15 @@ use solana_rbpf::{ assembler::assemble, ebpf, user_error::UserError, - verifier::{SbfVerifier, TautologyVerifier}, + verifier::check, 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(), )?; @@ -575,8 +576,9 @@ 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 a90c721f..208047ea 100644 --- a/tests/disassembler.rs +++ b/tests/disassembler.rs @@ -11,7 +11,6 @@ use solana_rbpf::{ assembler::assemble, static_analysis::Analysis, user_error::UserError, - verifier::TautologyVerifier, vm::{Config, SyscallRegistry, TestInstructionMeter}, }; @@ -23,8 +22,9 @@ 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 a0fa4c7a..8b2b3831 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::SbfVerifier, + verifier::check, vm::{Config, EbpfVm, SyscallObject, SyscallRegistry, TestInstructionMeter}, }; use std::{fs::File, io::Read}; @@ -127,10 +127,12 @@ fn test_fuzz_execute() { BpfSyscallU64::call, ) .unwrap(); - if let Ok(executable) = Executable::::from_elf::< - SbfVerifier, - >(bytes, Config::default(), syscall_registry) - { + if let Ok(executable) = Executable::::from_elf( + bytes, + Some(check), + Config::default(), + syscall_registry, + ) { let mut vm = EbpfVm::::new( &executable, &mut [], diff --git a/tests/ubpf_execution.rs b/tests/ubpf_execution.rs index ad2b6d77..e536c2d3 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::SbfVerifier, + verifier::check, 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, $config, syscall_registry).unwrap(); + let mut executable = assemble($source, Some(check), $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, $config, syscall_registry).unwrap(); + let mut executable = Executable::::from_elf(&elf, Some(check), $config, syscall_registry).unwrap(); test_interpreter_and_jit!(executable, $mem, $syscall_context, $check, $expected_instruction_count); } }; @@ -2889,9 +2889,13 @@ fn test_err_mem_access_out_of_bound() { ) .unwrap(); #[allow(unused_mut)] - let mut executable = Executable::::from_text_bytes::< - SbfVerifier, - >(&prog, config, syscall_registry, bpf_functions) + let mut executable = Executable::::from_text_bytes( + &prog, + Some(check), + config, + syscall_registry, + bpf_functions, + ) .unwrap(); test_interpreter_and_jit!( executable, @@ -3406,12 +3410,13 @@ 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, ) @@ -3547,8 +3552,9 @@ 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, ) @@ -3975,7 +3981,7 @@ fn test_err_unresolved_elf() { ..Config::default() }; assert!( - matches!(Executable::::from_elf::(&elf, config, syscall_registry), Err(EbpfError::ElfError(ElfError::UnresolvedSymbol(symbol, pc, offset))) if symbol == "log_64" && pc == 550 && offset == 4168) + 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) ); } @@ -4374,8 +4380,9 @@ 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 b9a1097e..15aeb59e 100644 --- a/tests/ubpf_verifier.rs +++ b/tests/ubpf_verifier.rs @@ -26,18 +26,29 @@ use solana_rbpf::{ assembler::assemble, ebpf, elf::Executable, + error::UserDefinedError, user_error::UserError, - verifier::{SbfVerifier, TautologyVerifier, Verifier, VerifierError}, + verifier::{check, 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(), ) @@ -49,16 +60,14 @@ fn test_verifier_success() { #[test] #[should_panic(expected = "NoProgram")] fn test_verifier_fail() { - struct FailingVerifier {} - impl Verifier for FailingVerifier { - fn verify(_prog: &[u8], _config: &Config) -> Result<(), VerifierError> { - Err(VerifierError::NoProgram) - } + fn verifier_fail(_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(), ) @@ -68,11 +77,12 @@ 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(), ) @@ -87,8 +97,9 @@ 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(), @@ -104,8 +115,9 @@ 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(), @@ -119,10 +131,11 @@ 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() @@ -141,10 +154,11 @@ 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() @@ -159,11 +173,12 @@ 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, @@ -177,11 +192,12 @@ 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(), ) @@ -191,10 +207,11 @@ 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(), ) @@ -204,10 +221,11 @@ 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(), ) @@ -221,8 +239,9 @@ 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(), @@ -233,10 +252,11 @@ 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(), ) @@ -271,8 +291,9 @@ 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(), ); @@ -305,8 +326,9 @@ 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() @@ -342,8 +364,9 @@ 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()