- Python 88.1%
- Shell 10.7%
- C 1.2%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
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> |
||
| config | ||
| diagnostics | ||
| packaging/fedora | ||
| patches | ||
| reports | ||
| scripts | ||
| selinux | ||
| syzkaller/sys/linux | ||
| tests | ||
| .gitattributes | ||
| .gitignore | ||
| LICENSE | ||
| README.md | ||
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
- Creates a 4 GiB raw GPT image with a case-sensitive
APFS-Linuxvolume and a case-insensitiveAPFS-Siblingvolume using macOS's own tools. - Populates both with deterministic files, hard links, a symlink, modes, and a native xattr; records baseline tree manifests and volume/container identities.
- Detaches the image and retains it unchanged as
baseline.raw. - Copies a compressed version into the VM and creates a separate writable copy.
- Attaches that copy as a loop disk, discovers its APFS partition by GPT type,
and runs
apfsckbefore mounting Linux's volume withreadwrite,cknodes,vol=0. - 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. Userun --full-fsxto include that known failing case; seereports/range-clone-gap.md. - Unmounts, remounts read-only, compares Linux contents, unmounts, and runs
apfsckagain. Captures kernel logs and detaches the loop device. - Exports the resulting image. The host refuses export if a guest loop device still references it.
- Attaches the returned image read-only to macOS, runs
diskutil verifyVolume(Apple'sfsck_apfs) for both volumes, and compares seeded contents/xattrs and Linux-generated contents, types, modes, and link counts. - 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
- 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%posttransnow 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-onlyapfusegates 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 thegeneric/346folio/transaction deadlock andgeneric/363large-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/563passes 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-402release build also passes isolatedgeneric/563and the 14-test default lane on the matching non-debug kernel, plus the eight historically sensitive targeted tests. Seereports/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 reducesgeneric/464from 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. Seereports/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 hardlinkENOMEMpropagation and a fatal spaceman error-pointer dereference; exact corrected reproducers pass checker/remount, Generic-KASAN/lockdep, read-only macOS, and apfuse gates. Seereports/asahi-16k-direct-allocation-fault-20260916.md. The subsequent 45-patch candidate fixes the deterministicgeneric/084hardlink/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. Seereports/asahi-16k-hardlink-race-20260916.md. The following 47-patch candidate closes the safegeneric/751–801census after two newly reproduced defects. It restores the mapping constraints and no-fail allocation guarantee lost by the modernbdev_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. Seereports/asahi-16k-generic-751-801-20260916.md. The remaininggeneric/473golden-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. Seereports/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 ancestorio.maxenforcement. 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. Seereports/asahi-16k-cgroup-migration-throttle-20260914.md. Two final-input follow-ons additionally preserve exact owner attribution through proactive reclaim andmemory.highdirect 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-stressgeneric/563control remains clean. Two exact-input extensions also usememory.oom.group=1to 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-drivenmemory.swap.highcoverage 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. Seereports/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. Seereports/16k-page-audit.mdandreports/asahi-16k-rootfs-20260911.mdandreports/asahi-16k-coexistence-20260911.md. - 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.mdandreports/package-manager-20260911.mdandreports/rpm-dnf-qualification-20260911.mdandreports/fedora-asahi-dnf5-qualification-20260911.mdandreports/selinux-enforcing-apfs-root-20260911.md. - 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
apfusegates. 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 pinnedlinux/arm64syzlang overlay now generatessyz_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 macOSfsck_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 intendedreadwrite,cknodesoptions; macOS and apfuse rejected its damaged fsroot, so it is explicitly not a safety-mode survivor. A normalized retry of that exact image withreadwrite,cknodes,vol=0now 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. Seereports/crash-barrier-20260910.mdandreports/metadata-crash-20260910.mdandreports/asahi-16k-crash-20260911.md,reports/asahi-16k-lockdep-20260912.md, andreports/asahi-16k-kasan-20260912.md, andreports/asahi-16k-io-fault-20260913.md, andreports/asahi-16k-crash-mutation-20260913.md, andreports/asahi-16k-post-flush-lies-20260917.md, andreports/asahi-16k-reorder-equivalence-20260917.md, andreports/asahi-16k-double-tears-20260917.md, andreports/asahi-16k-omission-plus-tear-20260917.md, andreports/crash-campaign-watchdogs-20260917.md, andreports/crash-shard-resume-and-overlap-search-20260917.md, andreports/syzkaller-apfs-preparation-20260917.md,reports/syzkaller-apfs-runtime-20260917.md,reports/syzkaller-apfs-safety-options-20260917.md, andreports/syzkaller-apfs-compression-seed-20260917.md, andreports/asahi-16k-allocation-fault-20260913.md. - 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.
- 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
- Asahi's shared-container objective
- linux-apfs-rw
- apfsprogs
- fstests
- Linux dm-log-writes
- Linux fault-injection framework
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.