Skip to content

v0.9.6 - Safety contract sealed

Pre-release
Pre-release

Choose a tag to compare

@jamesgober jamesgober released this 12 May 19:08
· 12 commits to main since this release

Release Notes for v0.9.6 - Unsafe audit and property tests

Version 0.9.6 - 2026-05-12

The safety-hardening pass that locks in the foundation. Every unsafe block in the crate (29 blocks across 6 files) now carries a // SAFETY: comment that names the syscall contract it relies on, demonstrates how local context establishes the preconditions, and cites the relevant man page or MSDN reference. docs/SAFETY.md joins the repo as the authoritative catalog of those invariants, grouped by category (mapping construction, advise, locking, atomic views, flush, platform shims, test helpers). Audit findings S1, S2, and S3 are now closed.

Alongside the comment overhaul, proptest 1.5 joins the dev-dependency set and three new integration test files (tests/proptest_bounds.rs, tests/proptest_atomic.rs, tests/proptest_flush.rs) exercise bounds-checking, atomic alignment, and FlushPolicy state transitions across roughly ten thousand randomized cases per cargo test run. The C1 regression scenario from the 0.9.5 audit is now under continuous property coverage. Setting PROPTEST_CASES=10000 runs the deep sweep, which the maintainer uses before tagging.

Highlights

  • 29 unsafe blocks audited. Every block in src/advise.rs, src/lock.rs, src/mmap.rs, src/utils.rs, src/atomic.rs, and src/watch.rs now has a SAFETY comment that states the syscall preconditions explicitly. Where the contract is platform-defined (POSIX madvise, mlock, msync, utime; Windows VirtualLock, VirtualUnlock, PrefetchVirtualMemory, GetSystemInfo), the comment cites the man page or MSDN page directly. Closes audit findings S1 (Windows windows_page_size), S2 (shallow comments throughout advise/lock/mmap), and S3 (lock-then-release-then-use-pointer pattern documented).
  • docs/SAFETY.md — the authoritative inventory grouped by category. Each section names the kernel surface, lists the invariants the crate establishes, links to the man page or MSDN reference, and explains how cross-process aliasing is handled (intra-process via parking_lot::RwLock, cross-process documented out-of-scope per REPS.md section 5.1).
  • Property test framework: proptest 1.5 pinned for MSRV 1.75 (default-features off; std, bit-set, fork, timeout opted in). 15 properties × 256-1024 cases per property covers (a) bounds-checking on as_slice / as_slice_mut / read_into / update_region / flush_range, (b) alignment on every atomic method including the slice variants, (c) FlushPolicy::EveryBytes mixed-write/flush_range invariants (the C1 regression scenario), EveryWrites triggering at N, and Manual never auto-flushing.
  • CI matrix fix: tests/atomic_view_resize_safety.rs (added in 0.9.5) now carries #![cfg(feature = "atomic")]. The matrix runs cargo test --no-default-features --features "<combo>" across 7 feature combos × 3 platforms; none of those combos include the atomic feature, so 21 shards failed compile on the 0.9.5 push. The full-build job uses --all-features and never caught it. One-line gate, full matrix green.
  • Node 24 migration: actions/checkout@v4 (Node 20, deprecated 2025-09-19) bumped to actions/checkout@v5 (Node 24) across all four CI workflow occurrences. Node 20 is forced to Node 24 starting 2026-06-02 and removed 2026-09-16; v5 is the recommended migration target.
  • Documentation reconciliation: docs/API.md and REPS.md walked end-to-end against the actual code. Stale claims corrected: MSRV 1.76 → 1.75 in API.md, version examples bumped to 0.9.6, atomic return types updated to the C3 wrapper forms (AtomicView<'_, T> / AtomicSliceView<'_, T>), stray character removed from the SegmentMut section, async section completed with update_region_async / flush_async / flush_range_async. REPS section 4 now reflects the actual surface: TouchHint { Never, Eager, Lazy }, as_slice_mut returning MappedSliceMut<'_>, the full FlushPolicy variant set, atomic_u32_slice added, lock API names corrected (lock/unlock/lock_all/unlock_all), ChangeKind { Modified, Metadata, Removed } aligned with the polling backend.

Bug fixes discovered by the new CI

The matrix CI on the 0.9.5 push surfaced one latent issue from that release that the all-features build had masked:

  • tests/atomic_view_resize_safety.rs compile failure. The test was authored as the C3 regression test in the 0.9.5 cycle. It uses mmap.atomic_u64(...) and mmap.atomic_u64_slice(...), both gated behind feature = "atomic". The test file itself was missing the crate-level #![cfg(feature = "atomic")] attribute. The 0.9.5 verification ran --all-features (which compiles the test fine) and --no-default-features (which builds the crate without compiling integration tests under partial feature flags), so the gap was invisible locally. The matrix CI runs cargo test --no-default-features --features "<combo>" for seven combinations ("", cow, locking, advise, cow locking, locking advise, cow locking advise) on Linux/macOS/Windows, and none of those combinations include atomic. Every one of the 21 shards therefore failed at compile time. The fix is a single line at the top of the test file; the deeper takeaway is that any new test depending on a feature-gated API must carry the matching crate gate.

Breaking changes

None in 0.9.6. The C3 atomic-view wrapper change shipped in 0.9.5 and is reflected in this release's documentation. Public API surface for 0.9.6 is purely additive at the implementation level (SAFETY comments are non-functional; property tests are dev-only; proptest is a [dev-dependencies] addition that does not affect downstream consumers).

Tests

  • 15 new property tests, minimum 256 cases per proptest! and 1024 cases on most (PROPTEST_CASES=10000 for the deep sweep before releases).
  • 99 unit + integration tests total under --all-features (84 pre-existing + 15 new property tests), 4 ignored (3 polling-watch tests gated on Windows mtime granularity, 1 hugepages fallback test).
  • CI matrix: 21 build-test shards (7 feature combos × 3 OS) all green on the fix-CI push. Code Quality (cargo fmt --all -- --check, cargo clippy --all-targets --all-features -- -D warnings) green. full-build (all-features check) and benchmarks (criterion under no-default and all-features) green. Local MSRV verification on cargo +1.75 build --all-features also clean.
  • Banned-words scan (per .dev/DIRECTIVES.md section 9): zero hits for comprehensive, robust, seamless, leverage across src/, tests/, examples/, benches/, docs/, README.md, CHANGELOG.md.

Notes

  • MSRV unchanged at Rust 1.75. proptest 1.5 was specifically chosen because the 1.x line holds 1.66+, well under our floor.
  • No new runtime dependencies. proptest is a [dev-dependencies] addition; downstream crates that depend on mmap-io see no change to their dependency graph or compile times.
  • No public API changes. The only Cargo.toml change is the version bump and the dev-dep addition. All breaking changes for this milestone are documentation reconciliation, not code-level.
  • The directives policy itself caught the Node 24 migration: .dev/DIRECTIVES.md section 5 had actions/checkout@v5 (Node 24-compatible). Older @v4 is acceptable through 2026-Q3 but should be migrated. as a standing item. The GitHub deprecation warning on the 0.9.5 push triggered the move.

Deferred (with documented reason)

  • Iterator zero-copy redesign (audit H1) is the headline item for 0.9.7. ChunkIterator / PageIterator currently allocate a fresh Vec<u8> per chunk; the redesign returns a MappedSlice<'_> wrapper that holds the read guard for the iterator's lifetime. This is a breaking change to the iterator API and is scoped with MappedSlice for RW reads (audit H4) alongside.
  • Lock-free reads for RW mappings (audit H3) — the current RwLock<MmapMut> serializes all RW access through a single lock. Moving to arc-swap or equivalent is a substantive design change; tentatively scoped to 0.10.0 or 1.0.0.
  • Native watch backends (inotify / FSEvents / ReadDirectoryChangesW) ship in 0.9.9 and unignore the three Windows watch tests that polling cannot serve reliably.
  • Fuzz suite (cargo-fuzz) and MIRI runs on the atomic module land in 0.9.10 as part of the pre-1.0 stabilization.
  • 0.9.8 is the async-polish release: cancellation-safety documentation per async method, possible read_into_async / read_slice_async additions, and the for_each_mut return-type flatten (audit E4).

Full Changelog: v0.9.4...v0.9.6