From 57e54b88d8a97674da11d1c905151b4b40bf035b Mon Sep 17 00:00:00 2001 From: xjd Date: Wed, 13 Mar 2024 11:27:59 +0800 Subject: [PATCH] Fuzzing snapshot2 * Fuzzing tests for snapshot2 * Randomize program and data --- fuzz/Cargo.toml | 9 ++ fuzz/fuzz_targets/snapshot2.rs | 184 +++++++++++++++++++++++++++++++++ src/snapshot2.rs | 3 + 3 files changed, 196 insertions(+) create mode 100644 fuzz/fuzz_targets/snapshot2.rs diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml index fcee6175..8b1ca668 100644 --- a/fuzz/Cargo.toml +++ b/fuzz/Cargo.toml @@ -16,6 +16,9 @@ spike-sys = "0.1.2" path = ".." features = ["asm"] +[dependencies.ckb-vm-definitions] +path = "../definitions" + # Prevent this from interfering with workspaces [workspace] members = ["."] @@ -43,3 +46,9 @@ 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 diff --git a/fuzz/fuzz_targets/snapshot2.rs b/fuzz/fuzz_targets/snapshot2.rs new file mode 100644 index 00000000..f84cf31a --- /dev/null +++ b/fuzz/fuzz_targets/snapshot2.rs @@ -0,0 +1,184 @@ +#![allow(dead_code)] +#![no_main] +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, DEFAULT_MEMORY_SIZE, + ISA_A, ISA_B, ISA_IMC, ISA_MOP, RISCV_PAGESIZE, +}; +use ckb_vm_definitions::asm::AsmCoreMachine; +use libfuzzer_sys::fuzz_target; +use std::collections::VecDeque; + +struct Deque { + n: VecDeque, +} + +impl Deque { + fn new(data: [u8; 96]) -> Self { + Self { + n: VecDeque::from(data), + } + } + + fn u8(&mut self) -> u8 { + let r = self.n.pop_front().unwrap(); + self.n.push_back(r); + r + } + + fn u16(&mut self) -> u16 { + let mut r = [0u8; 2]; + r.fill_with(|| self.u8()); + u16::from_le_bytes(r) + } + + fn u32(&mut self) -> u32 { + let mut r = [0u8; 4]; + r.fill_with(|| self.u8()); + u32::from_le_bytes(r) + } +} + +const DATA_SOURCE_PROGRAM: u32 = 0; +const DATA_SOURCE_CONTENT: u32 = 1; +const MAX_TX_SIZE: u32 = 600_000; + +#[derive(Clone)] +pub struct DummyData { + pub program: Bytes, + pub content: Bytes, +} + +impl DataSource 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_CONTENT => &self.content, + _ => unreachable!(), + }; + let offset = std::cmp::min(offset as usize, data.len()); + let full_size = data.len() - offset; + let real_size = std::cmp::min(full_size, length as usize); + Ok((data.slice(offset..offset + real_size), full_size as u64)) + } +} + +fn build_machine() -> DefaultMachine> { + 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() +} + +fn dump_loading_action(action: &LoadingAction) { + eprintln!( + "LoadingAction: addr = {}, size = {}, source = {}..{}, offset_from_addr = {}", + action.addr, action.size, action.source.start, action.source.end, action.offset_from_addr + ); +} + +#[derive(Debug)] +struct StoreBytesAction { + pub addr: u64, + pub offset: u64, + pub length: u64, +} + +fuzz_target!(|data: [u8; 96]| { + let mut deque = Deque::new(data); + let dummy_data = { + let mut program = vec![0u8; (deque.u32() % MAX_TX_SIZE) as usize]; + for i in 0..program.len() { + // fill with different pattern than content + program[i] = (i % 3) as u8; + } + let mut content = vec![0u8; (deque.u32() % MAX_TX_SIZE) as usize]; + for i in 0..content.len() { + content[i] = (i % 5) as u8 + 10; + } + DummyData { + program: program.into(), + content: content.into(), + } + }; + let mut loading_action_vec: Vec = Vec::new(); + for _ in 0..2 { + let p_vaddr = deque.u32() as u64; + let p_memsz = deque.u32() as u64; + let p_offset = deque.u32() as u64; + let p_filesz = deque.u32() as u64; + 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); + if slice_start >= slice_end || slice_end >= dummy_data.program.len() as u64 { + return; + } + 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.clone(), + entry: 0, + }; + + let mut machine1 = build_machine(); + let mut machine2 = build_machine(); + let result = machine1.load_program_with_metadata(&dummy_data.program, &metadata, &vec![]); + if result.is_err() { + return; + } + let result = ctx.mark_program(&mut machine1, &metadata, &0, 0); + if result.is_err() { + return; + } + let mut store_bytes_actions = vec![]; + for _ in 0..2 { + let length = deque.u32() as u64; + let offset = deque.u32() as u64; + let addr = deque.u32() as u64; + let result = ctx.store_bytes(&mut machine1, addr, &DATA_SOURCE_CONTENT, offset, length); + store_bytes_actions.push(StoreBytesAction { + addr, + offset, + length, + }); + if result.is_err() { + return; + } + } + let snapshot = ctx.make_snapshot(&mut machine1).unwrap(); + ctx.resume(&mut machine2, &snapshot).unwrap(); + for i in 0..DEFAULT_MEMORY_SIZE / RISCV_PAGESIZE { + 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[0..16] = {:?}", &mem1[0..32]); + eprintln!("mem2[0..16] = {:?}", &mem2[0..32]); + eprintln!("program length = {}", dummy_data.program.len()); + eprintln!("content length = {}", dummy_data.content.len()); + for action in &loading_action_vec { + dump_loading_action(action); + } + for action in &store_bytes_actions { + eprintln!("{:?}", action); + } + panic!("The memory restored by operation resume is not same as snapshot operation at page {}", i); + } + } +}); diff --git a/src/snapshot2.rs b/src/snapshot2.rs index 27bf471a..bb0caae5 100644 --- a/src/snapshot2.rs +++ b/src/snapshot2.rs @@ -267,6 +267,9 @@ impl> Snapshot2Context { start: u64, length: u64, ) -> Result<(), Error> { + if length == 0 { + return Ok(()); + } let page_indices = get_page_indices(start, length); for page in page_indices.0..=page_indices.1 { machine.memory_mut().set_flag(page, FLAG_DIRTY)?;