Skip to content

fix(lifecycle): recreate the ROM scanner across runtime rebuilds instead of reusing it against a freed bus - #90

Merged
mrmidi merged 2 commits into
mrmidi:mainfrom
mhellevang:fix/romscanner-stale-bus
Aug 1, 2026
Merged

fix(lifecycle): recreate the ROM scanner across runtime rebuilds instead of reusing it against a freed bus#90
mrmidi merged 2 commits into
mrmidi:mainfrom
mhellevang:fix/romscanner-stale-bus

Conversation

@mhellevang

@mhellevang mhellevang commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

What

ServiceContext::Reset destroys the controller (and with it the controller-owned FireWireBusImpl), but never dropped deps.romScanner. ROMScanner, ROMReader and ROMScanSession hold a bare Async::IFireWireBus& fixed at construction, so EnsureRomScanner reused the stale scanner after every internal runtime rebuild ("Reusing existing ROMScanner instance") and the next scan dereferenced freed memory in the ReadQuad path — fault offset 0x10 = FireWireBusImpl::async_. Deterministic on every scan after the first rebuild.

Observed on real hardware (2026-07-29, during HW validation of #61): dext SIGSEGV when scanner power-on triggered a bus reset during a runtime rebuild. Crash report available on request.

Fix

In Reset, detach the controller's attached shared_ptr copy and drop deps.romScanner before controller.reset(), so ~ROMScanner runs while the bus is still alive. EnsureRomScanner (re-entered from StartRuntime on wake/recovery) then takes its existing rebuild branch, binding the fresh scanner to the new controller's Bus() and the new scheduler queue (equally stale on reuse before this fix).

Teardown safety: ~ROMScannerROMScanSession::Abort() only sets the aborted_ flag and queues weak_ptr cleanup — no bus IO — and every queued FSM entry point checks aborted_ before touching the bus.

Scope

This implements only the safety floor: a scanner must not retain a borrowed bus reference after that bus is destroyed. It does not touch the ConfigROMStore cache or device identity, and does not change what gets rescanned. Fingerprint-validated rediscovery (preserve cached ROM identity across a reset, revalidate a BIB/root-directory fingerprint before resuming, full rescan only on change — the Apple/Linux model) is deliberately left as the follow-up design it belongs to; nothing here precludes it.

Split out of #61 per review feedback (HBA and Config ROM work kept separate). Same bug class as the IRMClient teardown fix — but the scanner was never rebuilt at all.

Testing

  • Host suite: 1375/1375 green
  • ./build.sh --scsi build green
  • HW-verified 2026-07-31 (MacBook Pro / macOS Tahoe + Nikon CoolScan 9000 over FireWire): power-cycling the device across two sleep/wake runtime rebuilds produced clean full ROM scans + SBP-2 login both times. The log shows ✅ ROMScanner created on the post-wake rebuild (never "Reusing existing ROMScanner instance"), wake-verify alive, and no dext crash. On main this exact sequence SIGSEGVs deterministically.

…st the new bus

ROMScanner, ROMReader and ROMScanSession hold a bare IFireWireBus& fixed at
construction, pointing into the controller-owned FireWireBusImpl.
ServiceContext::Reset destroyed the controller (and with it the bus) but never
dropped deps.romScanner, so EnsureRomScanner reused the stale scanner after
every internal runtime rebuild ("Reusing existing ROMScanner instance") and the
next scan dereferenced freed memory in ROMReader's ReadQuad path (fault offset
0x10 = FireWireBusImpl::async_). Observed as a dext SIGSEGV on real hardware
when scanner power-on triggered a bus reset during a runtime rebuild
(2026-07-29); deterministic on every scan after the first rebuild.

Detach the controller's shared_ptr copy first so ~ROMScanner runs while the bus
is still alive. The destructor only marks the session aborted and queues
weak_ptr cleanup — no bus IO — and every queued FSM entry point checks
aborted_ before touching the bus. EnsureRomScanner then takes its existing
rebuild branch, binding the fresh scanner to the new controller's Bus() and the
new scheduler queue (which was equally stale on reuse).

Pre-existing main bug, surfaced during HW validation of the login-target work;
same class as the IRMClient teardown fix, but the scanner was never rebuilt at
all.
mhellevang added a commit to mhellevang/ASFireWire that referenced this pull request Jul 30, 2026
…ds against the new bus"

Moved to its own PR (mrmidi#90) — HBA and Config ROM work kept separate per review.
Homebrew's xcodegen moved to 2.46.0, which emits the ASFW/ASFWDriver group
order differently than the committed project. CI regenerates and diffs the
pbxproj, so every branch fails until the committed file matches the new
generator output. main needs the same regeneration on its next change.
@mhellevang
mhellevang marked this pull request as ready for review July 31, 2026 19:18
@mrmidi

mrmidi commented Aug 1, 2026

Copy link
Copy Markdown
Owner

Hi. Thanks for contributing and splitting HBA/ConfigRom parts to separate PRs. I would still ask you to rebase to dev branch: It conflicts with current dev in DriverContext.cpp because teardown ordering has changed. Place the scanner detach/reset immediately before controller.reset(), preserving the newer audio and lifecycle teardown ordering.

@mhellevang

Copy link
Copy Markdown
Contributor Author

Hi. Thanks for contributing and splitting HBA/ConfigRom parts to separate PRs. I would still ask you to rebase to dev branch: It conflicts with current dev in DriverContext.cpp because teardown ordering has changed. Place the scanner detach/reset immediately before controller.reset(), preserving the newer audio and lifecycle teardown ordering.

Sure thing!

@mrmidi

mrmidi commented Aug 1, 2026

Copy link
Copy Markdown
Owner

Hi. Thanks for contributing and splitting HBA/ConfigRom parts to separate PRs. I would still ask you to rebase to dev branch: It conflicts with current dev in DriverContext.cpp because teardown ordering has changed. Place the scanner detach/reset immediately before controller.reset(), preserving the newer audio and lifecycle teardown ordering.

Sure thing!

I'm in doubt. See Discord, but probably worth merging it into main — I'll just cherry-pick it for dev. Just because it's fixing existing problem and nobody cares about other branches than main :)

@mrmidi

mrmidi commented Aug 1, 2026

Copy link
Copy Markdown
Owner

Ok, merging it since we've agreed on Discord.

@mrmidi
mrmidi merged commit 666e578 into mrmidi:main Aug 1, 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.

2 participants