#![no_main] //! A Pulse binding's typed signature is reconstructed by DECODING the accessor that returns its //! descriptor: the reader follows arbitrary control flow, constant-propagates through it, and then //! dereferences whatever addresses that produced — a returned element count, a base pointer, a name //! pointer per element, and a receiver it chases one call deep. Every one of those is whatever the file //! says it is, so a crafted (or truncated) `.so` can aim them anywhere, into non-code, off the end of a //! section, or into a cycle. The reader must answer with fewer signatures, never a panic and never a //! runaway. Also exercises the invariant the whole stage rests on: a recovered list has exactly as many //! parameters as the accessor's own count says, so a shifted layout cannot ship as a short signature. use libfuzzer_sys::fuzz_target; use source2rosetta::elf::CodeImage; use source2rosetta::{pulse, valvetab}; fuzz_target!(|data: &[u8]| { let Ok(img) = CodeImage::from_bytes(data.to_vec()) else { return; }; let bindings = valvetab::pulse_bindings(&img); let pairs: Vec<(u64, u64)> = bindings .iter() .take(64) .map(|b| (b.descriptor, b.arg_descriptor)) .collect(); let (sigs, stride, votes, _) = pulse::read_all(&img, &pairs, 1); assert_eq!(sigs.len(), pairs.len(), "one verdict per binding"); assert!(votes == 0 || stride > 0, "a voted-for stride is never zero"); for s in sigs.into_iter().flatten() { for p in s.args.iter().chain(&s.returns) { // A parameter that survived is fully formed: the name gate and the type gate both passed. assert!(!p.name.is_empty(), "shipped a nameless parameter"); assert!(p.ty >= -1, "shipped a type below PVAL_VOID"); } } });