Skip to content

Commit

Permalink
fuzz alu32
Browse files Browse the repository at this point in the history
  • Loading branch information
seanyoung committed Feb 18, 2024
1 parent 179a0f9 commit b1fdeb9
Show file tree
Hide file tree
Showing 2 changed files with 258 additions and 0 deletions.
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Self, ArithmeticOverflow>;
fn err_checked_sub(self, other: Self) -> Result<Self, ArithmeticOverflow>;
Expand Down
257 changes: 257 additions & 0 deletions tests/fuzz.rs
Original file line number Diff line number Diff line change
@@ -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 <[email protected]>
//
// Licensed under the Apache License, Version 2.0 <http://www.apache.org/licenses/LICENSE-2.0> or
// the MIT license <http://opensource.org/licenses/MIT>, 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::<RequisiteVerifier>().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::<BuiltinFunction<TestContextObject>>::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));
}

0 comments on commit b1fdeb9

Please sign in to comment.