Skip to content

v0.3.1 — P0 safety: wrong-disk wipe fix + Manage Disk crash fix

Choose a tag to compare

@cashcon57 cashcon57 released this 05 May 18:52
· 2 commits to main since this release

Mactoy v0.3.1 — P0 safety release: wrong-disk wipe + Manage Disk crash

Critical safety release. A user reported in issue #1 that v0.3.0 wiped the wrong external disk: they had two USB drives plugged in, selected disk5, confirmed disk5, and Mactoy wrote disk6. Root cause analysis confirmed the bug exists in every shipped Mactoy version (v0.1.0 through v0.3.0) — those releases have all been retroactively marked as prereleases on GitHub. v0.3.1 is the first release that's safe to install.

This release also fixes a SwiftUI crash on macOS 26 (Sequoia / Tahoe) when clicking the Manage Disk tab — three users hit it on M4 Studio / MBP M4 / Air M4 hardware.

What was broken in v0.3.0 and earlier

When you clicked Install Ventoy, Mactoy showed a confirmation sheet with the disk's name and size. You clicked Confirm. Internally, Mactoy then re-read the selected disk at the moment of execution rather than using the disk you'd just confirmed.

If — between you clicking Install and clicking Confirm — the disk-enumeration poll ran (it runs every 2 seconds) AND your originally-selected disk momentarily dropped off the bus (USB-C hub re-enumerating, sleep/wake, even normal power-management churn), applyDiskList cleared the selection and snapped it to the next available disk. The install then ran on that disk, not the one you confirmed.

The reporter had a SATA SSD at disk5 (the one they wanted to flash) and a USB-C SSD at disk6 (one with personal data). Mactoy wiped disk6.

v0.3.1 — six-layer iron-clad targeting defense

Every layer must pass for any byte to hit /dev/rdisk*.

  1. Confirmation captures full disk fingerprint. EraseConfirmation carries the complete DiskTarget — bsdName, sizeInBytes, mediaName, isExternal, isRemovable. Treated as authoritative from the moment you click Install onward.

  2. Selection frozen during confirmation. applyDiskList refuses to mutate selectedDiskBSD while a confirmation sheet is open. The disk list still updates; the selection does not.

  3. run() takes the captured target as an explicit parameter. No more re-reading selectedDisk from inside run(). confirmRun() threads the captured pair through; the helper-poll auto-resume path uses a stored pendingRunTarget to survive the helper-approval gap so the same disk is targeted on both first call and post-approval resume.

  4. App-side re-verification. Right before handing the plan to the daemon, run() calls DiskInfo.probe(bsdName:) and asserts the live disk's fingerprint matches the captured one (via the new DiskTarget.fingerprintMismatch(against:) helper). Any mismatch — size, mediaName, external/removable flags, BSD name — aborts with a clear error sheet and no XPC call is made.

  5. Daemon-side re-verification. VentoyDriver.executeFreshInstall, VentoyDriver.executeUpdate, and RawImageDriver.execute ALL re-probe the disk via DiskInfo.probe before opening /dev/rdisk*. The daemon is the last line of defense; it does not trust the app to have got the targeting right.

  6. BSD-name guard at execution. Even if the rest of the fingerprint coincidentally matches a different physical drive that landed at the same disk<N> slot, the captured BSD name must still match the current selectedDiskBSD or run() refuses with a "selection drifted" error.

The new DiskTarget.fingerprintMismatch(against:) helper centralizes the comparison. It's covered by 8 unit tests in DiskTargetFingerprintTests.swift, including the exact "RTL9210B-CG → SSD 840 PRO Seri" scenario from the user's report.

Manage Disk crash fix

Three users in issue #1 reported crashes when clicking the Manage Disk tab — stack traces showed swift_releaseswift_arrayDestroyHVStack.updateCache SIGSEGV inside SwiftUI itself.

Root cause: ContentView wrapped every panel in a ScrollView. ManageDiskPanel contains a List. List inside ScrollView is a known SwiftUI footgun — both manage their own scroll geometry and fight each other; on macOS 26 the fight surfaces as a use-after-free in the layout cache.

v0.3.1 branches the wrapper in ContentView: Manage Disk renders without the outer ScrollView (the inner List manages its own vertical overflow). The other panels (Install Ventoy / Update Ventoy / Flash Image) keep their ScrollView wrappers — they don't contain a List.

Install

# Download Mactoy-0.3.1.dmg from the release assets.
open Mactoy-0.3.1.dmg
# Drag Mactoy.app into /Applications.

Apple-notarized. If you have any prior Mactoy version installed, please update to v0.3.1 immediately — every version before this one has the wrong-disk bug.

Verification

  • swift build -c release clean for both Mactoy and mactoyd.
  • Universal binary verified via lipo -info (x86_64 arm64 on both products).
  • 38/38 tests pass (swift test) — 8 new fingerprint tests added.
  • Independent code review of the diff before tagging.

Known limitations

  • The original wrong-disk race is verified to be closed by static analysis and parser-level tests. Reproducing the exact disk-arbitration churn timing in an automated test would require a simulated DiskArbitration harness — that test rig is on the v0.4 roadmap. If you can reproduce a wrong-disk wipe on v0.3.1, please file a P0 issue immediately with log show --predicate 'subsystem == "com.mactoy"' --last 1h output.
  • All releases v0.1.0 through v0.3.0 remain available as prereleases for forensics. Do not install them.

SHA256 of Mactoy-0.3.1.dmg is published below the release assets.