Skip to content

Commit

Permalink
Fuzzing snapshot2 (#2)
Browse files Browse the repository at this point in the history
* Fuzzing tests for snapshot2

* Randomize program and data
  • Loading branch information
XuJiandong authored Mar 13, 2024
1 parent 953a947 commit 8cd605f
Show file tree
Hide file tree
Showing 3 changed files with 241 additions and 0 deletions.
11 changes: 11 additions & 0 deletions fuzz/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,14 @@ cargo-fuzz = true
libfuzzer-sys = "0.4"
spike-sys = "0.1.2"


[dependencies.ckb-vm]
path = ".."
features = ["asm"]

[dependencies.ckb-vm-definitions]
path = "../definitions"

# Prevent this from interfering with workspaces
[workspace]
members = ["."]
Expand Down Expand Up @@ -43,3 +47,10 @@ name = "isa_b"
path = "fuzz_targets/isa_b.rs"
test = false
doc = false


[[bin]]
name = "snapshot2"
path = "fuzz_targets/snapshot2.rs"
test = false
doc = false
223 changes: 223 additions & 0 deletions fuzz/fuzz_targets/snapshot2.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,223 @@
#![no_main]
use std::convert::TryInto;

use ckb_vm::{
elf::{LoadingAction, ProgramMetadata},
machine::VERSION2,
memory::{round_page_down, round_page_up, FLAG_EXECUTABLE, FLAG_FREEZED},
snapshot2::{DataSource, Snapshot2Context},
Bytes, CoreMachine, DefaultMachine, DefaultMachineBuilder, Error, Memory, ISA_A, ISA_B,
ISA_IMC, ISA_MOP, RISCV_PAGES, RISCV_PAGESIZE,
};
use ckb_vm_definitions::{asm::AsmCoreMachine, RISCV_MAX_MEMORY};
use libfuzzer_sys::fuzz_target;

const DATA_SOURCE_PROGRAM: u32 = 0;
const DATA_SOURCE_OTHERS: u32 = 1;

#[derive(Clone)]
pub struct DummyData {
pub program: Bytes,
pub data: Bytes,
}

pub struct StoreBytesAction {
pub addr: u64,
pub offset: u64,
pub length: u64,
}

pub trait Fuzzing {
const SIZE: usize = 0;
fn new_from_data(data: &[u8]) -> Self;
}

impl Fuzzing for StoreBytesAction {
const SIZE: usize = 12;
fn new_from_data(data: &[u8]) -> Self {
if data.len() < Self::SIZE {
return Self {
addr: 0,
offset: 0,
length: 0,
};
}
let addr =
(u32::from_le_bytes(data[0..4].try_into().unwrap()) % RISCV_MAX_MEMORY as u32) as u64;
let offset =
(u32::from_le_bytes(data[4..8].try_into().unwrap()) % RISCV_MAX_MEMORY as u32) as u64;
let length =
(u32::from_le_bytes(data[8..12].try_into().unwrap()) % RISCV_MAX_MEMORY as u32) as u64;
Self {
addr,
offset,
length,
}
}
}

impl Fuzzing for Vec<StoreBytesAction> {
fn new_from_data(data: &[u8]) -> Self {
if data.len() < StoreBytesAction::SIZE {
return vec![];
}
data.chunks(StoreBytesAction::SIZE)
.map(|d| StoreBytesAction::new_from_data(d))
.collect()
}
}

impl Fuzzing for DummyData {
const SIZE: usize = 4;
fn new_from_data(data: &[u8]) -> Self {
if data.len() < 4 {
return Self {
program: Bytes::new(),
data: Bytes::new(),
};
}
let program_size = u16::from_le_bytes(data[0..2].try_into().unwrap()) as usize;
let data_size = u16::from_le_bytes(data[2..4].try_into().unwrap()) as usize;
let mut program = vec![0u8; program_size];
for i in 0..program.len() {
program[i] = (i % 256) as u8;
}
let program = program.into();
let mut data = vec![0u8; data_size];
for i in 0..data.len() {
data[i] = (i % 255) as u8;
}
let data = data.into();
Self { program, data }
}
}

impl Fuzzing for LoadingAction {
const SIZE: usize = 16;
fn new_from_data(data: &[u8]) -> Self {
if data.len() < Self::SIZE {
return Self {
addr: 0,
size: 0,
flags: 0,
source: 0..0,
offset_from_addr: 0,
};
}
// mimic parse_elf
let mut p_vaddr = u32::from_le_bytes(data[0..4].try_into().unwrap());
p_vaddr = p_vaddr % RISCV_MAX_MEMORY as u32;

let mut p_memsz = u32::from_le_bytes(data[4..8].try_into().unwrap());
p_memsz = p_memsz % RISCV_MAX_MEMORY as u32;

let mut p_offset = u32::from_le_bytes(data[8..12].try_into().unwrap());
p_offset = p_offset % RISCV_MAX_MEMORY as u32;

let mut p_filesz = u32::from_le_bytes(data[12..16].try_into().unwrap());
p_filesz = p_filesz % RISCV_MAX_MEMORY as u32;

let aligned_start = round_page_down(p_vaddr as u64);
let padding_start = (p_vaddr as u64).wrapping_sub(aligned_start);

let size = round_page_up((p_memsz as u64).wrapping_add(padding_start));
let slice_start = p_offset;
let slice_end = p_offset.wrapping_add(p_filesz);
if slice_start > slice_end {
panic!("ElfSegmentAddrOrSizeError");
}
Self {
addr: aligned_start,
size,
flags: FLAG_EXECUTABLE | FLAG_FREEZED,
source: slice_start as u64..slice_end as u64,
offset_from_addr: padding_start,
}
}
}

impl Fuzzing for Vec<LoadingAction> {
fn new_from_data(data: &[u8]) -> Self {
if data.len() < LoadingAction::SIZE {
return vec![];
}
data.chunks(LoadingAction::SIZE)
.map(|d| LoadingAction::new_from_data(d))
.collect()
}
}

impl DataSource<u32> for DummyData {
fn load_data(&self, id: &u32, offset: u64, length: u64) -> Result<(Bytes, u64), Error> {
let data = match *id {
DATA_SOURCE_PROGRAM => &self.program,
DATA_SOURCE_OTHERS => &self.data,
_ => {
panic!("invalid id")
}
};
let offset = std::cmp::min(offset as usize, data.len());
let full_length = data.len() - offset;
let slice_length = if length > 0 {
std::cmp::min(full_length, length as usize)
} else {
full_length
};
Ok((
data.slice(offset..offset + slice_length),
full_length as u64,
))
}
}

fn build_machine() -> DefaultMachine<Box<AsmCoreMachine>> {
let isa = ISA_IMC | ISA_A | ISA_B | ISA_MOP;
let core_machine = AsmCoreMachine::new(isa, VERSION2, u64::max_value());
DefaultMachineBuilder::new(core_machine).build()
}

fuzz_target!(|data: &[u8]| {
let actions_length = LoadingAction::SIZE * 2;
let dummy_length = DummyData::SIZE;
let total_length = actions_length + dummy_length;
if data.len() < total_length {
return;
}
let mut machine1 = build_machine();
let mut machine2 = build_machine();

let actions = Vec::<LoadingAction>::new_from_data(&data[0..actions_length]);
let dummy_data = DummyData::new_from_data(&data[actions_length..actions_length + dummy_length]);
let mut ctx = Snapshot2Context::new(dummy_data);
let metadata = ProgramMetadata { actions, entry: 0 };
let result = ctx.mark_program(&mut machine1, &metadata, &0, 0);
if result.is_err() {
return;
}
// TODO: store_bytes
let result = ctx.make_snapshot(&mut machine1);
if result.is_err() {
return;
}
let snapshot = result.unwrap();
let result = ctx.resume(&mut machine2, &snapshot);
if result.is_err() {
return;
}

for i in 0..RISCV_PAGES {
let mem1 = machine1
.memory_mut()
.load_bytes((i * RISCV_PAGESIZE) as u64, RISCV_PAGESIZE as u64)
.unwrap();
let mem2 = machine2
.memory_mut()
.load_bytes((i * RISCV_PAGESIZE) as u64, RISCV_PAGESIZE as u64)
.unwrap();
if mem1 != mem2 {
eprintln!("mem1 = {:?}", mem1);
eprintln!("mem2 = {:?}", mem2);
panic!("The memory restored by operation resume is not same as snapshot operation at page {}", i);
}
}
});
7 changes: 7 additions & 0 deletions fuzz/run-snapshot2.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@

fuzz() {
cargo +nightly fuzz run -j $(nproc) $1 -- -max_total_time=$2 -timeout=2 -max_len=36 -len_control=1
}


fuzz snapshot2 300

0 comments on commit 8cd605f

Please sign in to comment.