diff --git a/CHANGELOG.md b/CHANGELOG.md index 633561b2..034b40ec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ after its public API and format compatibility policies are established. ## [Unreleased] +- Migration compatibility laws preserve version-one bytes and reject version-one authority at every forward prefix; public version/flag refusal tests and bounded seeded recovery-planner fuzzing extend transition evidence (#112). + - Completed migration recovery now verifies the version-two namespace before reporting success, refusing unknown reserved GC/recovery entries without effects while preserving published retention state (#111). Recovery storage implementors must supply the new read-only `verify_complete` capability. - Migration restart laws preserve complete filesystem witnesses when rejecting damaged records and pools, conflicting or substituted stages, invalid ordering, copied-root identity, changed inventory, foreign receipts, unknown names and wrong kinds (#111). diff --git a/docs/formats/segment-store-v2/requirements.md b/docs/formats/segment-store-v2/requirements.md index 69e9f524..8cda0a97 100644 --- a/docs/formats/segment-store-v2/requirements.md +++ b/docs/formats/segment-store-v2/requirements.md @@ -35,7 +35,7 @@ case is not evidence. | `KEEP-MIGRATION-005` | Unknown, out-of-order, substituted, corrupt, conflicting, or changed evidence is unrecoverable ambiguity | Forward-execution laws remain. Filesystem restart record, pair, ordering, root, pool and namespace laws assert exact existing refusal boundaries and preserve complete names, device/inode identities and bytes; see [restart matrix](../../testing-evidence/migration-restart-matrix.md) for scenarios, calibration, diagnostic limits and validation ownership. | Implemented in #111 candidate, including reserved complete-state namespace refusal; final review and integration pending | | `KEEP-MIGRATION-006` | Migration never rewrites or deletes admitted version-1 immutable bytes | exact segment, catalog, and head witnesses in `src/adapters/store_migration/filesystem_migration_storage_tests.rs`, `src/adapters/store_migration/filesystem_migration_recovery_tests.rs`, and `src/adapters/store_migration/filesystem_migration_recovery_truncation_tests.rs`; subprocess restart witnesses in `cargo xtask durability-crash-matrix --sequence migration` | Implemented in #108 | | `KEEP-MIGRATION-007` | Process death around every intent stage, canonical link, namespace prefix, marker stage, receipt stage, cleanup, and synchronization boundary reaches a documented lawful state | ordered phases and capabilities in `tests/store_migration_phase.rs` and `tests/store_migration_storage.rs`; exact phase-failure execution in `tests/store_migration_execution.rs`; production 21-phase forward execution in `filesystem_migration_storage_tests`; `cargo xtask durability-crash-matrix --sequence migration` runs 68 production subprocess cases at `KEEP-CRASH-053..=073`, debug and release | Implemented in #108 | -| `KEEP-MIGRATION-008` | Version-1 admission refuses every version-2 or partial-migration artifact after migration begins | `FORMAT` refusal before mutation in `filesystem_migration_authority_tests`; exact version-1 reopen refusal of a migrated root and separate version-2 namespace admission in `filesystem_initialization_namespace`; version-2 reopen returns a distinct `FilesystemVersionTwoAdmission` that no version-1 publisher can consume (pinned by `tests/version_two_admission_contract.rs`), admits every version-2 protocol directory under the Linux profile, and jointly admits the exact marker, intent, and receipt before returning writer authority, with aliased-directory, corrupt, oversized, and mutually inconsistent record refusals in `filesystem_version_two_admission_tests` and `filesystem_platform_profile_tests`; remaining compatibility and fuzz matrix | In progress in #112 | +| `KEEP-MIGRATION-008` | Version-1 admission refuses every version-2 or partial-migration artifact after migration begins | Every forward-prefix compatibility law preserves v1 HEAD/catalog/segment bytes and refuses v1 authority after migration effects; public decoder laws pin unsupported versions and every mandatory flag bit; bounded seeded migration parser/recovery-planner fuzzing explores valid and malformed transitions. Existing jointly bound version-two admission and root-identity laws remain. See [compatibility evidence](../../testing-evidence/migration-compatibility-fuzz.md) for coverage, calibration and limits. | Implemented | diff --git a/docs/testing-evidence/migration-compatibility-fuzz.md b/docs/testing-evidence/migration-compatibility-fuzz.md new file mode 100644 index 00000000..b05cfb46 --- /dev/null +++ b/docs/testing-evidence/migration-compatibility-fuzz.md @@ -0,0 +1,50 @@ +# Migration compatibility and recovery fuzz evidence + +This change addresses #112 and KEEP-MIGRATION-008 from base main `6051abb25a9fd33ae7ee0de5614514b709a4d82a`. Change kind: missing runtime verification and stronger fuzz exploration, with no production behavior change. Owner: `@flyingrobots`. This branch is independent of the #111 restart ambiguity PR and does not claim mainline integration before merging. + +## Claims and owners + +`filesystem_migration_compatibility_tests::every_migration_prefix_preserves_v1_bytes_but_refuses_v1_authority` executes every complete forward-phase prefix in fresh filesystem storage. Before migration begins, version-one authority reopens and verifies the same intent. After any migration effect, reopening requires the existing exact `FilesystemPlatformAdmissionError::Namespace` / `InvalidData` refusal. After the attempt, every original version-one HEAD, segment and catalog name/byte pair must remain unchanged. Existing recovery-prefix and process-death laws retain responsibility for interrupted operations and restart recovery. + +`tests/store_migration_compatibility.rs` admits the golden migration records through public decoders and tests unsupported versions and every single mandatory flag bit in marker, intent and receipt. Expected coordinates come from the format's big-endian version and flags fields; precise `UnsupportedVersion` and `UnsupportedFlags` precede checksum refusal. Existing `tests/segment_header.rs`, `tests/segment_header/mutation_laws.rs`, `tests/catalog/header_laws.rs` and `tests/publication_head.rs` own version-one grammar/version/flag refusals. + +`migration_format` retains record parser fuzzing and adds bounded `plan_store_migration_recovery` exploration. Its properties require version-one success exactly when no migration evidence exists, precise effects-before-intent refusal, and complete success only with a full namespace, no stages and jointly admitted records. The complete-record property reuses product decoders; it checks agreement between planner and admission, not an independent implementation of the binary grammar. Other planner outcomes still receive robustness exploration rather than a complete reference-model oracle. + +## Corpus and bounds + +Checked-in seed recipes cover record admission, malformed marker version, unsupported intent flags, corrupt receipt checksum, valid migration prefixes, complete migration, corrupt intent, an effect before durable intent, a stage surviving an effect, a namespace hole and a receipt preceding its marker. They are materialized by `cargo xtask prepare-fuzz-corpus` and exercised through the existing migration target in smoke and scheduled workflows. No seed-count assertion establishes correctness. + +Recovery selector 3 is a fuzz-only envelope, not a durable format: a 256-byte expected intent, record-presence byte, namespace-presence byte and six length-prefixed record payloads. The envelope lengths use little-endian u16; embedded product records retain their canonical big-endian fields. Each payload is limited to 513 bytes, allowing overlong records while bounding payload allocation to 3,078 bytes. A complete envelope consumes at most 3,349 bytes including its selector; trailing fuzz-input bytes are ignored. This is an exploration bound, not a new storage limit. The checked-in campaign also enforces its own input, timeout and RSS bounds. + +The fixed local run uses pinned nightly `nightly-2026-07-24`, cargo-fuzz 0.13.2, seed 112, 20,000 executions, a 4,096-byte input cap, five-second per-input timeout and 1,024 MiB RSS limit. The named starting corpus is archived before launch; generated descendants are retained separately from the checked-in recipes. Replay `cargo +nightly-2026-07-24 fuzz run migration_format -- -runs=20000 -seed=112 -max_len=4096 -timeout=5 -rss_limit_mb=1024`. The instrumented binary exports `__asan_init`, and the release fuzz calibration demonstrates active assertions. No product crash or new real counterexample was found in that bounded run; it is not an exhaustive correctness claim. + +## Calibration and validation + +Separate copied-source/build mutations produce behavioral RED: admitting migrating namespaces as v1 incorrectly succeeds at the first migration prefix; damaging HEAD after the final migration phase fails byte preservation; bypassing marker version/flag checks changes the precise failures to checksum errors; forcing version-one success for a retained intent stage fails the fuzz oracle on its named seed. Mutations are excluded from the candidate. The initial test-authoring mistake using little-endian field patches and compiler/lint corrections are retained in logs but are not product RED evidence. + +Focused runtime tests pass in debug and release, and the bounded seeded fuzz run passes. The PR records immutable candidate coordinates, full workspace checks, migration crash campaign results and hosted CI; no earlier SHA's green result transfers automatically. Stable Rust is pinned to 1.96.0. Rust runs use copied Docker sources on Linux aarch64, not a writable host checkout. + +The filesystem law is medium-size and uses repository-only platform admission to isolate compatibility; it does not prove production platform eligibility. Decoder examples are small. Fuzzing invokes parser/planner runtime, not filesystem recovery execution, power-loss simulation or arbitrary writer interleavings. Existing platform, crash and recovery owners retain those claims. Ordinary-test resource enforcement gaps remain as disclosed in the testing enforcement profile. + +The seed materialization tool test no longer freezes global or per-target case counts. Mainline #172 now supplies the stronger materialization witness: a named emitted partial-seal counterexample reaches the real recovery classifier and produces its exact typed refusal, followed by deterministic repeated materialization. The integration preserves that witness instead of restoring the weaker nonempty-output assertion; migration product confidence comes from actual decoder, planner and filesystem outcomes. Deletion criterion: incidental inventory totals did not guard a product contract and rejected legitimate added exploration. Retire the new laws only when their compatibility contract disappears or stronger evidence subsumes it. + +## Landing calibration closure + +The fresh review of merged candidate `6e7e2d6a9c965f0240e006c7d45a5bc3c7010d37`, tracked tree `437bc1971fc8a98f2d039e446b3f5008979974f9`, found that the original Marker failures stopped execution before the independent Intent/Receipt checks, and the original VersionOne control did not reach the other fuzz properties. This is a proof gap, not a product bug. One bounded batch now demonstrates the distinct assertions below without changing their expected outcomes or the candidate's runtime code. + +| Control | Violated observation and intended runtime failure | Receipt | +| --- | --- | --- | +| [Intent header admission](migration-compatibility-fuzz/intent.patch) | Skip only Intent version/flag checks; unchanged Marker admission passes, then the Intent assertions observe checksum failures instead of exact version/flag refusals. | [RED](migration-compatibility-fuzz/intent-red.txt) | +| [Receipt header admission](migration-compatibility-fuzz/receipt.patch) | Skip only Receipt version/flag checks; earlier Marker/Intent assertions pass, and Receipt's exact refusals fail. | [RED](migration-compatibility-fuzz/receipt-red.txt) | +| [Pre-intent effect coordinate](migration-compatibility-fuzz/pre-intent.patch) | After the actual planner runs, replace its Namespace effect refusal with Marker; the unchanged VersionOne property passes, then the exact pre-intent assertion fails. | [RED](migration-compatibility-fuzz/pre-intent-red.txt) | +| [Complete namespace](migration-compatibility-fuzz/namespace.patch) | After actual Complete planning, remove the observed reader fence; the full-namespace assertion fails. | [RED](migration-compatibility-fuzz/namespace-red.txt) | +| [Complete stage absence](migration-compatibility-fuzz/stage.patch) | After actual Complete planning, add observed staged evidence; the namespace assertion passes, then stage absence fails. | [RED](migration-compatibility-fuzz/stage-red.txt) | +| [Complete record admission](migration-compatibility-fuzz/records.patch) | After actual Complete planning, replace the receipt observation with invalid bytes; earlier properties pass, then joint record admission fails. | [RED](migration-compatibility-fuzz/records-red.txt) | + +The four fuzz controls calibrate the unchanged oracles by perturbing observations after calling the real planner. They are deliberately inconsistent observations, not claims of production planner defects or real on-disk counterexamples. The earlier filesystem authority, byte-preservation, Marker and VersionOne controls remain valid for their original scope. No mutation score or one-control-per-bit claim is made. An initial launcher expected the wrong source-path prefix in the fuzz panic text and stopped after the valid pre-intent failure; the raw assertion failure is retained, the log-matching prefix was corrected, and only the three remaining controls were launched. That launcher mismatch is not runtime RED. + +Apply one patch at a time with `git apply --unidiff-zero` to a fresh copy of the source above. For Intent/Receipt, run `cargo test --all-features --locked --test store_migration_compatibility`. For fuzz controls, first materialize the unchanged candidate's named seeds with `cargo xtask prepare-fuzz-corpus`, then run `cargo +nightly-2026-07-24 fuzz run migration_format -- -runs=1 -seed=112 -max_len=4096 -timeout=5 -rss_limit_mb=1024`. Use `recovery-effect-before-intent` for the pre-intent control and `recovery-complete` for the other three. Each experiment uses its own copied source/build directory; no mutation enters the PR. + +The unchanged candidate then passes [public decoder debug/release GREEN](migration-compatibility-fuzz/restored-headers-green.txt) and a fresh [20,000-run seeded fuzz GREEN](migration-compatibility-fuzz/restored-fuzz-green.txt). The latter's named starting corpus is archived before launch; generated descendants are separate retained execution artifacts. Both receipts pin the original tracked tree. Full copied-Docker validation also completes successfully at that tree, and all four hosted jobs in run `37155153324` pass. Final evidence-only successors still require their own hosted/static checks and exact-head review; those are recorded in the PR. + +Committed logs normalize only isolated container source/build prefixes and line-end/trailing-empty whitespace. Original raw logs, exact variants, source archive, starting corpus and traced launchers remain retained by the author. This closure adds no runtime behavior, exploration scope, physical-power-loss evidence or resource-enforcement claim beyond the boundaries above. diff --git a/docs/testing-evidence/migration-compatibility-fuzz/intent-red.txt b/docs/testing-evidence/migration-compatibility-fuzz/intent-red.txt new file mode 100644 index 00000000..321b18f0 --- /dev/null +++ b/docs/testing-evidence/migration-compatibility-fuzz/intent-red.txt @@ -0,0 +1,73 @@ ++ cd ++ CARGO_TARGET_DIR= ++ cargo test --all-features --locked --test store_migration_compatibility + Compiling rustix v1.1.4 + Compiling linux-raw-sys v0.12.1 + Compiling io-lifetimes v2.0.4 + Compiling bitflags v2.13.1 + Compiling io-lifetimes v3.0.1 + Compiling proc-macro2 v1.0.107 + Compiling io-extras v0.19.0 + Compiling cap-primitives v4.0.2 + Compiling shlex v2.0.1 + Compiling quote v1.0.47 + Compiling once_cell v1.21.4 + Compiling find-msvc-tools v0.1.9 + Compiling unicode-ident v1.0.24 + Compiling ambient-authority v0.0.2 + Compiling maybe-owned v0.3.4 + Compiling ipnet v2.12.0 + Compiling cap-std v4.0.2 + Compiling cap-fs-ext v4.0.2 + Compiling clap_lex v1.1.0 + Compiling cfg-if v1.0.4 + Compiling cc v1.3.0 + Compiling libc v0.2.186 + Compiling anstyle v1.0.14 + Compiling arrayref v0.3.9 + Compiling arrayvec v0.7.8 + Compiling constant_time_eq v0.4.2 + Compiling condtype v1.3.0 + Compiling regex-lite v0.1.9 + Compiling allocation-counter v0.8.1 + Compiling clap_builder v4.6.2 + Compiling syn v2.0.119 + Compiling blake3 v1.8.5 + Compiling clap v4.6.4 + Compiling rustix-linux-procfs v0.1.1 + Compiling fs-set-times v0.20.3 + Compiling divan-macros v0.1.21 + Compiling keep v0.0.0 () + Compiling divan v0.1.21 + Finished `test` profile [unoptimized + debuginfo] target(s) in 3.37s + Running tests/store_migration_compatibility.rs (/debug/deps/store_migration_compatibility-fbb66f7c5fa9ff33) + +running 2 tests +test unsupported_migration_versions_refuse_with_exact_coordinates ... FAILED +test every_unknown_mandatory_flag_refuses_without_downgrade ... FAILED + +failures: + +---- unsupported_migration_versions_refuse_with_exact_coordinates stdout ---- + +thread 'unsupported_migration_versions_refuse_with_exact_coordinates' (2069453) panicked at tests/store_migration_compatibility.rs:36:9: +assertion `left == right` failed + left: Some(ChecksumMismatch { expected: [133, 68, 127, 122, 231, 209, 183, 129, 12, 27, 150, 206, 179, 58, 125, 26, 91, 117, 149, 153, 171, 142, 116, 148, 187, 207, 21, 50, 125, 116, 53, 155], observed: [123, 236, 16, 204, 140, 30, 239, 90, 176, 232, 232, 182, 163, 50, 64, 187, 162, 145, 37, 45, 66, 99, 20, 125, 241, 52, 6, 46, 183, 13, 63, 31] }) + right: Some(UnsupportedVersion { expected: 2, observed: 0 }) +note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace + +---- every_unknown_mandatory_flag_refuses_without_downgrade stdout ---- + +thread 'every_unknown_mandatory_flag_refuses_without_downgrade' (2069452) panicked at tests/store_migration_compatibility.rs:74:9: +assertion `left == right` failed + left: Some(ChecksumMismatch { expected: [146, 191, 58, 134, 238, 87, 46, 110, 169, 158, 196, 30, 241, 211, 86, 58, 134, 88, 230, 64, 167, 53, 64, 238, 64, 23, 23, 129, 132, 179, 158, 13], observed: [123, 236, 16, 204, 140, 30, 239, 90, 176, 232, 232, 182, 163, 50, 64, 187, 162, 145, 37, 45, 66, 99, 20, 125, 241, 52, 6, 46, 183, 13, 63, 31] }) + right: Some(UnsupportedFlags { observed: 1 }) + + +failures: + every_unknown_mandatory_flag_refuses_without_downgrade + unsupported_migration_versions_refuse_with_exact_coordinates + +test result: FAILED. 0 passed; 2 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s + +error: test failed, to rerun pass `--test store_migration_compatibility` diff --git a/docs/testing-evidence/migration-compatibility-fuzz/intent.patch b/docs/testing-evidence/migration-compatibility-fuzz/intent.patch new file mode 100644 index 00000000..6a76dd60 --- /dev/null +++ b/docs/testing-evidence/migration-compatibility-fuzz/intent.patch @@ -0,0 +1,8 @@ +--- a/src/adapters/store_migration/migration_intent_decoder.rs ++++ b/src/adapters/store_migration/migration_intent_decoder.rs +@@ -52 +52 @@ fn validate_fixed_fields(encoded: &[u8]) -> Result<(), StoreMigrationIntentDecod +- if version != format::VERSION { ++ if false && version != format::VERSION { +@@ -66 +66 @@ fn validate_fixed_fields(encoded: &[u8]) -> Result<(), StoreMigrationIntentDecod +- if flags != 0 { ++ if false && flags != 0 { diff --git a/docs/testing-evidence/migration-compatibility-fuzz/namespace-red.txt b/docs/testing-evidence/migration-compatibility-fuzz/namespace-red.txt new file mode 100644 index 00000000..2b75ab98 --- /dev/null +++ b/docs/testing-evidence/migration-compatibility-fuzz/namespace-red.txt @@ -0,0 +1,89 @@ ++ cd ++ CARGO_TARGET_DIR= ++ cargo +nightly-2026-07-24 fuzz run migration_format /fuzz/corpus/migration_format/recovery-complete -- -runs=1 -seed=112 -max_len=4096 -timeout=5 -rss_limit_mb=1024 + Compiling libc v0.2.186 + Compiling find-msvc-tools v0.1.9 + Compiling shlex v2.0.1 + Compiling rustix v1.1.4 + Compiling io-lifetimes v2.0.4 + Compiling io-lifetimes v3.0.1 + Compiling linux-raw-sys v0.12.1 + Compiling bitflags v2.13.1 + Compiling io-extras v0.19.0 + Compiling cap-primitives v4.0.2 + Compiling serde_core v1.0.229 + Compiling once_cell v1.21.4 + Compiling maybe-owned v0.3.4 + Compiling ipnet v2.12.0 + Compiling ambient-authority v0.0.2 + Compiling cap-std v4.0.2 + Compiling zmij v1.0.23 + Compiling serde_json v1.0.151 + Compiling cap-fs-ext v4.0.2 + Compiling serde v1.0.229 + Compiling arrayvec v0.7.8 + Compiling arrayref v0.3.9 + Compiling itoa v1.0.18 + Compiling constant_time_eq v0.4.2 + Compiling cfg-if v1.0.4 + Compiling memchr v2.8.3 + Compiling arbitrary v1.4.2 + Compiling jobserver v0.1.35 + Compiling cc v1.3.0 + Compiling blake3 v1.8.5 + Compiling libfuzzer-sys v0.4.13 + Compiling fs-set-times v0.20.3 + Compiling rustix-linux-procfs v0.1.1 + Compiling xtask v0.0.0 (/xtask) + Compiling keep v0.0.0 () + Compiling keep-fuzz v0.0.0 (/fuzz) + Finished `release` profile [optimized + debuginfo] target(s) in 11.25s + Finished `release` profile [optimized + debuginfo] target(s) in 0.01s + Running `/aarch64-unknown-linux-gnu/release/migration_format -artifact_prefix=/fuzz/artifacts/migration_format/ -runs=1 -seed=112 -max_len=4096 -timeout=5 -rss_limit_mb=1024 /fuzz/corpus/migration_format/recovery-complete` +INFO: Running with entropic power schedule (0xFF, 100). +INFO: Seed: 112 +INFO: Loaded 1 modules (68676 inline 8-bit counters): 68676 [0xaaaabedb8aa0, 0xaaaabedc96e4), +INFO: Loaded 1 PC tables (68676 PCs): 68676 [0xaaaabedc96e8,0xaaaabeed5b28), +/aarch64-unknown-linux-gnu/release/migration_format: Running 1 inputs 1 time(s) each. +Running: /fuzz/corpus/migration_format/recovery-complete + +thread '' (2071732) panicked at fuzz_targets/migration_format/recovery.rs:94:5: +complete migration needs the entire namespace +note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace +==2071732== ERROR: libFuzzer: deadly signal + #0 0xaaaabe7e55f8 (/aarch64-unknown-linux-gnu/release/migration_format+0x2b55f8) (BuildId: 5c6567de9219462b5f2bc448fbf472a836eccee8) + #1 0xaaaabe830580 (/aarch64-unknown-linux-gnu/release/migration_format+0x300580) (BuildId: 5c6567de9219462b5f2bc448fbf472a836eccee8) + #2 0xaaaabe81f2ec (/aarch64-unknown-linux-gnu/release/migration_format+0x2ef2ec) (BuildId: 5c6567de9219462b5f2bc448fbf472a836eccee8) + #3 0xffffaa49a8bc (linux-vdso.so.1+0x8bc) (BuildId: 91bd37d5562c97f21721e188c8617e7fb312061b) + #4 0xffffa9fc7d7c (/lib/aarch64-linux-gnu/libc.so.6+0x87d7c) (BuildId: 4c1eca4527d1163b2dde55860b69f270158febb4) + #5 0xffffa9f7693c (/lib/aarch64-linux-gnu/libc.so.6+0x3693c) (BuildId: 4c1eca4527d1163b2dde55860b69f270158febb4) + #6 0xffffa9f61a80 (/lib/aarch64-linux-gnu/libc.so.6+0x21a80) (BuildId: 4c1eca4527d1163b2dde55860b69f270158febb4) + #7 0xaaaabe749fe0 (/aarch64-unknown-linux-gnu/release/migration_format+0x219fe0) (BuildId: 5c6567de9219462b5f2bc448fbf472a836eccee8) + #8 0xaaaabe749e30 (/aarch64-unknown-linux-gnu/release/migration_format+0x219e30) (BuildId: 5c6567de9219462b5f2bc448fbf472a836eccee8) + #9 0xaaaabe72f93c (/aarch64-unknown-linux-gnu/release/migration_format+0x1ff93c) (BuildId: 5c6567de9219462b5f2bc448fbf472a836eccee8) + #10 0xaaaabec3df54 (/aarch64-unknown-linux-gnu/release/migration_format+0x70df54) (BuildId: 5c6567de9219462b5f2bc448fbf472a836eccee8) + #11 0xaaaabec2ef20 (/aarch64-unknown-linux-gnu/release/migration_format+0x6fef20) (BuildId: 5c6567de9219462b5f2bc448fbf472a836eccee8) + #12 0xaaaabec2986c (/aarch64-unknown-linux-gnu/release/migration_format+0x6f986c) (BuildId: 5c6567de9219462b5f2bc448fbf472a836eccee8) + #13 0xaaaabec2f4b8 (/aarch64-unknown-linux-gnu/release/migration_format+0x6ff4b8) (BuildId: 5c6567de9219462b5f2bc448fbf472a836eccee8) + #14 0xaaaabe74a688 (/aarch64-unknown-linux-gnu/release/migration_format+0x21a688) (BuildId: 5c6567de9219462b5f2bc448fbf472a836eccee8) + #15 0xaaaabe80c4dc (/aarch64-unknown-linux-gnu/release/migration_format+0x2dc4dc) (BuildId: 5c6567de9219462b5f2bc448fbf472a836eccee8) + #16 0xaaaabe80fb44 (/aarch64-unknown-linux-gnu/release/migration_format+0x2dfb44) (BuildId: 5c6567de9219462b5f2bc448fbf472a836eccee8) + #17 0xaaaabe817cb8 (/aarch64-unknown-linux-gnu/release/migration_format+0x2e7cb8) (BuildId: 5c6567de9219462b5f2bc448fbf472a836eccee8) + #18 0xaaaabe818c08 (/aarch64-unknown-linux-gnu/release/migration_format+0x2e8c08) (BuildId: 5c6567de9219462b5f2bc448fbf472a836eccee8) + #19 0xaaaabe819954 (/aarch64-unknown-linux-gnu/release/migration_format+0x2e9954) (BuildId: 5c6567de9219462b5f2bc448fbf472a836eccee8) + #20 0xaaaabe81846c (/aarch64-unknown-linux-gnu/release/migration_format+0x2e846c) (BuildId: 5c6567de9219462b5f2bc448fbf472a836eccee8) + #21 0xaaaabe81f84c (/aarch64-unknown-linux-gnu/release/migration_format+0x2ef84c) (BuildId: 5c6567de9219462b5f2bc448fbf472a836eccee8) + #22 0xaaaabe8395b0 (/aarch64-unknown-linux-gnu/release/migration_format+0x3095b0) (BuildId: 5c6567de9219462b5f2bc448fbf472a836eccee8) + #23 0xaaaabe84246c (/aarch64-unknown-linux-gnu/release/migration_format+0x31246c) (BuildId: 5c6567de9219462b5f2bc448fbf472a836eccee8) + #24 0xaaaabe74a940 (/aarch64-unknown-linux-gnu/release/migration_format+0x21a940) (BuildId: 5c6567de9219462b5f2bc448fbf472a836eccee8) + #25 0xffffa9f62258 (/lib/aarch64-linux-gnu/libc.so.6+0x22258) (BuildId: 4c1eca4527d1163b2dde55860b69f270158febb4) + #26 0xffffa9f62338 (/lib/aarch64-linux-gnu/libc.so.6+0x22338) (BuildId: 4c1eca4527d1163b2dde55860b69f270158febb4) + #27 0xaaaabe74aaac (/aarch64-unknown-linux-gnu/release/migration_format+0x21aaac) (BuildId: 5c6567de9219462b5f2bc448fbf472a836eccee8) + +NOTE: libFuzzer has rudimentary signal handlers. + Combine libFuzzer with AddressSanitizer or similar for better crash reports. +SUMMARY: libFuzzer: deadly signal +MS: 0 ; base unit: 0000000000000000000000000000000000000000 +──────────────────────────────────────────────────────────────────────────────── + +Error: Fuzz target exited with exit status: 77 diff --git a/docs/testing-evidence/migration-compatibility-fuzz/namespace.patch b/docs/testing-evidence/migration-compatibility-fuzz/namespace.patch new file mode 100644 index 00000000..4e175303 --- /dev/null +++ b/docs/testing-evidence/migration-compatibility-fuzz/namespace.patch @@ -0,0 +1,9 @@ +--- a/fuzz/fuzz_targets/migration_format/recovery.rs ++++ b/fuzz/fuzz_targets/migration_format/recovery.rs +@@ -17 +17 @@ pub(super) fn exercise(input: &[u8]) -> Option<()> { +- let residue = StoreMigrationResidue { ++ let mut residue = StoreMigrationResidue { +@@ -27,0 +28,3 @@ pub(super) fn exercise(input: &[u8]) -> Option<()> { ++ if matches!(result, Ok(StoreMigrationRecoveryPlan::Complete)) { ++ residue.reader_fence = false; ++ } diff --git a/docs/testing-evidence/migration-compatibility-fuzz/pre-intent-red.txt b/docs/testing-evidence/migration-compatibility-fuzz/pre-intent-red.txt new file mode 100644 index 00000000..4e17e87d --- /dev/null +++ b/docs/testing-evidence/migration-compatibility-fuzz/pre-intent-red.txt @@ -0,0 +1,89 @@ ++ cd ++ CARGO_TARGET_DIR= ++ cargo +nightly-2026-07-24 fuzz run migration_format /fuzz/corpus/migration_format/recovery-effect-before-intent -- -runs=1 -seed=112 -max_len=4096 -timeout=5 -rss_limit_mb=1024 + Compiling libc v0.2.186 + Compiling shlex v2.0.1 + Compiling rustix v1.1.4 + Compiling find-msvc-tools v0.1.9 + Compiling linux-raw-sys v0.12.1 + Compiling bitflags v2.13.1 + Compiling io-lifetimes v3.0.1 + Compiling io-lifetimes v2.0.4 + Compiling io-extras v0.19.0 + Compiling cap-primitives v4.0.2 + Compiling serde_core v1.0.229 + Compiling once_cell v1.21.4 + Compiling ambient-authority v0.0.2 + Compiling zmij v1.0.23 + Compiling maybe-owned v0.3.4 + Compiling ipnet v2.12.0 + Compiling cap-std v4.0.2 + Compiling cap-fs-ext v4.0.2 + Compiling serde_json v1.0.151 + Compiling serde v1.0.229 + Compiling arrayref v0.3.9 + Compiling constant_time_eq v0.4.2 + Compiling cfg-if v1.0.4 + Compiling itoa v1.0.18 + Compiling memchr v2.8.3 + Compiling arrayvec v0.7.8 + Compiling arbitrary v1.4.2 + Compiling jobserver v0.1.35 + Compiling cc v1.3.0 + Compiling blake3 v1.8.5 + Compiling libfuzzer-sys v0.4.13 + Compiling rustix-linux-procfs v0.1.1 + Compiling fs-set-times v0.20.3 + Compiling xtask v0.0.0 (/xtask) + Compiling keep v0.0.0 () + Compiling keep-fuzz v0.0.0 (/fuzz) + Finished `release` profile [optimized + debuginfo] target(s) in 11.67s + Finished `release` profile [optimized + debuginfo] target(s) in 0.01s + Running `/aarch64-unknown-linux-gnu/release/migration_format -artifact_prefix=/fuzz/artifacts/migration_format/ -runs=1 -seed=112 -max_len=4096 -timeout=5 -rss_limit_mb=1024 /fuzz/corpus/migration_format/recovery-effect-before-intent` +INFO: Running with entropic power schedule (0xFF, 100). +INFO: Seed: 112 +INFO: Loaded 1 modules (68677 inline 8-bit counters): 68677 [0xaaaab8f88aa0, 0xaaaab8f996e5), +INFO: Loaded 1 PC tables (68677 PCs): 68677 [0xaaaab8f996e8,0xaaaab90a5b38), +/aarch64-unknown-linux-gnu/release/migration_format: Running 1 inputs 1 time(s) each. +Running: /fuzz/corpus/migration_format/recovery-effect-before-intent + +thread '' (2071062) panicked at fuzz_targets/migration_format/recovery.rs:86:9: +an effect before durable intent must retain its precise refusal: Err(EffectBeforeIntent { effect: Marker }) +note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace +==2071062== ERROR: libFuzzer: deadly signal + #0 0xaaaab89a4638 (/aarch64-unknown-linux-gnu/release/migration_format+0x2b4638) (BuildId: 44e6c108659f1420991e6fbba414cca723f782bb) + #1 0xaaaab89f0600 (/aarch64-unknown-linux-gnu/release/migration_format+0x300600) (BuildId: 44e6c108659f1420991e6fbba414cca723f782bb) + #2 0xaaaab89df36c (/aarch64-unknown-linux-gnu/release/migration_format+0x2ef36c) (BuildId: 44e6c108659f1420991e6fbba414cca723f782bb) + #3 0xffff9e7188bc (linux-vdso.so.1+0x8bc) (BuildId: 91bd37d5562c97f21721e188c8617e7fb312061b) + #4 0xffff9e247d7c (/lib/aarch64-linux-gnu/libc.so.6+0x87d7c) (BuildId: 4c1eca4527d1163b2dde55860b69f270158febb4) + #5 0xffff9e1f693c (/lib/aarch64-linux-gnu/libc.so.6+0x3693c) (BuildId: 4c1eca4527d1163b2dde55860b69f270158febb4) + #6 0xffff9e1e1a80 (/lib/aarch64-linux-gnu/libc.so.6+0x21a80) (BuildId: 4c1eca4527d1163b2dde55860b69f270158febb4) + #7 0xaaaab890a020 (/aarch64-unknown-linux-gnu/release/migration_format+0x21a020) (BuildId: 44e6c108659f1420991e6fbba414cca723f782bb) + #8 0xaaaab8909e70 (/aarch64-unknown-linux-gnu/release/migration_format+0x219e70) (BuildId: 44e6c108659f1420991e6fbba414cca723f782bb) + #9 0xaaaab88ef97c (/aarch64-unknown-linux-gnu/release/migration_format+0x1ff97c) (BuildId: 44e6c108659f1420991e6fbba414cca723f782bb) + #10 0xaaaab8e01fc0 (/aarch64-unknown-linux-gnu/release/migration_format+0x711fc0) (BuildId: 44e6c108659f1420991e6fbba414cca723f782bb) + #11 0xaaaab8df2f60 (/aarch64-unknown-linux-gnu/release/migration_format+0x702f60) (BuildId: 44e6c108659f1420991e6fbba414cca723f782bb) + #12 0xaaaab8ded8d8 (/aarch64-unknown-linux-gnu/release/migration_format+0x6fd8d8) (BuildId: 44e6c108659f1420991e6fbba414cca723f782bb) + #13 0xaaaab8df3524 (/aarch64-unknown-linux-gnu/release/migration_format+0x703524) (BuildId: 44e6c108659f1420991e6fbba414cca723f782bb) + #14 0xaaaab890a6c8 (/aarch64-unknown-linux-gnu/release/migration_format+0x21a6c8) (BuildId: 44e6c108659f1420991e6fbba414cca723f782bb) + #15 0xaaaab89cecb0 (/aarch64-unknown-linux-gnu/release/migration_format+0x2decb0) (BuildId: 44e6c108659f1420991e6fbba414cca723f782bb) + #16 0xaaaab89d0b64 (/aarch64-unknown-linux-gnu/release/migration_format+0x2e0b64) (BuildId: 44e6c108659f1420991e6fbba414cca723f782bb) + #17 0xaaaab89d7d38 (/aarch64-unknown-linux-gnu/release/migration_format+0x2e7d38) (BuildId: 44e6c108659f1420991e6fbba414cca723f782bb) + #18 0xaaaab89d8c88 (/aarch64-unknown-linux-gnu/release/migration_format+0x2e8c88) (BuildId: 44e6c108659f1420991e6fbba414cca723f782bb) + #19 0xaaaab89d99d4 (/aarch64-unknown-linux-gnu/release/migration_format+0x2e99d4) (BuildId: 44e6c108659f1420991e6fbba414cca723f782bb) + #20 0xaaaab89d84ec (/aarch64-unknown-linux-gnu/release/migration_format+0x2e84ec) (BuildId: 44e6c108659f1420991e6fbba414cca723f782bb) + #21 0xaaaab89df8cc (/aarch64-unknown-linux-gnu/release/migration_format+0x2ef8cc) (BuildId: 44e6c108659f1420991e6fbba414cca723f782bb) + #22 0xaaaab89f9630 (/aarch64-unknown-linux-gnu/release/migration_format+0x309630) (BuildId: 44e6c108659f1420991e6fbba414cca723f782bb) + #23 0xaaaab8a024ec (/aarch64-unknown-linux-gnu/release/migration_format+0x3124ec) (BuildId: 44e6c108659f1420991e6fbba414cca723f782bb) + #24 0xaaaab890a980 (/aarch64-unknown-linux-gnu/release/migration_format+0x21a980) (BuildId: 44e6c108659f1420991e6fbba414cca723f782bb) + #25 0xffff9e1e2258 (/lib/aarch64-linux-gnu/libc.so.6+0x22258) (BuildId: 4c1eca4527d1163b2dde55860b69f270158febb4) + #26 0xffff9e1e2338 (/lib/aarch64-linux-gnu/libc.so.6+0x22338) (BuildId: 4c1eca4527d1163b2dde55860b69f270158febb4) + #27 0xaaaab890aaec (/aarch64-unknown-linux-gnu/release/migration_format+0x21aaec) (BuildId: 44e6c108659f1420991e6fbba414cca723f782bb) + +NOTE: libFuzzer has rudimentary signal handlers. + Combine libFuzzer with AddressSanitizer or similar for better crash reports. +SUMMARY: libFuzzer: deadly signal +MS: 0 ; base unit: 0000000000000000000000000000000000000000 +──────────────────────────────────────────────────────────────────────────────── + +Error: Fuzz target exited with exit status: 77 diff --git a/docs/testing-evidence/migration-compatibility-fuzz/pre-intent.patch b/docs/testing-evidence/migration-compatibility-fuzz/pre-intent.patch new file mode 100644 index 00000000..e1134002 --- /dev/null +++ b/docs/testing-evidence/migration-compatibility-fuzz/pre-intent.patch @@ -0,0 +1,8 @@ +--- a/fuzz/fuzz_targets/migration_format/recovery.rs ++++ b/fuzz/fuzz_targets/migration_format/recovery.rs +@@ -27 +27,4 @@ pub(super) fn exercise(input: &[u8]) -> Option<()> { +- let result = plan_store_migration_recovery(&expected, &residue); ++ let mut result = plan_store_migration_recovery(&expected, &residue); ++ if matches!(result, Err(StoreMigrationRecoveryAmbiguity::EffectBeforeIntent { effect: StoreMigrationEffect::Namespace })) { ++ result = Err(StoreMigrationRecoveryAmbiguity::EffectBeforeIntent { effect: StoreMigrationEffect::Marker }); ++ } diff --git a/docs/testing-evidence/migration-compatibility-fuzz/receipt-red.txt b/docs/testing-evidence/migration-compatibility-fuzz/receipt-red.txt new file mode 100644 index 00000000..fb035a9c --- /dev/null +++ b/docs/testing-evidence/migration-compatibility-fuzz/receipt-red.txt @@ -0,0 +1,73 @@ ++ cd ++ CARGO_TARGET_DIR= ++ cargo test --all-features --locked --test store_migration_compatibility + Compiling rustix v1.1.4 + Compiling linux-raw-sys v0.12.1 + Compiling io-lifetimes v3.0.1 + Compiling io-lifetimes v2.0.4 + Compiling bitflags v2.13.1 + Compiling io-extras v0.19.0 + Compiling proc-macro2 v1.0.107 + Compiling once_cell v1.21.4 + Compiling unicode-ident v1.0.24 + Compiling cap-primitives v4.0.2 + Compiling shlex v2.0.1 + Compiling find-msvc-tools v0.1.9 + Compiling quote v1.0.47 + Compiling ambient-authority v0.0.2 + Compiling ipnet v2.12.0 + Compiling maybe-owned v0.3.4 + Compiling cap-std v4.0.2 + Compiling libc v0.2.186 + Compiling clap_lex v1.1.0 + Compiling cap-fs-ext v4.0.2 + Compiling cc v1.3.0 + Compiling anstyle v1.0.14 + Compiling cfg-if v1.0.4 + Compiling arrayvec v0.7.8 + Compiling constant_time_eq v0.4.2 + Compiling arrayref v0.3.9 + Compiling condtype v1.3.0 + Compiling regex-lite v0.1.9 + Compiling allocation-counter v0.8.1 + Compiling clap_builder v4.6.2 + Compiling syn v2.0.119 + Compiling blake3 v1.8.5 + Compiling clap v4.6.4 + Compiling fs-set-times v0.20.3 + Compiling rustix-linux-procfs v0.1.1 + Compiling divan-macros v0.1.21 + Compiling keep v0.0.0 () + Compiling divan v0.1.21 + Finished `test` profile [unoptimized + debuginfo] target(s) in 3.29s + Running tests/store_migration_compatibility.rs (/debug/deps/store_migration_compatibility-fbb66f7c5fa9ff33) + +running 2 tests +test every_unknown_mandatory_flag_refuses_without_downgrade ... FAILED +test unsupported_migration_versions_refuse_with_exact_coordinates ... FAILED + +failures: + +---- every_unknown_mandatory_flag_refuses_without_downgrade stdout ---- + +thread 'every_unknown_mandatory_flag_refuses_without_downgrade' (2070424) panicked at tests/store_migration_compatibility.rs:78:9: +assertion `left == right` failed + left: Some(ChecksumMismatch { expected: [11, 194, 223, 112, 148, 57, 20, 183, 29, 150, 55, 192, 156, 193, 249, 49, 157, 174, 43, 212, 91, 255, 231, 10, 171, 82, 82, 128, 229, 134, 117, 129], observed: [58, 106, 95, 41, 191, 175, 239, 251, 148, 1, 222, 91, 168, 20, 192, 156, 52, 90, 219, 173, 105, 232, 186, 5, 49, 227, 235, 30, 187, 11, 104, 29] }) + right: Some(UnsupportedFlags { observed: 1 }) +note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace + +---- unsupported_migration_versions_refuse_with_exact_coordinates stdout ---- + +thread 'unsupported_migration_versions_refuse_with_exact_coordinates' (2070425) panicked at tests/store_migration_compatibility.rs:43:9: +assertion `left == right` failed + left: Some(ChecksumMismatch { expected: [88, 80, 113, 9, 191, 149, 9, 28, 166, 135, 141, 7, 174, 138, 110, 59, 186, 173, 168, 220, 144, 226, 13, 202, 34, 94, 228, 78, 243, 23, 253, 245], observed: [58, 106, 95, 41, 191, 175, 239, 251, 148, 1, 222, 91, 168, 20, 192, 156, 52, 90, 219, 173, 105, 232, 186, 5, 49, 227, 235, 30, 187, 11, 104, 29] }) + right: Some(UnsupportedVersion { expected: 2, observed: 0 }) + + +failures: + every_unknown_mandatory_flag_refuses_without_downgrade + unsupported_migration_versions_refuse_with_exact_coordinates + +test result: FAILED. 0 passed; 2 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s + +error: test failed, to rerun pass `--test store_migration_compatibility` diff --git a/docs/testing-evidence/migration-compatibility-fuzz/receipt.patch b/docs/testing-evidence/migration-compatibility-fuzz/receipt.patch new file mode 100644 index 00000000..5cd5a6a2 --- /dev/null +++ b/docs/testing-evidence/migration-compatibility-fuzz/receipt.patch @@ -0,0 +1,8 @@ +--- a/src/adapters/store_migration/migration_receipt_decoder.rs ++++ b/src/adapters/store_migration/migration_receipt_decoder.rs +@@ -51 +51 @@ fn validate_fixed_fields(encoded: &[u8]) -> Result<(), StoreMigrationReceiptDeco +- if version != format::VERSION { ++ if false && version != format::VERSION { +@@ -65 +65 @@ fn validate_fixed_fields(encoded: &[u8]) -> Result<(), StoreMigrationReceiptDeco +- if flags != 0 { ++ if false && flags != 0 { diff --git a/docs/testing-evidence/migration-compatibility-fuzz/records-red.txt b/docs/testing-evidence/migration-compatibility-fuzz/records-red.txt new file mode 100644 index 00000000..8971f464 --- /dev/null +++ b/docs/testing-evidence/migration-compatibility-fuzz/records-red.txt @@ -0,0 +1,89 @@ ++ cd ++ CARGO_TARGET_DIR= ++ cargo +nightly-2026-07-24 fuzz run migration_format /fuzz/corpus/migration_format/recovery-complete -- -runs=1 -seed=112 -max_len=4096 -timeout=5 -rss_limit_mb=1024 + Compiling libc v0.2.186 + Compiling find-msvc-tools v0.1.9 + Compiling shlex v2.0.1 + Compiling rustix v1.1.4 + Compiling linux-raw-sys v0.12.1 + Compiling bitflags v2.13.1 + Compiling io-lifetimes v3.0.1 + Compiling io-lifetimes v2.0.4 + Compiling io-extras v0.19.0 + Compiling serde_core v1.0.229 + Compiling once_cell v1.21.4 + Compiling cap-primitives v4.0.2 + Compiling ambient-authority v0.0.2 + Compiling cap-std v4.0.2 + Compiling zmij v1.0.23 + Compiling maybe-owned v0.3.4 + Compiling ipnet v2.12.0 + Compiling serde v1.0.229 + Compiling cap-fs-ext v4.0.2 + Compiling serde_json v1.0.151 + Compiling memchr v2.8.3 + Compiling arrayvec v0.7.8 + Compiling arrayref v0.3.9 + Compiling constant_time_eq v0.4.2 + Compiling cfg-if v1.0.4 + Compiling itoa v1.0.18 + Compiling arbitrary v1.4.2 + Compiling jobserver v0.1.35 + Compiling cc v1.3.0 + Compiling blake3 v1.8.5 + Compiling libfuzzer-sys v0.4.13 + Compiling fs-set-times v0.20.3 + Compiling rustix-linux-procfs v0.1.1 + Compiling xtask v0.0.0 (/xtask) + Compiling keep v0.0.0 () + Compiling keep-fuzz v0.0.0 (/fuzz) + Finished `release` profile [optimized + debuginfo] target(s) in 11.87s + Finished `release` profile [optimized + debuginfo] target(s) in 0.01s + Running `/aarch64-unknown-linux-gnu/release/migration_format -artifact_prefix=/fuzz/artifacts/migration_format/ -runs=1 -seed=112 -max_len=4096 -timeout=5 -rss_limit_mb=1024 /fuzz/corpus/migration_format/recovery-complete` +INFO: Running with entropic power schedule (0xFF, 100). +INFO: Seed: 112 +INFO: Loaded 1 modules (68684 inline 8-bit counters): 68684 [0xaaaae1668aa0, 0xaaaae16796ec), +INFO: Loaded 1 PC tables (68684 PCs): 68684 [0xaaaae16796f0,0xaaaae1785bb0), +/aarch64-unknown-linux-gnu/release/migration_format: Running 1 inputs 1 time(s) each. +Running: /fuzz/corpus/migration_format/recovery-complete + +thread '' (2073028) panicked at fuzz_targets/migration_format/recovery.rs:114:5: +complete migration needs jointly admitted records +note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace +==2073028== ERROR: libFuzzer: deadly signal + #0 0xaaaae10826b8 (/aarch64-unknown-linux-gnu/release/migration_format+0x2b26b8) (BuildId: 7b934e98ea0f5cc8bae5cbe5e2e46cbe6a61b977) + #1 0xaaaae10cd7a0 (/aarch64-unknown-linux-gnu/release/migration_format+0x2fd7a0) (BuildId: 7b934e98ea0f5cc8bae5cbe5e2e46cbe6a61b977) + #2 0xaaaae10bc50c (/aarch64-unknown-linux-gnu/release/migration_format+0x2ec50c) (BuildId: 7b934e98ea0f5cc8bae5cbe5e2e46cbe6a61b977) + #3 0xffff967e38bc (linux-vdso.so.1+0x8bc) (BuildId: 91bd37d5562c97f21721e188c8617e7fb312061b) + #4 0xffff96307d7c (/lib/aarch64-linux-gnu/libc.so.6+0x87d7c) (BuildId: 4c1eca4527d1163b2dde55860b69f270158febb4) + #5 0xffff962b693c (/lib/aarch64-linux-gnu/libc.so.6+0x3693c) (BuildId: 4c1eca4527d1163b2dde55860b69f270158febb4) + #6 0xffff962a1a80 (/lib/aarch64-linux-gnu/libc.so.6+0x21a80) (BuildId: 4c1eca4527d1163b2dde55860b69f270158febb4) + #7 0xaaaae0fe8098 (/aarch64-unknown-linux-gnu/release/migration_format+0x218098) (BuildId: 7b934e98ea0f5cc8bae5cbe5e2e46cbe6a61b977) + #8 0xaaaae0fe7ee8 (/aarch64-unknown-linux-gnu/release/migration_format+0x217ee8) (BuildId: 7b934e98ea0f5cc8bae5cbe5e2e46cbe6a61b977) + #9 0xaaaae0fcf9fc (/aarch64-unknown-linux-gnu/release/migration_format+0x1ff9fc) (BuildId: 7b934e98ea0f5cc8bae5cbe5e2e46cbe6a61b977) + #10 0xaaaae14e9194 (/aarch64-unknown-linux-gnu/release/migration_format+0x719194) (BuildId: 7b934e98ea0f5cc8bae5cbe5e2e46cbe6a61b977) + #11 0xaaaae14da160 (/aarch64-unknown-linux-gnu/release/migration_format+0x70a160) (BuildId: 7b934e98ea0f5cc8bae5cbe5e2e46cbe6a61b977) + #12 0xaaaae14d4aac (/aarch64-unknown-linux-gnu/release/migration_format+0x704aac) (BuildId: 7b934e98ea0f5cc8bae5cbe5e2e46cbe6a61b977) + #13 0xaaaae14da6f8 (/aarch64-unknown-linux-gnu/release/migration_format+0x70a6f8) (BuildId: 7b934e98ea0f5cc8bae5cbe5e2e46cbe6a61b977) + #14 0xaaaae0fe8740 (/aarch64-unknown-linux-gnu/release/migration_format+0x218740) (BuildId: 7b934e98ea0f5cc8bae5cbe5e2e46cbe6a61b977) + #15 0xaaaae10aa864 (/aarch64-unknown-linux-gnu/release/migration_format+0x2da864) (BuildId: 7b934e98ea0f5cc8bae5cbe5e2e46cbe6a61b977) + #16 0xaaaae10add24 (/aarch64-unknown-linux-gnu/release/migration_format+0x2ddd24) (BuildId: 7b934e98ea0f5cc8bae5cbe5e2e46cbe6a61b977) + #17 0xaaaae10b4ee0 (/aarch64-unknown-linux-gnu/release/migration_format+0x2e4ee0) (BuildId: 7b934e98ea0f5cc8bae5cbe5e2e46cbe6a61b977) + #18 0xaaaae10b5e30 (/aarch64-unknown-linux-gnu/release/migration_format+0x2e5e30) (BuildId: 7b934e98ea0f5cc8bae5cbe5e2e46cbe6a61b977) + #19 0xaaaae10b6b7c (/aarch64-unknown-linux-gnu/release/migration_format+0x2e6b7c) (BuildId: 7b934e98ea0f5cc8bae5cbe5e2e46cbe6a61b977) + #20 0xaaaae10b5694 (/aarch64-unknown-linux-gnu/release/migration_format+0x2e5694) (BuildId: 7b934e98ea0f5cc8bae5cbe5e2e46cbe6a61b977) + #21 0xaaaae10bca6c (/aarch64-unknown-linux-gnu/release/migration_format+0x2eca6c) (BuildId: 7b934e98ea0f5cc8bae5cbe5e2e46cbe6a61b977) + #22 0xaaaae10d67d0 (/aarch64-unknown-linux-gnu/release/migration_format+0x3067d0) (BuildId: 7b934e98ea0f5cc8bae5cbe5e2e46cbe6a61b977) + #23 0xaaaae10df68c (/aarch64-unknown-linux-gnu/release/migration_format+0x30f68c) (BuildId: 7b934e98ea0f5cc8bae5cbe5e2e46cbe6a61b977) + #24 0xaaaae0fe8a00 (/aarch64-unknown-linux-gnu/release/migration_format+0x218a00) (BuildId: 7b934e98ea0f5cc8bae5cbe5e2e46cbe6a61b977) + #25 0xffff962a2258 (/lib/aarch64-linux-gnu/libc.so.6+0x22258) (BuildId: 4c1eca4527d1163b2dde55860b69f270158febb4) + #26 0xffff962a2338 (/lib/aarch64-linux-gnu/libc.so.6+0x22338) (BuildId: 4c1eca4527d1163b2dde55860b69f270158febb4) + #27 0xaaaae0fe8b6c (/aarch64-unknown-linux-gnu/release/migration_format+0x218b6c) (BuildId: 7b934e98ea0f5cc8bae5cbe5e2e46cbe6a61b977) + +NOTE: libFuzzer has rudimentary signal handlers. + Combine libFuzzer with AddressSanitizer or similar for better crash reports. +SUMMARY: libFuzzer: deadly signal +MS: 0 ; base unit: 0000000000000000000000000000000000000000 +──────────────────────────────────────────────────────────────────────────────── + +Error: Fuzz target exited with exit status: 77 diff --git a/docs/testing-evidence/migration-compatibility-fuzz/records.patch b/docs/testing-evidence/migration-compatibility-fuzz/records.patch new file mode 100644 index 00000000..034396ef --- /dev/null +++ b/docs/testing-evidence/migration-compatibility-fuzz/records.patch @@ -0,0 +1,9 @@ +--- a/fuzz/fuzz_targets/migration_format/recovery.rs ++++ b/fuzz/fuzz_targets/migration_format/recovery.rs +@@ -17 +17 @@ pub(super) fn exercise(input: &[u8]) -> Option<()> { +- let residue = StoreMigrationResidue { ++ let mut residue = StoreMigrationResidue { +@@ -27,0 +28,3 @@ pub(super) fn exercise(input: &[u8]) -> Option<()> { ++ if matches!(result, Ok(StoreMigrationRecoveryPlan::Complete)) { ++ residue.receipt = Some(vec![0]); ++ } diff --git a/docs/testing-evidence/migration-compatibility-fuzz/restored-fuzz-green.txt b/docs/testing-evidence/migration-compatibility-fuzz/restored-fuzz-green.txt new file mode 100644 index 00000000..039b698c --- /dev/null +++ b/docs/testing-evidence/migration-compatibility-fuzz/restored-fuzz-green.txt @@ -0,0 +1,271 @@ +437bc1971fc8a98f2d039e446b3f5008979974f9 + Compiling libc v0.2.186 + Compiling rustix v1.1.4 + Compiling find-msvc-tools v0.1.9 + Compiling shlex v2.0.1 + Compiling linux-raw-sys v0.12.1 + Compiling bitflags v2.13.1 + Compiling io-lifetimes v3.0.1 + Compiling io-lifetimes v2.0.4 + Compiling io-extras v0.19.0 + Compiling once_cell v1.21.4 + Compiling cap-primitives v4.0.2 + Compiling serde_core v1.0.229 + Compiling cap-std v4.0.2 + Compiling zmij v1.0.23 + Compiling ambient-authority v0.0.2 + Compiling maybe-owned v0.3.4 + Compiling ipnet v2.12.0 + Compiling cap-fs-ext v4.0.2 + Compiling serde_json v1.0.151 + Compiling serde v1.0.229 + Compiling constant_time_eq v0.4.2 + Compiling cfg-if v1.0.4 + Compiling itoa v1.0.18 + Compiling arrayvec v0.7.8 + Compiling arrayref v0.3.9 + Compiling memchr v2.8.3 + Compiling arbitrary v1.4.2 + Compiling jobserver v0.1.35 + Compiling cc v1.3.0 + Compiling blake3 v1.8.5 + Compiling libfuzzer-sys v0.4.13 + Compiling rustix-linux-procfs v0.1.1 + Compiling fs-set-times v0.20.3 + Compiling xtask v0.0.0 (/xtask) + Compiling keep v0.0.0 () + Compiling keep-fuzz v0.0.0 (/fuzz) + Finished `release` profile [optimized + debuginfo] target(s) in 11.48s + Finished `release` profile [optimized + debuginfo] target(s) in 0.01s + Running `/aarch64-unknown-linux-gnu/release/migration_format -artifact_prefix=/fuzz/artifacts/migration_format/ -runs=20000 -seed=112 -max_len=4096 -timeout=5 -rss_limit_mb=1024 fuzz/corpus/migration_format` +INFO: Running with entropic power schedule (0xFF, 100). +INFO: Seed: 112 +INFO: Loaded 1 modules (68674 inline 8-bit counters): 68674 [0xaaaaeaab8aa0, 0xaaaaeaac96e2), +INFO: Loaded 1 PC tables (68674 PCs): 68674 [0xaaaaeaac96e8,0xaaaaeabd5b08), +INFO: 20 files found in fuzz/corpus/migration_format +INFO: seed corpus: files: 20 min: 97b max: 1487b total: 22744b rss: 36Mb +#21 INITED cov: 423 ft: 938 corp: 20/22Kb exec/s: 0 rss: 40Mb +#22 NEW cov: 424 ft: 1000 corp: 21/23Kb lim: 1487 exec/s: 0 rss: 40Mb L: 1487/1487 MS: 1 ChangeBit- +#23 NEW cov: 426 ft: 1002 corp: 22/23Kb lim: 1487 exec/s: 0 rss: 40Mb L: 258/1487 MS: 1 InsertByte- +#25 NEW cov: 431 ft: 1008 corp: 23/25Kb lim: 1487 exec/s: 0 rss: 40Mb L: 1487/1487 MS: 2 ChangeBit-CrossOver- +#31 NEW cov: 436 ft: 1015 corp: 24/26Kb lim: 1487 exec/s: 0 rss: 41Mb L: 813/1487 MS: 1 EraseBytes- +#42 NEW cov: 439 ft: 1018 corp: 25/26Kb lim: 1487 exec/s: 0 rss: 41Mb L: 609/1487 MS: 1 ChangeBit- +#43 NEW cov: 441 ft: 1020 corp: 26/26Kb lim: 1487 exec/s: 0 rss: 41Mb L: 137/1487 MS: 1 InsertRepeatedBytes- +#46 NEW cov: 443 ft: 1022 corp: 27/27Kb lim: 1487 exec/s: 0 rss: 41Mb L: 646/1487 MS: 3 ChangeByte-ChangeBinInt-CrossOver- +#52 NEW cov: 444 ft: 1023 corp: 28/28Kb lim: 1487 exec/s: 0 rss: 42Mb L: 1441/1487 MS: 1 CrossOver- +#53 NEW cov: 447 ft: 1026 corp: 29/30Kb lim: 1487 exec/s: 0 rss: 42Mb L: 1487/1487 MS: 1 CopyPart- +#54 REDUCE cov: 447 ft: 1026 corp: 29/30Kb lim: 1487 exec/s: 0 rss: 42Mb L: 151/1487 MS: 1 EraseBytes- +#55 NEW cov: 448 ft: 1027 corp: 30/31Kb lim: 1487 exec/s: 0 rss: 42Mb L: 1452/1487 MS: 1 CrossOver- +#70 NEW cov: 450 ft: 1029 corp: 31/32Kb lim: 1487 exec/s: 0 rss: 42Mb L: 935/1487 MS: 5 ChangeASCIIInt-CopyPart-CrossOver-ShuffleBytes-EraseBytes- +#95 REDUCE cov: 450 ft: 1029 corp: 31/32Kb lim: 1487 exec/s: 0 rss: 42Mb L: 105/1487 MS: 5 CMP-InsertRepeatedBytes-ShuffleBytes-CopyPart-EraseBytes- DE: "\001\000\000\000\000\000\000`"- +#103 NEW cov: 456 ft: 1036 corp: 32/34Kb lim: 1487 exec/s: 0 rss: 42Mb L: 1487/1487 MS: 3 ChangeBit-ChangeBit-CopyPart- +#104 NEW cov: 457 ft: 1045 corp: 33/34Kb lim: 1487 exec/s: 0 rss: 42Mb L: 734/1487 MS: 1 InsertRepeatedBytes- +#105 REDUCE cov: 457 ft: 1045 corp: 33/34Kb lim: 1487 exec/s: 0 rss: 42Mb L: 1064/1487 MS: 1 EraseBytes- +#126 NEW cov: 458 ft: 1046 corp: 34/35Kb lim: 1487 exec/s: 0 rss: 42Mb L: 1487/1487 MS: 1 CMP- DE: "\001\000\000\000"- +#147 NEW cov: 460 ft: 1048 corp: 35/36Kb lim: 1487 exec/s: 0 rss: 42Mb L: 613/1487 MS: 1 PersAutoDict- DE: "\001\000\000\000"- +#154 REDUCE cov: 460 ft: 1048 corp: 35/36Kb lim: 1487 exec/s: 0 rss: 42Mb L: 71/1487 MS: 2 ChangeByte-EraseBytes- +#160 NEW cov: 461 ft: 1049 corp: 36/37Kb lim: 1487 exec/s: 0 rss: 43Mb L: 1487/1487 MS: 1 ShuffleBytes- +#164 NEW cov: 463 ft: 1051 corp: 37/38Kb lim: 1487 exec/s: 0 rss: 43Mb L: 1011/1487 MS: 4 ChangeBit-ChangeBinInt-CopyPart-EraseBytes- +#168 NEW cov: 464 ft: 1053 corp: 38/38Kb lim: 1487 exec/s: 0 rss: 43Mb L: 166/1487 MS: 4 PersAutoDict-ChangeBinInt-ChangeBit-CrossOver- DE: "\001\000\000\000"- +#184 NEW cov: 466 ft: 1055 corp: 39/40Kb lim: 1487 exec/s: 0 rss: 43Mb L: 1061/1487 MS: 1 CrossOver- +#201 NEW cov: 468 ft: 1057 corp: 40/41Kb lim: 1487 exec/s: 0 rss: 43Mb L: 1074/1487 MS: 2 ChangeByte-CrossOver- +#287 NEW cov: 468 ft: 1108 corp: 41/42Kb lim: 1487 exec/s: 0 rss: 43Mb L: 1015/1487 MS: 1 CopyPart- +#293 NEW cov: 468 ft: 1109 corp: 42/42Kb lim: 1487 exec/s: 0 rss: 43Mb L: 610/1487 MS: 1 InsertByte- +#324 NEW cov: 470 ft: 1111 corp: 43/43Kb lim: 1487 exec/s: 0 rss: 44Mb L: 809/1487 MS: 1 CrossOver- +#358 REDUCE cov: 470 ft: 1111 corp: 43/43Kb lim: 1487 exec/s: 0 rss: 44Mb L: 1062/1487 MS: 4 InsertRepeatedBytes-ChangeASCIIInt-ChangeBinInt-EraseBytes- +#414 NEW cov: 471 ft: 1112 corp: 44/43Kb lim: 1487 exec/s: 0 rss: 44Mb L: 327/1487 MS: 1 CrossOver- +#415 NEW cov: 472 ft: 1113 corp: 45/45Kb lim: 1487 exec/s: 0 rss: 44Mb L: 1487/1487 MS: 1 ChangeBit- +#427 REDUCE cov: 472 ft: 1113 corp: 45/45Kb lim: 1487 exec/s: 0 rss: 44Mb L: 124/1487 MS: 2 CMP-EraseBytes- DE: "\377\377\000`\000\356\3529"- +#439 NEW cov: 474 ft: 1115 corp: 46/46Kb lim: 1487 exec/s: 0 rss: 44Mb L: 1074/1487 MS: 2 ChangeASCIIInt-EraseBytes- +#497 NEW cov: 475 ft: 1116 corp: 47/46Kb lim: 1487 exec/s: 0 rss: 44Mb L: 357/1487 MS: 3 ChangeBit-CrossOver-CMP- DE: "\000\000\000\000"- +#515 NEW cov: 476 ft: 1117 corp: 48/47Kb lim: 1487 exec/s: 0 rss: 44Mb L: 1126/1487 MS: 3 ChangeByte-InsertByte-InsertRepeatedBytes- +#528 NEW cov: 477 ft: 1118 corp: 49/49Kb lim: 1487 exec/s: 0 rss: 45Mb L: 1463/1487 MS: 3 ChangeByte-ShuffleBytes-CrossOver- +#557 NEW cov: 478 ft: 1119 corp: 50/50Kb lim: 1487 exec/s: 0 rss: 45Mb L: 1173/1487 MS: 4 ShuffleBytes-ShuffleBytes-PersAutoDict-InsertRepeatedBytes- DE: "\001\000\000\000"- +#570 REDUCE cov: 478 ft: 1119 corp: 50/50Kb lim: 1487 exec/s: 0 rss: 45Mb L: 147/1487 MS: 3 CopyPart-ChangeBit-EraseBytes- +#572 NEW cov: 479 ft: 1120 corp: 51/50Kb lim: 1487 exec/s: 0 rss: 45Mb L: 740/1487 MS: 2 InsertRepeatedBytes-CMP- DE: "\336].|\266\276\007\006(p4\352O\243\214q\354\367\313\313e\264\343\300d\034\205\266\027\230WJ"- +#619 NEW cov: 480 ft: 1121 corp: 52/51Kb lim: 1487 exec/s: 0 rss: 45Mb L: 442/1487 MS: 2 ShuffleBytes-CrossOver- +#645 REDUCE cov: 480 ft: 1121 corp: 52/51Kb lim: 1487 exec/s: 0 rss: 45Mb L: 598/1487 MS: 1 EraseBytes- +#674 NEW cov: 482 ft: 1123 corp: 53/52Kb lim: 1487 exec/s: 0 rss: 45Mb L: 1487/1487 MS: 4 ChangeASCIIInt-ChangeBinInt-ChangeBit-PersAutoDict- DE: "\001\000\000\000"- +#691 REDUCE cov: 482 ft: 1123 corp: 53/52Kb lim: 1487 exec/s: 0 rss: 45Mb L: 483/1487 MS: 2 CrossOver-EraseBytes- +#761 REDUCE cov: 482 ft: 1123 corp: 53/52Kb lim: 1487 exec/s: 0 rss: 45Mb L: 57/1487 MS: 5 InsertRepeatedBytes-EraseBytes-ChangeBinInt-ChangeBit-EraseBytes- +#776 REDUCE cov: 482 ft: 1123 corp: 53/52Kb lim: 1487 exec/s: 0 rss: 45Mb L: 1028/1487 MS: 5 ChangeBit-ChangeASCIIInt-ShuffleBytes-ChangeBit-EraseBytes- +#827 REDUCE cov: 482 ft: 1123 corp: 53/52Kb lim: 1487 exec/s: 0 rss: 45Mb L: 1390/1487 MS: 1 EraseBytes- +#854 NEW cov: 484 ft: 1125 corp: 54/52Kb lim: 1487 exec/s: 0 rss: 45Mb L: 717/1487 MS: 2 ChangeASCIIInt-EraseBytes- +#910 REDUCE cov: 484 ft: 1125 corp: 54/52Kb lim: 1487 exec/s: 0 rss: 46Mb L: 710/1487 MS: 1 EraseBytes- +#953 NEW cov: 489 ft: 1130 corp: 55/54Kb lim: 1487 exec/s: 0 rss: 46Mb L: 1487/1487 MS: 3 PersAutoDict-ChangeByte-ChangeByte- DE: "\000\000\000\000"- +#1060 NEW cov: 490 ft: 1131 corp: 56/55Kb lim: 1487 exec/s: 0 rss: 46Mb L: 1487/1487 MS: 2 ChangeASCIIInt-ChangeBinInt- +#1078 REDUCE cov: 490 ft: 1131 corp: 56/55Kb lim: 1487 exec/s: 0 rss: 46Mb L: 33/1487 MS: 3 PersAutoDict-ShuffleBytes-EraseBytes- DE: "\000\000\000\000"- +#1093 REDUCE cov: 490 ft: 1131 corp: 56/55Kb lim: 1487 exec/s: 0 rss: 46Mb L: 102/1487 MS: 5 InsertRepeatedBytes-CopyPart-PersAutoDict-InsertByte-EraseBytes- DE: "\001\000\000\000\000\000\000`"- +#1100 REDUCE cov: 490 ft: 1131 corp: 56/55Kb lim: 1487 exec/s: 0 rss: 46Mb L: 712/1487 MS: 2 InsertByte-EraseBytes- +#1166 REDUCE cov: 490 ft: 1131 corp: 56/54Kb lim: 1487 exec/s: 0 rss: 46Mb L: 367/1487 MS: 1 EraseBytes- +#1252 NEW cov: 496 ft: 1137 corp: 57/55Kb lim: 1487 exec/s: 0 rss: 47Mb L: 525/1487 MS: 1 EraseBytes- +#1299 REDUCE cov: 496 ft: 1137 corp: 57/55Kb lim: 1487 exec/s: 0 rss: 47Mb L: 410/1487 MS: 2 ChangeBit-EraseBytes- +#1352 REDUCE cov: 496 ft: 1137 corp: 57/55Kb lim: 1487 exec/s: 0 rss: 47Mb L: 32/1487 MS: 3 ChangeByte-ChangeBit-EraseBytes- +#1379 REDUCE cov: 496 ft: 1137 corp: 57/55Kb lim: 1487 exec/s: 0 rss: 47Mb L: 76/1487 MS: 2 ShuffleBytes-EraseBytes- +#1391 REDUCE cov: 496 ft: 1137 corp: 57/55Kb lim: 1487 exec/s: 0 rss: 47Mb L: 552/1487 MS: 2 ChangeASCIIInt-EraseBytes- +#1452 NEW cov: 497 ft: 1138 corp: 58/55Kb lim: 1487 exec/s: 0 rss: 47Mb L: 97/1487 MS: 1 ChangeBit- +#1513 REDUCE cov: 497 ft: 1138 corp: 58/55Kb lim: 1487 exec/s: 0 rss: 47Mb L: 711/1487 MS: 1 EraseBytes- +#1575 REDUCE cov: 497 ft: 1138 corp: 58/54Kb lim: 1487 exec/s: 0 rss: 48Mb L: 1119/1487 MS: 2 CopyPart-EraseBytes- +#1631 NEW cov: 498 ft: 1139 corp: 59/55Kb lim: 1487 exec/s: 0 rss: 48Mb L: 849/1487 MS: 1 EraseBytes- +#1642 REDUCE cov: 498 ft: 1139 corp: 59/55Kb lim: 1487 exec/s: 0 rss: 48Mb L: 1051/1487 MS: 1 EraseBytes- +#1646 REDUCE cov: 499 ft: 1140 corp: 60/55Kb lim: 1487 exec/s: 0 rss: 48Mb L: 12/1487 MS: 4 ChangeBit-CrossOver-ChangeByte-CMP- DE: "\377\377"- +#1699 REDUCE cov: 499 ft: 1140 corp: 60/55Kb lim: 1487 exec/s: 0 rss: 48Mb L: 459/1487 MS: 3 InsertRepeatedBytes-ChangeByte-EraseBytes- +#1700 REDUCE cov: 499 ft: 1140 corp: 60/55Kb lim: 1487 exec/s: 0 rss: 48Mb L: 458/1487 MS: 1 EraseBytes- +#1786 REDUCE cov: 499 ft: 1140 corp: 60/55Kb lim: 1487 exec/s: 0 rss: 48Mb L: 488/1487 MS: 1 EraseBytes- +#1918 REDUCE cov: 499 ft: 1140 corp: 60/55Kb lim: 1487 exec/s: 0 rss: 49Mb L: 143/1487 MS: 2 CMP-CrossOver- DE: "\001\000\000\000\000\000\001\002"- +#2002 REDUCE cov: 499 ft: 1140 corp: 60/54Kb lim: 1487 exec/s: 0 rss: 49Mb L: 548/1487 MS: 4 ChangeASCIIInt-InsertByte-ChangeASCIIInt-EraseBytes- +#2114 REDUCE cov: 499 ft: 1140 corp: 60/54Kb lim: 1487 exec/s: 0 rss: 49Mb L: 407/1487 MS: 2 ChangeByte-EraseBytes- +#2173 REDUCE cov: 499 ft: 1140 corp: 60/54Kb lim: 1487 exec/s: 0 rss: 49Mb L: 493/1487 MS: 4 CMP-InsertByte-ChangeBit-EraseBytes- DE: "\350+\277\2731)\356e6\350\365\255#\367\025^\027\233z$\312\365\217\231\261\255\247w\270_\305\373"- +#2178 REDUCE cov: 499 ft: 1140 corp: 60/54Kb lim: 1487 exec/s: 0 rss: 49Mb L: 107/1487 MS: 5 InsertRepeatedBytes-CopyPart-CrossOver-ChangeByte-EraseBytes- +#2181 REDUCE cov: 499 ft: 1140 corp: 60/54Kb lim: 1487 exec/s: 0 rss: 49Mb L: 354/1487 MS: 3 ChangeBit-ChangeBit-EraseBytes- +#2236 REDUCE cov: 499 ft: 1140 corp: 60/54Kb lim: 1487 exec/s: 0 rss: 49Mb L: 245/1487 MS: 5 ChangeASCIIInt-ShuffleBytes-CrossOver-CopyPart-EraseBytes- +#2328 REDUCE cov: 499 ft: 1140 corp: 60/54Kb lim: 1487 exec/s: 0 rss: 50Mb L: 355/1487 MS: 2 ChangeASCIIInt-EraseBytes- +#2352 REDUCE cov: 499 ft: 1140 corp: 60/54Kb lim: 1487 exec/s: 0 rss: 50Mb L: 514/1487 MS: 4 InsertRepeatedBytes-CMP-ChangeASCIIInt-EraseBytes- DE: "\001\000\000\000"- +#2414 REDUCE cov: 499 ft: 1140 corp: 60/53Kb lim: 1487 exec/s: 0 rss: 50Mb L: 1009/1487 MS: 2 PersAutoDict-EraseBytes- DE: "\350+\277\2731)\356e6\350\365\255#\367\025^\027\233z$\312\365\217\231\261\255\247w\270_\305\373"- +#2441 NEW cov: 500 ft: 1141 corp: 61/55Kb lim: 1487 exec/s: 0 rss: 50Mb L: 1487/1487 MS: 2 CMP-CopyPart- DE: "\037\001\000\000\000\000\000\000"- +#2567 NEW cov: 501 ft: 1142 corp: 62/55Kb lim: 1487 exec/s: 0 rss: 50Mb L: 6/1487 MS: 1 EraseBytes- +#2682 REDUCE cov: 501 ft: 1142 corp: 62/55Kb lim: 1487 exec/s: 0 rss: 50Mb L: 205/1487 MS: 5 CopyPart-ChangeByte-CrossOver-PersAutoDict-EraseBytes- DE: "\001\000\000\000"- +#2719 REDUCE cov: 501 ft: 1142 corp: 62/55Kb lim: 1487 exec/s: 0 rss: 50Mb L: 762/1487 MS: 2 ShuffleBytes-EraseBytes- +#2730 REDUCE cov: 501 ft: 1142 corp: 62/54Kb lim: 1487 exec/s: 0 rss: 51Mb L: 367/1487 MS: 1 EraseBytes- +#2833 NEW cov: 501 ft: 1143 corp: 63/56Kb lim: 1487 exec/s: 0 rss: 51Mb L: 1399/1487 MS: 3 ChangeASCIIInt-InsertByte-CopyPart- +#2834 REDUCE cov: 501 ft: 1143 corp: 63/55Kb lim: 1487 exec/s: 0 rss: 51Mb L: 614/1487 MS: 1 EraseBytes- +#2895 REDUCE cov: 501 ft: 1143 corp: 63/55Kb lim: 1487 exec/s: 0 rss: 51Mb L: 298/1487 MS: 1 EraseBytes- +#2932 REDUCE cov: 501 ft: 1143 corp: 63/55Kb lim: 1487 exec/s: 0 rss: 51Mb L: 831/1487 MS: 2 ChangeASCIIInt-EraseBytes- +#3063 REDUCE cov: 501 ft: 1143 corp: 63/54Kb lim: 1487 exec/s: 0 rss: 51Mb L: 66/1487 MS: 1 EraseBytes- +#3090 REDUCE cov: 501 ft: 1143 corp: 63/54Kb lim: 1487 exec/s: 0 rss: 51Mb L: 819/1487 MS: 2 CMP-EraseBytes- DE: "\001\000"- +#3096 REDUCE cov: 501 ft: 1143 corp: 63/54Kb lim: 1487 exec/s: 0 rss: 51Mb L: 719/1487 MS: 1 EraseBytes- +#3133 NEW cov: 502 ft: 1144 corp: 64/55Kb lim: 1487 exec/s: 0 rss: 51Mb L: 1487/1487 MS: 2 ChangeBinInt-ChangeByte- +#3135 REDUCE cov: 502 ft: 1144 corp: 64/55Kb lim: 1487 exec/s: 0 rss: 51Mb L: 494/1487 MS: 2 ChangeByte-EraseBytes- +#3304 REDUCE cov: 502 ft: 1144 corp: 64/55Kb lim: 1487 exec/s: 0 rss: 52Mb L: 471/1487 MS: 4 PersAutoDict-CopyPart-PersAutoDict-EraseBytes- DE: "\001\000\000\000\000\000\000`"-"\377\377"- +#3380 REDUCE cov: 502 ft: 1144 corp: 64/55Kb lim: 1487 exec/s: 0 rss: 52Mb L: 1265/1487 MS: 1 EraseBytes- +#3507 REDUCE cov: 502 ft: 1144 corp: 64/55Kb lim: 1487 exec/s: 0 rss: 52Mb L: 327/1487 MS: 2 ChangeBinInt-EraseBytes- +#3524 REDUCE cov: 502 ft: 1144 corp: 64/55Kb lim: 1487 exec/s: 0 rss: 52Mb L: 1411/1487 MS: 2 EraseBytes-InsertRepeatedBytes- +#3536 NEW cov: 503 ft: 1145 corp: 65/55Kb lim: 1487 exec/s: 0 rss: 52Mb L: 609/1487 MS: 2 CopyPart-CMP- DE: "\001\000\001`\000\353\313\202"- +#3544 REDUCE cov: 505 ft: 1148 corp: 66/56Kb lim: 1487 exec/s: 0 rss: 52Mb L: 1009/1487 MS: 3 ChangeBit-ShuffleBytes-ShuffleBytes- +#3616 REDUCE cov: 505 ft: 1148 corp: 66/56Kb lim: 1487 exec/s: 0 rss: 52Mb L: 10/1487 MS: 2 ChangeBinInt-EraseBytes- +#3633 REDUCE cov: 505 ft: 1148 corp: 66/56Kb lim: 1487 exec/s: 0 rss: 52Mb L: 374/1487 MS: 2 InsertByte-CrossOver- +#3645 REDUCE cov: 505 ft: 1148 corp: 66/56Kb lim: 1487 exec/s: 0 rss: 52Mb L: 5/1487 MS: 2 CopyPart-EraseBytes- +#3831 REDUCE cov: 505 ft: 1148 corp: 66/56Kb lim: 1487 exec/s: 0 rss: 53Mb L: 49/1487 MS: 1 EraseBytes- +#3986 REDUCE cov: 505 ft: 1148 corp: 66/56Kb lim: 1487 exec/s: 0 rss: 53Mb L: 380/1487 MS: 5 ChangeBit-PersAutoDict-ChangeBinInt-PersAutoDict-EraseBytes- DE: "\001\000\000\000"-"\001\000\000\000"- +#4098 REDUCE cov: 505 ft: 1148 corp: 66/55Kb lim: 1487 exec/s: 0 rss: 53Mb L: 876/1487 MS: 2 InsertRepeatedBytes-EraseBytes- +#4141 REDUCE cov: 505 ft: 1148 corp: 66/55Kb lim: 1487 exec/s: 0 rss: 53Mb L: 999/1487 MS: 3 PersAutoDict-InsertRepeatedBytes-EraseBytes- DE: "\336].|\266\276\007\006(p4\352O\243\214q\354\367\313\313e\264\343\300d\034\205\266\027\230WJ"- +#4144 NEW cov: 506 ft: 1149 corp: 67/56Kb lim: 1487 exec/s: 0 rss: 53Mb L: 1357/1487 MS: 3 ChangeBit-ChangeBinInt-EraseBytes- +#4207 REDUCE cov: 506 ft: 1149 corp: 67/56Kb lim: 1487 exec/s: 0 rss: 53Mb L: 54/1487 MS: 3 ChangeByte-ChangeBit-EraseBytes- +#4220 REDUCE cov: 506 ft: 1149 corp: 67/56Kb lim: 1487 exec/s: 0 rss: 53Mb L: 313/1487 MS: 3 ShuffleBytes-CopyPart-EraseBytes- +#4351 NEW cov: 507 ft: 1150 corp: 68/57Kb lim: 1487 exec/s: 0 rss: 54Mb L: 1010/1487 MS: 1 InsertByte- +#4357 REDUCE cov: 507 ft: 1150 corp: 68/57Kb lim: 1487 exec/s: 0 rss: 54Mb L: 9/1487 MS: 1 EraseBytes- +#4449 REDUCE cov: 507 ft: 1150 corp: 68/57Kb lim: 1487 exec/s: 0 rss: 54Mb L: 3/1487 MS: 2 CopyPart-EraseBytes- +#4453 REDUCE cov: 507 ft: 1150 corp: 68/57Kb lim: 1487 exec/s: 0 rss: 54Mb L: 1280/1487 MS: 4 ChangeASCIIInt-ChangeByte-ChangeByte-EraseBytes- +#4577 REDUCE cov: 507 ft: 1150 corp: 68/56Kb lim: 1487 exec/s: 0 rss: 54Mb L: 541/1487 MS: 4 ChangeBinInt-CopyPart-CrossOver-CrossOver- +#4704 REDUCE cov: 507 ft: 1150 corp: 68/56Kb lim: 1487 exec/s: 0 rss: 54Mb L: 34/1487 MS: 2 PersAutoDict-EraseBytes- DE: "\001\000"- +#4742 REDUCE cov: 507 ft: 1150 corp: 68/56Kb lim: 1487 exec/s: 0 rss: 54Mb L: 1178/1487 MS: 3 ChangeBinInt-CopyPart-EraseBytes- +#4758 REDUCE cov: 507 ft: 1150 corp: 68/56Kb lim: 1487 exec/s: 0 rss: 54Mb L: 39/1487 MS: 1 EraseBytes- +#4821 REDUCE cov: 507 ft: 1150 corp: 68/56Kb lim: 1487 exec/s: 0 rss: 55Mb L: 435/1487 MS: 3 ChangeByte-EraseBytes-CopyPart- +#4967 REDUCE cov: 507 ft: 1150 corp: 68/56Kb lim: 1487 exec/s: 0 rss: 55Mb L: 302/1487 MS: 1 EraseBytes- +#4979 REDUCE cov: 507 ft: 1150 corp: 68/56Kb lim: 1487 exec/s: 0 rss: 55Mb L: 1333/1487 MS: 2 ChangeBinInt-EraseBytes- +#5036 REDUCE cov: 508 ft: 1151 corp: 69/57Kb lim: 1487 exec/s: 0 rss: 55Mb L: 1116/1487 MS: 2 ChangeASCIIInt-EraseBytes- +#5131 REDUCE cov: 508 ft: 1151 corp: 69/57Kb lim: 1487 exec/s: 0 rss: 55Mb L: 972/1487 MS: 5 ChangeBinInt-InsertRepeatedBytes-CMP-CMP-EraseBytes- DE: "\347\254D]\037}\353\301X\367\020l\035\"+\037\273+\3369\265\026}N\200te\254u\312\323\271"-"\011O>\0361\250\002Y\374\321#r\003\352ll\305\006U\024\253\336\260\035\246\003\303\031K\011j\004"- +#5260 REDUCE cov: 508 ft: 1151 corp: 69/57Kb lim: 1487 exec/s: 0 rss: 55Mb L: 293/1487 MS: 4 CrossOver-ShuffleBytes-ChangeBit-EraseBytes- +#5441 REDUCE cov: 508 ft: 1151 corp: 69/57Kb lim: 1487 exec/s: 0 rss: 56Mb L: 2/1487 MS: 1 EraseBytes- +#5652 REDUCE cov: 508 ft: 1151 corp: 69/57Kb lim: 1487 exec/s: 0 rss: 56Mb L: 1/1487 MS: 1 EraseBytes- +#5854 REDUCE cov: 508 ft: 1151 corp: 69/57Kb lim: 1487 exec/s: 0 rss: 56Mb L: 885/1487 MS: 2 ShuffleBytes-EraseBytes- +#5868 REDUCE cov: 508 ft: 1151 corp: 69/57Kb lim: 1487 exec/s: 0 rss: 56Mb L: 25/1487 MS: 4 ChangeByte-InsertByte-InsertRepeatedBytes-EraseBytes- +#6314 REDUCE cov: 508 ft: 1151 corp: 69/57Kb lim: 1487 exec/s: 0 rss: 57Mb L: 372/1487 MS: 1 EraseBytes- +#6373 REDUCE cov: 508 ft: 1151 corp: 69/57Kb lim: 1487 exec/s: 0 rss: 57Mb L: 29/1487 MS: 4 InsertRepeatedBytes-EraseBytes-ChangeByte-EraseBytes- +#6419 REDUCE cov: 508 ft: 1151 corp: 69/57Kb lim: 1487 exec/s: 0 rss: 57Mb L: 1364/1487 MS: 1 EraseBytes- +#6656 REDUCE cov: 508 ft: 1151 corp: 69/57Kb lim: 1487 exec/s: 0 rss: 58Mb L: 712/1487 MS: 2 ChangeBit-EraseBytes- +#6758 REDUCE cov: 508 ft: 1151 corp: 69/57Kb lim: 1487 exec/s: 0 rss: 58Mb L: 5/1487 MS: 2 ShuffleBytes-EraseBytes- +#6875 REDUCE cov: 508 ft: 1151 corp: 69/57Kb lim: 1487 exec/s: 0 rss: 58Mb L: 275/1487 MS: 2 ShuffleBytes-EraseBytes- +#7006 REDUCE cov: 508 ft: 1151 corp: 69/57Kb lim: 1487 exec/s: 0 rss: 58Mb L: 899/1487 MS: 1 EraseBytes- +#7008 REDUCE cov: 508 ft: 1151 corp: 69/56Kb lim: 1487 exec/s: 0 rss: 58Mb L: 527/1487 MS: 2 PersAutoDict-EraseBytes- DE: "\336].|\266\276\007\006(p4\352O\243\214q\354\367\313\313e\264\343\300d\034\205\266\027\230WJ"- +#7009 REDUCE cov: 508 ft: 1151 corp: 69/56Kb lim: 1487 exec/s: 0 rss: 58Mb L: 1328/1487 MS: 1 EraseBytes- +#7027 REDUCE cov: 509 ft: 1152 corp: 70/57Kb lim: 1487 exec/s: 0 rss: 58Mb L: 661/1487 MS: 3 ShuffleBytes-EraseBytes-InsertRepeatedBytes- +#7083 REDUCE cov: 509 ft: 1152 corp: 70/57Kb lim: 1487 exec/s: 0 rss: 58Mb L: 28/1487 MS: 1 EraseBytes- +#7108 REDUCE cov: 509 ft: 1152 corp: 70/57Kb lim: 1487 exec/s: 0 rss: 58Mb L: 3/1487 MS: 5 ChangeByte-ChangeBinInt-ChangeByte-ChangeBinInt-EraseBytes- +#7173 REDUCE cov: 509 ft: 1152 corp: 70/57Kb lim: 1487 exec/s: 0 rss: 58Mb L: 434/1487 MS: 5 ChangeASCIIInt-InsertByte-InsertByte-ChangeByte-EraseBytes- +#7181 REDUCE cov: 509 ft: 1152 corp: 70/57Kb lim: 1487 exec/s: 0 rss: 58Mb L: 21/1487 MS: 3 ShuffleBytes-CopyPart-EraseBytes- +#7217 REDUCE cov: 509 ft: 1152 corp: 70/57Kb lim: 1487 exec/s: 0 rss: 59Mb L: 178/1487 MS: 1 EraseBytes- +#7223 REDUCE cov: 509 ft: 1152 corp: 70/57Kb lim: 1487 exec/s: 0 rss: 59Mb L: 1484/1487 MS: 1 CrossOver- +#7295 REDUCE cov: 509 ft: 1152 corp: 70/57Kb lim: 1487 exec/s: 0 rss: 59Mb L: 24/1487 MS: 2 ShuffleBytes-EraseBytes- +#7364 REDUCE cov: 509 ft: 1152 corp: 70/57Kb lim: 1487 exec/s: 0 rss: 59Mb L: 2/1487 MS: 4 ShuffleBytes-CrossOver-ChangeBinInt-EraseBytes- +#7523 REDUCE cov: 509 ft: 1152 corp: 70/57Kb lim: 1487 exec/s: 0 rss: 59Mb L: 387/1487 MS: 4 CMP-ShuffleBytes-InsertByte-EraseBytes- DE: "\265\236\012\022"- +#7574 REDUCE cov: 509 ft: 1152 corp: 70/57Kb lim: 1487 exec/s: 0 rss: 59Mb L: 971/1487 MS: 1 EraseBytes- +#7590 REDUCE cov: 509 ft: 1152 corp: 70/57Kb lim: 1487 exec/s: 0 rss: 59Mb L: 1/1487 MS: 1 EraseBytes- +#7754 REDUCE cov: 509 ft: 1152 corp: 70/57Kb lim: 1487 exec/s: 0 rss: 59Mb L: 133/1487 MS: 4 ChangeByte-ChangeBinInt-ChangeBinInt-EraseBytes- +#7875 REDUCE cov: 510 ft: 1153 corp: 71/57Kb lim: 1487 exec/s: 0 rss: 60Mb L: 582/1487 MS: 1 EraseBytes- +#7922 REDUCE cov: 510 ft: 1153 corp: 71/57Kb lim: 1487 exec/s: 0 rss: 60Mb L: 16/1487 MS: 2 ChangeASCIIInt-EraseBytes- +#7928 REDUCE cov: 510 ft: 1153 corp: 71/57Kb lim: 1487 exec/s: 0 rss: 60Mb L: 38/1487 MS: 1 EraseBytes- +#7994 REDUCE cov: 510 ft: 1153 corp: 71/57Kb lim: 1487 exec/s: 0 rss: 60Mb L: 1320/1487 MS: 1 EraseBytes- +#8342 REDUCE cov: 510 ft: 1153 corp: 71/57Kb lim: 1487 exec/s: 0 rss: 60Mb L: 1473/1487 MS: 3 CopyPart-ChangeByte-EraseBytes- +#8366 REDUCE cov: 510 ft: 1153 corp: 71/57Kb lim: 1487 exec/s: 0 rss: 60Mb L: 353/1487 MS: 4 CMP-ChangeASCIIInt-PersAutoDict-EraseBytes- DE: "\000\004\000\000\000\000\000\000"-"\350+\277\2731)\356e6\350\365\255#\367\025^\027\233z$\312\365\217\231\261\255\247w\270_\305\373"- +#8386 REDUCE cov: 510 ft: 1153 corp: 71/57Kb lim: 1487 exec/s: 0 rss: 60Mb L: 23/1487 MS: 5 CrossOver-ChangeByte-ChangeBinInt-PersAutoDict-EraseBytes- DE: "\377\377\000`\000\356\3529"- +#8847 NEW cov: 511 ft: 1154 corp: 72/59Kb lim: 1487 exec/s: 0 rss: 61Mb L: 1487/1487 MS: 1 ChangeBit- +#9373 REDUCE cov: 511 ft: 1154 corp: 72/59Kb lim: 1487 exec/s: 0 rss: 62Mb L: 118/1487 MS: 1 EraseBytes- +#9427 REDUCE cov: 511 ft: 1154 corp: 72/58Kb lim: 1487 exec/s: 0 rss: 62Mb L: 276/1487 MS: 4 EraseBytes-ShuffleBytes-ShuffleBytes-InsertRepeatedBytes- +#10493 REDUCE cov: 511 ft: 1154 corp: 72/58Kb lim: 1497 exec/s: 0 rss: 63Mb L: 105/1487 MS: 1 EraseBytes- +#10639 REDUCE cov: 511 ft: 1154 corp: 72/58Kb lim: 1497 exec/s: 0 rss: 63Mb L: 100/1487 MS: 1 EraseBytes- +#10671 REDUCE cov: 511 ft: 1154 corp: 72/58Kb lim: 1497 exec/s: 0 rss: 64Mb L: 363/1487 MS: 2 CopyPart-EraseBytes- +#11195 NEW cov: 513 ft: 1156 corp: 73/59Kb lim: 1497 exec/s: 0 rss: 64Mb L: 583/1487 MS: 4 ChangeBit-ChangeBinInt-PersAutoDict-InsertByte- DE: "\037\001\000\000\000\000\000\000"- +#11245 REDUCE cov: 513 ft: 1156 corp: 73/59Kb lim: 1497 exec/s: 0 rss: 64Mb L: 368/1487 MS: 5 CopyPart-InsertByte-ShuffleBytes-PersAutoDict-EraseBytes- DE: "\011O>\0361\250\002Y\374\321#r\003\352ll\305\006U\024\253\336\260\035\246\003\303\031K\011j\004"- +#11526 REDUCE cov: 513 ft: 1156 corp: 73/59Kb lim: 1497 exec/s: 0 rss: 65Mb L: 17/1487 MS: 1 EraseBytes- +#11723 REDUCE cov: 513 ft: 1156 corp: 73/59Kb lim: 1497 exec/s: 0 rss: 65Mb L: 654/1487 MS: 2 PersAutoDict-EraseBytes- DE: "\001\000\000\000\000\000\000`"- +#11809 REDUCE cov: 513 ft: 1156 corp: 73/59Kb lim: 1497 exec/s: 0 rss: 65Mb L: 1326/1487 MS: 1 EraseBytes- +#11979 REDUCE cov: 513 ft: 1156 corp: 73/59Kb lim: 1497 exec/s: 0 rss: 65Mb L: 258/1487 MS: 5 ChangeBit-EraseBytes-ChangeBinInt-ChangeByte-InsertRepeatedBytes- +#12185 NEW cov: 514 ft: 1157 corp: 74/59Kb lim: 1497 exec/s: 0 rss: 66Mb L: 609/1487 MS: 1 ChangeBinInt- +#12341 REDUCE cov: 514 ft: 1157 corp: 74/59Kb lim: 1497 exec/s: 0 rss: 66Mb L: 1318/1487 MS: 1 EraseBytes- +#12653 REDUCE cov: 514 ft: 1157 corp: 74/59Kb lim: 1497 exec/s: 0 rss: 66Mb L: 353/1487 MS: 2 EraseBytes-CopyPart- +#12974 REDUCE cov: 514 ft: 1157 corp: 74/59Kb lim: 1497 exec/s: 0 rss: 67Mb L: 270/1487 MS: 1 EraseBytes- +#13210 REDUCE cov: 514 ft: 1157 corp: 74/59Kb lim: 1497 exec/s: 0 rss: 67Mb L: 19/1487 MS: 1 EraseBytes- +#13211 REDUCE cov: 514 ft: 1157 corp: 74/59Kb lim: 1497 exec/s: 0 rss: 67Mb L: 12/1487 MS: 1 EraseBytes- +#13233 REDUCE cov: 514 ft: 1158 corp: 75/60Kb lim: 1497 exec/s: 0 rss: 67Mb L: 1107/1487 MS: 2 ChangeBinInt-CopyPart- +#13310 REDUCE cov: 514 ft: 1158 corp: 75/60Kb lim: 1497 exec/s: 0 rss: 67Mb L: 16/1487 MS: 2 PersAutoDict-EraseBytes- DE: "\001\000\000\000"- +#13554 REDUCE cov: 514 ft: 1158 corp: 75/60Kb lim: 1497 exec/s: 0 rss: 68Mb L: 1271/1487 MS: 4 CopyPart-ChangeBinInt-PersAutoDict-EraseBytes- DE: "\377\377"- +#13706 REDUCE cov: 514 ft: 1158 corp: 75/60Kb lim: 1497 exec/s: 0 rss: 68Mb L: 15/1487 MS: 2 PersAutoDict-EraseBytes- DE: "\001\000\001`\000\353\313\202"- +#13907 REDUCE cov: 515 ft: 1159 corp: 76/61Kb lim: 1497 exec/s: 0 rss: 68Mb L: 519/1487 MS: 1 EraseBytes- +#14033 REDUCE cov: 515 ft: 1159 corp: 76/61Kb lim: 1497 exec/s: 0 rss: 68Mb L: 8/1487 MS: 1 EraseBytes- +#14084 REDUCE cov: 515 ft: 1159 corp: 76/61Kb lim: 1497 exec/s: 0 rss: 68Mb L: 416/1487 MS: 1 EraseBytes- +#14285 REDUCE cov: 515 ft: 1159 corp: 76/61Kb lim: 1497 exec/s: 0 rss: 69Mb L: 743/1487 MS: 1 EraseBytes- +#14416 REDUCE cov: 515 ft: 1159 corp: 76/61Kb lim: 1497 exec/s: 0 rss: 69Mb L: 369/1487 MS: 1 EraseBytes- +#15254 REDUCE cov: 515 ft: 1159 corp: 76/60Kb lim: 1497 exec/s: 0 rss: 70Mb L: 1377/1487 MS: 3 ShuffleBytes-CopyPart-EraseBytes- +#15491 REDUCE cov: 515 ft: 1159 corp: 76/60Kb lim: 1497 exec/s: 0 rss: 70Mb L: 1012/1487 MS: 2 CMP-EraseBytes- DE: "\376\377\377\377\377\377\377\377"- +#15829 REDUCE cov: 515 ft: 1159 corp: 76/60Kb lim: 1497 exec/s: 0 rss: 71Mb L: 4/1487 MS: 3 PersAutoDict-CMP-EraseBytes- DE: "\037\001\000\000\000\000\000\000"-"\000\000\000\000\000\000\000\000"- +#15844 REDUCE cov: 517 ft: 1161 corp: 77/61Kb lim: 1497 exec/s: 0 rss: 71Mb L: 527/1487 MS: 5 PersAutoDict-ChangeBinInt-ChangeBinInt-CrossOver-InsertRepeatedBytes- DE: "\336].|\266\276\007\006(p4\352O\243\214q\354\367\313\313e\264\343\300d\034\205\266\027\230WJ"- +#16631 REDUCE cov: 517 ft: 1161 corp: 77/61Kb lim: 1497 exec/s: 0 rss: 72Mb L: 12/1487 MS: 2 ChangeByte-EraseBytes- +#16667 NEW cov: 517 ft: 1162 corp: 78/62Kb lim: 1497 exec/s: 0 rss: 72Mb L: 1190/1487 MS: 1 InsertRepeatedBytes- +#16833 REDUCE cov: 517 ft: 1162 corp: 78/62Kb lim: 1497 exec/s: 0 rss: 72Mb L: 1234/1487 MS: 1 EraseBytes- +#16900 REDUCE cov: 517 ft: 1162 corp: 78/61Kb lim: 1497 exec/s: 0 rss: 72Mb L: 768/1487 MS: 2 InsertRepeatedBytes-CrossOver- +#16901 REDUCE cov: 517 ft: 1162 corp: 78/61Kb lim: 1497 exec/s: 0 rss: 72Mb L: 758/1487 MS: 1 CrossOver- +#17047 REDUCE cov: 517 ft: 1162 corp: 78/61Kb lim: 1497 exec/s: 0 rss: 72Mb L: 1318/1487 MS: 1 EraseBytes- +#17454 REDUCE cov: 517 ft: 1162 corp: 78/61Kb lim: 1497 exec/s: 0 rss: 73Mb L: 3/1487 MS: 2 ShuffleBytes-EraseBytes- +#17706 REDUCE cov: 517 ft: 1162 corp: 78/60Kb lim: 1497 exec/s: 0 rss: 73Mb L: 628/1487 MS: 2 ChangeASCIIInt-CrossOver- +#17975 NEW cov: 517 ft: 1163 corp: 79/62Kb lim: 1497 exec/s: 0 rss: 73Mb L: 1213/1487 MS: 4 InsertRepeatedBytes-ChangeBit-ChangeBinInt-PersAutoDict- DE: "\000\004\000\000\000\000\000\000"- +#18261 REDUCE cov: 518 ft: 1164 corp: 80/62Kb lim: 1497 exec/s: 0 rss: 74Mb L: 525/1487 MS: 1 CrossOver- +#18799 REDUCE cov: 518 ft: 1164 corp: 80/62Kb lim: 1497 exec/s: 0 rss: 75Mb L: 356/1487 MS: 3 ChangeBit-InsertRepeatedBytes-EraseBytes- +#19006 REDUCE cov: 518 ft: 1164 corp: 80/62Kb lim: 1497 exec/s: 0 rss: 75Mb L: 690/1487 MS: 2 ChangeByte-EraseBytes- +#19277 REDUCE cov: 518 ft: 1164 corp: 80/62Kb lim: 1497 exec/s: 0 rss: 75Mb L: 12/1487 MS: 1 EraseBytes- +#19497 REDUCE cov: 518 ft: 1164 corp: 80/62Kb lim: 1497 exec/s: 0 rss: 76Mb L: 690/1487 MS: 5 EraseBytes-ChangeBinInt-EraseBytes-ChangeBit-InsertRepeatedBytes- +#19579 REDUCE cov: 518 ft: 1164 corp: 80/62Kb lim: 1497 exec/s: 0 rss: 76Mb L: 361/1487 MS: 2 CopyPart-EraseBytes- +#19672 REDUCE cov: 518 ft: 1164 corp: 80/61Kb lim: 1497 exec/s: 0 rss: 76Mb L: 597/1487 MS: 3 ChangeBit-PersAutoDict-EraseBytes- DE: "\001\000\000\000\000\000\000`"- +#19763 REDUCE cov: 518 ft: 1164 corp: 80/61Kb lim: 1497 exec/s: 0 rss: 76Mb L: 1146/1487 MS: 1 EraseBytes- +#19974 REDUCE cov: 518 ft: 1164 corp: 80/61Kb lim: 1497 exec/s: 0 rss: 76Mb L: 346/1487 MS: 1 EraseBytes- +#20000 DONE cov: 518 ft: 1164 corp: 80/61Kb lim: 1497 exec/s: 0 rss: 76Mb +###### Recommended dictionary. ###### +"\001\000\000\000\000\000\000`" # Uses: 146 +"\001\000\000\000" # Uses: 140 +"\377\377\000`\000\356\3529" # Uses: 147 +"\000\000\000\000" # Uses: 141 +"\336].|\266\276\007\006(p4\352O\243\214q\354\367\313\313e\264\343\300d\034\205\266\027\230WJ" # Uses: 110 +"\377\377" # Uses: 113 +"\001\000\000\000\000\000\001\002" # Uses: 114 +"\350+\277\2731)\356e6\350\365\255#\367\025^\027\233z$\312\365\217\231\261\255\247w\270_\305\373" # Uses: 88 +"\037\001\000\000\000\000\000\000" # Uses: 100 +"\001\000" # Uses: 101 +"\001\000\001`\000\353\313\202" # Uses: 95 +"\347\254D]\037}\353\301X\367\020l\035\"+\037\273+\3369\265\026}N\200te\254u\312\323\271" # Uses: 81 +"\011O>\0361\250\002Y\374\321#r\003\352ll\305\006U\024\253\336\260\035\246\003\303\031K\011j\004" # Uses: 88 +"\265\236\012\022" # Uses: 69 +"\000\004\000\000\000\000\000\000" # Uses: 53 +"\376\377\377\377\377\377\377\377" # Uses: 22 +"\000\000\000\000\000\000\000\000" # Uses: 26 +###### End of recommended dictionary. ###### +Done 20000 runs in 0 second(s) diff --git a/docs/testing-evidence/migration-compatibility-fuzz/restored-headers-green.txt b/docs/testing-evidence/migration-compatibility-fuzz/restored-headers-green.txt new file mode 100644 index 00000000..12f246bb --- /dev/null +++ b/docs/testing-evidence/migration-compatibility-fuzz/restored-headers-green.txt @@ -0,0 +1,18 @@ +437bc1971fc8a98f2d039e446b3f5008979974f9 + Finished `test` profile [unoptimized + debuginfo] target(s) in 0.03s + Running tests/store_migration_compatibility.rs (/debug/deps/store_migration_compatibility-fbb66f7c5fa9ff33) + +running 2 tests +test unsupported_migration_versions_refuse_with_exact_coordinates ... ok +test every_unknown_mandatory_flag_refuses_without_downgrade ... ok + +test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s + + Finished `release` profile [optimized] target(s) in 0.01s + Running tests/store_migration_compatibility.rs (/release/deps/store_migration_compatibility-07447e856cc2ca90) + +running 2 tests +test unsupported_migration_versions_refuse_with_exact_coordinates ... ok +test every_unknown_mandatory_flag_refuses_without_downgrade ... ok + +test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s diff --git a/docs/testing-evidence/migration-compatibility-fuzz/stage-red.txt b/docs/testing-evidence/migration-compatibility-fuzz/stage-red.txt new file mode 100644 index 00000000..a062a4aa --- /dev/null +++ b/docs/testing-evidence/migration-compatibility-fuzz/stage-red.txt @@ -0,0 +1,89 @@ ++ cd ++ CARGO_TARGET_DIR= ++ cargo +nightly-2026-07-24 fuzz run migration_format /fuzz/corpus/migration_format/recovery-complete -- -runs=1 -seed=112 -max_len=4096 -timeout=5 -rss_limit_mb=1024 + Compiling libc v0.2.186 + Compiling rustix v1.1.4 + Compiling find-msvc-tools v0.1.9 + Compiling shlex v2.0.1 + Compiling io-lifetimes v3.0.1 + Compiling linux-raw-sys v0.12.1 + Compiling bitflags v2.13.1 + Compiling io-lifetimes v2.0.4 + Compiling io-extras v0.19.0 + Compiling cap-primitives v4.0.2 + Compiling serde_core v1.0.229 + Compiling once_cell v1.21.4 + Compiling maybe-owned v0.3.4 + Compiling ipnet v2.12.0 + Compiling ambient-authority v0.0.2 + Compiling cap-std v4.0.2 + Compiling zmij v1.0.23 + Compiling serde_json v1.0.151 + Compiling serde v1.0.229 + Compiling cap-fs-ext v4.0.2 + Compiling itoa v1.0.18 + Compiling memchr v2.8.3 + Compiling arrayvec v0.7.8 + Compiling arrayref v0.3.9 + Compiling constant_time_eq v0.4.2 + Compiling cfg-if v1.0.4 + Compiling arbitrary v1.4.2 + Compiling jobserver v0.1.35 + Compiling cc v1.3.0 + Compiling blake3 v1.8.5 + Compiling libfuzzer-sys v0.4.13 + Compiling fs-set-times v0.20.3 + Compiling rustix-linux-procfs v0.1.1 + Compiling xtask v0.0.0 (/xtask) + Compiling keep v0.0.0 () + Compiling keep-fuzz v0.0.0 (/fuzz) + Finished `release` profile [optimized + debuginfo] target(s) in 11.40s + Finished `release` profile [optimized + debuginfo] target(s) in 0.01s + Running `/aarch64-unknown-linux-gnu/release/migration_format -artifact_prefix=/fuzz/artifacts/migration_format/ -runs=1 -seed=112 -max_len=4096 -timeout=5 -rss_limit_mb=1024 /fuzz/corpus/migration_format/recovery-complete` +INFO: Running with entropic power schedule (0xFF, 100). +INFO: Seed: 112 +INFO: Loaded 1 modules (68677 inline 8-bit counters): 68677 [0xaaaabd878aa0, 0xaaaabd8896e5), +INFO: Loaded 1 PC tables (68677 PCs): 68677 [0xaaaabd8896e8,0xaaaabd995b38), +/aarch64-unknown-linux-gnu/release/migration_format: Running 1 inputs 1 time(s) each. +Running: /fuzz/corpus/migration_format/recovery-complete + +thread '' (2072380) panicked at fuzz_targets/migration_format/recovery.rs:98:5: +complete migration must not leave staged evidence +note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace +==2072380== ERROR: libFuzzer: deadly signal + #0 0xaaaabd294638 (/aarch64-unknown-linux-gnu/release/migration_format+0x2b4638) (BuildId: 1e95d9363dc86c525b940ce3f2608e686425a5df) + #1 0xaaaabd2e1600 (/aarch64-unknown-linux-gnu/release/migration_format+0x301600) (BuildId: 1e95d9363dc86c525b940ce3f2608e686425a5df) + #2 0xaaaabd2d036c (/aarch64-unknown-linux-gnu/release/migration_format+0x2f036c) (BuildId: 1e95d9363dc86c525b940ce3f2608e686425a5df) + #3 0xffffa6fa88bc (linux-vdso.so.1+0x8bc) (BuildId: 91bd37d5562c97f21721e188c8617e7fb312061b) + #4 0xffffa6ad7d7c (/lib/aarch64-linux-gnu/libc.so.6+0x87d7c) (BuildId: 4c1eca4527d1163b2dde55860b69f270158febb4) + #5 0xffffa6a8693c (/lib/aarch64-linux-gnu/libc.so.6+0x3693c) (BuildId: 4c1eca4527d1163b2dde55860b69f270158febb4) + #6 0xffffa6a71a80 (/lib/aarch64-linux-gnu/libc.so.6+0x21a80) (BuildId: 4c1eca4527d1163b2dde55860b69f270158febb4) + #7 0xaaaabd1fa020 (/aarch64-unknown-linux-gnu/release/migration_format+0x21a020) (BuildId: 1e95d9363dc86c525b940ce3f2608e686425a5df) + #8 0xaaaabd1f9e70 (/aarch64-unknown-linux-gnu/release/migration_format+0x219e70) (BuildId: 1e95d9363dc86c525b940ce3f2608e686425a5df) + #9 0xaaaabd1df97c (/aarch64-unknown-linux-gnu/release/migration_format+0x1ff97c) (BuildId: 1e95d9363dc86c525b940ce3f2608e686425a5df) + #10 0xaaaabd6fbfec (/aarch64-unknown-linux-gnu/release/migration_format+0x71bfec) (BuildId: 1e95d9363dc86c525b940ce3f2608e686425a5df) + #11 0xaaaabd6ecfb8 (/aarch64-unknown-linux-gnu/release/migration_format+0x70cfb8) (BuildId: 1e95d9363dc86c525b940ce3f2608e686425a5df) + #12 0xaaaabd6e7904 (/aarch64-unknown-linux-gnu/release/migration_format+0x707904) (BuildId: 1e95d9363dc86c525b940ce3f2608e686425a5df) + #13 0xaaaabd6ed550 (/aarch64-unknown-linux-gnu/release/migration_format+0x70d550) (BuildId: 1e95d9363dc86c525b940ce3f2608e686425a5df) + #14 0xaaaabd1fa6c8 (/aarch64-unknown-linux-gnu/release/migration_format+0x21a6c8) (BuildId: 1e95d9363dc86c525b940ce3f2608e686425a5df) + #15 0xaaaabd2bc4b4 (/aarch64-unknown-linux-gnu/release/migration_format+0x2dc4b4) (BuildId: 1e95d9363dc86c525b940ce3f2608e686425a5df) + #16 0xaaaabd2c0bcc (/aarch64-unknown-linux-gnu/release/migration_format+0x2e0bcc) (BuildId: 1e95d9363dc86c525b940ce3f2608e686425a5df) + #17 0xaaaabd2c8d44 (/aarch64-unknown-linux-gnu/release/migration_format+0x2e8d44) (BuildId: 1e95d9363dc86c525b940ce3f2608e686425a5df) + #18 0xaaaabd2c9c94 (/aarch64-unknown-linux-gnu/release/migration_format+0x2e9c94) (BuildId: 1e95d9363dc86c525b940ce3f2608e686425a5df) + #19 0xaaaabd2ca9e0 (/aarch64-unknown-linux-gnu/release/migration_format+0x2ea9e0) (BuildId: 1e95d9363dc86c525b940ce3f2608e686425a5df) + #20 0xaaaabd2c94f8 (/aarch64-unknown-linux-gnu/release/migration_format+0x2e94f8) (BuildId: 1e95d9363dc86c525b940ce3f2608e686425a5df) + #21 0xaaaabd2d08cc (/aarch64-unknown-linux-gnu/release/migration_format+0x2f08cc) (BuildId: 1e95d9363dc86c525b940ce3f2608e686425a5df) + #22 0xaaaabd2ea630 (/aarch64-unknown-linux-gnu/release/migration_format+0x30a630) (BuildId: 1e95d9363dc86c525b940ce3f2608e686425a5df) + #23 0xaaaabd2f34ec (/aarch64-unknown-linux-gnu/release/migration_format+0x3134ec) (BuildId: 1e95d9363dc86c525b940ce3f2608e686425a5df) + #24 0xaaaabd1fa980 (/aarch64-unknown-linux-gnu/release/migration_format+0x21a980) (BuildId: 1e95d9363dc86c525b940ce3f2608e686425a5df) + #25 0xffffa6a72258 (/lib/aarch64-linux-gnu/libc.so.6+0x22258) (BuildId: 4c1eca4527d1163b2dde55860b69f270158febb4) + #26 0xffffa6a72338 (/lib/aarch64-linux-gnu/libc.so.6+0x22338) (BuildId: 4c1eca4527d1163b2dde55860b69f270158febb4) + #27 0xaaaabd1faaec (/aarch64-unknown-linux-gnu/release/migration_format+0x21aaec) (BuildId: 1e95d9363dc86c525b940ce3f2608e686425a5df) + +NOTE: libFuzzer has rudimentary signal handlers. + Combine libFuzzer with AddressSanitizer or similar for better crash reports. +SUMMARY: libFuzzer: deadly signal +MS: 0 ; base unit: 0000000000000000000000000000000000000000 +──────────────────────────────────────────────────────────────────────────────── + +Error: Fuzz target exited with exit status: 77 diff --git a/docs/testing-evidence/migration-compatibility-fuzz/stage.patch b/docs/testing-evidence/migration-compatibility-fuzz/stage.patch new file mode 100644 index 00000000..940755f0 --- /dev/null +++ b/docs/testing-evidence/migration-compatibility-fuzz/stage.patch @@ -0,0 +1,9 @@ +--- a/fuzz/fuzz_targets/migration_format/recovery.rs ++++ b/fuzz/fuzz_targets/migration_format/recovery.rs +@@ -17 +17 @@ pub(super) fn exercise(input: &[u8]) -> Option<()> { +- let residue = StoreMigrationResidue { ++ let mut residue = StoreMigrationResidue { +@@ -27,0 +28,3 @@ pub(super) fn exercise(input: &[u8]) -> Option<()> { ++ if matches!(result, Ok(StoreMigrationRecoveryPlan::Complete)) { ++ residue.intent_stage = Some(Vec::new()); ++ } diff --git a/fuzz/README.md b/fuzz/README.md index f310a259..8c4d554b 100644 --- a/fuzz/README.md +++ b/fuzz/README.md @@ -70,10 +70,7 @@ retention-manifest, and retention-head decoders. The canonical one-root generation keeps mutations inside framing, semantic, ordering, checksum, and digest validation; every admitted value must retain its exact input bytes. -The `migration_format` seeds select the public format-marker, migration-intent, -and completion-receipt decoders. The receipt seed carries its exact marker and -intent dependencies so mutations exercise integrity and cross-record binding; -every admitted value must retain its exact input bytes. +The `migration_format` seeds select public migration record decoders and bounded recovery planning. Receipt seeds carry exact marker and intent dependencies; malformed seeds exercise version, mandatory-flag and checksum refusals. Recovery seeds include valid prefixes and contradictory transition evidence. Properties require v1 admission only without migration evidence, precise pre-intent effect refusal, and complete success only with a full namespace and jointly admitted records. See [migration compatibility evidence](../docs/testing-evidence/migration-compatibility-fuzz.md) for the fuzz-only envelope, bounds, replay and oracle limits. The `segment_format` seeds select the public segment-header, record-header, complete-record, seal, and complete-segment boundaries. Canonical empty, diff --git a/fuzz/fuzz_targets/migration_format.rs b/fuzz/fuzz_targets/migration_format.rs index 739c906d..fa5b7672 100644 --- a/fuzz/fuzz_targets/migration_format.rs +++ b/fuzz/fuzz_targets/migration_format.rs @@ -2,6 +2,9 @@ //! This target owns canonical store-migration record parser fuzzing. +#[path = "migration_format/recovery.rs"] +pub mod recovery; + use keep::{ AdmittedStoreFormatMarker, AdmittedStoreMigrationIntent, AdmittedStoreMigrationReceipt, }; @@ -17,6 +20,9 @@ fuzz_target!(|bytes: &[u8]| { match selector { 0 => marker(input), 1 => intent(input), + 3 => { + let _ = recovery::exercise(input); + } _ => receipt(input), } }); diff --git a/fuzz/fuzz_targets/migration_format/recovery.rs b/fuzz/fuzz_targets/migration_format/recovery.rs new file mode 100644 index 00000000..b04dd33b --- /dev/null +++ b/fuzz/fuzz_targets/migration_format/recovery.rs @@ -0,0 +1,115 @@ +//! Bounded recovery-residue fuzz input and specified success/refusal properties. + +use keep::{ + AdmittedStoreFormatMarker, AdmittedStoreMigrationIntent, AdmittedStoreMigrationReceipt, + StoreMigrationEffect, StoreMigrationRecoveryAmbiguity, StoreMigrationRecoveryPlan, + StoreMigrationResidue, plan_store_migration_recovery, +}; + +const INTENT_BYTES: usize = 256; +const MAX_RECORD_BYTES: usize = 513; + +pub(super) fn exercise(input: &[u8]) -> Option<()> { + let (expected_bytes, input) = input.split_at_checked(INTENT_BYTES)?; + let expected = AdmittedStoreMigrationIntent::decode(expected_bytes).ok()?; + let (&records, input) = input.split_first()?; + let (&namespace, mut input) = input.split_first()?; + let residue = StoreMigrationResidue { + intent_stage: present(records & 1, record(&mut input)?), + intent: present(records & 2, record(&mut input)?), + marker_stage: present(records & 4, record(&mut input)?), + marker: present(records & 8, record(&mut input)?), + receipt_stage: present(records & 16, record(&mut input)?), + receipt: present(records & 32, record(&mut input)?), + reader_fence: namespace & 1 != 0, + namespace_prefix: [2, 4, 8, 16, 32, 64].map(|bit| namespace & bit != 0), + }; + let result = plan_store_migration_recovery(&expected, &residue); + assert_version_one(&residue, &result); + assert_pre_intent_effects(&residue, &result); + if matches!(result, Ok(StoreMigrationRecoveryPlan::Complete)) { + assert_complete(&residue); + } + Some(()) +} + +fn record<'a>(input: &mut &'a [u8]) -> Option<&'a [u8]> { + let (length, remainder) = input.split_at_checked(2)?; + let length = usize::from(u16::from_le_bytes(length.try_into().ok()?)); + if length > MAX_RECORD_BYTES { + return None; + } + let (bytes, remainder) = remainder.split_at_checked(length)?; + *input = remainder; + Some(bytes) +} + +fn present(presence: u8, bytes: &[u8]) -> Option> { + (presence != 0).then(|| bytes.to_vec()) +} + +type Plan = Result; + +fn assert_version_one(residue: &StoreMigrationResidue, result: &Plan) { + let no_migration_evidence = residue.intent.is_none() + && residue.intent_stage.is_none() + && !residue.reader_fence + && !residue.namespace_prefix.contains(&true) + && residue.marker.is_none() + && residue.marker_stage.is_none() + && residue.receipt.is_none() + && residue.receipt_stage.is_none(); + assert_eq!( + matches!(result, Ok(StoreMigrationRecoveryPlan::VersionOne)), + no_migration_evidence, + "version-one admission requires absence of all migration evidence" + ); +} + +fn assert_pre_intent_effects(residue: &StoreMigrationResidue, result: &Plan) { + if residue.intent.is_some() { + return; + } + let effect = if residue.reader_fence || residue.namespace_prefix.contains(&true) { + Some(StoreMigrationEffect::Namespace) + } else if residue.marker.is_some() || residue.marker_stage.is_some() { + Some(StoreMigrationEffect::Marker) + } else if residue.receipt.is_some() || residue.receipt_stage.is_some() { + Some(StoreMigrationEffect::Receipt) + } else { + None + }; + if let Some(expected) = effect { + assert!( + matches!(result, Err(StoreMigrationRecoveryAmbiguity::EffectBeforeIntent { effect }) if *effect == expected), + "an effect before durable intent must retain its precise refusal: {result:?}" + ); + } +} + +fn assert_complete(residue: &StoreMigrationResidue) { + assert!( + residue.reader_fence && residue.namespace_prefix.iter().all(|present| *present), + "complete migration needs the entire namespace" + ); + assert!( + residue.intent_stage.is_none() + && residue.marker_stage.is_none() + && residue.receipt_stage.is_none(), + "complete migration must not leave staged evidence" + ); + let records = residue + .intent + .as_deref() + .zip(residue.marker.as_deref()) + .zip(residue.receipt.as_deref()); + let admitted = records.and_then(|((intent, marker), receipt)| { + let intent = AdmittedStoreMigrationIntent::decode(intent).ok()?; + let marker = AdmittedStoreFormatMarker::decode(marker).ok()?; + AdmittedStoreMigrationReceipt::decode(receipt, &intent, &marker).ok() + }); + assert!( + admitted.is_some(), + "complete migration needs jointly admitted records" + ); +} diff --git a/src/adapters/store_migration.rs b/src/adapters/store_migration.rs index 7a1b5dee..4088b3e4 100644 --- a/src/adapters/store_migration.rs +++ b/src/adapters/store_migration.rs @@ -50,6 +50,8 @@ pub use filesystem_migration_recovery_refusal::{ FilesystemMigrationRecoveryRefusal, FilesystemMigrationResidueKind, }; #[cfg(test)] +mod filesystem_migration_compatibility_tests; +#[cfg(test)] mod filesystem_migration_current_recovery_tests; #[cfg(test)] mod filesystem_migration_pair_admission_tests; diff --git a/src/adapters/store_migration/filesystem_migration_compatibility_tests.rs b/src/adapters/store_migration/filesystem_migration_compatibility_tests.rs new file mode 100644 index 00000000..948a2f94 --- /dev/null +++ b/src/adapters/store_migration/filesystem_migration_compatibility_tests.rs @@ -0,0 +1,57 @@ +//! Migration preserves version-one bytes while irrevocably refusing version-one authority. + +use super::filesystem_migration_recovery_tests::version_one_witness; +use super::filesystem_migration_test_fixture::{maximum_policy, open_authority}; +use super::migration_resumption::{MigrationRecords, execute_phase}; +use super::{StoreMigrationPhase, StoreMigrationStorage}; +use crate::adapters::{FilesystemPlatformAdmission, FilesystemPlatformAdmissionError}; +use std::{error::Error, io}; + +// Size: medium. Oracle: KEEP-MIGRATION-008 forbids v1 authority after the first +// migration effect, while every original segment/catalog/head byte is preserved. +// Delete only if the one-way migration contract disappears or stronger runtime laws subsume it. +#[test] +fn every_migration_prefix_preserves_v1_bytes_but_refuses_v1_authority() -> Result<(), Box> +{ + for count in 0..=StoreMigrationPhase::ALL.len() { + let (store, mut authority) = + open_authority(&format!("migration-v1-compatibility-{count}"))?; + let before = version_one_witness(store.path())?; + let intent = authority.observe_intent()?; + StoreMigrationStorage::verify_current(&mut authority, &intent)?; + let records = MigrationRecords::for_intent(&intent); + for phase in StoreMigrationPhase::ALL.iter().take(count) { + execute_phase(&mut authority, *phase, &records)?; + } + drop(authority); + match ( + count, + FilesystemPlatformAdmission::reopen_unchecked_for_tests(store.path()), + ) { + (0, Ok(admission)) => { + let authority = + super::FilesystemStoreMigrationAuthority::open(admission, maximum_policy())?; + authority.verify_current(&intent)?; + } + (_, Err(FilesystemPlatformAdmissionError::Namespace { source })) if count > 0 => { + assert_eq!(source.kind(), io::ErrorKind::InvalidData, "prefix {count}"); + } + (_, result) => { + return Err(format!( + "prefix {count}: incorrect v1 authority outcome: {}", + result + .err() + .map_or_else(|| "admitted".to_owned(), |error| format!("{error:?}")) + ) + .into()); + } + } + assert_eq!( + version_one_witness(store.path())?, + before, + "prefix {count}: v1 immutable bytes changed" + ); + store.remove()?; + } + Ok(()) +} diff --git a/tests/store_migration_compatibility.rs b/tests/store_migration_compatibility.rs new file mode 100644 index 00000000..54332c3d --- /dev/null +++ b/tests/store_migration_compatibility.rs @@ -0,0 +1,100 @@ +//! Public migration record admission preserves precise version and mandatory-flag refusals. + +#[allow( + dead_code, + reason = "shared immutable record fixtures include vectors used by other codec laws" +)] +#[path = "store_migration_receipt/fixture.rs"] +mod fixture; +mod support; + +use keep::{ + AdmittedStoreFormatMarker as Marker, AdmittedStoreMigrationIntent as Intent, + AdmittedStoreMigrationReceipt as Receipt, StoreFormatMarkerDecodeError as MarkerError, + StoreMigrationIntentDecodeError as IntentError, + StoreMigrationReceiptDecodeError as ReceiptError, +}; +use std::error::Error; + +// Size: small. Oracle: all migration records use version 2 at bytes 16..18 (v2 format specification). +// Delete only if version 2 is retired or stronger public codec laws subsume it. +#[test] +fn unsupported_migration_versions_refuse_with_exact_coordinates() -> Result<(), Box> { + let marker = fixture::marker_bytes()?; + let intent = fixture::intent_bytes()?; + let admitted_marker = Marker::decode(&marker)?; + let admitted_intent = Intent::decode(&intent)?; + for version in [0_u16, 1, 3, u16::MAX] { + let field = version.to_be_bytes(); + assert_eq!( + Marker::decode(&changed(marker.clone(), 16, &field)?).err(), + Some(MarkerError::UnsupportedVersion { + expected: 2, + observed: version + }) + ); + assert_eq!( + Intent::decode(&changed(intent.clone(), 16, &field)?).err(), + Some(IntentError::UnsupportedVersion { + expected: 2, + observed: version + }) + ); + assert_eq!( + Receipt::decode( + &changed(fixture::receipt_bytes()?, 16, &field)?, + &admitted_intent, + &admitted_marker + ) + .err(), + Some(ReceiptError::UnsupportedVersion { + expected: 2, + observed: version + }) + ); + } + Ok(()) +} + +// Size: small. Oracle: every bit of the v2 flags word at bytes 20..24 is mandatory and unsupported. +// Delete only when specific flag bits gain documented compatible semantics. +#[test] +fn every_unknown_mandatory_flag_refuses_without_downgrade() -> Result<(), Box> { + let marker = fixture::marker_bytes()?; + let intent = fixture::intent_bytes()?; + let admitted_marker = Marker::decode(&marker)?; + let admitted_intent = Intent::decode(&intent)?; + for bit in 0..32 { + let flags = 1_u32.checked_shl(bit).ok_or("flag shift overflow")?; + let field = flags.to_be_bytes(); + assert_eq!( + Marker::decode(&changed(marker.clone(), 20, &field)?).err(), + Some(MarkerError::UnsupportedFlags { observed: flags }) + ); + assert_eq!( + Intent::decode(&changed(intent.clone(), 20, &field)?).err(), + Some(IntentError::UnsupportedFlags { observed: flags }) + ); + assert_eq!( + Receipt::decode( + &changed(fixture::receipt_bytes()?, 20, &field)?, + &admitted_intent, + &admitted_marker + ) + .err(), + Some(ReceiptError::UnsupportedFlags { observed: flags }) + ); + } + Ok(()) +} + +fn changed(mut bytes: Vec, start: usize, field: &[u8]) -> Result, Box> { + let end = start + .checked_add(field.len()) + .ok_or("field range overflow")?; + bytes + .get_mut(start..end) + .ok_or("field outside fixture")? + .copy_from_slice(field); + Ok(bytes) +} diff --git a/xtask/src/fuzz_seed_corpus.rs b/xtask/src/fuzz_seed_corpus.rs index 72d995dc..48dd7d20 100644 --- a/xtask/src/fuzz_seed_corpus.rs +++ b/xtask/src/fuzz_seed_corpus.rs @@ -6,6 +6,7 @@ mod cdc_seeds; mod filesystem; mod identity_seeds; mod layout_seeds; +mod migration_recovery_seeds; mod migration_seeds; mod retention_seeds; mod segment_seeds; diff --git a/xtask/src/fuzz_seed_corpus/migration_recovery_seeds.rs b/xtask/src/fuzz_seed_corpus/migration_recovery_seeds.rs new file mode 100644 index 00000000..9f5e9f0d --- /dev/null +++ b/xtask/src/fuzz_seed_corpus/migration_recovery_seeds.rs @@ -0,0 +1,65 @@ +//! Migration recovery seeds encode valid prefixes and contradictory transition evidence. + +use super::{FuzzSeedError, Seed, prefixed}; + +pub(super) fn seeds( + marker: &[u8], + intent: &[u8], + receipt: &[u8], +) -> Result, FuzzSeedError> { + let records = [intent, intent, marker, marker, receipt, receipt]; + let mut seeds = Vec::new(); + for (name, presence, namespace) in [ + ("recovery-version-one", 0, 0), + ("recovery-intent-stage", 1, 0), + ("recovery-durable-intent", 2, 0), + ("recovery-partial-namespace", 2, 7), + ("recovery-full-namespace", 2, 127), + ("recovery-marker-stage", 6, 127), + ("recovery-marker", 10, 127), + ("recovery-receipt-stage", 26, 127), + ("recovery-complete", 42, 127), + ("recovery-effect-before-intent", 0, 1), + ("recovery-stage-after-effect", 3, 1), + ("recovery-namespace-hole", 2, 5), + ("recovery-receipt-before-marker", 18, 127), + ] { + seeds.push(seed(name, presence, namespace, intent, records)?); + } + let mut corrupt = intent.to_vec(); + let checksum = corrupt + .last_mut() + .ok_or_else(|| FuzzSeedError::violation("empty intent fixture"))?; + *checksum ^= 1; + seeds.push(seed( + "recovery-corrupt-intent", + 2, + 0, + intent, + [intent, &corrupt, marker, marker, receipt, receipt], + )?); + Ok(seeds) +} + +fn seed( + name: &'static str, + presence: u8, + namespace: u8, + expected: &[u8], + records: [&[u8]; 6], +) -> Result { + if expected.len() != 256 || records.iter().any(|record| record.len() > 513) { + return Err(FuzzSeedError::violation( + "recovery seed exceeds its record framing bounds", + )); + } + let mut payload = expected.to_vec(); + payload.extend_from_slice(&[presence, namespace]); + for record in records { + let length = u16::try_from(record.len()) + .map_err(|_| FuzzSeedError::violation("recovery seed record exceeds framing bound"))?; + payload.extend_from_slice(&length.to_le_bytes()); + payload.extend_from_slice(record); + } + Seed::new("migration_format", name, prefixed(3, &payload)?) +} diff --git a/xtask/src/fuzz_seed_corpus/migration_seeds.rs b/xtask/src/fuzz_seed_corpus/migration_seeds.rs index 30d01b8c..f2d2dc94 100644 --- a/xtask/src/fuzz_seed_corpus/migration_seeds.rs +++ b/xtask/src/fuzz_seed_corpus/migration_seeds.rs @@ -23,7 +23,7 @@ pub(super) fn seeds(files: &RepositoryFiles) -> Result, FuzzSeedError> let marker = segment_store_v2_fixture::read_hex(files, marker_fixture)?; let intent = segment_store_v2_fixture::read_hex(files, intent_fixture)?; let receipt = segment_store_v2_fixture::read_hex(files, receipt_fixture)?; - Ok(vec![ + let mut seeds = vec![ Seed::new( "migration_format", "format-marker", @@ -39,7 +39,12 @@ pub(super) fn seeds(files: &RepositoryFiles) -> Result, FuzzSeedError> "migration-receipt", receipt_seed(receipt_selector, &marker, &intent, &receipt)?, )?, - ]) + ]; + seeds.extend(malformed_seeds(&marker, &intent, &receipt)?); + seeds.extend(super::migration_recovery_seeds::seeds( + &marker, &intent, &receipt, + )?); + Ok(seeds) } fn receipt_seed( @@ -68,6 +73,42 @@ fn receipt_seed( prefixed(selector, &payload) } +fn malformed_seeds( + marker: &[u8], + intent: &[u8], + receipt: &[u8], +) -> Result, FuzzSeedError> { + let mut bad_marker = marker.to_vec(); + *bad_marker + .get_mut(17) + .ok_or_else(|| FuzzSeedError::violation("marker lacks version"))? = 3; + let mut bad_intent = intent.to_vec(); + *bad_intent + .get_mut(23) + .ok_or_else(|| FuzzSeedError::violation("intent lacks flags"))? = 1; + let mut bad_receipt = receipt.to_vec(); + *bad_receipt + .last_mut() + .ok_or_else(|| FuzzSeedError::violation("receipt lacks checksum"))? ^= 1; + Ok(vec![ + Seed::new( + "migration_format", + "unsupported-marker-version", + prefixed(0, &bad_marker)?, + )?, + Seed::new( + "migration_format", + "unsupported-intent-flag", + prefixed(1, &bad_intent)?, + )?, + Seed::new( + "migration_format", + "corrupt-receipt-checksum", + receipt_seed(2, marker, intent, &bad_receipt)?, + )?, + ]) +} + #[cfg(test)] mod tests { use super::{FuzzSeedError, MAX_SEED_BYTES, receipt_seed};