read what the binary says about itself: names, signatures, prototypes; gen v2
This commit is contained in:
parent
54ef572202
commit
c458b4cb50
34 changed files with 58363 additions and 192 deletions
34
fuzz/fuzz_targets/fuzz_pulse.rs
Normal file
34
fuzz/fuzz_targets/fuzz_pulse.rs
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
#![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");
|
||||
}
|
||||
}
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue