Skip to content

Commit

Permalink
add fuzzer for ObjectCache, used for compressed serialization
Browse files Browse the repository at this point in the history
  • Loading branch information
arvidn committed Dec 16, 2024
1 parent 210d7e5 commit 563cbf7
Show file tree
Hide file tree
Showing 5 changed files with 110 additions and 2 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions fuzz/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ cargo-fuzz = true
[dependencies]
libfuzzer-sys = { workspace = true }
clvmr = { workspace = true }
chia-sha2 = { workspace = true }

[[bin]]
name = "fuzz_run_program"
Expand Down Expand Up @@ -77,3 +78,9 @@ name = "keccak"
path = "fuzz_targets/keccak.rs"
test = false
doc = false

[[bin]]
name = "object-cache"
path = "fuzz_targets/object_cache.rs"
test = false
doc = false
73 changes: 71 additions & 2 deletions fuzz/fuzz_targets/fuzzing_utils.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use clvmr::allocator::Allocator;
use clvmr::allocator::NodePtr;
use chia_sha2::Sha256;
use clvmr::allocator::{Allocator, NodePtr, SExp};

pub struct BitCursor<'a> {
data: &'a [u8],
Expand Down Expand Up @@ -77,3 +77,72 @@ pub fn make_tree(a: &mut Allocator, cursor: &mut BitCursor, short_atoms: bool) -
}
}
}

fn hash_atom(buf: &[u8]) -> [u8; 32] {
let mut ctx = Sha256::new();
ctx.update([1_u8]);
ctx.update(buf);
ctx.finalize()
}

fn hash_pair(left: &[u8; 32], right: &[u8; 32]) -> [u8; 32] {
let mut ctx = Sha256::new();
ctx.update([2_u8]);
ctx.update(left);
ctx.update(right);
ctx.finalize()
}

enum TreeOp {
SExp(NodePtr),
Cons,
}

pub fn tree_hash(a: &Allocator, node: NodePtr) -> [u8; 32] {
let mut hashes = Vec::new();
let mut ops = vec![TreeOp::SExp(node)];

while let Some(op) = ops.pop() {
match op {
TreeOp::SExp(node) => match a.sexp(node) {
SExp::Atom => {
hashes.push(hash_atom(a.atom(node).as_ref()));
}
SExp::Pair(left, right) => {
ops.push(TreeOp::Cons);
ops.push(TreeOp::SExp(left));
ops.push(TreeOp::SExp(right));
}
},
TreeOp::Cons => {
let first = hashes.pop().unwrap();
let rest = hashes.pop().unwrap();
hashes.push(hash_pair(&first, &rest));
}
}
}

assert!(hashes.len() == 1);
hashes[0]
}

pub fn visit_tree(a: &Allocator, node: NodePtr, mut visit: impl FnMut(&Allocator, NodePtr)) {
let mut nodes = vec![node];
let mut visited_index = 0;

while nodes.len() > visited_index {
match a.sexp(nodes[visited_index]) {
SExp::Atom => {}
SExp::Pair(left, right) => {
nodes.push(left);
nodes.push(right);
}
}
visited_index += 1;
}

// visit nodes bottom-up (right to left).
for node in nodes.into_iter().rev() {
visit(a, node);
}
}
30 changes: 30 additions & 0 deletions fuzz/fuzz_targets/object_cache.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#![no_main]
mod fuzzing_utils;

use clvmr::serde::{node_to_bytes, serialized_length, treehash, ObjectCache};
use clvmr::Allocator;
use libfuzzer_sys::fuzz_target;

use fuzzing_utils::{make_tree, tree_hash, visit_tree, BitCursor};

fn do_fuzz(data: &[u8], short_atoms: bool) {
let mut cursor = BitCursor::new(data);
let mut allocator = Allocator::new();
let program = make_tree(&mut allocator, &mut cursor, short_atoms);

let mut hash_cache = ObjectCache::new(treehash);
let mut length_cache = ObjectCache::new(serialized_length);
visit_tree(&allocator, program, |a, node| {
let expect_hash = tree_hash(a, node);
let expect_len = node_to_bytes(a, node).unwrap().len() as u64;
let computed_hash = hash_cache.get_or_calculate(a, &node).unwrap();
let computed_len = length_cache.get_or_calculate(a, &node).unwrap();
assert_eq!(computed_hash, &expect_hash);
assert_eq!(computed_len, &expect_len);
});
}

fuzz_target!(|data: &[u8]| {
do_fuzz(data, true);
do_fuzz(data, false);
});
1 change: 1 addition & 0 deletions src/serde/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ mod test;
pub use de::node_from_bytes;
pub use de_br::{node_from_bytes_backrefs, node_from_bytes_backrefs_record};
pub use de_tree::{parse_triples, ParsedTriple};
pub use object_cache::{serialized_length, treehash, ObjectCache};
pub use ser::{node_to_bytes, node_to_bytes_limit};
pub use ser_br::{node_to_bytes_backrefs, node_to_bytes_backrefs_limit};
pub use tools::{
Expand Down

0 comments on commit 563cbf7

Please sign in to comment.