Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions crates/ssz/tests/encode_decode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,31 @@ fn vec_of_vecs_empty() {
assert_eq!(Vec::<Vec<u8>>::from_ssz_bytes(&encoded).unwrap(), val);
}

// A zero first offset would imply 0 elements while the offset/scope boundary
// describes 1 element spanning the whole input; it must be rejected up front.
// See https://github.com/leanEthereum/leanSpec/issues/1175
#[test]
fn vec_of_vecs_zero_first_offset() {
let bytes = [0x00, 0x00, 0x00, 0x00, 0xAA, 0xBB, 0xCC, 0xDD];
assert_eq!(
Vec::<Vec<u8>>::from_ssz_bytes(&bytes),
Err(DecodeError::InvalidFirstOffset {
expected: 4,
got: 0
})
);

// Same with no trailing data: only an offset table pointing at itself.
let bytes = [0x00, 0x00, 0x00, 0x00];
assert_eq!(
Vec::<Vec<u8>>::from_ssz_bytes(&bytes),
Err(DecodeError::InvalidFirstOffset {
expected: 4,
got: 0
})
);
}

// ── ContainerEncoder / ContainerDecoder ──

/// Simulates a container: { a: u32, b: Vec<u8>, c: u16 }
Expand Down
19 changes: 19 additions & 0 deletions fuzz/fuzz_targets/offset_adversarial.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@ struct FixedThenVar {
data: Vec<u8>,
}

#[derive(Debug, PartialEq, SszEncode, SszDecode)]
struct VarItemsField {
items: Vec<Vec<u8>>,
}

fuzz_target!(|data: &[u8]| {
// Attempt decode of each struct — must never panic regardless of input.
// If decode succeeds, roundtrip must hold.
Expand Down Expand Up @@ -61,4 +66,18 @@ fuzz_target!(|data: &[u8]| {
let decoded = FixedThenVar::from_ssz_bytes(&encoded).unwrap();
assert_eq!(decoded, v, "FixedThenVar roundtrip");
}

// Lists of variable-length items have their own offset table, exercising
// decode_variable_length_items_with_max rather than ContainerDecoder.
if let Ok(v) = Vec::<Vec<u8>>::from_ssz_bytes(data) {
let encoded = v.to_ssz();
let decoded = Vec::<Vec<u8>>::from_ssz_bytes(&encoded).unwrap();
assert_eq!(decoded, v, "Vec<Vec<u8>> roundtrip");
}

if let Ok(v) = VarItemsField::from_ssz_bytes(data) {
let encoded = v.to_ssz();
let decoded = VarItemsField::from_ssz_bytes(&encoded).unwrap();
assert_eq!(decoded, v, "VarItemsField roundtrip");
}
});
Loading