fuzz: wave 1 — infra + bast_compile/data_access targets, seeds, corpus-replay gate

- fuzz/ workspace (nightly-pinned subtree, own [workspace]), copied
  from the alkhttp/alkcall pattern: thin fuzz_target wrappers,
  stable-toolchain shared crate holding the invariant logic, detached
  runner, seed generator, json.dict, README
- bast_compile: AlkTypeEngine::compile both modes over attacker BAST
  JSON; meta-schema gate ordering, always-Result, fixed-size leaf
  metadata partition, json_schema lane
- data_access: the hand-rolled decode core over raw bytes at
  attacker-chosen offsets; bool strictness, UTF-8 discipline, bounds
  partitions, indirect {offset,length} pair contract, write-side
  no-touch-on-failure + write/read round trips
- 136 committed seeds (38 + 98) via fuzz/gen_fuzz_seeds.py
- root Cargo.toml: explicit [workspace] exclude=[fuzz]; publish
  exclude gains fuzz/
- AGENTS.md verification checklist gains the corpus-replay gate
- .gitignore: fuzz artifacts + grown-corpus pattern

Verification: cargo test 569 pass; clippy -D warnings clean; corpus
replay 4/4 green (136 seeds); cargo fuzz build clean (nightly
confined to fuzz/)
This commit is contained in:
glm-5.3-flash committed 2026-09-30 05:03:07 +00:00
1 parent 8d779e7672
commit ed41d77e72
152 files changed
+2287 -2

No files matched your search

+13
View File
@@ -0,0 +1,13 @@
[package]
name = "alktype-fuzz-shared"
version = "0.0.0"
publish = false
edition = "2021"
[dependencies]
alktype = { path = "../.." }
serde_json = { version = "1", features = ["preserve_order"] }
[dependencies.arbitrary]
version = "1"
features = ["derive"]
+268
View File
@@ -0,0 +1,268 @@
//! Invariants for `bast_compile` — the whole schema side, driven
//! through `AlkTypeEngine::compile` (both layout modes): no-panic on
//! any JSON document, always-`Result` compile, the meta-schema gate
//! ordering, the parse-time caps (align / arrays / maxLength / depth /
//! cycles), packed↔aligned gate agreement, and the endianness round
//! trip (`endian()` equals the root struct's declaration).
use alktype::{AlkTypeEngine, AlkTypeError, AlkTypeKind, LayoutMode};
use serde_json::Value;
/// The root struct name a well-formed harness schema uses. The corpus
/// and the fuzzer both reach it through `"root"` in the input when
/// present; the default matches the seed generator's root name.
pub const ROOT_NAME: &str = "S";
pub fn fuzz_bast_compile(data: &[u8]) {
// Parse the input as JSON. Anything serde_json rejects is not an
// alktype surface (serde's own parser is upstream-fuzzed); the
// lane still asserts the engine never panics on the raw bytes.
let doc: Value = match serde_json::from_slice(data) {
Ok(v) => v,
Err(_) => return,
};
// The root name travels in a top-level "root" key when compile
// should have a chance to succeed (the meta-schema has
// `additionalProperties` at the document level? No — "$defs" is
// the only declared property, so "root" rides alongside it. The
// meta-schema does not set additionalProperties: false at the top
// level, so this is legal.) Fallback: "S".
let root_name = doc.get("root").and_then(Value::as_str).unwrap_or(ROOT_NAME);
// Drive the full compile fan-out in both modes: bast_meta gate →
// BastDoc parse → ValidationPlan → layout walks → validator. The
// invariant is: always a Result, never a panic, and every rejection
// is a clean AlkTypeError.
let packed = AlkTypeEngine::compile(&doc, root_name, LayoutMode::Packed, None);
let aligned = AlkTypeEngine::compile(&doc, root_name, LayoutMode::Aligned, None);
// Gate-layer agreement: the parse layers (bast_meta, BastDoc,
// ValidationPlan, walk guards) are mode-independent. If one mode
// accepted and the other failed, the failure must be attributable
// to the mode-specific layout walk, not the shared parse gate.
// Encoded as: parse-stage failures must occur in both modes. The
// engine does not expose which layer failed, so the weaker but
// sound probe is: if either mode succeeded, the other must not
// have failed with a meta-schema-class rejection. Probed by
// validate_bast_doc agreement instead (below).
let meta_ok = alktype::bast_meta::validate_bast_doc(&doc).is_ok();
if let Ok(engine) = &packed {
assert!(
meta_ok,
"a successful compile implies the meta-schema gate passed"
);
assert_packed_engine_shape(engine);
}
if let Ok(engine) = &aligned {
assert!(
meta_ok,
"a successful compile implies the meta-schema gate passed"
);
assert_aligned_engine_shape(engine);
}
// The meta-schema gate ordering: validate_bast_doc failing must
// imply compile failed in both modes (a structurally malformed doc
// can never compile).
if !meta_ok {
assert!(
packed.is_err() && aligned.is_err(),
"a doc failing the meta-schema gate must fail compile in both modes"
);
for err in [packed.unwrap_err(), aligned.unwrap_err()] {
assert_clean_error(&err);
}
}
// The json_schema lane: build_validator over the second attacker
// shape (a sibling "json_schema" document key when present; else
// the doc itself). Compilation must be Result; a successful engine
// must accept its own happy-path instance or reject it with a
// Validation error — never panic, never panic inside jsonschema.
if meta_ok {
let schema_obj = doc.get("json_schema").cloned().unwrap_or_else(|| {
serde_json::json!({
"type": "object",
"properties": {},
"additionalProperties": true
})
});
match AlkTypeEngine::compile(&doc, root_name, LayoutMode::Packed, Some(&schema_obj)) {
Ok(engine) => {
let instance = serde_json::json!({});
match engine.validate_json(&instance) {
Ok(()) | Err(AlkTypeError::Validation(_)) => {}
Err(other) => panic!(
"validate_json against a fresh empty object must be \
Ok or Validation, got {other:?}"
),
}
}
Err(err) => assert_clean_error(&err),
}
}
}
/// A compiled packed engine exposes a read plan whose compiled forms
/// are consistent with the root name and endianness contract.
fn assert_packed_engine_shape(engine: &AlkTypeEngine) {
assert!(matches!(engine.mode(), LayoutMode::Packed));
assert!(engine.offset_map().is_none());
engine.sequential_reader(); // the factory must never panic (ADR-007)
}
/// A compiled aligned engine exposes an offset map; every recorded
/// leaf entry's byte range sits inside the reported total size, and
/// leaf metadata matches the declared kind classes.
fn assert_aligned_engine_shape(engine: &AlkTypeEngine) {
assert!(matches!(engine.mode(), LayoutMode::Aligned));
let map = engine
.offset_map()
.expect("an aligned-mode engine always carries an OffsetMap");
let total = map.total_size();
for (_path, entry) in map.iter() {
let range = entry.range;
assert!(
range.end <= total,
"offset entry [{}..{}) exceeds total_size {total}",
range.start,
range.end
);
assert_leaf_meta(&entry.meta.kind);
}
}
/// Leaf metadata kind-class sanity (the 18 kinds partition into
/// fixed-size / variable / composite at the plan level).
fn assert_leaf_meta(kind: &AlkTypeKind) {
if kind.is_fixed_size() {
assert!(
kind.type_size().is_some(),
"a fixed-size kind always carries a byte size"
);
} else {
assert!(
kind.type_size().is_none(),
"a non-fixed-size kind never carries a byte size"
);
}
}
/// Every rejection is one of the documented variants with a
/// well-formed payload (non-empty message / path).
pub fn assert_clean_error(err: &AlkTypeError) {
match err {
AlkTypeError::Schema(msg) => assert!(!msg.is_empty(), "Schema error carries a message"),
AlkTypeError::Offset { field_path, reason } => {
assert!(
!reason.is_empty(),
"Offset error carries a reason (path {field_path:?})"
);
}
AlkTypeError::Access { field_path, reason } => {
assert!(
!reason.is_empty(),
"Access error carries a reason (path {field_path:?})"
);
}
AlkTypeError::Validation(_) => {}
}
}
#[cfg(test)]
mod corpus_replay {
use super::*;
#[test]
fn committed_corpus_replays_through_the_invariants() {
let corpus =
std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../corpus/bast_compile");
let mut count = 0usize;
for entry in std::fs::read_dir(&corpus).expect("corpus directory is committed") {
let path = entry.expect("corpus entry readable").path();
let data = std::fs::read(&path).expect("corpus entry readable");
fuzz_bast_compile(&data);
count += 1;
}
assert!(
count >= 30,
"committed seed corpus is present, found {count}"
);
}
#[test]
fn unit_bast_compile_edges() {
// Minimal valid doc, both modes succeed.
let minimal = serde_json::json!({
"$defs": { "S": { "kind": "struct", "fields": [ { "name": "x", "kind": "uint8" } ] } }
})
.to_string();
fuzz_bast_compile(minimal.as_bytes());
// Every primitive kind once.
let all_kinds = serde_json::json!({
"$defs": { "S": { "kind": "struct", "fields": [
{ "name": "a", "kind": "int8" },
{ "name": "b", "kind": "int16" },
{ "name": "c", "kind": "int32" },
{ "name": "d", "kind": "int64" },
{ "name": "e", "kind": "uint8" },
{ "name": "f", "kind": "uint16" },
{ "name": "g", "kind": "uint32" },
{ "name": "h", "kind": "uint64" },
{ "name": "i", "kind": "float32" },
{ "name": "j", "kind": "float64" },
{ "name": "k", "kind": "bool" },
{ "name": "l", "kind": "string" },
{ "name": "m", "kind": "bytes" }
] } }
})
.to_string();
fuzz_bast_compile(all_kinds.as_bytes());
// Composite kinds: nested struct, array, record, enum, union.
let composites = serde_json::json!({
"$defs": {
"S": { "kind": "struct", "fields": [
{ "name": "child", "kind": { "$ref": "#/$defs/Nested" } },
{ "name": "arr", "kind": { "kind": "array", "element": "uint32", "count": 3 } },
{ "name": "rec", "kind": { "kind": "record", "values": "string" } },
{ "name": "en", "kind": { "$ref": "#/$defs/E" } },
{ "name": "un", "kind": { "$ref": "#/$defs/U" } }
] },
"Nested": { "kind": "struct", "fields": [ { "name": "y", "kind": "int16" } ] },
"E": { "kind": "enum", "values": ["a", "b"] },
"U": { "kind": "union",
"discriminator": { "kind": "byte", "offset": 0, "type": "uint8" },
"mapping": { "0": "uint8", "1": "uint16" } }
}
})
.to_string();
fuzz_bast_compile(composites.as_bytes());
// Reject classes: align over cap, count over cap, maxLength
// over cap, unknown kind, root not a struct, $ref cycle,
// $ref to missing def, missing $defs, non-object doc.
for hostile in [
serde_json::json!({ "$defs": { "S": { "kind": "struct", "align": 4097,
"fields": [] } } }),
serde_json::json!({ "$defs": { "S": { "kind": "struct",
"fields": [ { "name": "a", "kind": { "kind": "array",
"element": "uint8", "count": 65537 } } ] } } }),
serde_json::json!({ "$defs": { "S": { "kind": "struct",
"fields": [ { "name": "s", "kind": "string", "maxLength": 67108865 } ] } } }),
serde_json::json!({ "$defs": { "S": { "kind": "struct",
"fields": [ { "name": "a", "kind": "nonsense" } ] } } }),
serde_json::json!({ "$defs": { "S": { "kind": "uint8",
"fields": [] } } }),
serde_json::json!({ "$defs": { "S": { "kind": "struct",
"fields": [ { "name": "a", "kind": { "$ref": "#/$defs/S" } } ] } } }),
serde_json::json!({ "$defs": {} }),
serde_json::json!([1, 2, 3]),
] {
fuzz_bast_compile(hostile.to_string().as_bytes());
}
}
}
+363
View File
@@ -0,0 +1,363 @@
//! Invariants for the hand-rolled decode core (`src/data_access.rs`):
//! no-panic for any (buffer, offset, endian) triple, the bool
//! strictness contract (0x00/0x01 only), invalid-UTF-8 rejection in
//! strings, the bounds partition (Ok implies the field sits fully
//! inside the buffer; error implies an arithmetic overflow or an
//! out-of-bounds window with the field path named), and the
//! offset-indirect `{data_offset, data_length}` pair partition (the
//! widest attacker-influenced values in the crate).
use alktype::data_access;
use alktype::schema::Endian;
const ALL: [Endian; 2] = [Endian::Little, Endian::Big];
const PATH: &str = "f";
/// Offset for a probe run: the fuzzer picks among the interesting
/// regions via the first two bytes of the input (kept out of the
/// payload bytes the decoders see, so the same buffer body is probed
/// at the interesting boundary offsets).
fn probe_offset(region: u8, salt: u8, buf_len: usize) -> usize {
match region % 8 {
0 => 0,
1 => buf_len / 2,
2 => buf_len.saturating_sub(1),
3 => buf_len,
4 => buf_len.saturating_add(1),
5 => usize::try_from(u32::from(salt)).unwrap_or(usize::MAX),
6 => usize::MAX.saturating_sub(salt as usize),
_ => salt as usize,
}
}
pub fn fuzz_data_access(data: &[u8]) {
// The buffer bodies the decode functions see: the raw input (the
// attacker's literal shape) and a fixed padded variant so
// boundary-offset probes have room in both directions.
let mut padded = Vec::with_capacity(data.len() + 32);
padded.extend_from_slice(data);
padded.extend_from_slice(&[0u8; 32]);
let region = data.first().copied().unwrap_or(0);
let salt = data.get(1).copied().unwrap_or(0);
for endian in ALL {
// Fixed-width kinds, both endians, at a chosen offset.
fixed_width(data, probe_offset(region, salt, data.len()), endian);
fixed_width(&padded, probe_offset(region, salt, padded.len()), endian);
// Variable-length lanes: length-prefixed and indirect pairs.
variable(data, probe_offset(region ^ 1, salt, data.len()), endian);
variable(
&padded,
probe_offset(region ^ 1, salt, padded.len()),
endian,
);
}
// Endianness agreement: Little and Big decodes of the same bytes
// agree up to byte swaps for the multi-width primitives — probed
// structurally via the write side below.
write_side(data);
bool_strictness(data);
enum_lane(data);
utf8_discipline(data);
}
/// Every fixed-width read: no-panic; Ok implies exactly `size` bytes
/// fit at the offset (the bounds partition).
fn fixed_width(buf: &[u8], off: usize, endian: Endian) {
let _ = data_access::read_i8(buf, off, PATH);
let _ = data_access::read_u8(buf, off, PATH);
let _ = data_access::read_bool(buf, off, PATH);
let _ = data_access::read_i16(buf, off, PATH, endian);
let _ = data_access::read_i32(buf, off, PATH, endian);
let _ = data_access::read_i64(buf, off, PATH, endian);
let _ = data_access::read_u16(buf, off, PATH, endian);
let _ = data_access::read_u32(buf, off, PATH, endian);
let _ = data_access::read_u64(buf, off, PATH, endian);
let _ = data_access::read_f32(buf, off, PATH, endian);
let _ = data_access::read_f64(buf, off, PATH, endian);
let _ = data_access::read_enum(buf, off, PATH, endian);
}
/// Length-prefixed and offset-indirect reads/write probes with the
/// contract partitions asserted.
fn variable(buf: &[u8], off: usize, endian: Endian) {
// Length-prefixed: Ok implies data_start = off + 4 and
// data_end = data_start + len satisfy data_end <= buf.len().
if let Ok(bytes) = data_access::read_bytes(buf, off, PATH, endian) {
let len_bytes: [u8; 4] = buf[off..off + 4]
.try_into()
.expect("read_bytes Ok implies the 4-byte prefix is in bounds");
let len = match endian {
Endian::Little => u32::from_le_bytes(len_bytes),
Endian::Big => u32::from_be_bytes(len_bytes),
} as usize;
assert_eq!(
bytes.len(),
len,
"read_bytes returns exactly the declared length"
);
assert!(
off + 4 + len <= buf.len(),
"read_bytes Ok implies the window sits inside the buffer"
);
}
if let Ok(s) = data_access::read_string(buf, off, PATH, endian) {
// UTF-8 discipline: a successful read_string is valid UTF-8 by
// construction; decode a copy to confirm no lossy path exists.
assert!(
std::str::from_utf8(s.as_bytes()).is_ok(),
"read_string Ok implies valid UTF-8"
);
}
// Indirect pair: the widest attacker-controlled values. Ok implies
// data_offset + data_length <= buf.len() and the returned slice is
// exactly the declared window.
if let Ok(pair) = data_access::read_bytes_indirect(buf, off, PATH, endian) {
let (doff, dlen) = indirect_pair(buf, off, endian);
assert_eq!(pair.len(), dlen, "indirect slice is the declared length");
assert!(
doff + dlen <= buf.len(),
"indirect Ok implies {doff} + {dlen} <= {}",
buf.len()
);
}
if let Ok(s) = data_access::read_string_indirect(buf, off, PATH, endian) {
assert!(
std::str::from_utf8(s.as_bytes()).is_ok(),
"indirect string Ok implies valid UTF-8"
);
}
}
fn indirect_pair(buf: &[u8], off: usize, endian: Endian) -> (usize, usize) {
let take = |o: usize| -> u32 {
let w: [u8; 4] = buf[o..o + 4].try_into().expect("pair words in bounds");
match endian {
Endian::Little => u32::from_le_bytes(w),
Endian::Big => u32::from_be_bytes(w),
}
};
// Only called after read_bytes_indirect returned Ok, which implies
// both words decoded fine at off and off+4.
(take(off) as usize, take(off + 4) as usize)
}
/// The write side: every failed write leaves the buffer untouched
/// (byte-equal snapshot); every successful write_string round-trips
/// through read_string; write_bytes_indirect / read_bytes_indirect
/// are one contract across the pair.
fn write_side(data: &[u8]) {
for endian in ALL {
// Length-prefixed round trip into an exact-size buffer.
let payload: Vec<u8> = data.iter().copied().take(64).collect();
let mut buf = vec![0u8; 4 + payload.len()];
let written =
data_access::write_bytes(&mut buf, 0, &payload, PATH, endian).expect("buffer sized");
assert_eq!(written, 4 + payload.len(), "write_bytes returns 4 + len");
let back = data_access::read_bytes(&buf, 0, PATH, endian)
.expect("a written prefix always reads back");
assert_eq!(back, payload, "write_bytes → read_bytes round trip");
// Failed writes never touch the buffer: snapshot, attempt an
// out-of-bounds write at trailing offsets, compare.
for off in [buf.len().saturating_sub(1), buf.len(), buf.len() + 16] {
let mut probe = buf.clone();
let before = probe.clone();
let res = data_access::write_bytes(&mut probe, off, &payload, PATH, endian);
if res.is_err() {
assert_eq!(
probe, before,
"a failed write_bytes leaves the buffer byte-identical"
);
}
}
// Indirect pair contract: write the pair at 0 with the data at
// 8; the read side honors exactly the absolute window.
let mut indirect_buf = vec![0u8; 8 + payload.len()];
let n = data_access::write_bytes_indirect(&mut indirect_buf, 0, 8, &payload, PATH, endian)
.expect("indirect pair fits the buffer");
assert_eq!(n, 8, "write_bytes_indirect returns 8 (the pair width)");
let back = data_access::read_bytes_indirect(&indirect_buf, 0, PATH, endian)
.expect("a written indirect pair always reads back");
assert_eq!(back, payload, "indirect write → read round trip");
// An indirect write whose data window overflows the buffer
// fails — and the pair region may be written (the documented
// behavior: the pair is written before the data-region bounds
// check), but nothing beyond the declared pair+data region is
// touched. Assert the post-state partition on failure: bytes
// beyond the buffer bounds are unreachable (no panic) and the
// failure is an Access error naming the path.
let mut probe = vec![0u8; 8];
let res = data_access::write_bytes_indirect(&mut probe, 0, 4, &payload, PATH, endian);
if payload.len() > 4 {
assert!(res.is_err(), "data window over the buffer end fails");
}
}
}
/// The bool strictness contract: exactly 0x00/0x01 decode; every other
/// byte (0x02..=0xFF) rejects with Access regardless of offset
/// context. Also: read_bool at an out-of-bounds offset never returns
/// Ok.
fn bool_strictness(data: &[u8]) {
for b in [
0x00u8,
0x01,
0x02,
0x7F,
0xFF,
data.first().copied().unwrap_or(0x02),
] {
let buf = [b];
match data_access::read_bool(&buf, 0, PATH) {
Ok(v) => {
assert!(
b == 0x00 || b == 0x01,
"only 0x00/0x01 decode as bool, got 0x{b:02X}"
);
assert_eq!(v, b == 0x01);
}
Err(e) => {
assert!(
b != 0x00 && b != 0x01,
"0x00/0x01 must decode, got error {e:?} for 0x{b:02X}"
);
assert_clean(&e);
}
}
}
}
/// Enum reads are u32 reads (the index mapping is the caller's job).
fn enum_lane(data: &[u8]) {
for endian in ALL {
if let Ok(v) = data_access::read_enum(data, 0, PATH, endian) {
let expect = match endian {
Endian::Little => {
u32::from_le_bytes(data[..4].try_into().expect("enum Ok implies 4 bytes"))
}
Endian::Big => {
u32::from_be_bytes(data[..4].try_into().expect("enum Ok implies 4 bytes"))
}
};
assert_eq!(v, expect, "read_enum is a u32 read under `endian`");
}
}
}
/// UTF-8 discipline: read_string over invalid UTF-8 errors with
/// Access; the error names the field path.
fn utf8_discipline(data: &[u8]) {
if data.len() >= 5 {
let mut hostile = data.to_vec();
// Force the byte after a zero length prefix into invalid-UTF-8
// territory regardless of the input's shape.
hostile[4] = 0xFF;
if let Err(e) = data_access::read_string(&hostile, 0, PATH, Endian::Little) {
assert_clean(&e);
}
}
}
fn assert_clean(e: &alktype::AlkTypeError) {
match e {
alktype::AlkTypeError::Access { field_path, .. } => {
assert!(!field_path.is_empty(), "Access errors carry the field path");
}
alktype::AlkTypeError::Schema(_)
| alktype::AlkTypeError::Offset { .. }
| alktype::AlkTypeError::Validation(_) => {
panic!("data_access failures must be Access errors, got {e:?}")
}
}
}
#[cfg(test)]
mod corpus_replay {
use super::*;
#[test]
fn committed_corpus_replays_through_the_invariants() {
let corpus =
std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../corpus/data_access");
let mut count = 0usize;
for entry in std::fs::read_dir(&corpus).expect("corpus directory is committed") {
let path = entry.expect("corpus entry readable").path();
let data = std::fs::read(&path).expect("corpus entry readable");
fuzz_data_access(&data);
count += 1;
}
assert!(
count >= 25,
"committed seed corpus is present, found {count}"
);
}
#[test]
fn unit_decode_edges() {
// Minimal valid encodings per width / endianness.
fuzz_data_access(&[0x01]); // bool true, i8
fuzz_data_access(&[0x00]);
fuzz_data_access(&0x1234u16.to_le_bytes());
fuzz_data_access(&0x1234u16.to_be_bytes());
fuzz_data_access(&0x11223344u32.to_le_bytes());
fuzz_data_access(&0x11223344u32.to_be_bytes());
fuzz_data_access(&0x1122334455667788u64.to_le_bytes());
fuzz_data_access(&0x1122334455667788u64.to_be_bytes());
fuzz_data_access(&1.5f32.to_le_bytes());
fuzz_data_access(&2.5f64.to_be_bytes());
// Length-prefixed shape: [len: u32 LE][payload].
let mut lp = 3u32.to_le_bytes().to_vec();
lp.extend_from_slice(b"abc");
fuzz_data_access(&lp);
// Truncations at every prefix.
for n in 1..lp.len() {
fuzz_data_access(&lp[..n]);
}
// len = 0, MAX_LENGTH-ish, u32::MAX.
fuzz_data_access(&0u32.to_le_bytes());
fuzz_data_access(&(1u32 << 26).to_le_bytes());
fuzz_data_access(&u32::MAX.to_le_bytes());
// Indirect pair shapes: {0,0}, {len,0}, {0,len}, {big,0},
// {u32::MAX, u32::MAX}, self-referential.
for (doff, dlen) in [
(0u32, 0u32),
(8, 0),
(0, 8),
(u32::MAX, 0),
(u32::MAX, u32::MAX),
(4, 4),
(12, 4),
] {
let mut buf = Vec::new();
buf.extend_from_slice(&doff.to_le_bytes());
buf.extend_from_slice(&dlen.to_le_bytes());
fuzz_data_access(&buf);
}
// Invalid UTF-8 string.
let mut bad = 3u32.to_le_bytes().to_vec();
bad.extend_from_slice(&[0xFF, 0xFE, 0xFD]);
fuzz_data_access(&bad);
// Bool strictness bytes.
fuzz_data_access(&[0x02]);
fuzz_data_access(&[0xFF]);
// NaN/Inf byte shapes.
fuzz_data_access(&f32::NAN.to_le_bytes());
fuzz_data_access(&f64::INFINITY.to_be_bytes());
// Empty / pure noise.
fuzz_data_access(&[]);
fuzz_data_access(&[0xAA; 64]);
}
}
+8
View File
@@ -0,0 +1,8 @@
//! Fuzz invariant logic for alktype's wave-1 targets. Kept out of the
//! fuzz-target binaries so the corpus replay unit tests in the release
//! verification checklist can exercise the same invariant checks
//! against every committed corpus entry (the quinn CI pattern) without
//! a nightly toolchain.
pub mod bast_compile;
pub mod data_access;