decode_payload.rs
⎇
Raw
1//! Fuzzes the payload decoders directly, behind the AEAD.
2//!
3//! ```sh
4//! cargo +nightly fuzz run decode_payload
5//! ```
6//!
7//! This is where the structural parsing lives — point counts that disagree with
8//! the payload length, unknown enum discriminants, arithmetic on attacker-chosen
9//! lengths. Reaching it through a sealed datagram would require forging a
10//! Poly1305 tag, so it gets its own target with the crypto stripped away. In
11//! production only a client holding a valid token can reach this code, which
12//! bounds the blast radius but does not make it safe to panic in.
13
14#![no_main]
15
16use libfuzzer_sys::fuzz_target;
17use otproto::{Message, MsgType};
18
19fuzz_target!(|data: &[u8]| {
20 // First byte picks the message type; the rest is the payload.
21 let Some((&ty, payload)) = data.split_first() else {
22 return;
23 };
24 let Ok(ty) = MsgType::try_from(ty & 0x0F) else {
25 return;
26 };
27
28 if let Ok(msg) = Message::decode_payload(ty, payload) {
29 assert_eq!(msg.msg_type(), ty);
30 // Anything that decodes must re-encode to the same *length*, and
31 // `payload_len` must agree without allocating. Byte equality is not
32 // asserted because reserved bytes are ignored on decode and written as
33 // zero on encode — deliberately, so a later version can populate them.
34 let re = msg.encode_payload();
35 assert_eq!(re.len(), payload.len());
36 assert_eq!(msg.payload_len(), payload.len());
37 }
38});
39