Skip to content

Commit

Permalink
Fix fuzz
Browse files Browse the repository at this point in the history
  • Loading branch information
mohanson committed Mar 13, 2024
1 parent 8cd605f commit eb92214
Showing 1 changed file with 82 additions and 174 deletions.
256 changes: 82 additions & 174 deletions fuzz/fuzz_targets/snapshot2.rs
Original file line number Diff line number Diff line change
@@ -1,171 +1,67 @@
#![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,
ISA_IMC, ISA_MOP,
};
use ckb_vm_definitions::{asm::AsmCoreMachine, RISCV_MAX_MEMORY};
use libfuzzer_sys::fuzz_target;
use std::collections::VecDeque;

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,
struct Deque {
n: VecDeque<u8>,
}

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;
impl Deque {
fn new(data: [u8; 512]) -> Self {
Self {
addr,
offset,
length,
n: VecDeque::from(data),
}
}
}

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()
fn u8(&mut self) -> u8 {
let r = self.n.pop_front().unwrap();
self.n.push_back(r);
r
}
}

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 }
fn u16(&mut self) -> u16 {
let mut r = [0u8; 2];
r.fill_with(|| self.u8());
u16::from_le_bytes(r)
}
}

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,
}
fn u64(&mut self) -> u64 {
let mut r = [0u8; 8];
r.fill_with(|| self.u8());
u64::from_le_bytes(r)
}
}

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()
}
const DATA_SOURCE_PROGRAM: u32 = 0;
const DATA_SOURCE_CONTENT: u32 = 1;

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

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,
DATA_SOURCE_CONTENT => &self.content,
_ => {
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,
data.slice(offset as usize..offset as usize + length as usize),
length as u64,
))
}
}
Expand All @@ -176,48 +72,60 @@ fn build_machine() -> DefaultMachine<Box<AsmCoreMachine>> {
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;
fuzz_target!(|data: [u8; 512]| {
let mut deque = Deque::new(data);
let dummy_data = {
let mut program = vec![0u8; deque.u16() as usize + 1024];
program.fill_with(|| deque.u8());
let mut content = vec![0u8; deque.u16() as usize + 1024];
content.fill_with(|| deque.u8());
DummyData {
program: program.into(),
content: content.into(),
}
};
let mut loading_action_vec: Vec<LoadingAction> = Vec::new();
for i in 0..deque.u8() as usize {
let segsize = deque.u64() % 1024;
let p_vaddr = i as u64 * 16 * 1024;
let p_memsz = segsize;
let p_offset = deque.u64() % (dummy_data.program.len() as u64 - segsize);
let p_filesz = segsize;
let aligned_start = round_page_down(p_vaddr);
let padding_start = (p_vaddr).wrapping_sub(aligned_start);
let size = round_page_up((p_memsz).wrapping_add(padding_start));
let slice_start = p_offset;
let slice_end = p_offset.wrapping_add(p_filesz);
assert!(slice_start <= slice_end);
loading_action_vec.push(LoadingAction {
addr: aligned_start,
size,
flags: FLAG_EXECUTABLE | FLAG_FREEZED,
source: slice_start as u64..slice_end as u64,
offset_from_addr: padding_start,
})
}
let mut ctx = Snapshot2Context::new(dummy_data.clone());
let metadata = ProgramMetadata {
actions: loading_action_vec,
entry: 0,
};

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);
}
}
machine1
.load_program_with_metadata(&dummy_data.program, &metadata, &vec![])
.unwrap();
ctx.mark_program(&mut machine1, &metadata, &0, 0).unwrap();
let snapshot = ctx.make_snapshot(&mut machine1).unwrap();
ctx.resume(&mut machine2, &snapshot).unwrap();
let mem1 = machine1
.memory_mut()
.load_bytes(0, RISCV_MAX_MEMORY as u64)
.unwrap();
let mem2 = machine2
.memory_mut()
.load_bytes(0, RISCV_MAX_MEMORY as u64)
.unwrap();
assert_eq!(mem1, mem2);
});

0 comments on commit eb92214

Please sign in to comment.