diff --git a/src/lib.rs b/src/lib.rs index def5dcf59..540838036 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -52,6 +52,7 @@ pub mod vm; #[cfg(all(feature = "jit", not(target_os = "windows"), target_arch = "x86_64"))] mod x86; +#[allow(dead_code)] trait ErrCheckedArithmetic: Sized { fn err_checked_add(self, other: Self) -> Result; fn err_checked_sub(self, other: Self) -> Result; diff --git a/tests/fuzz.rs b/tests/fuzz.rs new file mode 100644 index 000000000..389beb968 --- /dev/null +++ b/tests/fuzz.rs @@ -0,0 +1,257 @@ +#![allow(clippy::arithmetic_side_effects)] +#![cfg(all(feature = "jit", not(target_os = "windows"), target_arch = "x86_64"))] +// Copyright 2020 Solana Maintainers +// +// Licensed under the Apache License, Version 2.0 or +// the MIT license , at your option. This file may not be +// copied, modified, or distributed except according to those terms. + +extern crate byteorder; +extern crate libc; +extern crate solana_rbpf; +extern crate test_utils; +extern crate thiserror; + +use rand::{rngs::SmallRng, RngCore, SeedableRng}; +use solana_rbpf::{ + assembler::assemble, + ebpf, + memory_region::MemoryRegion, + program::{BuiltinFunction, BuiltinProgram, FunctionRegistry}, + static_analysis::Analysis, + verifier::RequisiteVerifier, + vm::{Config, ContextObject, TestContextObject}, +}; +use std::sync::Arc; +use test_utils::create_vm; + +macro_rules! test_interpreter_and_jit { + (register, $function_registry:expr, $location:expr => $syscall_function:expr) => { + $function_registry + .register_function_hashed($location.as_bytes(), $syscall_function) + .unwrap(); + }; + ($executable:expr, $mem:tt, $context_object:expr $(,)?) => { + let expected_instruction_count = $context_object.get_remaining(); + #[allow(unused_mut)] + let mut context_object = $context_object; + $executable.verify::().unwrap(); + let ( + instruction_count_interpreter, + interpreter_final_pc, + _tracer_interpreter, + interpreter_result, + ) = { + let mut mem = $mem; + let mem_region = MemoryRegion::new_writable(&mut mem, ebpf::MM_INPUT_START); + let mut context_object = context_object.clone(); + create_vm!( + vm, + &$executable, + &mut context_object, + stack, + heap, + vec![mem_region], + None + ); + let (instruction_count_interpreter, result) = vm.execute_program(&$executable, true); + ( + instruction_count_interpreter, + vm.registers[11], + vm.context_object_pointer.clone(), + result.unwrap(), + ) + }; + #[cfg(all(not(windows), target_arch = "x86_64"))] + { + #[allow(unused_mut)] + $executable.jit_compile().unwrap(); + let mut mem = $mem; + let mem_region = MemoryRegion::new_writable(&mut mem, ebpf::MM_INPUT_START); + create_vm!( + vm, + &$executable, + &mut context_object, + stack, + heap, + vec![mem_region], + None + ); + let (instruction_count_jit, result) = vm.execute_program(&$executable, false); + let tracer_jit = &vm.context_object_pointer; + if !TestContextObject::compare_trace_log(&_tracer_interpreter, tracer_jit) { + let analysis = Analysis::from_executable(&$executable).unwrap(); + let stdout = std::io::stdout(); + analysis + .disassemble_trace_log(&mut stdout.lock(), &_tracer_interpreter.trace_log) + .unwrap(); + analysis + .disassemble_trace_log(&mut stdout.lock(), &tracer_jit.trace_log) + .unwrap(); + panic!(); + } + assert_eq!( + result.unwrap(), + interpreter_result, + "Unexpected result for JIT" + ); + assert_eq!( + instruction_count_interpreter, instruction_count_jit, + "Interpreter and JIT instruction meter diverged", + ); + assert_eq!( + interpreter_final_pc, vm.registers[11], + "Interpreter and JIT instruction final PC diverged", + ); + } + if $executable.get_config().enable_instruction_meter { + assert_eq!( + instruction_count_interpreter, expected_instruction_count, + "Instruction meter did not consume expected amount" + ); + } + }; +} + +macro_rules! test_interpreter_and_jit_asm { + ($source:expr, $config:expr, $mem:tt, ($($location:expr => $syscall_function:expr),* $(,)?), $context_object:expr $(,)?) => { + #[allow(unused_mut)] + { + let mut config = $config; + config.enable_instruction_tracing = true; + let mut function_registry = FunctionRegistry::>::default(); + $(test_interpreter_and_jit!(register, function_registry, $location => $syscall_function);)* + let loader = Arc::new(BuiltinProgram::new_loader(config, function_registry)); + let mut executable = assemble($source, loader).unwrap(); + test_interpreter_and_jit!(executable, $mem, $context_object); + } + }; + ($source:expr, $mem:tt, ($($location:expr => $syscall_function:expr),* $(,)?), $context_object:expr $(,)?) => { + #[allow(unused_mut)] + { + test_interpreter_and_jit_asm!($source, Config::default(), $mem, ($($location => $syscall_function),*), $context_object); + } + }; +} + +// BPF_ALU : Arithmetic and Logic +#[test] +fn fuzz_alu() { + let seed = 0xC2DB2F8F282284A0; + let mut prng = SmallRng::seed_from_u64(seed); + + for src in 0..10 { + for dst in 0..10 { + for _ in 0..10 { + test_ins(format!("mov64 r{src}, r{dst}"), &mut prng); + test_ins(format!("add64 r{src}, r{dst}"), &mut prng); + test_ins(format!("sub64 r{src}, r{dst}"), &mut prng); + test_ins(format!("xor64 r{src}, r{dst}"), &mut prng); + test_ins(format!("and64 r{src}, r{dst}"), &mut prng); + test_ins(format!("lmul64 r{src}, r{dst}"), &mut prng); + test_ins(format!("uhmul64 r{src}, r{dst}"), &mut prng); + test_ins(format!("shmul64 r{src}, r{dst}"), &mut prng); + test_ins(format!("udiv64 r{src}, r{dst}"), &mut prng); + test_ins(format!("urem64 r{src}, r{dst}"), &mut prng); + test_ins(format!("sdiv64 r{src}, r{dst}"), &mut prng); + + test_ins(format!("lsh64 r{src}, r{dst}"), &mut prng); + test_ins(format!("rsh64 r{src}, r{dst}"), &mut prng); + test_ins(format!("arsh64 r{src}, r{dst}"), &mut prng); + + test_ins(format!("mov32 r{src}, r{dst}"), &mut prng); + test_ins(format!("add32 r{src}, r{dst}"), &mut prng); + test_ins(format!("sub32 r{src}, r{dst}"), &mut prng); + test_ins(format!("xor32 r{src}, r{dst}"), &mut prng); + test_ins(format!("and32 r{src}, r{dst}"), &mut prng); + test_ins(format!("lmul32 r{src}, r{dst}"), &mut prng); + // test_ins(format!("uhmul32 r{src}, r{dst}"), &mut prng); + // test_ins(format!("shmul32 r{src}, r{dst}"), &mut prng); + test_ins(format!("udiv32 r{src}, r{dst}"), &mut prng); + test_ins(format!("urem32 r{src}, r{dst}"), &mut prng); + test_ins(format!("sdiv32 r{src}, r{dst}"), &mut prng); + + test_ins(format!("lsh32 r{src}, r{dst}"), &mut prng); + test_ins(format!("rsh32 r{src}, r{dst}"), &mut prng); + test_ins(format!("arsh32 r{src}, r{dst}"), &mut prng); + } + } + + for _ in 0..10 { + let mut imm = prng.next_u32() as i32; + + test_ins(format!("mov64 r{src}, {imm}"), &mut prng); + test_ins(format!("add64 r{src}, {imm}"), &mut prng); + test_ins(format!("sub64 r{src}, {imm}"), &mut prng); + test_ins(format!("xor64 r{src}, {imm}"), &mut prng); + test_ins(format!("and64 r{src}, {imm}"), &mut prng); + test_ins(format!("lmul64 r{src}, {imm}"), &mut prng); + test_ins(format!("uhmul64 r{src}, {imm}"), &mut prng); + test_ins(format!("shmul64 r{src}, {imm}"), &mut prng); + test_ins(format!("udiv64 r{src}, {imm}"), &mut prng); + test_ins(format!("urem64 r{src}, {imm}"), &mut prng); + test_ins(format!("sdiv64 r{src}, {imm}"), &mut prng); + test_ins(format!("srem64 r{src}, {imm}"), &mut prng); + + test_ins(format!("mov32 r{src}, {imm}"), &mut prng); + test_ins(format!("add32 r{src}, {imm}"), &mut prng); + test_ins(format!("sub32 r{src}, {imm}"), &mut prng); + test_ins(format!("xor32 r{src}, {imm}"), &mut prng); + test_ins(format!("and32 r{src}, {imm}"), &mut prng); + test_ins(format!("lmul32 r{src}, {imm}"), &mut prng); + test_ins(format!("udiv32 r{src}, {imm}"), &mut prng); + test_ins(format!("urem32 r{src}, {imm}"), &mut prng); + test_ins(format!("sdiv32 r{src}, {imm}"), &mut prng); + test_ins(format!("srem32 r{src}, {imm}"), &mut prng); + + imm &= 63; + + test_ins(format!("lsh64 r{src}, {imm}"), &mut prng); + test_ins(format!("rsh64 r{src}, {imm}"), &mut prng); + test_ins(format!("arsh64 r{src}, {imm}"), &mut prng); + test_ins(format!("hor64 r{src}, {imm}"), &mut prng); + + imm &= 31; + + test_ins(format!("lsh32 r{src}, {imm}"), &mut prng); + test_ins(format!("rsh32 r{src}, {imm}"), &mut prng); + test_ins(format!("arsh32 r{src}, {imm}"), &mut prng); + + test_ins(format!("be64 r{src}"), &mut prng); + test_ins(format!("be32 r{src}"), &mut prng); + test_ins(format!("be16 r{src}"), &mut prng); + } + } +} + +fn test_ins(ins: String, prng: &mut SmallRng) { + let mut input = [0u8; 80]; + + prng.fill_bytes(&mut input); + + let asm = format!( + " + ldxdw r9, [r1+72] + ldxdw r8, [r1+64] + ldxdw r7, [r1+56] + ldxdw r6, [r1+48] + ldxdw r5, [r1+40] + ldxdw r4, [r1+32] + ldxdw r3, [r1+24] + ldxdw r2, [r1+16] + ldxdw r0, [r1+0] + ldxdw r1, [r1+8] + {ins} + xor64 r0, r1 + xor64 r0, r2 + xor64 r0, r3 + xor64 r0, r4 + xor64 r0, r5 + xor64 r0, r6 + xor64 r0, r7 + xor64 r0, r8 + xor64 r0, r9 + exit" + ); + test_interpreter_and_jit_asm!(asm.as_str(), input, (), TestContextObject::new(21)); +}