No description
  • Python 88.1%
  • Shell 10.7%
  • C 1.2%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Satya Benson df4f6c6d97 Add MIT license and a licensing section to the README
The repository's own files are MIT. The patches keep the licenses of the
projects they modify; the README lists each upstream.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-05 15:30:47 -04:00
config Add contained APFS syzkaller runtime and corpus validation 2026-09-17 13:37:03 -04:00
diagnostics Expand 16K page-size qualification 2026-09-11 10:09:42 -04:00
packaging/fedora package resumable APFS root relabel recovery 2026-09-11 21:05:30 -04:00
patches Add compressed-read APFS syzkaller seed 2026-09-17 15:12:35 -04:00
reports Record extended inline APFS fuzzing 2026-09-17 15:55:35 -04:00
scripts Require exact safety options for syzkaller APFS corpus 2026-09-24 10:51:01 -04:00
selinux qualify SELinux-enforcing APFS root 2026-09-11 19:10:15 -04:00
syzkaller/sys/linux lab: prepare pinned APFS syzkaller descriptions 2026-09-17 09:12:22 -04:00
tests Require exact safety options for syzkaller APFS corpus 2026-09-24 10:51:01 -04:00
.gitattributes Add APFS crash barriers and coexistence campaigns 2026-09-10 13:29:30 -04:00
.gitignore Set up reproducible APFS macOS Linux round-trip lab 2026-09-10 11:54:44 -04:00
LICENSE Add MIT license and a licensing section to the README 2026-10-05 15:30:47 -04:00
README.md Add MIT license and a licensing section to the README 2026-10-05 15:30:47 -04:00

APFS shared-container research lab

The target is Asahi Linux using a dedicated root volume inside an existing macOS APFS container, sharing its free space while preserving macOS data, bootability, snapshots, and recovery. Only an additional EFI system partition should be needed.

This repository currently provides an image-only research lab for clean unmount, bounded crash-prefix replay, snapshot preservation, alternating macOS/Linux writers, shared-space exhaustion, and native-16-KiB APFS-root boots with an offline DNF5 lifecycle. A native Fedora integration RPM now provides a guarded status/relabel/recovery interface and passes interrupted relabel resume. It does not qualify the driver for a real Mac's internal container.

Run it

On this Mac, prerequisites and the apfs-lab QEMU/Lima VM are installed. From this directory:

uv run python scripts/lab.py run
uv run python scripts/rootfs.py
uv run python scripts/package.py
uv run python scripts/rpm-package.py
uv run python scripts/fedora-package.py
uv run python scripts/page-size.py
uv run python scripts/asahi16-xfstests.py
uv run python scripts/asahi16-xfstests.py --debug
python3 scripts/asahi16-truncate-lock.py \
  --module-path /opt/apfs-lab/asahi16-kasan/apfs.ko
python3 scripts/asahi16-fiemap473.py \
  --module-path /opt/apfs-lab/asahi16-kasan/apfs.ko
python3 scripts/asahi16-cgroup-stress.py \
  --cycles 128 --payload-mib 8 --throttle-bps 1048576
python3 scripts/asahi16-multivolume-cgroup.py --payload-mib 8
uv run python scripts/campaign.py --cycles 3 --snapshots --pressure
uv run python scripts/crash.py
uv run python scripts/crash.py --metadata
python3 scripts/apfuse_oracle.py \
  --image artifacts/apfs-root-boot-fallback/20260911T233107Z/root-final.raw
python3 scripts/asahi16-io-fault.py \
  --template artifacts/asahi16-kasan-template/20260912T164539Z \
  --module-sha256 845f312ffeeb57282dcd37428d31df9d0918fb7c15e9a67ac496ee5aef8506c4
python3 scripts/asahi16-crash-mutation.py \
  --template artifacts/asahi16-kasan-template/20260912T164539Z \
  --module-sha256 3b126a1efdac6584e83ec9163271941972745092a95b13e83bb21cd357ddd5bd \
  --campaign tail-subsets
python3 scripts/asahi16-crash-mutation.py \
  --template artifacts/asahi16-kasan-template/20260912T164539Z \
  --module-sha256 d326d1a24bbcc803dfbb2e16f101fcc9de2f18176761839ed18bf930cf0980b4 \
  --campaign triple-omissions --shard 1/128 \
  --capture-from artifacts/asahi16-crash-mutation/20260917T032543Z/report.json
python3 scripts/verify_crash_shards.py \
  artifacts/asahi16-crash-mutation/20260917T033357Z/report.json
python3 scripts/run_crash_shards.py \
  --template artifacts/asahi16-kasan-template/20260912T164539Z \
  --module-sha256 d326d1a24bbcc803dfbb2e16f101fcc9de2f18176761839ed18bf930cf0980b4 \
  --campaign triple-omissions --shard-count 128 --max-shards 1 \
  --capture-from artifacts/asahi16-crash-mutation/20260917T032543Z/report.json
python3 scripts/analyze_write_epochs.py \
  --log-gzip artifacts/asahi16-rootfs/20260917T123519Z/results/writes.raw.gz \
  --require-no-overlaps
python3 scripts/prepare_syzkaller_apfs.py
python3 scripts/asahi16-crash-mutation.py \
  --template artifacts/asahi16-kasan-template/20260912T164539Z \
  --module-sha256 d326d1a24bbcc803dfbb2e16f101fcc9de2f18176761839ed18bf930cf0980b4 \
  --campaign post-flush-lies \
  --capture-from artifacts/asahi16-crash-mutation/20260917T032543Z/report.json
python3 scripts/asahi16-crash-mutation.py \
  --template artifacts/asahi16-kasan-template/20260912T164539Z \
  --module-sha256 d326d1a24bbcc803dfbb2e16f101fcc9de2f18176761839ed18bf930cf0980b4 \
  --campaign double-sector-tears --shard 2/1024 \
  --capture-from artifacts/asahi16-crash-mutation/20260917T032543Z/report.json
python3 scripts/asahi16-crash-mutation.py \
  --template artifacts/asahi16-kasan-template/20260912T164539Z \
  --module-sha256 d326d1a24bbcc803dfbb2e16f101fcc9de2f18176761839ed18bf930cf0980b4 \
  --campaign omission-plus-tear --shard 2/512 \
  --capture-from artifacts/asahi16-crash-mutation/20260917T032543Z/report.json
python3 scripts/asahi16-allocation-fault.py \
  --template artifacts/asahi16-kasan-template/20260912T164539Z \
  --module-sha256 f65d4f9a984309eebaa3c083b3a7e8bc9747f9fdadf3101dd103a348e3a31d07 \
  --intervals 1 --workloads first-write \
  --required-function apfs_read_single_ip_bitmap --stacktrace-depth 4 \
  --allow-untriggered --noatime
python3 scripts/allocation_site_inventory.py
python3 scripts/allocation_coverage.py \
  --inventory artifacts/allocation-site-inventory/20260917T002441Z/report.json \
  --reports \
    artifacts/asahi16-allocation-fault/20260914T191404Z/report.json
python3 scripts/build-apfsprogs-candidate.py
python3 scripts/build-asahi16-candidate.py \
  --activate-profile asahi16-kasan
python3 scripts/build-compat-candidate.py
python3 scripts/compat4k-smoke.py \
  --build-report artifacts/compat-module/20260914T162542Z/report.json

On a fresh Apple Silicon Mac:

brew install qemu lima
uv run python scripts/lab.py bootstrap
uv run python scripts/fedora_lab.py bootstrap
uv run python scripts/lab.py run

Python 3.11 or later is required; the scripts use only its standard library. Bootstrap checks out exact source revisions, creates a dedicated Ubuntu ARM64 VM, installs build dependencies, and builds the APFS module, all four apfsprogs tools, and fstests (including fsx and fsstress). It records the kernel, kernel binary hash, configuration, package versions, and VM-tool versions. Existing clean source checkouts must match the pins; bootstrap refuses to reset modified or differently versioned source trees.

Debug/KASAN candidate modules must be built with scripts/build-asahi16-candidate.py. It creates a new guest build directory from the pinned host source and complete ordered patch series, retains the source archive, module, build log, modinfo, and hash manifest, and backs up an active disposable-VM profile before replacing it. This avoids accidentally qualifying a module assembled from a stale mutable guest tree. scripts/build-compat-candidate.py applies the same complete manifest in a fresh directory and compiles it against the disposable Ubuntu 6.8/4 KiB guest, retaining a separate module, source archive, build log, and hash report. This is a compile-compatibility gate only. scripts/compat4k-smoke.py binds a selected report to its exact module and adds a fresh-image truncate/fsx/remount/checker runtime gate without widening the result to macOS return or physical storage.

The allocation inventory/coverage pair keeps source enumeration separate from runtime evidence. The current campaign directly injects 46 of 53 source allocation sites across 3,025 phases. Six are unobserved, not proven unexecuted, and one is observed only at an ambiguous multi-allocation function. Five of the six are compiled-out compatibility branches; the other requires a pre-normalization-insensitive APFS fixture. Inlined and multi-site mappings must match a recorded runtime return offset; interval ordinals alone are not accepted as source-site evidence.

The third apfsprogs patch adds bounds-checked codec-8 LZVN validation, using an attributed decoder adapted from Aurora Silicon's apfuse. A fresh build from the pinned source and complete ordered patch stack is produced by scripts/build-apfsprogs-candidate.py; individual 16 KiB runs bind that exact binary through its retained build report rather than changing a VM-global checker.

The VM uses four CPUs, 6 GiB RAM, and a 64 GiB virtual root disk. It has no host directory mounts. Image files are copied into the guest; physical host disks are not passed through. Guest sudo is used to load the module and mount loop devices. Host formatting targets are derived only from attaching a newly created file under artifacts/.

What a run does

  1. Creates a 4 GiB raw GPT image with a case-sensitive APFS-Linux volume and a case-insensitive APFS-Sibling volume using macOS's own tools.
  2. Populates both with deterministic files, hard links, a symlink, modes, and a native xattr; records baseline tree manifests and volume/container identities.
  3. Detaches the image and retains it unchanged as baseline.raw.
  4. Copies a compressed version into the VM and creates a separate writable copy.
  5. Attaches that copy as a loop disk, discovers its APFS partition by GPT type, and runs apfsck before mounting Linux's volume with readwrite,cknodes,vol=0.
  6. Exercises writes, fsync, hard links, symlinks, replacement rename, open-after-unlink, mmap across a 4 KiB boundary, a sparse file, and SQLite. Runs 1,000 fsx operations with fixed seed 12345. Unsupported fsx operations are reported in the command log; an operation count is not a claim that every optional Linux filesystem feature works. Partial-range cloning is explicitly excluded with -J: the pinned driver supports whole-file clones only. Use run --full-fsx to include that known failing case; see reports/range-clone-gap.md.
  7. Unmounts, remounts read-only, compares Linux contents, unmounts, and runs apfsck again. Captures kernel logs and detaches the loop device.
  8. Exports the resulting image. The host refuses export if a guest loop device still references it.
  9. Attaches the returned image read-only to macOS, runs diskutil verifyVolume (Apple's fsck_apfs) for both volumes, and compares seeded contents/xattrs and Linux-generated contents, types, modes, and link counts.
  10. Checks that macOS validation did not change the image hash, and writes a JSON report. A checker failure or mismatch makes the run fail.

scripts/rootfs.py uses the same containment and macOS validation path, but replaces the fsx workload with the Linux root semantic gate documented in reports/rootfs-qualification.md. Its diagnostic status and semantic root_ready result are deliberately separate.

No repair is performed. The original baseline and post-Linux image are retained. The image is never simultaneously mounted by both operating systems.

Artifacts and reproducibility

Each run is stored under artifacts/runs/<UTC timestamp>/:

  • report.json: combined result, checks, source pins, script hashes, environment.
  • inputs/: the exact controller/worker/manifest/workload scripts for that run.
  • host.log: host/transfer commands and their output.
  • baseline.raw, after.raw, and compressed transfers: reproduction images.
  • baseline-trees.json, container-before.json, container-after.json.
  • results/guest.json, results/commands.log, results/dmesg.txt.
  • results/expected-linux.json: expected post-write filesystem contents.

Large images and third-party checkouts are ignored by Git. Compact findings live in reports/. There is no automatic deletion of failed-run evidence. The VM also retains its run directories under ~/apfs-lab/runs/. Budget approximately 8 GiB of allocated host/guest storage per successful run until sparse transfer and retention management are added.

The cloud base image is pinned by SHA-256 in config/lima.yaml; driver, apfsprogs, log-writes, fstests, and syzkaller revisions are pinned in scripts/lab.py. The optional host-side apfuse second oracle is independently pinned there at 9bcffa1cc7a3b73d094dc62e7202bf3ec9d90cae. It builds only the inspection CLI, attaches only regular retained images under artifacts/ read-only, runs info, verify, and space, and requires the whole-image hash to remain unchanged. Pass --apfuse-oracle to scripts/lab.py run to make that optional cross-implementation result a required gate; the FUSE writer is never invoked. Research patches under patches/ are applied after source extraction, hashed in environment.json, and kept separate from the pinned upstream checkout. The built module, running kernel binary, and config are copied to artifacts/ at bootstrap and hashed.

scripts/fedora_lab.py bootstrap creates a separate apfs-fedora VM from the pinned Fedora Cloud 44 ARM64 image, updates it, builds the same patched module against its native kernel, and records RPM 6 and DNF5 versions. The native package gate downloads and retains a Fedora/Asahi closure, then disconnects the transaction network namespace and uses only its generated local repository. See reports/fedora-asahi-dnf5-qualification-20260911.md. APT and Fedora package versions are recorded, and each passing native-package run retains its exact RPM closure, but the upstream repositories are not snapshotted. Rebuilding either whole VM later is not bit-for-bit reproducible. Retain the VMs, kernel artifacts, and passing RPM closure with the baseline.

VM operation

limactl shell apfs-lab
limactl shell apfs-fedora
limactl stop apfs-lab
limactl stop apfs-fedora
limactl start -y apfs-lab
limactl start -y apfs-fedora

The module is loaded by a run command and is not configured to auto-mount anything. Both VM roots remain on their conventional Linux filesystems. Serial and host-agent logs are available under ~/.lima/apfs-lab/ and ~/.lima/apfs-fedora/.

The first kernel is Ubuntu 6.8.0-139-generic, with 4 KiB pages. This provides a baseline supported by the pinned APFS module; it is not the final Asahi kernel. The guest includes fio, gdb, SQLite, RPM/DNF repository tools, ACL/xattr utilities, and device-mapper tools. Its kernel supports dm-log-writes, dm-flakey, dm-delay, and UBSAN. fstests is built at ~/apfs-lab/src/xfstests. The native 16 KiB harness uses two fresh sparse loop images, private APFS mkfs/fsck dispatchers, explicit tests, checker gates, and leak-free teardown. Its smoke tranche passes 13 tests with one capability skip; the multiprocess phase of generic/013 exposed a transaction/folio lock-order deadlock that now has six fresh-image regression passes. Wider selection adds 24 passes; unsupported partial-range clones remain explicit failures. Active Generic KASAN reproduced the intermittent generic/076 writeback stall as a transaction-gate deadlock, now corrected by patch 0020. The Fedora Asahi debug flavor also exposed and drove fixes for a mount lock cycle and unsafe cross-volume transaction reuse. The final stack passes the smoke and wider tranches with KASAN and lockdep enabled, plus 200 concurrent mount/write/unmount cycles across two volumes in one container. A follow-on early-generic tranche found and corrected a missing-ACL-delete bug and a Linux 7.1 mmap/transaction lock inversion; the combined 22-patch candidate passes its 15-test smoke/ACL set and all six runnable tests from the 25-test discovery batch under active Generic KASAN. Its clean-build manifest, DNF5 rerun, and fresh file-data and metadata crash campaigns also pass; the provenance correction and exact evidence boundary are recorded in the xfstests report. A later mmap/fsx tranche found a buffered-write folio/transaction deadlock and three distinct 16 KiB EOF/extent-cache defects. The resulting clean 26-patch build passes five fresh generic/346 runs, ten fresh generic/363 runs totaling one million fsx operations, and all eight runnable tests from generic/351–400. Exact failure traces and the guarded replay tool are retained. A targeted truncate contention lane then reproduced the residual truncate_setsize()/readahead deadlock. The clean 35-patch build removes the audited tail, truncate, short-write, and mmap-fault semaphore-to-folio waits, then closes a buffer-list race exposed by the 4 KiB compatibility lane and coalesces physically contiguous block mappings. It also keeps live and reloaded VFS block accounting consistent for sparse and 4 GiB files, advances ctime for xattr changes, and reports transient pre-transaction data EIO without leaving the container read-only after the device heals. It passes 1,000 traced contention cycles, the targeted and default KASAN xfstests lanes, 86 passing outcomes from the bounded generic/401–750 census, and a separate 4 KiB runtime smoke. The patched formatter also refuses recognized existing filesystems unless explicitly forced. One explained open census result and ten preclassified safety/platform exclusions remain explicit. See reports/asahi-16k-xfstests-20260911.md and reports/asahi-16k-lockdep-20260912.md and reports/asahi-16k-kasan-20260912.md and reports/asahi-16k-mmap-eof-20260913.md and reports/asahi-16k-truncate-lock-20260913.md and reports/asahi-16k-generic-census-20260913.md.

Next engineering gates

  1. 16 KiB page support: the expanded boundary lane, full Fedora root semantics, and native Fedora/Asahi DNF5 workload now pass on official Asahi kernel 7.1.13-402.asahi.fc44.aarch64+16k, including 10,000 fixed-seed fsx operations, real package scriptlets and history, clean Linux remount/checker gates, and macOS return checks. Four multi-block-page extent/truncate bugs were found and corrected. A three-generation coexistence campaign with a Linux-created sibling snapshot and ENOSPC recovery also passes on that kernel. Fresh file-data and metadata crash-prefix campaigns also pass after adding fallback from incomplete checkpoints. A real Asahi kernel package transition, old-generation rollback, and recovery from a SIGKILL inside kernel-core %posttrans now pass on the live APFS root with SELinux enforcing. The same recovered A/B generations also pass automatic UEFI fallback and promotion under enforcement, with zero-AVC audits and macOS return validation. This required an APFS filesystem policy declaration and LSM inode-label initialization in the driver. The candidate build remains explicitly experimental. Active Generic KASAN reproduced the open writeback stall as a commit/writeback transaction-gate deadlock; patch 0020 now passes repeated reproduction, smoke, wider-fstests, multi-volume, and final crash-prefix gates. Clean manifest-built release-kernel modules also pass repeated APFS-root update/recovery and automatic-fallback campaigns; deeper fault models, a full fstests run, and hardware lanes remain. Deterministic complete-bio write and uncached-read EIO now pass recovery, checker, macOS, and independent read-only apfuse gates under the final 16 KiB KASAN build; torn and reordered writes remain outside that EIO result. Separate pre-flush mutation lanes now pass 70 exhaustive single-persisted/single-omitted-write states, 238 exhaustive single-write sector tears, all 256 persistence subsets of an eight-write tail window, all 595 unordered two-write omissions from a 35-write epoch, and the original five-case regression while preserving the preceding fsync promise. Task- and APFS-stack-scoped one-shot page and slab allocation failures also pass recovery and return validation across the complete configured interval domain 1 through 128 for each allocator. A further clean 26-patch follow-on fixes the generic/346 folio/transaction deadlock and generic/363 large-page EOF pollution; its exact Generic-KASAN module passes repeated fresh-image regressions and the release variants repeat all kernel, fallback, and seeded package-recovery lanes. The clean 31-patch successor additionally closes the audited tail, truncate, short-write, and mmap-fault semaphore-to-folio waits; it passes 1,000 traced contention cycles, the targeted/default KASAN xfstests lanes, and a 4 KiB truncate/fsx/remount smoke, and compiles against Ubuntu 6.8 plus both retained Fedora/Asahi 7.1 release kernels. The clean 41-patch successor attributes buffered and mmap-dirtied file-data bios to the dirty folio's effective I/O cgroup while charging shared checksummed container metadata to the root cgroup, without claiming generic BDI cgroup-writeback support. generic/563 passes six consecutive isolated runs and a final exact-hash combined KASAN pressure lane. A pressure-sensitive metadata-read allocation failure found during that work is corrected by preserving __bread_gfp() allocation guarantees. The same manifest passes the default and targeted 16 KiB KASAN lanes, a 4 KiB runtime smoke, and compile checks for both retained Fedora/Asahi release kernels. Its exact -402 release build also passes isolated generic/563 and the 14-test default lane on the matching non-debug kernel, plus the eight historically sensitive targeted tests. See reports/asahi-16k-cgroup-writeback-20260914.md. The minimal 42-patch follow-on replaces two hot folio-first transaction retries with a one-tick scheduler backoff. It reduces generic/464 from an interrupted multi-minute release-kernel drain to 51–55 seconds while retaining the qualified transaction and cgroup rules. A broader timer/bio experiment was rejected after two intermittent read-only failures. The minimal candidate passes repeated release runs, the 22-test release and active-KASAN lanes, 4 KiB compatibility, and the exact cgroup and two-volume macOS/apfuse return gates. See reports/asahi-16k-transaction-batching-20260914.md. A source-directed allocation follow-on uses live function ranges and calibrated kernel stack filters to inject 46 distinct direct allocation callers in the final 47-patch module. It adds current-kernel mount setup, protected-file-key ioctls, and inline LZFSE metadata/decoder coverage. It found and corrected stale hardlink ENOMEM propagation and a fatal spaceman error-pointer dereference; exact corrected reproducers pass checker/remount, Generic-KASAN/lockdep, read-only macOS, and apfuse gates. See reports/asahi-16k-direct-allocation-fault-20260916.md. The subsequent 45-patch candidate fixes the deterministic generic/084 hardlink/unlink race without relaxing transaction aborts. It recognizes the VFS-valid case where the looked-up source dentry was removed but another sibling link still preserves the inode, validates that surviving sibling record, and avoids converting the stale name. Six isolated KASAN repetitions, wider hardlink and default lanes, and the 4 KiB compatibility runtime pass. See reports/asahi-16k-hardlink-race-20260916.md. The following 47-patch candidate closes the safe generic/751–801 census after two newly reproduced defects. It restores the mapping constraints and no-fail allocation guarantee lost by the modern bdev_getblk() compatibility path, then reconciles page-cache dirty and writeback tags after APFS's hand-submitted data-buffer I/O completes. The bounded range now records 18 passes, 30 explicit capability skips, and three preclassified hardware/topology exclusions. Three isolated repetitions of each reproducer, the combined historical and default KASAN lanes, both retained Fedora/Asahi release-kernel builds, and the 4 KiB compatibility runtime all pass. See reports/asahi-16k-generic-751-801-20260916.md. The remaining generic/473 golden-output mismatch is now classified with an unfiltered FIEMAP probe. Five fresh images show the queried data region is physically split into 8 KiB and 56 KiB runs; reporting one 64 KiB physical extent would be false. The mappings are stable across unmount/check/remount with clean KASAN/lockdep and checker gates. The test remains an explicit explained non-pass, not a rewritten pass. See reports/asahi-16k-fiemap473-20260916.md. A dedicated nested-cgroup follow-on then passes 849 live process migrations, 608 remove/recreate-before-fsync CSS lifetimes, exact buffered, mmap, and alternating-leaf byte attribution, and both leaf and ancestor io.max enforcement. Six full KASAN campaigns reproduce 8 MiB fsync times of about 8 seconds at 1 MiB/s and 4 seconds at 2 MiB/s, versus 0.02–0.03 seconds unthrottled, with clean checkers and teardown. An offline-owner complete-bio EIO transaction abort also passes checker, remount, macOS-return, and apfuse gates. See reports/asahi-16k-cgroup-migration-throttle-20260914.md. Two final-input follow-ons additionally preserve exact owner attribution through proactive reclaim and memory.high direct reclaim, then apply simultaneous 1 MiB/s read and write limits with exact 8 MiB counters and verified read content. KASAN, lockdep, checkers, and cleanup remain clean. Two subsequent 128-cycle campaigns survive a dirtying task's hard-memcg OOM, force roughly 146 MiB of anonymous state into disposable zram without an OOM while retaining exact APFS ownership, and enforce concurrent 32-read/ 64-write IOPS limits with exact device counters and verified content. A post-stress generic/563 control remains clean. Two exact-input extensions also use memory.oom.group=1 to kill a dirty writer and live sibling together while an external committer safely completes the exact owner-attributed data; all final gates remain clean. The next exact-input pair extends that group kill across sibling descendant leaves, removes a swap-backed owner below a finite swap cap before fsync, and proves stricter ancestor IOPS limits override permissive leaf limits. The loop-on-Btrfs harness also now rejects backing-file resource errors exposed by synthetic resident-cap pressure. A subsequent pair approaches a finite 16 MiB swap cap, OOM-kills and removes the dirty owner, and again completes exact attribution from the external committer with clean final gates. Event-driven memory.swap.high coverage then stops within one 16 KiB allocation of the first throttle event, holds the swapped working set for two seconds, and commits exact owner-attributed data without OOM. A further two-volume campaign starts independently throttled writers on both volumes of one macOS-created container. Four full runs demonstrate the expected shared-container serialization, bounded physical CoW amplification, negligible committer attribution, exact content on Linux and macOS, and clean apfuse/checker/KASAN/lockdep gates. See reports/asahi-16k-multivolume-cgroup-20260914.md. Two final exact-input runs add simultaneous 128/256-write-IOPS owner limits on the two volumes, retain separate attribution across their shared commit, and pass exact Linux/macOS content plus apfuse return validation. See reports/16k-page-audit.md and reports/asahi-16k-rootfs-20260911.md and reports/asahi-16k-coexistence-20260911.md.
  2. Broader semantics: the first root requirements matrix, Linux xattr/ACL/security gate, deterministic dpkg lifecycle, offline RPM/DNF4, and native Fedora/Asahi DNF5 scriptlet lifecycles pass on 4 KiB clean-unmount fixtures. Signed Asahi kernel-core/modules transactions, APFS-aware dracut generation, rollback, and interrupted-update repair now pass under the 16 KiB kernel. SELinux-enforcing package update, interruption, recovery, and automatic boot-entry fallback now pass through a real ARM64 UEFI/GRUB FAT boot volume and APFS-root userspace handshake. The image-only installer plan and first native fstests test/scratch setup are complete. The transaction/folio lock inversion, mount lock ordering, cross-volume transaction isolation, and writeback reentry now pass their active-KASAN and debug-kernel reproducers. Deeper filesystem and fault-injection coverage remain required before authorizing any mutation-capable installer. A two-boot Fedora root proof with a live offline DNF5 lifecycle now passes. See reports/rootfs-qualification.md and reports/package-manager-20260911.md and reports/rpm-dnf-qualification-20260911.md and reports/fedora-asahi-dnf5-qualification-20260911.md and reports/selinux-enforcing-apfs-root-20260911.md.
  3. Crash testing: the first file-data and Linux-metadata flush-aware ordered-prefix campaigns passed after adding explicit checkpoint cache barriers on the original 4 KiB fixtures. A fresh representative-kernel campaign exposed a general checkpoint-recovery defect. Checkpoint selection now validates object-map, checkpoint-map, and ephemeral-object dependencies and falls back to an older complete descriptor. Fresh official-Asahi-kernel file-data (30/30) and latest metadata (63/63) prefix campaigns pass Linux and read-only macOS validation under active Generic KASAN. A first deterministic complete-bio EIO campaign also passes write-error propagation, recovery, post-fault writes, checker, read-only macOS, and independent apfuse gates. Bounded-exhaustive second-transaction pre-flush mutation lanes now pass 70 single-persisted/single-omitted-write states and 238 single-write internal sector tears, all 256 persistence subsets of an eight-write tail window, all 595 unordered two-write omissions from a 35-write epoch, plus the original five-case regression. Task- and APFS-stack scoped allocation failures pass 256 injected path-create phases and 530 additional injected open-resource phases across configured intervals 1–128; 238 discovery phases that found no qualifying allocation are labeled untriggered. Three namespace/symlink follow-ons add 569 triggered rename, hardlink/unlink, and symlink-read failures across 768 further phases, with every checker and Linux/macOS/apfuse return gate passing. Large-xattr deletion, truncate, and ACL conversion add another 539 triggered failures across 768 full-domain phases with the same clean gates. Expand this to additional workload/source-site enumeration. A reusable fingerprinted 34-write capture now passes the first two 47-state shards of all 5,984 unordered triple omissions; 126 of 128 shards remain before that exact model is exhaustive. A complete 53-state single-write post-FLUSH storage-lie lane classifies 33 strict containment passes and 20 failures; this is a contract-violation damage envelope, not a durability pass. All retained pre-FLUSH epochs have pairwise disjoint physical ranges, proving all fixed-membership and fixed-tear reorderings byte-equivalent. The first three reusable double-tear shards pass 81 of 27,489 exact states. Overlapping write workloads and the remaining multi-tear shards remain. A marked writeback-pressure trace adds 12,895 writes across 18 completed stable epochs and one trailing interval; exact per-epoch analysis finds zero overlaps, while correctly rejecting the two pressure passes as separated by stable boundaries rather than a same-epoch reorder test. The first three omission-plus-tear shards also pass 48 of 7,854 exact mixed states. Forced host-command watchdog tests and a live deadline-enabled three-oracle smoke pass; deeper watchdog fault injection remains. A pinned linux/arm64 syzlang overlay now generates syz_mount_image$apfs, and a custom 16 KiB Generic-KASAN+KCOV kernel passes both an exact-seed smoke and a strict 120-second APFS-module-focused manager campaign. A subsequent 600-second run completed 1,218 executions with 8,036 focused coverage units and no fatal kernel finding or resource leak. Its three retained corpus programs replayed deterministically: the original image mounted, while two malformed mutations were rejected consistently by Linux, native macOS fsck_apfs, and apfuse. Bounded extraction preserved every image hash, automatic hash-identified corpus retention passes end to end, and no attachment or VM resource leaked. A second metadata-rich seed also passes exact smoke and a 410-execution focused campaign. One mutation mounted only after syzkaller removed the intended readwrite,cknodes options; macOS and apfuse rejected its damaged fsroot, so it is explicitly not a safety-mode survivor. A normalized retry of that exact image with readwrite,cknodes,vol=0 now fails closed at the first bad-checksum node, leaves the image byte-identical, and leaks no mount, loop, or module. Additional seed diversity and surviving-mutation durability gates remain. A third macOS-created compression seed now forces an exact 192,726-byte read through mount/open/read/close. A lab-only syzkaller patch raises its coverage-output area from 6 to 14 MiB so all four calls retain KCOV; the exact smoke and a 409-execution focused campaign pass, and four retained original-byte programs pass deterministic Linux replay plus native macOS and apfuse validation. A separate controlled inline codec-11 seed also passes exact read smoke and a 461-execution campaign with nine retained programs; eight valid-option mounts succeed and one corrupted-option variant is cleanly rejected. All nine unchanged images pass both read-only oracles. Identical extracted partitions now share one reported image artifact instead of consuming one full disk copy per syscall variant. A follow-on 600-second inline campaign completes 995 executions and retains seven programs with no kernel finding or resource leak. Four exact-image, reviewed-option variants pass Linux and both read-only oracles. Three variants alter two partition images only after losing or corrupting the required safety options; Linux accepts those unchecked mounts, while macOS and apfuse reject their container or spaceman checksums. They remain explicitly outside the safety and durability claim. See reports/crash-barrier-20260910.md and reports/metadata-crash-20260910.md and reports/asahi-16k-crash-20260911.md, reports/asahi-16k-lockdep-20260912.md, and reports/asahi-16k-kasan-20260912.md, and reports/asahi-16k-io-fault-20260913.md, and reports/asahi-16k-crash-mutation-20260913.md, and reports/asahi-16k-post-flush-lies-20260917.md, and reports/asahi-16k-reorder-equivalence-20260917.md, and reports/asahi-16k-double-tears-20260917.md, and reports/asahi-16k-omission-plus-tear-20260917.md, and reports/crash-campaign-watchdogs-20260917.md, and reports/crash-shard-resume-and-overlap-search-20260917.md, and reports/syzkaller-apfs-preparation-20260917.md, reports/syzkaller-apfs-runtime-20260917.md, reports/syzkaller-apfs-safety-options-20260917.md, and reports/syzkaller-apfs-compression-seed-20260917.md, and reports/asahi-16k-allocation-fault-20260913.md.
  4. Real coexistence fixtures: synthetic snapshots, alternating writers, and shared-space ENOSPC recovery have passed. Add native macOS-created snapshots, encrypted siblings, volume quotas/reserves, and system volume groups. Current fixtures are ordinary data volumes, not a macOS installation or bootable group.
  5. Platform qualification: fixtures from supported released macOS versions, then a dedicated recoverable Asahi-supported Mac for internal-storage and installer/boot/recovery testing. Current host fixtures come from macOS 27.0.

The first Stage 7 boot proof is documented in reports/apfs-root-boot-20260911.md. It copies a full Fedora root into a fresh 8 GiB detached APFS container, validates both volumes from macOS, mounts that volume as / under official Asahi kernel 7.1.13-402.asahi.fc44.aarch64+16k, and pivots through dracut into systemd. The live APFS root completes an offline DNF5 install/upgrade/remove/reinstall cycle with RPM scriptlets, powers off, boots again to verify persisted state, then passes native Linux and read-only macOS checks. It is a QEMU proof only: no bootloader was installed and no physical disk was passed through.

The follow-on Stage 7 kernel lane is documented in reports/kernel-update-recovery-20260911.md. It transitions between signed Asahi 7.1.13-401 and -402 kernel packages, boots both exported APFS-aware initramfs generations, rolls back to the old generation, cuts power inside the new kernel's real %posttrans kernel-install add, repairs from the old generation, and boots both recovered generations. GRUB's inability to probe APFS is avoided by keeping kernels, initramfs images, and the durable selector on a conventional boot volume.

The automatic selector lane is documented in reports/automatic-boot-fallback-20260911.md. A standalone ARM64 GRUB image persists the candidate attempt before launch, automatically falls back from a failed B boot to A, promotes B only after a successful APFS-root userspace handshake, and then selects B persistently. The final FAT fsck, native APFS inspection, and macOS return validation all pass. This remains a contained QEMU proof rather than actual Asahi/m1n1/U-Boot or physical recovery testing.

The combined enforcing lane is documented in reports/selinux-enforcing-update-fallback-20260911.md. It repeats the real Asahi kernel transaction, rollback, SIGKILL, repair, and automatic UEFI fallback sequence with SELinux enforcing on every successful APFS-root boot. Both final full-tree audits report zero AVC denials.

The packaged recovery lane is documented in reports/integration-package-relabel-recovery-20260911.md. The native Fedora RPM installs apfs-rootctl, the SELinux APFS filesystem declaration, and a systemd resume service. A host SIGKILL at the beginning of the /usr subtree is recovered automatically on the next enforcing boot; DNF5 remove/reinstall, persistent reboot, 5,000 recovered fixture labels, native APFS checks, and read-only macOS return validation all pass.

reports/repeated-package-relabel-faults-20260911.md extends that result to three independent seeded cycles with 0.30, 2.34, and 1.21 second cuts inside the /usr relabel. A second three-cycle campaign using the clean manifest-built release-kernel module passes 12 enforcing boots with zero kernel findings and zero AVC denials. All six cycles pass the same recovery, DNF5, label, APFS, and macOS gates.

The plan-only installer safety gate is documented in reports/installer-plan-safety-20260911.md. It inventories APFS containers, volume identities, roles, encryption, capacity, and snapshots through read-only virtual-image attachments. It has no execution interface and rejects devices, symlinks, outside paths, duplicate names, inadequate capacity, and --apply.

The native Asahi runner accepts a previously recorded template containing the detached baseline, kernel, initramfs, and fixture metadata. For example:

The crash controller requires the selected root disk to be idle and refuses to start if Lima or another process still has it open.

python3 scripts/asahi16.py \
  --template artifacts/asahi16-rootfs/20260911T151218Z \
  --operations 10000 --debug

python3 scripts/asahi16.py \
  --template artifacts/asahi16-rootfs/20260911T151218Z \
  --worker fedora-package-guest.py \
  --native-rpms artifacts/fedora-packages/20260911T135340Z/results/native-rpms

python3 scripts/asahi16-campaign.py \
  --template artifacts/asahi16-rootfs/20260911T151218Z \
  --module-sha256 bdbe4dfc3c6922828ff3138012992a69d60bc92c739f7be1e9ae07e6fe1549db \
  --cycles 3 --snapshots --pressure

python3 scripts/asahi16-crash.py \
  --template artifacts/asahi16-rootfs/20260911T151218Z \
  --module-sha256 bdbe4dfc3c6922828ff3138012992a69d60bc92c739f7be1e9ae07e6fe1549db

python3 scripts/asahi16-crash.py \
  --template artifacts/asahi16-rootfs/20260911T151218Z \
  --module-sha256 bdbe4dfc3c6922828ff3138012992a69d60bc92c739f7be1e9ae07e6fe1549db \
  --metadata

python3 scripts/apfs-root-kernel.py \
  --input-image artifacts/apfs-root/20260911T191221Z/root-after.raw \
  --fixture-metadata artifacts/apfs-root/20260911T191221Z \
  --template artifacts/asahi16-rootfs/20260911T151218Z \
  --bootstrap-initramfs artifacts/apfs-root-initramfs/initramfs-symlink.img \
  --kernel-payload artifacts/kernel-packages/20260911T194518Z

python3 scripts/apfs-root-kernel.py \
  --input-image artifacts/apfs-root/20260911T191221Z/root-after.raw \
  --fixture-metadata artifacts/apfs-root/20260911T191221Z \
  --template artifacts/asahi16-rootfs/20260911T151218Z \
  --bootstrap-initramfs artifacts/apfs-root-selinux-inputs/20260911T230540Z/initramfs.img \
  --kernel-payload artifacts/kernel-packages/20260911T231307Z \
  --selinux-enforcing

python3 scripts/apfs-root-boot-fallback.py

python3 scripts/apfs-root-boot-fallback.py \
  --kernel-run artifacts/apfs-root-kernel/20260911T231912Z \
  --module-sha256 e81f6d9e0a4f8794bba2697c3758f295e9c3c65585a638bd93c92d100c50da19 \
  --selinux-enforcing

python3 scripts/apfs-root-selinux.py \
  --input-image artifacts/apfs-root-boot-fallback/20260911T204729Z/root-final.raw \
  --fixture-metadata artifacts/apfs-root-kernel/20260911T200430Z \
  --template artifacts/asahi16-rootfs/20260911T151218Z \
  --kernel artifacts/apfs-root-selinux-inputs/20260911T230540Z/vmlinuz \
  --initramfs artifacts/apfs-root-selinux-inputs/20260911T230540Z/initramfs.img \
  --module-sha256 e81f6d9e0a4f8794bba2697c3758f295e9c3c65585a638bd93c92d100c50da19

python3 scripts/apfs-root-package.py \
  --package-dir artifacts/apfs-root-integration-packages/20260912T010000Z

python3 scripts/apfs-root-package-campaign.py \
  --package-dir artifacts/apfs-root-integration-packages/20260912T010000Z \
  --cycles 3 --seed 20260912

python3 scripts/apfs-root-install-plan-test.py \
  --image artifacts/apfs-root-package/20260912T011819Z/root-final.raw

It clones the prepared disposable Fedora disk, streams debug output, enforces a timeout, leaves the source VM disk unchanged, and requires read-only macOS return validation unless --skip-macos is used for a bounded diagnostic. The macOS gate also rejects known integrity diagnostics such as a file-extent gap even if fsck_apfs exits zero. An advisory lock prevents concurrent commands from this checkout because fixture names and the test VM are shared.

The coexistence wrapper creates a new detached fixture and passes only images under this checkout's disposable artifacts/ tree to the native runner. It alternates macOS and Linux writers, revalidates both volumes after every Linux phase, and never attaches a production disk.

Sources

Licensing

This repository's own scripts, tests, configuration and reports are under the MIT license; see LICENSE.

The patches are derivative works of the projects they modify and carry those projects' licenses, not MIT:

Path Upstream License
patches/linux-apfs-rw/, diagnostics/ linux-apfs-rw GPL-2.0-only
patches/apfsprogs/ apfsprogs GPL-2.0
patches/syzkaller/ syzkaller Apache-2.0
patches/xfstests/ fstests (xfstests) GPL-2.0
patches/fedora-kernel/ Fedora kernel package configuration (kernel-aarch64-16k-debug-fedora.config) GPL-2.0, as the Linux kernel

patches/apfsprogs/0003-validate-lzvn-compressed-files.patch adds an LZVN decoder adapted from Aurora Silicon's apfuse, which is licensed MIT OR GPL-2.0-only; that file keeps its original copyright notice.