Issue #784: CI: extend the unsafe_crates job - Miri daily, Kani and fuzzing, failures reported and the checks documented

Open and answer this issue on GitHub.

ready · opened 2026-10-24 · updated 2026-10-24 · 0 comments

What this is about

Every unsafe the toolchain needs lives in a crate of its own under crates/unsafe/ (ADR-218): byte-blocks, c-boundary, c-text, checked-text, child-usage, crc32c, file-ring, low-bits, polled, proven-index, ref-or-box, shared-bytes, tether, text-key. Each one is its own workspace, and crates/nikaia-std forbids unsafe_code. So these crates are the only place where memory safety depends on hand-written reasoning instead of the compiler. Each crate’s README states that reasoning for every unsafe.

The owner’s goal is reliability that people outside the project can see. A check that runs only on someone’s machine does not count. It has to run in CI, regularly, and it has to be documented where readers look. The owner asked for Miri, and for fuzzing and Kani where they make sense and are possible, all built into the existing workflow rather than a second one.

Today

Why that is not enough

Decision (taken here)

To build

  1. Daily cron:
    • add 41 2 * * * beside 17 3 * * 1 in nikaia.yml;
    • pass github.event.schedule to ci-changes.sh: the daily cron sets only unsafe=true, the weekly one keeps all;
    • print rustc +nightly -V in the log of unsafe_crates, so a drift can be dated.
    • Test: a workflow_dispatch still turns everything on. Check the cron branch of ci-changes.sh locally by setting the variable by hand.
  2. Miri seeds: in check-unsafe-crates.sh, add -Zmiri-many-seeds=0..8 for shared-bytes and tether, under both aliasing models.
    • Test: the script passes locally and in unsafe_crates.
  3. Kani:
    • write the harnesses as #[cfg(kani)] mod proofs in each crate named above;
    • declare cfg(kani) in each crate’s [lints.rust] unexpected_cfgs, so clippy stays clean;
    • add --kani to check-unsafe-crates.sh, so a contributor runs it the same way as CI;
    • add the job unsafe_kani to nikaia.yml, with the Kani version pinned (cargo install --locked kani-verifier@<v>, cargo kani setup), and add it to promote’s needs;
    • state the bound of each harness in the crate’s README, next to the argument it checks.
    • Test: each harness passes. Weaken one precondition in a scratch change and see Kani produce a counterexample; do not commit that change.
  4. Fuzzing:
    • write the targets in crates/unsafe/<crate>/fuzz/, a crate of its own outside the crate’s workspace, so the workspace and its Cargo.lock stay as they are;
    • add --fuzz RUNS to check-unsafe-crates.sh;
    • add the job unsafe_fuzz to nikaia.yml and to promote’s needs. On a push it runs -runs=100000 -seed=1 with no cache. On the daily cron it runs -runs=1000000 with the corpus cached per target. A crash uploads the reproducing input as an artifact.
    • Test: each target runs its budget clean. Break the reference comparison in a scratch change and see the target find it; do not commit that change.
  5. Failure reporting: a step at the end of the scheduled run (issues: write, if: github.event_name == 'schedule'). On a failure in any job, it opens “unsafe crates: scheduled check failing” (label ready), or comments on it if it is already open, with the run link, the failing job and the nightly version. On a green run, it closes that issue.
    • Test: a workflow_dispatch input that forces a failure opens the issue, and the next green run closes it.
  6. Documentation:
    • a page in guide/ (and on the website, where the guide is published), “How the unsafe code is checked”, with the per-crate table from the decision above and a link to the workflow’s runs;
    • a link to it from README.md;
    • in ADR-218, in D2 or the consequences, name Kani and fuzzing beside Miri, and say which crates each one covers and why the others are left out;
    • in scripts/ci-changes.sh, say what the daily cron turns on.
  7. No version raise: this is CI, crates outside nikaia-std, the guide and a note in an ADR that decides nothing new for the language.

Back to Open Issues