//! Fuzzes the payload decoders directly, behind the AEAD. //! //! ```sh //! cargo +nightly fuzz run decode_payload //! ``` //! //! This is where the structural parsing lives — point counts that disagree with //! the payload length, unknown enum discriminants, arithmetic on attacker-chosen //! lengths. Reaching it through a sealed datagram would require forging a //! Poly1305 tag, so it gets its own target with the crypto stripped away. In //! production only a client holding a valid token can reach this code, which //! bounds the blast radius but does not make it safe to panic in. #![no_main] use libfuzzer_sys::fuzz_target; use otproto::{Message, MsgType}; fuzz_target!(|data: &[u8]| { // First byte picks the message type; the rest is the payload. let Some((&ty, payload)) = data.split_first() else { return; }; let Ok(ty) = MsgType::try_from(ty & 0x0F) else { return; }; if let Ok(msg) = Message::decode_payload(ty, payload) { assert_eq!(msg.msg_type(), ty); // Anything that decodes must re-encode to the same *length*, and // `payload_len` must agree without allocating. Byte equality is not // asserted because reserved bytes are ignored on decode and written as // zero on encode — deliberately, so a later version can populate them. let re = msg.encode_payload(); assert_eq!(re.len(), payload.len()); assert_eq!(msg.payload_len(), payload.len()); } });