Repository navigation
Fix: Prevent BSOD from RtPacketsCount race in RtPacketObject - #33
Open
kosmos.ebi (buchizo) wants to merge 3 commits into
Open
kosmos.ebi (buchizo) wants to merge 3 commits into
kosmos.ebi (buchizo) wants to merge 3 commits into
Conversation
There was a problem hiding this comment.
🟡 Changes recommended
Lock creation failures are unhandled, and packet state is still read before locking.
Get a fresh assessment by requesting another Copilot review.
Pull request overview
Adds synchronization to prevent RT packet teardown from racing with copy operations and causing divide-by-zero crashes.
Changes:
- Adds a dedicated RT packet wait lock.
- Protects packet setup, teardown, and copy loops.
File summaries
| File | Description |
|---|---|
RtPacketObject.h |
Declares the packet-state lock. |
RtPacketObject.cpp |
Creates and applies the lock around packet access. |
Review details
Suppressed comments (1)
src/uac2-driver/RtPacketObject.cpp:749
- Acquire the packet lock before reading packet state. The entry trace and
RtPacketSizeassertion currently run first, so concurrentUnsetRtPacketscan still change these fields during those reads and can trigger the assertion in checked builds. Validate the index first, then resolvertPacketInfo, acquire the lock, and only then log/assert packet fields.
WdfWaitLockAcquire(m_rtPacketLock, nullptr);
- Files reviewed: 2/2 changed files
- Comments generated: 2
- Review effort level: Balanced
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Pete Brown (Psychlist1972)
requested a review
from Akihiro Tokumasu (ymh-akihiro-tokumasu)
October 3, 2026 18:38
Akihiro Tokumasu (ymh-akihiro-tokumasu)
approved these changes
Oct 6, 2026
Akihiro Tokumasu (ymh-akihiro-tokumasu)
left a comment
Collaborator
There was a problem hiding this comment.
Reviewed the changes and verified the fix during testing.
Thanks for the fix. LGTM.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.


Summary
Fixes a
0x1000007E(SYSTEM_THREAD_EXCEPTION_NOT_HANDLED) BSOD caused by an integer divide-by-zero (STATUS_INTEGER_DIVIDE_BY_ZERO,0xC0000094) inside the mixing thread. A kernel minidump (attached,081826-11625-01.dmp) captured while a USB Audio 2.0 render stream was active onUSBAudio2-ACX.syswas analyzed with WinDbg to identify the exact faulting instruction and root cause.Crash analysis (from the attached dump)
!analyze -vagainst the dump (with local build symbols) resolved the fault precisely:The divisor operand
[r14+r15+8]lines up exactly with theRtPacketsCountfield offset (8 bytes, right after the leadingPVOID* RtPackets) inRT_PACKET_INFO(RtPacketObject.h), confirming the crashing statement is:rtPacketIndex %= rtPacketInfo->RtPacketsCount; // RtPacketObject.cpp:556with
RtPacketsCount == 0at the moment of the modulo.Why
RtPacketObject::UnsetRtPackets()resetsRtPackets/RtPacketsCount/RtPacketSizeto zero when a stream's RtPacket buffers are freed (EvtStreamFreeRtPackets→CStreamEngine::FreeRtPackets→USBAudioAcxDriverStreamUnsetRtPackets). This path is serialized only byDEVICE_CONTEXT::StreamWaitLock.RtPacketObject::CopyFromRtPacketToOutputData()/CopyToRtPacketFromInputData()are called from the mixing/capture thread (StreamObject::MixingEngineThreadMain) while holding a different lock,DEVICE_CONTEXT::StreamEngineWaitLock. These functions readRtPacketsCount(andRtPackets) repeatedly across their copy loop and divide/modulo by it.Because
StreamWaitLockandStreamEngineWaitLockdo not exclude each other, there is a window where:RenderStreamEngine[deviceIndex]->GetCurrentState() == AcxStreamStateRunand entersCopyFromRtPacketToOutputData.UnsetRtPacketsruns (e.g., stream teardown, or a re-AllocateRtPacketsfailure path inCStreamEngine::AllocateRtPacketsthat callsFreeRtPacketson error) and zeroesRtPacketsCount.% RtPacketsCountdivides by zero → bugcheck.The existing
RtPacketSize == 0guard at function entry does not close this window:RtPacketSizeandRtPacketsCountare always set/cleared together, but the guard is checked once at entry while the divisor is re-read every loop iteration ? a single point-in-time check cannot protect against a value that changes mid-loop on another thread.Changes
RtPacketObject.h: add a privateWDFWAITLOCK m_rtPacketLock, created in the constructor and parented to the device object (same pattern as existing locks inStreamObject).RtPacketObject.cpp:SetRtPackets/UnsetRtPackets: acquirem_rtPacketLockaround the read/write ofRtPackets/RtPacketsCount/RtPacketSize.CopyFromRtPacketToOutputData/CopyToRtPacketFromInputData: acquirem_rtPacketLockimmediately after resolving theRT_PACKET_INFO*for the device, and release it at the single shared_Exitlabel each function already funnels every early-return path through, so the lock now covers the entire copy loop (all reads ofRtPacketsCount/RtPackets).This makes
Set/UnsetRtPacketsand the copy paths mutually exclusive, soRtPacketsCountcan never be observed as zero mid-copy while a divide/modulo against it is in flight.Notes
m_rtPacketLockis always the innermost lock acquired (never held while acquiringStreamWaitLockorStreamEngineWaitLock), so this does not introduce a new lock-ordering hazard with existing usages of those two locks.PASSIVE_LEVEL(PAGED_CODE_SEG), so aWDFWAITLOCKis appropriate; noDISPATCH_LEVELcallers are involved.RtPacketObject.cpp:556, dividing byRtPacketsCount). It is unrelated to a separate, not-yet-merged upstream fix (ef7a512d) for a different divide-by-zero inTransferObject::CalculateEstimatedQPCPosition(dividing bym_transferredBytesInThisIrp, tracked as issue [BUG]: Crash #27) ? that one is out of scope for this PR.081826-11625-01.dmp) is attached for reference.081826-11625-01.dmp