Skip to content

fix(build): gate SCSI HBA behind opt-in build flag — default build can boot-panic at cold boot#55

Merged
mrmidi merged 3 commits into
mrmidi:mainfrom
mhellevang:fix/gate-scsi-entitlement
Jul 11, 2026
Merged

fix(build): gate SCSI HBA behind opt-in build flag — default build can boot-panic at cold boot#55
mrmidi merged 3 commits into
mrmidi:mainfrom
mhellevang:fix/gate-scsi-entitlement

Conversation

@mhellevang

@mhellevang mhellevang commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

TL;DR

Since #46 merged, a default build of main can kernel-panic a machine that cold-boots with a FireWire OHCI controller attached and no SBP-2 device present — a boot panic loop, ~60–80 s after power-on, no device plugged in. A contributor hit this in the wild (#54). This PR makes the SCSI HBA opt-in at build time (./build.sh --scsi / ASFW_ENABLE_SCSI=YES), so the default artifact carries neither the ASFWSCSIControllerService personality nor the restricted scsicontroller entitlement.

The panic

panic(cpu 0 caller ...): busy timeout[0], (60s): 'pci11c1,5901' (1e,4020001) @IOService.cpp:5986
Panicked task: watchdogd

IOKit registry busy-timeout at boot: the FW643 PCI nub never went quiet within watchdogd's 60 s boot-quiesce window.

Root cause

Two independent mechanisms converge on this exact panic signature, and both start from the same trigger: the ASFWSCSIControllerService personality matches the FW643 PCI nub at boot and the kernel instantiates IOUserSCSIParallelInterfaceController.

Mechanism 1 — stranded probe (dext runs fine). The chain is fully confirmed in the code:

  1. Cold boot with an FW643 attached (PCIe card or Apple TB→FireWire adapter — the adapter is an FW643), no SBP-2 device on the bus. Audio interfaces are not SBP-2, so this is the normal state for every audio user.
  2. The HBA starts; UserTargetPresentForID(0) answers true unconditionally → the framework auto-creates SCSI target 0.
  3. SAM probes target 0 with INQUIRY; the probe's fTimeoutInMilliSec is 0 (infinite).
  4. The SBP-2 bridge is not ready (no login — and with no SBP-2 device, never will be), so the deferred-INQUIRY path holds the completion with no hold deadline (ASFWSCSIController.cpp, TryHoldInquiry; the stored timeoutMs is only used on replay). The only paths that release a held INQUIRY are SBP-2 login, Stop(), and a task abort — none fires here.
  5. Target-0 registration never completes; the busy chain up to pci11c1,5901 never drains; watchdogd panics the boot at 60 s.

This mechanism is independent of SIP/AMFI state, and the same IOService.cpp:5986 signature was observed twice during HBA development with a live, running dext (including an IOSCSITargetDevice + IOThunderboltPort busy chain matching the reporter's TB-adapter topology).

Mechanism 2 — AMFI kill (dext never runs). The ad-hoc signature carrying the restricted scsicontroller entitlement gets the dext killed at launch on an AMFI-enforcing machine. Since everything runs in one process (IOUserServerOneProcess), that takes the audio driver down too, and the orphaned kernel stub strands the same busy chain.

The panic report in #54 does not discriminate between the two — both end in the identical watchdogd busy-timeout. Mechanism 1 is the better-supported theory (code-confirmed chain; the reporter ran pre-#46 ad-hoc builds carrying equally-restricted entitlements without issue, which argues against a suddenly-stricter AMFI). A cheap discriminator, if the reporter can run it after a panicking boot: log show --boot -1 --predicate 'eventMessage CONTAINS "[SCSIHBA]"' --info --debug — any output means the dext launched, ruling out the AMFI path. Either way, this PR removes both mechanisms for default builds: no personality → no kernel stub, no restricted entitlement → nothing for AMFI to reject.

What this PR does

  • ASFW_ENABLE_SCSI build setting, default NO, on the dext target (both configs):
    • selects the entitlements file: ASFWDriver.entitlements (audio-only, default) vs ASFWDriver+SCSI.entitlements
    • preprocesses the ASFWSCSIControllerService personality out of Info.plist (INFOPLIST_PREPROCESS + -traditional)
  • ./build.sh --scsi passes ASFW_ENABLE_SCSI=YES
  • sign.sh auto-detects the personality in the built dext and picks the matching entitlements file, so signature and Info.plist cannot diverge
  • README: opt-in section documenting both panic mechanisms, an explicit warning that --scsi builds are not cold-boot-safe without a powered-on SBP-2 device (until the HBA-side fix lands), panic-loop recovery steps, and a tester warning to uninstall the extension before re-enabling SIP

The entitlement + personality are gated together deliberately: the personality without the entitlement leaves an orphaned kernel stub (mechanism 2's path), and the entitlement without the personality gets the whole one-process dext killed by AMFI on enforcing machines, taking audio down with it.

All SCSI/SBP-2 code still compiles in every build — only packaging is gated. Scanner users opt in with one flag.

Verification

Built both flag states via plain xcodebuild and via build.sh+sign.sh, and inspected the artifacts:

state Info.plist personality signed entitlement
default absent absent
--scsi / ASFW_ENABLE_SCSI=YES present (IOClass, IOPCIMatch intact) present

plutil -lint clean, variable substitution intact, sign.sh auto-detect exercised end-to-end in both states. The reporter's independently-made strip build (same shape as the default artifact) boots clean on the affected machine.

Follow-ups (not in this PR)

This PR mitigates #54 for default builds but does not fix the underlying HBA defect — #54 should stay open (or be tracked by follow-up 1) until that lands.

  1. The HBA-side fix so --scsi builds are also boot-safe: gate UserTargetPresentForID on SBP-2 login and create/destroy the target from the login observer (framework hotplug model). Needs hardware verification with a scanner; will follow as a separate PR.
  2. Existing release assets: any published tester build cut before this change includes the SCSI HBA and will boot-panic testers in the same scenario. Recommend re-cutting from this branch (default = audio-only) and/or adding a prominent warning; optionally publish a separate -scsi asset.
  3. If Apple grants the scsicontroller family entitlement for real distribution signing, the default can be revisited.

The ASFWSCSIControllerService personality instantiates a kernel-side
IOUserSCSIParallelInterfaceController as soon as the FireWire card
matches at boot. On machines where AMFI enforces entitlements, the
ad-hoc-signed dext carrying the restricted scsicontroller entitlement
is killed at launch, and the orphaned kernel stub panics the kernel
about a minute after boot — a boot loop, with no device attached.

Default build now ships neither the personality nor the entitlement:
- ASFW_ENABLE_SCSI=NO (pbxproj) selects the entitlements file and
  preprocesses the SCSI personality out of Info.plist
- ./build.sh --scsi opts in (xcodebuild ASFW_ENABLE_SCSI=YES)
- sign.sh picks the matching entitlements file by detecting the
  personality in the built dext, so signature and plist never diverge

Verified both flag states: built artifacts carry personality and
scsicontroller entitlement together or not at all.
The README and build.sh attributed the boot panic solely to the
AMFI-kill path ("only on a machine that runs with SIP disabled"),
implying SIP-off machines are safe. They are not: with the dext
running, the HBA reports target 0 present unconditionally and the
probe INQUIRY is held with no deadline waiting for an SBP-2 login
that never arrives when no SBP-2 device is on the bus — the stalled
target registration trips the same 60 s registry busy-timeout panic
(IOService.cpp:5986) regardless of SIP state.

Document both paths, warn that --scsi builds are not cold-boot-safe
without a powered-on SBP-2 device until the HBA-side fix (create the
target at SBP-2 login instead of boot) lands, and note that a stalled
probe on a running system can still panic later on adapter unplug or
extension teardown.
@mhellevang mhellevang changed the title fix(build): gate SCSI HBA behind opt-in build flag — default build boot-panics machines at cold boot fix(build): gate SCSI HBA behind opt-in build flag — default build can boot-panic at cold boot Jul 10, 2026
@mhellevang mhellevang marked this pull request as ready for review July 10, 2026 15:10

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR mitigates a cold-boot kernel panic risk by making the SCSI HBA (SBP-2 bridging via SCSIControllerDriverKit) opt-in at build/sign time, so default builds ship audio-only without the SCSI personality or restricted scsicontroller entitlement.

Changes:

  • Adds an ASFW_ENABLE_SCSI build setting (default NO) to gate the ASFWSCSIControllerService personality via Info.plist preprocessing and to select the correct entitlement set.
  • Extends build.sh with --scsi to pass ASFW_ENABLE_SCSI=YES to xcodebuild.
  • Updates sign.sh to auto-detect whether the SCSI personality is present in the built .dext and sign with matching entitlements; documents the opt-in and recovery steps in the README.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated no comments.

Show a summary per file
File Description
sign.sh Detects SCSI personality in the built dext and selects matching entitlements to prevent entitlement/personality mismatch.
README.md Documents SCSI HBA opt-in, panic mechanisms, cold-boot warning, and recovery/uninstall guidance.
build.sh Adds --scsi flag that enables the SCSI packaging path by passing ASFW_ENABLE_SCSI=YES to Xcode builds.
ASFWDriver/Info.plist Wraps the SCSI controller personality in #if ASFW_ENABLE_SCSI so it’s excluded by default.
ASFWDriver/ASFWDriver+SCSI.entitlements Introduces a dedicated entitlement set that includes com.apple.developer.driverkit.family.scsicontroller.
ASFWDriver/ASFWDriver.entitlements Removes the restricted scsicontroller entitlement from the default (audio-only) entitlement set.
ASFW.xcodeproj/project.pbxproj Wires ASFW_ENABLE_SCSI to entitlement selection and Info.plist preprocessing/definitions.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@mrmidi mrmidi merged commit 04bca57 into mrmidi:main Jul 11, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants