From aef8d9f6ab8178d924bbb19befc64cb8be8823a1 Mon Sep 17 00:00:00 2001 From: "glm-5.3-flash" Date: Wed, 30 Sep 2026 07:02:35 +0000 Subject: [PATCH] =?UTF-8?q?fuzz:=20wave=203=20=E2=80=94=20validate=5Fpair?= =?UTF-8?q?=20two-input=20harness,=2044=20seeds,=20release-budget=20campai?= =?UTF-8?q?gns=20next?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Target 5 (§3): compile an attacker schema (10-lane menu incl. raw JSON bytes lane) in both modes, then hammer the hostile buffer through validate_bytes, an independent materialize_packed/materialize_aligned, read_field over every offset-map leaf, junk field paths, and the packed sequential walk under the spin bound. Invariants coded (per §3 target 5): mode agreement (aligned Ok ⇒ packed Ok; packed-Ok/aligned-Err only for the documented ADR-006/ADR-008/ offset-indirect rejections), the materialize⇄validate_bytes verdict lattice with verbatim error propagation, unknown-path echo, serde round-trip of materialized output, non-finite-float Access pinning, out-of-range enum Validation pinning, and the record-count spin bound. 44 committed seeds (menu/raw lanes × valid/valid, hostile-schema/ valid-bytes, valid-schema/hostile-bytes incl. a per-prefix truncation sweep, NaN/Inf, enum 99, spin fixtures, mode-agreement pins), hand- encoded against the pinned arbitrary 1.4.2 derive layout and pinned by decode tests. The aligned maxLength-reservation offset pin (s@8..72, tail@72, total 76) caught a fixture assumption error pre-commit. Verification: corpus replay 29/29 green (44 new seeds decode+replay), main crate 570 tests pass, clippy -D warnings clean (crate + shared), wasm clean, cargo fuzz build clean. Hand-run drives (indirect pair escape, enum-Validation, unknown discriminator, trailing garbage) all held. --- fuzz/Cargo.toml | 7 + fuzz/corpus/validate_pair/seed-000 | Bin 0 -> 35 bytes fuzz/corpus/validate_pair/seed-001 | Bin 0 -> 165 bytes fuzz/corpus/validate_pair/seed-002 | Bin 0 -> 43 bytes fuzz/corpus/validate_pair/seed-003 | Bin 0 -> 67 bytes fuzz/corpus/validate_pair/seed-004 | Bin 0 -> 19 bytes fuzz/corpus/validate_pair/seed-005 | Bin 0 -> 27 bytes fuzz/corpus/validate_pair/seed-006 | Bin 0 -> 27 bytes fuzz/corpus/validate_pair/seed-007 | Bin 0 -> 35 bytes fuzz/corpus/validate_pair/seed-008 | Bin 0 -> 27 bytes fuzz/corpus/validate_pair/seed-009 | Bin 0 -> 59 bytes fuzz/corpus/validate_pair/seed-010 | Bin 0 -> 179 bytes fuzz/corpus/validate_pair/seed-011 | Bin 0 -> 42 bytes fuzz/corpus/validate_pair/seed-012 | Bin 0 -> 42 bytes fuzz/corpus/validate_pair/seed-013 | Bin 0 -> 42 bytes fuzz/corpus/validate_pair/seed-014 | Bin 0 -> 42 bytes fuzz/corpus/validate_pair/seed-015 | Bin 0 -> 42 bytes fuzz/corpus/validate_pair/seed-016 | Bin 0 -> 42 bytes fuzz/corpus/validate_pair/seed-017 | Bin 0 -> 42 bytes fuzz/corpus/validate_pair/seed-018 | Bin 0 -> 42 bytes fuzz/corpus/validate_pair/seed-019 | Bin 0 -> 42 bytes fuzz/corpus/validate_pair/seed-020 | Bin 0 -> 42 bytes fuzz/corpus/validate_pair/seed-021 | Bin 0 -> 42 bytes fuzz/corpus/validate_pair/seed-022 | Bin 0 -> 42 bytes fuzz/corpus/validate_pair/seed-023 | Bin 0 -> 42 bytes fuzz/corpus/validate_pair/seed-024 | Bin 0 -> 42 bytes fuzz/corpus/validate_pair/seed-025 | Bin 0 -> 67 bytes fuzz/corpus/validate_pair/seed-026 | Bin 0 -> 67 bytes fuzz/corpus/validate_pair/seed-027 | Bin 0 -> 43 bytes fuzz/corpus/validate_pair/seed-028 | Bin 0 -> 43 bytes fuzz/corpus/validate_pair/seed-029 | Bin 0 -> 29 bytes fuzz/corpus/validate_pair/seed-030 | Bin 0 -> 29 bytes fuzz/corpus/validate_pair/seed-031 | Bin 0 -> 21 bytes fuzz/corpus/validate_pair/seed-032 | Bin 0 -> 21 bytes fuzz/corpus/validate_pair/seed-033 | Bin 0 -> 28 bytes fuzz/corpus/validate_pair/seed-034 | Bin 0 -> 59 bytes fuzz/corpus/validate_pair/seed-035 | Bin 0 -> 19 bytes fuzz/corpus/validate_pair/seed-036 | Bin 0 -> 35 bytes fuzz/corpus/validate_pair/seed-037 | Bin 0 -> 28 bytes fuzz/corpus/validate_pair/seed-038 | Bin 0 -> 28 bytes fuzz/corpus/validate_pair/seed-039 | Bin 0 -> 28 bytes fuzz/corpus/validate_pair/seed-040 | Bin 0 -> 28 bytes fuzz/corpus/validate_pair/seed-041 | Bin 0 -> 28 bytes fuzz/corpus/validate_pair/seed-042 | Bin 0 -> 28 bytes fuzz/corpus/validate_pair/seed-043 | Bin 0 -> 28 bytes fuzz/fuzz_targets/validate_pair.rs | 7 + fuzz/gen_fuzz_seeds.py | 175 +++++- fuzz/shared/src/lib.rs | 3 +- fuzz/shared/src/validate_pair.rs | 847 +++++++++++++++++++++++++++++ 49 files changed, 1037 insertions(+), 2 deletions(-) create mode 100644 fuzz/corpus/validate_pair/seed-000 create mode 100644 fuzz/corpus/validate_pair/seed-001 create mode 100644 fuzz/corpus/validate_pair/seed-002 create mode 100644 fuzz/corpus/validate_pair/seed-003 create mode 100644 fuzz/corpus/validate_pair/seed-004 create mode 100644 fuzz/corpus/validate_pair/seed-005 create mode 100644 fuzz/corpus/validate_pair/seed-006 create mode 100644 fuzz/corpus/validate_pair/seed-007 create mode 100644 fuzz/corpus/validate_pair/seed-008 create mode 100644 fuzz/corpus/validate_pair/seed-009 create mode 100644 fuzz/corpus/validate_pair/seed-010 create mode 100644 fuzz/corpus/validate_pair/seed-011 create mode 100644 fuzz/corpus/validate_pair/seed-012 create mode 100644 fuzz/corpus/validate_pair/seed-013 create mode 100644 fuzz/corpus/validate_pair/seed-014 create mode 100644 fuzz/corpus/validate_pair/seed-015 create mode 100644 fuzz/corpus/validate_pair/seed-016 create mode 100644 fuzz/corpus/validate_pair/seed-017 create mode 100644 fuzz/corpus/validate_pair/seed-018 create mode 100644 fuzz/corpus/validate_pair/seed-019 create mode 100644 fuzz/corpus/validate_pair/seed-020 create mode 100644 fuzz/corpus/validate_pair/seed-021 create mode 100644 fuzz/corpus/validate_pair/seed-022 create mode 100644 fuzz/corpus/validate_pair/seed-023 create mode 100644 fuzz/corpus/validate_pair/seed-024 create mode 100644 fuzz/corpus/validate_pair/seed-025 create mode 100644 fuzz/corpus/validate_pair/seed-026 create mode 100644 fuzz/corpus/validate_pair/seed-027 create mode 100644 fuzz/corpus/validate_pair/seed-028 create mode 100644 fuzz/corpus/validate_pair/seed-029 create mode 100644 fuzz/corpus/validate_pair/seed-030 create mode 100644 fuzz/corpus/validate_pair/seed-031 create mode 100644 fuzz/corpus/validate_pair/seed-032 create mode 100644 fuzz/corpus/validate_pair/seed-033 create mode 100644 fuzz/corpus/validate_pair/seed-034 create mode 100644 fuzz/corpus/validate_pair/seed-035 create mode 100644 fuzz/corpus/validate_pair/seed-036 create mode 100644 fuzz/corpus/validate_pair/seed-037 create mode 100644 fuzz/corpus/validate_pair/seed-038 create mode 100644 fuzz/corpus/validate_pair/seed-039 create mode 100644 fuzz/corpus/validate_pair/seed-040 create mode 100644 fuzz/corpus/validate_pair/seed-041 create mode 100644 fuzz/corpus/validate_pair/seed-042 create mode 100644 fuzz/corpus/validate_pair/seed-043 create mode 100644 fuzz/fuzz_targets/validate_pair.rs create mode 100644 fuzz/shared/src/validate_pair.rs diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml index 7dff102..2728f7d 100644 --- a/fuzz/Cargo.toml +++ b/fuzz/Cargo.toml @@ -42,4 +42,11 @@ test = false doc = false bench = false +[[bin]] +name = "validate_pair" +path = "fuzz_targets/validate_pair.rs" +test = false +doc = false +bench = false + [workspace] \ No newline at end of file diff --git a/fuzz/corpus/validate_pair/seed-000 b/fuzz/corpus/validate_pair/seed-000 new file mode 100644 index 0000000000000000000000000000000000000000..32eaf8ede68fb1e78d98aca650c5579c9406f7a4 GIT binary patch literal 35 acmZQzfB{B9Ms`L9Fl2(#8H|~XN(=x8aRC|t literal 0 HcmV?d00001 diff --git a/fuzz/corpus/validate_pair/seed-001 b/fuzz/corpus/validate_pair/seed-001 new file mode 100644 index 0000000000000000000000000000000000000000..f04cffa912e0694845196c6c8ff8f72947b5a28a GIT binary patch literal 165 ncmZQzU|?Wm1QHC4f{YAc$PT5MpmYXfCX@w{rx&RNvy}k=lbHcs literal 0 HcmV?d00001 diff --git a/fuzz/corpus/validate_pair/seed-002 b/fuzz/corpus/validate_pair/seed-002 new file mode 100644 index 0000000000000000000000000000000000000000..3e1d22ceb225c3388b2907552c9a7bff0fdf2813 GIT binary patch literal 43 icmZQzU|?Wk1QHC4+>8uh$OENWpmYjjB4Y_-A_D*ncLB)& literal 0 HcmV?d00001 diff --git a/fuzz/corpus/validate_pair/seed-003 b/fuzz/corpus/validate_pair/seed-003 new file mode 100644 index 0000000000000000000000000000000000000000..b3912efff7073bd9898111dbe918ce5096025c52 GIT binary patch literal 67 kcmZQzU|?VdVg?3AMn(oOWJ05}84Vd(F~p1**&t#J03a#>YXATM literal 0 HcmV?d00001 diff --git a/fuzz/corpus/validate_pair/seed-004 b/fuzz/corpus/validate_pair/seed-004 new file mode 100644 index 0000000000000000000000000000000000000000..119766ade41b54f159b4155ba4de7d40333a3b2f GIT binary patch literal 19 YcmZQzU|?VYVg?3ACPqd^21a8B0083vKL7v# literal 0 HcmV?d00001 diff --git a/fuzz/corpus/validate_pair/seed-005 b/fuzz/corpus/validate_pair/seed-005 new file mode 100644 index 0000000000000000000000000000000000000000..f666d06766b254471abba75b0ddcac9594e87b26 GIT binary patch literal 27 gcmZQzU|?VeVg?3AMn)z^W=0l9E=F!f9!6dU00LkDNB{r; literal 0 HcmV?d00001 diff --git a/fuzz/corpus/validate_pair/seed-006 b/fuzz/corpus/validate_pair/seed-006 new file mode 100644 index 0000000000000000000000000000000000000000..c072b07d926762f6bdacab779bdcfe6e6395432c GIT binary patch literal 27 gcmZQzU|?Wp1QHC4jEqc-%#19IT#Vd|JdC^y00MRZNdN!< literal 0 HcmV?d00001 diff --git a/fuzz/corpus/validate_pair/seed-007 b/fuzz/corpus/validate_pair/seed-007 new file mode 100644 index 0000000000000000000000000000000000000000..f16f8f4ff831e00851ca455aba8bac2a70bce0e2 GIT binary patch literal 35 fcmZQzU|`??Vg?3AK}L2)1~6oT(ix1Ij7kgu2x$Q# literal 0 HcmV?d00001 diff --git a/fuzz/corpus/validate_pair/seed-008 b/fuzz/corpus/validate_pair/seed-008 new file mode 100644 index 0000000000000000000000000000000000000000..12b95a82412586803860dbf49671405a4be635c3 GIT binary patch literal 27 UcmZQzU|`^61QHC4t56{W05Kp1V*mgE literal 0 HcmV?d00001 diff --git a/fuzz/corpus/validate_pair/seed-009 b/fuzz/corpus/validate_pair/seed-009 new file mode 100644 index 0000000000000000000000000000000000000000..0ad92b8655ae0a31b806da177f49866c8141e12c GIT binary patch literal 59 zcmZQzU}#{hW>jKSVN796WlUo%2C}VyBDIXQ3}C=0$jHvf0ESFZI)gEjQHcQn);R^* literal 0 HcmV?d00001 diff --git a/fuzz/corpus/validate_pair/seed-010 b/fuzz/corpus/validate_pair/seed-010 new file mode 100644 index 0000000000000000000000000000000000000000..1e6e424ae95c6a24af464fa3d6f25526813daf57 GIT binary patch literal 179 zcmY+6OA3QP5JdZ3MY0MWK)i;_kcIdG(HL+>5JJGCThp_0nxcoUdSzxOKMFR!ny6nb zHBu=(=ouIZeowjN)K1JytnxDv#BS_#-jUc*{=lL{re992w0v~W)S;cS_paTWBbA&# KZ%JeRc-jS`a2rMd literal 0 HcmV?d00001 diff --git a/fuzz/corpus/validate_pair/seed-011 b/fuzz/corpus/validate_pair/seed-011 new file mode 100644 index 0000000000000000000000000000000000000000..3ffe3e131a14f7501ebf7fcd5b5f20b25e851734 GIT binary patch literal 42 hcmZQzfPw}_MnOh)Mg}lsh0+;}sf;;5n9r!h001Sa0^|Sy literal 0 HcmV?d00001 diff --git a/fuzz/corpus/validate_pair/seed-012 b/fuzz/corpus/validate_pair/seed-012 new file mode 100644 index 0000000000000000000000000000000000000000..fc2dc9b30f4c5ad925aa418123b8cc11e2ee1d08 GIT binary patch literal 42 hcmZQzfPw}lMnOh)Mg}lsh0+;}sf;;5n9r!h001S)0_6Yz literal 0 HcmV?d00001 diff --git a/fuzz/corpus/validate_pair/seed-013 b/fuzz/corpus/validate_pair/seed-013 new file mode 100644 index 0000000000000000000000000000000000000000..32d709d54ad3e8ca2c6b23ff39077da97897fed4 GIT binary patch literal 42 hcmZQzfPw~QMnOh)Mg}lsh0+;}sf;;5n9r!h001TF0_Fe! literal 0 HcmV?d00001 diff --git a/fuzz/corpus/validate_pair/seed-014 b/fuzz/corpus/validate_pair/seed-014 new file mode 100644 index 0000000000000000000000000000000000000000..17e0a38bb8a3d9238287a856349f8b92b03e2917 GIT binary patch literal 42 hcmZQzfPw}VMnOh)Mg}lsh0+;}sf;;5n9r!h001Tl0_Ok# literal 0 HcmV?d00001 diff --git a/fuzz/corpus/validate_pair/seed-015 b/fuzz/corpus/validate_pair/seed-015 new file mode 100644 index 0000000000000000000000000000000000000000..ca9e845120e207651978ffbf16f2695a882ff5ab GIT binary patch literal 42 hcmZQzfPw~AMnOh)Mg}lsh0+;}sf;;5n9r!h001T_0_Xq$ literal 0 HcmV?d00001 diff --git a/fuzz/corpus/validate_pair/seed-016 b/fuzz/corpus/validate_pair/seed-016 new file mode 100644 index 0000000000000000000000000000000000000000..95eb5542b9b28fe101f6ae20e79b462674206661 GIT binary patch literal 42 hcmZQzfPw}#MnOh)Mg}lsh0+;}sf;;5n9r!h001UQ0_gw% literal 0 HcmV?d00001 diff --git a/fuzz/corpus/validate_pair/seed-017 b/fuzz/corpus/validate_pair/seed-017 new file mode 100644 index 0000000000000000000000000000000000000000..6b5426424c4fbd2b4f025f44c2fdbd6bf36e5a07 GIT binary patch literal 42 hcmZQzfPw~gMnOh)Mg}lsh0+;}sf;;5n9r!h001Uw0_p$& literal 0 HcmV?d00001 diff --git a/fuzz/corpus/validate_pair/seed-018 b/fuzz/corpus/validate_pair/seed-018 new file mode 100644 index 0000000000000000000000000000000000000000..4848a27ca25552d08d877c42cfac638b9813c686 GIT binary patch literal 42 hcmZQzfPw}NMnOh)Mg}lsh0+;}sf;;5n9r!h001V50_y+( literal 0 HcmV?d00001 diff --git a/fuzz/corpus/validate_pair/seed-019 b/fuzz/corpus/validate_pair/seed-019 new file mode 100644 index 0000000000000000000000000000000000000000..42a48faf727b2d5241f68a9d027d5e724bb347ac GIT binary patch literal 42 hcmZQzfPw~2MnOh)Mg}lsh0+;}sf;;5n9r!h001Vb0_*?) literal 0 HcmV?d00001 diff --git a/fuzz/corpus/validate_pair/seed-020 b/fuzz/corpus/validate_pair/seed-020 new file mode 100644 index 0000000000000000000000000000000000000000..b1b5dad3d79240c7b93f2d8493f0ccaa7739d4ed GIT binary patch literal 42 hcmZQzfPw}tMnOh)Mg}lsh0+;}sf;;5n9r!h001V*0_^|* literal 0 HcmV?d00001 diff --git a/fuzz/corpus/validate_pair/seed-021 b/fuzz/corpus/validate_pair/seed-021 new file mode 100644 index 0000000000000000000000000000000000000000..09314355e6babd43a5ca850c924bb33d77b1ed7b GIT binary patch literal 42 hcmZQzfPw~YMnOh)Mg}lsh0+;}sf;;5n9r!h001WG0`33+ literal 0 HcmV?d00001 diff --git a/fuzz/corpus/validate_pair/seed-022 b/fuzz/corpus/validate_pair/seed-022 new file mode 100644 index 0000000000000000000000000000000000000000..3a3fbf0cabed28227ab054fa2e7d5df90dc0b41b GIT binary patch literal 42 hcmZQzfPw}dMnOh)Mg}lsh0+;}sf;;5n9r!h001Wm0`C9- literal 0 HcmV?d00001 diff --git a/fuzz/corpus/validate_pair/seed-023 b/fuzz/corpus/validate_pair/seed-023 new file mode 100644 index 0000000000000000000000000000000000000000..d0165f9205609584b40edd15c5b9093ca78321e9 GIT binary patch literal 42 hcmZQzfPw~IMnOh)Mg}lsh0+;}sf;;5n9r!h001W`0`LF; literal 0 HcmV?d00001 diff --git a/fuzz/corpus/validate_pair/seed-024 b/fuzz/corpus/validate_pair/seed-024 new file mode 100644 index 0000000000000000000000000000000000000000..a6c1920684e9aa4b28c7bac2dd9c6e32079fa27e GIT binary patch literal 42 hcmZQzfPw}-MnOh)Mg}lsh0+;}sf;;5n9r!h001XR0`UL< literal 0 HcmV?d00001 diff --git a/fuzz/corpus/validate_pair/seed-025 b/fuzz/corpus/validate_pair/seed-025 new file mode 100644 index 0000000000000000000000000000000000000000..002ef231b3e60923ed4bcff0c54747f947f6ed96 GIT binary patch literal 67 kcmZQzU|?VdVg?6BMn(oOWJ05}84Vd(F~p1**&t#J04}Nls{jB1 literal 0 HcmV?d00001 diff --git a/fuzz/corpus/validate_pair/seed-026 b/fuzz/corpus/validate_pair/seed-026 new file mode 100644 index 0000000000000000000000000000000000000000..659c7729ce2e9e86ab1aab1e0fe68c43ca3b0b76 GIT binary patch literal 67 kcmZQzU|?VdVuk~ZjEoFm$b?2`Ga53oVu%?rvO&Zc086R@D*ylh literal 0 HcmV?d00001 diff --git a/fuzz/corpus/validate_pair/seed-027 b/fuzz/corpus/validate_pair/seed-027 new file mode 100644 index 0000000000000000000000000000000000000000..c8cd9ee7226a1e5a68d482f71890b3440495cf06 GIT binary patch literal 43 icmZQzU|?Wk1QHI6+>8uh$OENWpmYjjB4Y_-A_D*v_W}t3 literal 0 HcmV?d00001 diff --git a/fuzz/corpus/validate_pair/seed-028 b/fuzz/corpus/validate_pair/seed-028 new file mode 100644 index 0000000000000000000000000000000000000000..e5fb1efd4af9bc471f05b35c6e1d309b18ee9e87 GIT binary patch literal 43 icmZQzU|?Wk1QG`rxfvP2kOxY$K). Shape has 4: + # Raw, Empty, Truncate(u8), Padded. + # PairInput = { lane: DocLane, mode: bool, shape: Shape, + # buffer: Vec } — buffer is the take-rest field. + # mode byte: 0 = packed, 1 = aligned. + LANE = {name: variant_u32(i, 2) for i, name in enumerate(["menu", "raw"])} + SHAPE = {name: variant_u32(i, 4) for i, name in enumerate( + ["raw", "empty", "trunc", "padded"])} + + def shape(name, payload=None): + b = bytearray(SHAPE[name]) + if name == "trunc": + b.append(payload if payload is not None else 0) + return bytes(b) + + def pair(lane_bytes, aligned, shape_bytes, buffer): + out = bytearray(lane_bytes) + out.append(1 if aligned else 0) + out.extend(shape_bytes) + for b in buffer: + out.append(0x01) + out.append(b) + out.append(0x00) + return bytes(out) + + def menu_lane(index): + b = bytearray(LANE["menu"]) + b.append(index) + return bytes(b) + + def raw_lane(doc): + b = bytearray(LANE["raw"]) + if isinstance(doc, str): + doc = doc.encode() + for byte in doc: + b.append(0x01) + b.append(byte) + b.append(0x00) + return bytes(b) + + # --- Buffer builders (all little-endian unless noted) --- + + def sandwich_packed(s): + # Menu 0/1 packed: u8 a | u32 n | string s | u8 tail. + return (struct.pack("I", hdr) + struct.pack(">H", a) + struct.pack(">H", b) + + i = 0 + + def w(data): + nonlocal i + write_seed("validate_pair", i, data) + i += 1 + + # valid/valid: both modes, the happy pair. + w(pair(menu_lane(0), False, shape("raw"), sandwich_packed(b"hi"))) + w(pair(menu_lane(1), True, shape("raw"), sandwich_aligned(b"hi"))) + w(pair(menu_lane(2), True, shape("raw"), indirect(b"data"))) + w(pair(menu_lane(3), False, shape("raw"), + record(2, [(b"k1", 5), (b"k2", 6)]))) + w(pair(menu_lane(4), False, shape("raw"), union(0, 0x33))) + be = big_endian(0x01020304, 0x0A0B, 0x0C0D) + w(pair(menu_lane(7), False, shape("raw"), be)) + w(pair(menu_lane(7), True, shape("raw"), be)) + + # hostile-schema/valid-bytes: the reject menus (8 empty, 9 cycle) + # and a raw-lane hostile doc; the buffer must never be driven. + w(pair(menu_lane(8), False, shape("raw"), sandwich_packed(b"hi"))) + w(pair(menu_lane(9), True, shape("raw"), b"\xAA" * 8)) + w(pair(raw_lane('{"$defs":{}}'), False, shape("raw"), sandwich_packed(b"hi"))) + # Raw-lane valid doc with the root hint (drives the fused lane's + # happy path). + w(pair(raw_lane('{"root":"S","$defs":{"S":{"kind":"struct","fields":' + '[{"name":"x","kind":"uint8"}]}}}'), + False, shape("raw"), struct.pack("), +} + +/// Post-decode transform of the hostile bytes (the read_opseq shape +/// set: raw, empty, truncate at n, fixed 64-byte zero pad). +#[derive(Debug, Arbitrary, Clone, Copy)] +pub enum Shape { + Raw, + Empty, + Truncate(u8), + Padded, +} + +/// The typed fuzz input. `buffer` (the take-rest field) carries the +/// hostile buffer body. +#[derive(Debug, Arbitrary)] +pub struct PairInput { + lane: DocLane, + mode: bool, + shape: Shape, + buffer: Vec, +} + +pub fn fuzz_validate_pair(data: &[u8]) { + let Ok(input) = PairInput::arbitrary_take_rest(arbitrary::Unstructured::new(data)) else { + return; + }; + + let (doc, root_name) = match &input.lane { + DocLane::Menu(i) => { + let text = MENUS[(*i as usize) % MENUS.len()]; + let doc = match serde_json::from_str::(text) { + Ok(v) => v, + Err(_) => return, + }; + (doc, "S".to_string()) + } + DocLane::Raw(bytes) => { + // The genuinely fused lane: the attacker's doc is bytes of + // the same provenance as the buffer. Anything serde_json + // rejects is not an alktype surface. + let doc = match serde_json::from_slice::(bytes) { + Ok(v) => v, + Err(_) => return, + }; + let root = doc + .get("root") + .and_then(Value::as_str) + .unwrap_or("S") + .to_string(); + (doc, root) + } + }; + let mode = if input.mode { LayoutMode::Packed } else { LayoutMode::Aligned }; + + let packed = AlkTypeEngine::compile(&doc, &root_name, LayoutMode::Packed, None); + let aligned = AlkTypeEngine::compile(&doc, &root_name, LayoutMode::Aligned, None); + assert_mode_agreement(&packed, &aligned); + + let engine = match mode { + LayoutMode::Packed => packed, + LayoutMode::Aligned => aligned, + }; + let Ok(engine) = engine else { + return; + }; + assert_engine_shape(&engine); + + let buffer = shape_buffer(&input.shape, &input.buffer); + junk_paths(&engine, &root_name); + + drive_pair(&engine, &doc, &root_name, &buffer); + + if engine.mode() == LayoutMode::Packed { + packed_walk(&engine, &buffer); + } +} + +/// Cross-mode gate agreement (the wave-1 invariant, re-pinned over the +/// hostile-doc lanes at the pair level): the parse layers are +/// mode-independent, so aligned Ok ⇒ packed Ok; a packed Ok with +/// aligned Err is allowed only when the aligned walk rejects for its +/// documented mode-specific reasons (ADR-006/ADR-008/record +/// offset-indirect), surfacing as `Offset`. +fn assert_mode_agreement( + packed: &Result, + aligned: &Result, +) { + match (packed, aligned) { + (Ok(_), Ok(_)) => {} + (Err(p), Err(a)) => { + assert_clean(p); + assert_clean(a); + } + (Ok(_), Err(a)) => match a { + AlkTypeError::Offset { reason, .. } => assert!( + reason.contains("ADR-006") + || reason.contains("ADR-008") + || reason.contains("offset-indirect"), + "aligned compile rejection after a packed Ok must name a \ + documented aligned-mode rejection (ADR-006/ADR-008 or the \ + record offset-indirect rule), got: {reason}" + ), + other => panic!( + "aligned compile rejection after a packed Ok must be an \ + Offset-class mode rejection, got {other:?}" + ), + }, + (Err(_), Ok(_)) => { + panic!("aligned Ok ⇒ packed Ok must hold (the aligned walk is a \ + strict subset of the packed walkers)") + } + } +} + +fn assert_clean(err: &AlkTypeError) { + match err { + AlkTypeError::Schema(msg) => { + assert!(!msg.is_empty(), "Schema error carries a message"); + } + AlkTypeError::Offset { reason, .. } | AlkTypeError::Access { reason, .. } => { + assert!(!reason.is_empty(), "Offset/Access errors carry a reason"); + } + AlkTypeError::Validation(_) => {} + } +} + +fn assert_engine_shape(engine: &AlkTypeEngine) { + match engine.mode() { + LayoutMode::Packed => { + assert!(engine.offset_map().is_none()); + assert!(engine.sequential_reader().is_some(), "packed factory"); + } + LayoutMode::Aligned => { + assert!(engine.offset_map().is_some()); + assert!(engine.sequential_reader().is_none(), "aligned factory"); + } + } +} + +/// Unknown paths must always error, echo the path, and never panic. +fn junk_paths(engine: &AlkTypeEngine, _root: &str) { + for junk in JUNK_PATHS { + let err = match engine.read_field(&[], junk) { + Ok(_) => panic!("junk path {junk:?} must never resolve"), + Err(e) => e, + }; + match &err { + AlkTypeError::Offset { field_path, .. } => { + assert_eq!(field_path, junk, "aligned unknown path echoes back"); + } + AlkTypeError::Access { field_path, .. } => { + assert_eq!(field_path, junk, "packed read_field echoes back"); + } + other => panic!("junk path must be Offset/Access, got {other:?}"), + } + assert_clean(&err); + } +} + +fn shape_buffer(shape: &Shape, bytes: &[u8]) -> Vec { + match shape { + Shape::Raw => bytes.to_vec(), + Shape::Empty => Vec::new(), + Shape::Truncate(n) => bytes[..bytes.len().min(*n as usize)].to_vec(), + Shape::Padded => { + let mut v = bytes.to_vec(); + v.extend_from_slice(&[0u8; 64]); + v + } + } +} + +/// The pair lane: validate_bytes and an independent materialize must +/// compose to the same result, byte-leaf reads must agree under a +/// successful validate, and the materialized `Value` must be +/// serde-safe. +fn drive_pair(engine: &AlkTypeEngine, doc: &Value, root: &str, buffer: &[u8]) { + let vb = engine.validate_bytes(buffer); + + let independent: Result = match engine.mode() { + LayoutMode::Packed => { + let plan = match ReadPlan::compile(doc, root) { + Ok(p) => p, + Err(e) => panic!("a compiled engine implies an equal plan compile: {e:?}"), + }; + materialize::materialize_packed(&plan, buffer) + } + LayoutMode::Aligned => { + let offset_map = engine + .offset_map() + .expect("an aligned engine carries its offset map"); + let bast_doc = match BastDoc::new(doc, root) { + Ok(d) => d, + Err(e) => panic!("a compiled engine implies an equal BastDoc: {e:?}"), + }; + materialize::materialize_aligned(&bast_doc, buffer, offset_map) + } + }; + + match independent { + Err(e_mat) => { + assert_clean(&e_mat); + let Err(e_vb) = vb else { + panic!( + "materialize Err ⇒ validate_bytes Err must hold, got \ + validate_bytes Ok while materializing failed: {e_mat:?}" + ) + }; + assert_eq!( + format!("{e_vb:?}"), + format!("{e_mat:?}"), + "validate_bytes propagates the materializer's error verbatim" + ); + } + Ok(value) => { + let plan_res = engine.validation_plan().validate(&value); + match plan_res { + Ok(()) => assert!( + vb.is_ok(), + "materialize Ok + plan Ok ⇒ validate_bytes Ok, got {vb:?}" + ), + Err(e_val) => { + assert_clean(&e_val); + let Err(ref e_vb) = vb else { + panic!( + "materialize Ok + plan Err ⇒ validate_bytes Err, got Ok" + ) + }; + assert_eq!( + format!("{e_vb:?}"), + format!("{e_val:?}"), + "validate_bytes on a materialized value is exactly the \ + plan walk's verdict" + ); + } + } + + // validate_bytes Ok ⇒ every offset-map leaf reads back + // through `read_field` over the same buffer. + if vb.is_ok() { + if let Some(map) = engine.offset_map() { + for (path, entry) in map.iter() { + if matches!( + entry.meta.kind, + alktype::AlkTypeKind::Struct + | alktype::AlkTypeKind::Union + | alktype::AlkTypeKind::Array + | alktype::AlkTypeKind::Record + ) { + continue; + } + match engine.read_field(buffer, path) { + Ok(_) => {} + Err(e) => panic!( + "validate_bytes Ok ⇒ read_field({path}) Ok, got {e:?}" + ), + } + assert!( + entry.range.end <= buffer.len(), + "a validated leaf sits inside the buffer" + ); + } + } + } + + // serde-safety: the output survives to_vec + parse back, + // structurally equal (preserve_order keeps key order). + let bytes = serde_json::to_vec(&value) + .expect("a materialized Value is serde-serializable"); + let back = serde_json::from_slice::(&bytes) + .expect("a materialized Value survives its own JSON encoding"); + assert_eq!(value, back, "materialize output serde round-trips"); + } + } +} + +/// The packed walk (packed mode only): plan order over a possibly +/// hostile buffer under the spin bound, errors always clean. +fn packed_walk(engine: &AlkTypeEngine, buffer: &[u8]) { + let Some(mut reader) = engine.sequential_reader() else { + return; + }; + let field_count = reader.plan().fields().len(); + let bound = walk_bound(buffer.len(), field_count); + + let mut steps = 0usize; + let mut names = Vec::new(); + loop { + steps += 1; + if steps > bound { + panic!( + "the pair walk exceeded the spin bound at step {steps} over a \ + {len}-byte buffer with {fields} fields", + len = buffer.len(), + fields = field_count + ); + } + match reader.read_next(buffer) { + Ok(Some((name, _))) => { + assert!(reader.position() <= buffer.len(), "cursor in bounds"); + names.push(name.to_string()); + } + Ok(None) => break, + Err(e) => { + assert_clean(&e); + break; + } + } + } + if names.len() == field_count { + let expected: Vec = + reader.plan().fields().iter().map(|f| f.name().to_string()).collect(); + assert_eq!( + names, expected, + "a full walk over a validating buffer reports fields in plan order" + ); + } +} + +/// Same bound formula as the read_opseq target: a successful step +/// advances the cursor, and every inner iteration verifies ≥ 4 bytes. +fn walk_bound(buffer_len: usize, field_count: usize) -> usize { + buffer_len.saturating_add(field_count).saturating_add(2) +} + +#[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/validate_pair"); + 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_validate_pair(&data); + count += 1; + } + assert!( + count >= 10, + "committed seed corpus is present, found {count}" + ); + } + + /// Byte encodings against the pinned arbitrary 1.4.2 derive layout: + /// field order { lane: DocLane, mode: bool, shape: Shape, buffer: + /// Vec } with buffer the take-rest field. DocLane has 2 + /// variants (Menu(u8), Raw(Vec)); Shape has 4 (Raw, Empty, + /// Truncate(u8), Padded). Enum variant selection is the 4-byte LE + /// multiply-shift u32; Vec elements are keep-going byte + + /// element bytes with a 0x00 stop; bools are one byte (& 1). + mod enc { + pub fn variant_u32(k: u64, count: u64) -> [u8; 4] { + let mut v = (k << 32).div_ceil(count); + while (v.wrapping_mul(count) >> 32) != k { + v += 1; + } + u32::try_from(v).expect("k < count always admits a < 2^32 v").to_le_bytes() + } + + pub const LANE_MENU: u64 = 0; + pub const LANE_RAW: u64 = 1; + + pub const SHAPE_RAW: u64 = 0; + pub const SHAPE_EMPTY: u64 = 1; + pub const SHAPE_TRUNCATE: u64 = 2; + pub const SHAPE_PADDED: u64 = 3; + + pub fn lane_menu(index: u8) -> Vec { + let mut v = variant_u32(LANE_MENU, 2).to_vec(); + v.push(index); + v + } + + pub fn lane_raw(doc: &[u8]) -> Vec { + let mut v = variant_u32(LANE_RAW, 2).to_vec(); + for b in doc { + v.push(0x01); + v.push(*b); + } + v.push(0x00); + v + } + + pub fn shape_field(tag: u64) -> Vec { + variant_u32(tag, 4).to_vec() + } + + pub fn shape_truncate(n: u8) -> Vec { + let mut v = shape_field(SHAPE_TRUNCATE); + v.push(n); + v + } + + pub fn pair(lane: Vec, aligned: bool, shape: Vec, buffer: &[u8]) -> Vec { + let mut v = lane; + v.push(u8::from(aligned)); // mode: false = packed, true = aligned + v.extend_from_slice(&shape); + for b in buffer { + v.push(0x01); + v.push(*b); + } + v.push(0x00); + v + } + } + + /// Buffers matching the menu schemas (little-endian where declared; + /// menu 7 is big-endian). + mod buffers { + /// Menu 0/1 packed body: u8 a | u32 n | string s | u8 tail. + pub fn sandwich_packed(s: &[u8]) -> Vec { + let mut buf = Vec::new(); + buf.push(0x11); + buf.extend_from_slice(&7u32.to_le_bytes()); + buf.extend_from_slice(&(s.len() as u32).to_le_bytes()); + buf.extend_from_slice(s); + buf.push(0x22); + buf + } + + /// Menu 1 aligned body with `s` as a fixed `maxLength` region + /// (64 bytes reserved at the entry, 8..72): a at 0, n at 4, + /// s prefix at 8, s data inline 12..12+len, tail at 72 (the + /// offset pin test below keeps these honest). + pub fn sandwich_aligned(s: &[u8]) -> Vec { + let mut buf = vec![0u8; 12 + 64 + 1]; + buf[0] = 0x11; + buf[4..8].copy_from_slice(&7u32.to_le_bytes()); + buf[8..12].copy_from_slice(&(s.len() as u32).to_le_bytes()); + buf[12..12 + s.len()].copy_from_slice(s); + buf[72] = 0x22; + buf + } + + /// Menu 2 aligned offset-indirect body: u32 id | u32 data_off | + /// u32 data_len | data region at data_off. + pub fn indirect(data: &[u8]) -> Vec { + let mut buf = vec![0u8; 12 + data.len()]; + buf[0..4].copy_from_slice(&0x0Bu32.to_le_bytes()); + buf[4..8].copy_from_slice(&12u32.to_le_bytes()); + buf[8..12].copy_from_slice(&(data.len() as u32).to_le_bytes()); + buf[12..].copy_from_slice(data); + buf + } + + /// Menu 3 packed body: u32 k | record count | entries. + pub fn record(count: u32, entries: &[(&[u8], u32)]) -> Vec { + let mut buf = Vec::new(); + buf.extend_from_slice(&1u32.to_le_bytes()); + buf.extend_from_slice(&count.to_le_bytes()); + for (key, value) in entries { + buf.extend_from_slice(&(key.len() as u32).to_le_bytes()); + buf.extend_from_slice(key); + buf.extend_from_slice(&value.to_le_bytes()); + } + buf + } + + /// Menu 4 packed body: u16 pre | disc byte | variant u8. + pub fn union(disc: u8, v: u8) -> Vec { + let mut buf = Vec::new(); + buf.extend_from_slice(&0x0102u16.to_le_bytes()); + buf.push(disc); + buf.push(v); + buf + } + + /// Menu 5 body: u8 tag | f64 (chosen bit pattern). + pub fn float(bits: u64) -> Vec { + let mut buf = Vec::new(); + buf.push(0x07); + buf.extend_from_slice(&bits.to_le_bytes()); + buf + } + + /// Menu 6 body: u32 enum index | u8 t. + pub fn enum_val(index: u32) -> Vec { + let mut buf = Vec::new(); + buf.extend_from_slice(&index.to_le_bytes()); + buf.push(0x09); + buf + } + + /// Menu 7 big-endian body: u32 hdr | u16 vals[0] | u16 vals[1]. + pub fn big_endian(hdr: u32, a: u16, b: u16) -> Vec { + let mut buf = Vec::new(); + buf.extend_from_slice(&hdr.to_be_bytes()); + buf.extend_from_slice(&a.to_be_bytes()); + buf.extend_from_slice(&b.to_be_bytes()); + buf + } + } + + #[test] + fn decode_lands_on_the_intended_variants() { + fn decode(data: &[u8]) -> (String, bool, String, usize) { + let input = PairInput::arbitrary_take_rest(arbitrary::Unstructured::new(data)) + .expect("the encoding decodes"); + let lane = match &input.lane { + DocLane::Menu(i) => format!("M{i}"), + DocLane::Raw(_) => "Raw".to_string(), + }; + let shape = match input.shape { + Shape::Raw => "Raw".to_string(), + Shape::Empty => "Empty".to_string(), + Shape::Truncate(n) => format!("T{n}"), + Shape::Padded => "P".to_string(), + }; + (lane, input.mode, shape, input.buffer.len()) + } + + let seed = + enc::pair(enc::lane_menu(3), true, enc::shape_truncate(5), &[9, 9, 9]); + assert_eq!(decode(&seed), ("M3".to_string(), true, "T5".to_string(), 3)); + let seed = enc::pair( + enc::lane_raw(br#"{"$defs":{}}"#), + false, + enc::shape_field(enc::SHAPE_PADDED), + &[1], + ); + assert_eq!(decode(&seed), ("Raw".to_string(), false, "P".to_string(), 1)); + let seed = enc::pair(enc::lane_menu(0), false, enc::shape_field(enc::SHAPE_RAW), &[]); + assert_eq!(decode(&seed), ("M0".to_string(), false, "Raw".to_string(), 0)); + } + + #[test] + fn valid_valid_both_modes_agree_and_validate() { + // Menu 0 packed with a complete body: validate Ok, independent + // materialize agrees, leaves read back, serde round-trips. + fuzz_validate_pair(&enc::pair( + enc::lane_menu(0), + false, + enc::shape_field(enc::SHAPE_RAW), + &buffers::sandwich_packed(b"hi"), + )); + // Menu 1 (maxLength reservation) in aligned mode. + fuzz_validate_pair(&enc::pair( + enc::lane_menu(1), + true, + enc::shape_field(enc::SHAPE_RAW), + &buffers::sandwich_aligned(b"hi"), + )); + // Menu 2 aligned offset-indirect: the full pair contract. + fuzz_validate_pair(&enc::pair( + enc::lane_menu(2), + true, + enc::shape_field(enc::SHAPE_RAW), + &buffers::indirect(b"data"), + )); + // Menu 3 packed record with two entries. + fuzz_validate_pair(&enc::pair( + enc::lane_menu(3), + false, + enc::shape_field(enc::SHAPE_RAW), + &buffers::record(2, &[(b"k1", 5), (b"k2", 6)]), + )); + // Menu 4 packed union (disc 0 → Small). Valid in packed. + fuzz_validate_pair(&enc::pair( + enc::lane_menu(4), + false, + enc::shape_field(enc::SHAPE_RAW), + &buffers::union(0, 0x33), + )); + // Menu 7 big-endian fixed shape, both modes. + let be = buffers::big_endian(0x0102_0304, 0x0A0B, 0x0C0D); + fuzz_validate_pair(&enc::pair(enc::lane_menu(7), false, enc::shape_field(enc::SHAPE_RAW), &be)); + fuzz_validate_pair(&enc::pair(enc::lane_menu(7), true, enc::shape_field(enc::SHAPE_RAW), &be)); + } + + #[test] + fn hostile_schema_with_valid_bytes_is_a_clean_rejection() { + // Menus 8/9 never compile; the byte lane must not be driven. + fuzz_validate_pair(&enc::pair( + enc::lane_menu(8), + false, + enc::shape_field(enc::SHAPE_RAW), + &buffers::sandwich_packed(b"hi"), + )); + fuzz_validate_pair(&enc::pair( + enc::lane_menu(9), + true, + enc::shape_field(enc::SHAPE_RAW), + &[0xAA; 8], + )); + // Raw-lane hostile doc with a valid sibling buffer. + fuzz_validate_pair(&enc::pair( + enc::lane_raw(br#"{"$defs":{}}"#), + false, + enc::shape_field(enc::SHAPE_RAW), + &buffers::sandwich_packed(b"hi"), + )); + } + + #[test] + fn valid_schema_with_hostile_bytes_agrees_on_rejection() { + // Truncation sweep over the packed sandwich: every prefix must + // materialize-fail with the error propagated verbatim by + // validate_bytes. + let body = buffers::sandwich_packed(b"hello"); + for n in 1..body.len() { + fuzz_validate_pair(&enc::pair( + enc::lane_menu(0), + false, + enc::shape_truncate(n as u8), + &body, + )); + } + // Aligned truncated at the record/union boundaries (empty + + // padded shapes over menu 2 and 3). + fuzz_validate_pair(&enc::pair( + enc::lane_menu(2), + true, + enc::shape_field(enc::SHAPE_EMPTY), + &buffers::indirect(b"data"), + )); + fuzz_validate_pair(&enc::pair( + enc::lane_menu(3), + false, + enc::shape_field(enc::SHAPE_PADDED), + &buffers::record(2, &[(b"k1", 5), (b"k2", 6)]), + )); + } + + #[test] + fn the_aligned_sandwich_uses_the_documented_offsets() { + // Pin the aligned layout offsets of menu 1 so the + // `sandwich_aligned` fixture stays honest across engine + // changes: a (u8), n (u32 aligned to 4), s reservation + // (maxLength 64 reserved inline at the entry, 8..72), tail at + // 72. The prefix sits at the reservation start; the string + // data follows it. + let doc: serde_json::Value = serde_json::from_str(MENUS[1]) + .expect("menu 1 parses"); + let engine = AlkTypeEngine::compile(&doc, "S", LayoutMode::Aligned, None) + .expect("menu 1 compiles aligned"); + let map = engine.offset_map().expect("aligned engine"); + assert_eq!(map.get("a").expect("a").range.start, 0); + assert_eq!(map.get("n").expect("n").range.start, 4); + let s = map.get("s").expect("s"); + assert_eq!(s.range, alktype::ByteRange { start: 8, end: 72 }); + assert_eq!(map.get("tail").expect("tail").range.start, 72); + assert_eq!(map.total_size(), 76); + } + + #[test] + fn non_finite_floats_are_access_not_silent_values() { + // NaN through materialize must be an Access error naming the + // reason — never a silent 0.0/Null in the output (the plan's + // §3 target-5 non-finite invariant, src/materialize.rs's + // number_from_f64). + fuzz_validate_pair(&enc::pair( + enc::lane_menu(5), + false, + enc::shape_field(enc::SHAPE_RAW), + &buffers::float(0x7FF8_0000_0000_0000), // f64 NaN + )); + fuzz_validate_pair(&enc::pair( + enc::lane_menu(5), + true, + enc::shape_field(enc::SHAPE_RAW), + &buffers::float(0x7FF0_0000_0000_0000), // f64 Inf + )); + } + + #[test] + fn out_of_range_enum_is_a_validation_error_not_materialization_loss() { + // Enum index 99: bytes decode fine (u32), materialize Ok, the + // plan walk must reject with Validation — and validate_bytes + // must surface exactly that. + fuzz_validate_pair(&enc::pair( + enc::lane_menu(6), + false, + enc::shape_field(enc::SHAPE_RAW), + &buffers::enum_val(99), + )); + } + + #[test] + fn hostile_record_count_fails_fast_with_access() { + // The §6 candidate-1 wave-3 form: count far above the wire can + // afford. materialize (both directly and through + // validate_bytes) must fail fast with Access naming the + // record — the spin bound lives in the target's walk bound on + // the sequential side and in this fixture's fast-fail on the + // materialize side. + fuzz_validate_pair(&enc::pair( + enc::lane_menu(3), + false, + // Truncate right after the count: the first iteration has + // nothing to verify. + enc::shape_truncate(8), + &{ + let mut buf = + buffers::record(0xFFFF_FFFF, &[]); + buf.truncate(8); + buf + }, + )); + // count = 65536 with zero everything: every iteration must + // verify ≥ 4 bytes, so it fails inside the byte budget. + fuzz_validate_pair(&enc::pair( + enc::lane_menu(3), + false, + enc::shape_field(enc::SHAPE_RAW), + &{ + let mut buf = buffers::record(0x0001_0000, &[]); + buf.extend_from_slice(&[0u8; 16]); + buf + }, + )); + } + + #[test] + fn packed_lane_rejects_read_field_and_union_aligns_reject() { + // Mode-agreement pins at the harness level: menu 4 (TUnion) + // compiles packed but must fail aligned (ADR-008); menu 0 must + // fail aligned (ADR-006); both must compile-packed with a + // valid body. + fuzz_validate_pair(&enc::pair( + enc::lane_menu(4), + true, + enc::shape_field(enc::SHAPE_RAW), + &buffers::union(0, 0x33), + )); + fuzz_validate_pair(&enc::pair( + enc::lane_menu(0), + true, + enc::shape_field(enc::SHAPE_RAW), + &buffers::sandwich_packed(b"hi"), + )); + } +} \ No newline at end of file