Skip to content

Commit dfc8e44

Browse files
authored
Merge pull request #33 from eth-hca/test/new-fuzz-targets
test: add fuzz_rlp_decode and fuzz_evm_opcode targets
2 parents 4d48442 + bacf632 commit dfc8e44

5 files changed

Lines changed: 88 additions & 11 deletions

File tree

Makefile

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ CARGO_NIGHTLY := $(shell which cargo 2>/dev/null || echo $(HOME)/.cargo/bin/carg
55
test test-unit test-property test-vector test-doc test-all test-no-default test-one \
66
bench bench-hash bench-merkle bench-address bench-flow \
77
fuzz fuzz-merkle fuzz-proof fuzz-witness fuzz-rlp \
8+
fuzz-rlp-decode fuzz-evm-opcode fuzz-smoke \
89
example-create example-spend example-verify examples \
910
cli cli-help cli-demo \
1011
cli-create cli-derive cli-proof cli-verify cli-signing \
@@ -79,7 +80,7 @@ bench-flow:
7980
# Fuzz (requires nightly)
8081
# ─────────────────────────────────────────────
8182

82-
fuzz: fuzz-merkle fuzz-proof fuzz-witness fuzz-rlp
83+
fuzz: fuzz-merkle fuzz-proof fuzz-witness fuzz-rlp fuzz-rlp-decode fuzz-evm-opcode
8384

8485
fuzz-merkle:
8586
$(CARGO_NIGHTLY) fuzz run fuzz_merkle
@@ -93,6 +94,20 @@ fuzz-witness:
9394
fuzz-rlp:
9495
$(CARGO_NIGHTLY) fuzz run fuzz_rlp
9596

97+
fuzz-rlp-decode:
98+
$(CARGO_NIGHTLY) fuzz run fuzz_rlp_decode
99+
100+
fuzz-evm-opcode:
101+
$(CARGO_NIGHTLY) fuzz run fuzz_evm_opcode
102+
103+
fuzz-smoke:
104+
$(CARGO_NIGHTLY) fuzz run fuzz_merkle -- -max_total_time=30
105+
$(CARGO_NIGHTLY) fuzz run fuzz_proof -- -max_total_time=30
106+
$(CARGO_NIGHTLY) fuzz run fuzz_witness -- -max_total_time=30
107+
$(CARGO_NIGHTLY) fuzz run fuzz_rlp -- -max_total_time=30
108+
$(CARGO_NIGHTLY) fuzz run fuzz_rlp_decode -- -max_total_time=30
109+
$(CARGO_NIGHTLY) fuzz run fuzz_evm_opcode -- -max_total_time=30
110+
96111
# ─────────────────────────────────────────────
97112
# Examples
98113
# ─────────────────────────────────────────────

fuzz/Cargo.toml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,3 +40,17 @@ path = "fuzz_targets/fuzz_rlp.rs"
4040
test = false
4141
doc = false
4242
bench = false
43+
44+
[[bin]]
45+
name = "fuzz_rlp_decode"
46+
path = "fuzz_targets/fuzz_rlp_decode.rs"
47+
test = false
48+
doc = false
49+
bench = false
50+
51+
[[bin]]
52+
name = "fuzz_evm_opcode"
53+
path = "fuzz_targets/fuzz_evm_opcode.rs"
54+
test = false
55+
doc = false
56+
bench = false
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#![no_main]
2+
3+
use hca_rs::evm::opcode::validate_leaf_script;
4+
use libfuzzer_sys::fuzz_target;
5+
6+
fuzz_target!(|data: &[u8]| {
7+
// Feed arbitrary bytecode — including PUSH data, truncated PUSH at EOF,
8+
// banned opcodes, and random sequences — into the validator.
9+
// Must never panic — only return Ok or Err.
10+
let _ = validate_leaf_script(data);
11+
});
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
#![no_main]
2+
3+
use hca_rs::rlp::{decode_bytes, decode_hca_tx, decode_list, decode_uint};
4+
use libfuzzer_sys::fuzz_target;
5+
6+
fuzz_target!(|data: &[u8]| {
7+
if data.is_empty() {
8+
return;
9+
}
10+
11+
// Use first byte to select which decoder to fuzz
12+
let test_type = data[0] % 4;
13+
let input = &data[1..];
14+
15+
match test_type {
16+
0 => {
17+
// Fuzz decode_bytes: malformed inputs, truncated data, oversized lengths
18+
let _ = decode_bytes(input);
19+
}
20+
1 => {
21+
// Fuzz decode_uint: should never return a value wider than u128
22+
let _ = decode_uint(input);
23+
}
24+
2 => {
25+
// Fuzz decode_list: random list prefixes, truncated payloads
26+
let _ = decode_list(input);
27+
}
28+
3 => {
29+
// Fuzz decode_hca_tx: full transaction decoding with arbitrary bytes
30+
// Must never panic — only return Ok or Err
31+
let _ = decode_hca_tx(input);
32+
}
33+
_ => unreachable!(),
34+
}
35+
});

src/rlp.rs

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -253,16 +253,17 @@ pub fn decode_bytes(input: &[u8]) -> HcaResult<(Vec<u8>, usize)> {
253253
len
254254
)));
255255
}
256-
if input.len() < 1 + len_of_len + len {
256+
let total = (1usize)
257+
.checked_add(len_of_len)
258+
.and_then(|n| n.checked_add(len))
259+
.ok_or_else(|| HcaError::RlpDecodeError("length overflow".to_string()))?;
260+
if input.len() < total {
257261
return Err(HcaError::RlpDecodeError(format!(
258262
"long string payload truncated: need {} bytes",
259263
len
260264
)));
261265
}
262-
return Ok((
263-
input[1 + len_of_len..1 + len_of_len + len].to_vec(),
264-
1 + len_of_len + len,
265-
));
266+
return Ok((input[1 + len_of_len..total].to_vec(), total));
266267
}
267268

268269
Err(HcaError::RlpDecodeError(format!(
@@ -334,16 +335,17 @@ pub fn decode_list(input: &[u8]) -> HcaResult<(Vec<u8>, usize)> {
334335
));
335336
}
336337
let len = decode_usize_be(&input[1..1 + len_of_len])?;
337-
if input.len() < 1 + len_of_len + len {
338+
let total = (1usize)
339+
.checked_add(len_of_len)
340+
.and_then(|n| n.checked_add(len))
341+
.ok_or_else(|| HcaError::RlpDecodeError("length overflow".to_string()))?;
342+
if input.len() < total {
338343
return Err(HcaError::RlpDecodeError(format!(
339344
"long list payload truncated: need {} bytes",
340345
len
341346
)));
342347
}
343-
Ok((
344-
input[1 + len_of_len..1 + len_of_len + len].to_vec(),
345-
1 + len_of_len + len,
346-
))
348+
Ok((input[1 + len_of_len..total].to_vec(), total))
347349
}
348350

349351
/// Decode a raw HCA typed transaction.

0 commit comments

Comments
 (0)