Fix double IRP completion, filter factory error handling, and other bugs from patch review - #145
eiz-claude wants to merge 1 commit into
Conversation
i'm going to respectfully decline this. i just pasted the SAR codebase into gemini and understand about 0.01% of what actually happens |
Cherry-picked and reworked from a review of fGeorjje's patch (fGeorjje/SynchronousAudioRouter@dcaf4b1). Only the changes that fix verified bugs are taken; the rest of that patch is either a no-op or introduces new bugs (see the PR description). Driver: - SarWaitHandleQueue completed the IRP itself when the output buffer was too small, and SarIrpDeviceControl completed it again (bugcheck 0x44). - SarWaitHandleQueue leaked the remaining queue items and their process handles when SarTransferQueuedHandle failed part way through. - SarCreateEndpoint overwrote the status of the first KsCreateFilterFactory call with the second and used both factories without checking, so a failed factory creation (e.g. duplicate endpoint IDs) dereferenced null. - SarDeleteControlContext unmapped the client's section view with ZwCurrentProcess(), but it can run in an arbitrary process when a client releases the last reference to an orphaned context. Move the unmap to SarOrphanControlContext, which runs in the mapping process. - SarKsPinRtGetBufferCore dereferenced endpoint->owner before its own null check, and left buffer cells and a mapped view behind on failure. - getPhysicalConnection in both filter descriptors had a no-op statement where the symbolic link terminator should have been written. - Track the pending-endpoint work item with an explicit flag instead of inferring it from the pending list being empty. - Reject endpoints with a channel count of zero. SarAsio / SarConfigure: - getChannelInfo used strcpy_s into a 32-byte name, which aborts the host process for long endpoint names. - SarClient::stop leaked the notification event handles. - initInnerDriver left a driver whose init() failed in _innerDriver. - InstalledAsioDrivers tested an LSTATUS with SUCCEEDED(), so a failed RegOpenKeyEx was never detected. - Guard the PKEY_Unknown_DevicePath read against an empty PROPVARIANT. - Handle calloc failure when allocating virtual channel buffers. Co-authored-by: Paul Schwandes <paul@schwandes.de> Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015Ae1KbSGxTuh8F6i85MGxp
ec2af9a to
666cc54
Compare
VM testing: a separate NULL-dereference bugcheck on master, fixed by this PRRan with the harness from #147 on a disposable Windows 11 25H2 VM with test signing and Driver Verifier (standard flags) on What it found on master. The race bugchecked the master driver: Why. Master reads This PR fixes it. Moving the load after the check makes the check real again, so the handler returns
Master with only this one-line change survives the race, which pins the crash on the missing check. What this does not show. This is not established as the cause of the reported start-up problem. That report describes Ardour and the audio UI freezing for tens of seconds before the bugcheck; this crash is immediate, with no hang before it. The reporter's minidump should settle it: this bug appears as Also seen on both builds: SarAsio's format-change broadcast on every endpoint activation invalidates open streams, repeatedly for seconds at 16 endpoint pairs, and mid-stream dropouts hit many endpoints at the same instant. 🤖 Generated with Claude Code |
This is the result of reviewing fGeorjje/SynchronousAudioRouter@dcaf4b1 by @fGeorjje (Paul Schwandes), an AI-generated patch that reportedly stopped BSODs on a 32-channel WDM → X32 setup. Each hunk was checked against the actual code paths. This PR takes the changes that fix real bugs, reimplements one of them differently, and leaves out the rest with reasons below. Paul is credited as co-author on the commit.
Build status. Verified on the
eiz-claudefork using the #143 pipeline on top of master: driver (x64), SarAsio (x64, x86), and, with the follow-up CI change that adds them to the usermode job, SarConfigure and SarCtl (x64, x86) all compile with no new warnings. It still needs a retest on the original reporter's setup, since nothing in the original patch clearly explains the BSOD they saw. The filter factory fix is the best candidate.Taken
SarIrpDeviceControlcompletes every non-pending IRP again. Latent, since SarAsio never sends a small buffer, but it is a real bugcheck 0x44.breakleft the remaining items in the local list, leaking pool and process handles.KsCreateFilterFactoryoverwrote the first call's status and bothKsFilterFactoryUpdateCacheDatacalls ran on possibly null factories. Two endpoints with the same ID would hit this. Rewritten to check each call and log which one failed.SymbolicLinkName[len/2];was a no-op statement in both filter descriptors._innerDriverleft set after a failed init(). The wrapper then calls into a driver that never initialized instead of falling back to running without one.SUCCEEDED(RegOpenKeyEx(...))in both tinyasio copies. LSTATUS errors are positive, so it never failed. Cosmetic effect only.Taken with a different implementation
SarOrphanControlContext, which runs during IRP_MJ_CLEANUP in the context of the process that mapped it. The driver never touches that view itself; all register access goes through the per-process contexts.endpoint->ownerdereference before the null check in the same function. Note thatSarKsPinClosealready reclaims the cells on the normal path, so this only matters if the audio engine retries the buffer allocation after a failure.Not taken: no-ops or unverified claims
RemoveEntryList(&head)followed byAppendTailListis a correct O(1) list splice. The three sites were rewritten into equivalent loops.FsContext2instead of the table works but fixes nothing.SarGetEndpointFromIrp, so an endpoint can't outlive its owner.try_to_lockin tick() "to stop deadlocking". stop() and tick() never nest, so there was no deadlock. The rewrite also polls the completion port with a syscall on every tick of the real-time thread instead of only on a generation change.KSAUDIO_SPEAKER_7POINT1layout rather than7POINT1_SURROUND. Endpoints above 8 channels still get DIRECTOUT, so it doesn't help the reporter's case either. Worth a separate discussion if someone wants it.SarEndpointin sar.h.Not taken: would introduce bugs
IoSetCancelRoutinereturns null. The cancel routine only completes the IRP if it finds it in the pending list, and PostHandleQueue has already removed it under the spinlock. In that race nobody completes the IRP and the ASIO host's thread hangs forever on exit. The existing "whoever removes the list entry completes it" rule is correct.filterUserbefore unregistering, andSarFilterMatchesCurrentProcessreads it without a lock. An in-flight callback would dereference null, and the window is exactly when endpoints are being disabled, which triggers MMDevices registry traffic. Could be done correctly as a follow-up by unregistering first.GetOverlappedResult, so endpoint creation failures are silently treated as success.Testing
SarKsPinRtGetBufferCorein 2 of 2 start-up races and this PR passed; details in the comments. That crash is a separate bug from the reported start-up freeze.🤖 Generated with Claude Code
https://claude.ai/code/session_015Ae1KbSGxTuh8F6i85MGxp