Index: /trunk/src/VBox/Devices/Audio/AudioMixer.cpp
===================================================================
--- /trunk/src/VBox/Devices/Audio/AudioMixer.cpp	(revision 103133)
+++ /trunk/src/VBox/Devices/Audio/AudioMixer.cpp	(revision 103134)
@@ -2012,4 +2012,7 @@
     RT_NOREF(idStream);
 
+    int rc = RTCritSectEnter(&pSink->CritSect);
+    AssertRCReturn(rc, rc);
+
     /*
      * Figure how much that we can push down.
@@ -2023,5 +2026,6 @@
     Log3Func(("idStream=%u: cbSinkWritable=%#RX32 cbCircBufReadable=%#RX32 -> cbToTransfer=%#RX32 @%#RX64\n",
               idStream, cbSinkWritable, cbCircBufReadable, cbToTransfer, offStream));
-    AssertMsg(!(pSink->fStatus & AUDMIXSINK_STS_DRAINING) || cbCircBufReadable == pSink->cbDmaLeftToDrain,
+    /* Note: There now can be more data in the DMA buffer than initially announced.  See @bugref{10354}. */
+    AssertMsg(!(pSink->fStatus & AUDMIXSINK_STS_DRAINING) || cbCircBufReadable >= pSink->cbDmaLeftToDrain,
               ("cbCircBufReadable=%#x cbDmaLeftToDrain=%#x\n", cbCircBufReadable, pSink->cbDmaLeftToDrain));
 
@@ -2081,4 +2085,9 @@
         Assert(cbToTransfer2 == 0);
 
+    Log3Func(("idStream=%u: cbCircBufUsed=%RX32 left\n", idStream, (uint32_t)RTCircBufUsed(pCircBuf)));
+
+    RTCritSectLeave(&pSink->CritSect);
+
+    LogFlowFuncLeave();
     return offStream;
 }
Index: /trunk/src/VBox/Devices/Audio/DevHdaStream.cpp
===================================================================
--- /trunk/src/VBox/Devices/Audio/DevHdaStream.cpp	(revision 103133)
+++ /trunk/src/VBox/Devices/Audio/DevHdaStream.cpp	(revision 103134)
@@ -798,16 +798,4 @@
      */
 
-    /* (Re-)Allocate the stream's internal DMA buffer,
-     * based on the timing *and* PCM properties we just got above. */
-    if (pStreamR3->State.pCircBuf)
-    {
-        RTCircBufDestroy(pStreamR3->State.pCircBuf);
-        pStreamR3->State.pCircBuf = NULL;
-        pStreamR3->State.StatDmaBufSize = 0;
-        pStreamR3->State.StatDmaBufUsed = 0;
-    }
-    pStreamShared->State.offWrite = 0;
-    pStreamShared->State.offRead  = 0;
-
     /*
      * The default internal ring buffer size must be:
@@ -858,14 +846,35 @@
     if (RT_SUCCESS(rc))
     {
-        rc = RTCircBufCreate(&pStreamR3->State.pCircBuf, cbCircBuf);
-        if (RT_SUCCESS(rc))
+        /**
+         * Note: Only re-create the DMA buffer if the size actually has changed.
+         *
+         *       Otherwise do *not* reset the stream's circular buffer here, as the audio mixer still relies on
+         *       previously announced DMA data (via AudioMixerSinkDrainAndStop()) and processes it asynchronously.
+         *       Resetting the buffer here will cause a race condition.  See @bugref{10354}. */
+        if (pStreamR3->State.StatDmaBufSize != cbCircBuf)
         {
-            pStreamR3->State.StatDmaBufSize = cbCircBuf;
-
-            /*
-             * Forward the timer frequency hint to TM as well for better accuracy on
-             * systems w/o preemption timers (also good for 'info timers').
-             */
-            PDMDevHlpTimerSetFrequencyHint(pDevIns, pStreamShared->hTimer, uTransferHz);
+            /* (Re-)Allocate the stream's internal DMA buffer,
+             * based on the timing *and* PCM properties we just got above. */
+            if (pStreamR3->State.pCircBuf)
+            {
+                RTCircBufDestroy(pStreamR3->State.pCircBuf);
+                pStreamR3->State.pCircBuf = NULL;
+                pStreamR3->State.StatDmaBufSize = 0;
+                pStreamR3->State.StatDmaBufUsed = 0;
+            }
+            pStreamShared->State.offWrite = 0;
+            pStreamShared->State.offRead  = 0;
+
+            rc = RTCircBufCreate(&pStreamR3->State.pCircBuf, cbCircBuf);
+            if (RT_SUCCESS(rc))
+            {
+                pStreamR3->State.StatDmaBufSize = cbCircBuf;
+
+                /*
+                 * Forward the timer frequency hint to TM as well for better accuracy on
+                 * systems w/o preemption timers (also good for 'info timers').
+                 */
+                PDMDevHlpTimerSetFrequencyHint(pDevIns, pStreamShared->hTimer, uTransferHz);
+            }
         }
     }
@@ -1004,8 +1013,7 @@
     pStreamShared->State.fInputPreBuffered = false;
 
-    if (pStreamR3->State.pCircBuf)
-        RTCircBufReset(pStreamR3->State.pCircBuf);
-    pStreamShared->State.offWrite = 0;
-    pStreamShared->State.offRead  = 0;
+    /* Note: Do *not* reset the stream's circular buffer here, as the audio mixer still relies on
+     *       previously announced DMA data (via AudioMixerSinkDrainAndStop()) and processes it asynchronously.
+     *       Resetting the buffer here will cause a race condition.  See @bugref{10354}. */
 
     /* Report that we're done resetting this stream. */
@@ -1042,4 +1050,6 @@
     if (pSink)
     {
+        AudioMixerSinkLock(pSink);
+
         if (fEnable)
         {
@@ -1061,4 +1071,6 @@
             rc = AudioMixerSinkDrainAndStop(pSink,
                                             pStreamR3->State.pCircBuf ? (uint32_t)RTCircBufUsed(pStreamR3->State.pCircBuf) : 0);
+
+        AudioMixerSinkUnlock(pSink);
     }
     if (   RT_SUCCESS(rc)
Index: /trunk/src/VBox/Devices/Audio/DevIchAc97.cpp
===================================================================
--- /trunk/src/VBox/Devices/Audio/DevIchAc97.cpp	(revision 103133)
+++ /trunk/src/VBox/Devices/Audio/DevIchAc97.cpp	(revision 103134)
@@ -1731,6 +1731,7 @@
     LogFunc(("[SD%RU8]\n", pStream->u8SD));
 
-    if (pStreamCC->State.pCircBuf)
-        RTCircBufReset(pStreamCC->State.pCircBuf);
+    /* Note: Do *not* reset the stream's circular buffer here, as the audio mixer still relies on
+     *       previously announced DMA data (via AudioMixerSinkDrainAndStop()) and processes it asynchronously.
+     *       Resetting the buffer here will cause a race condition.  See @bugref{10354}. */
 
     pStream->Regs.bdbar    = 0;
@@ -2199,5 +2200,9 @@
     if (   pStreamCC->State.pCircBuf
         && RTCircBufSize(pStreamCC->State.pCircBuf) == cbCircBuf)
-        RTCircBufReset(pStreamCC->State.pCircBuf);
+    {
+        /* Note: Do *not* reset the stream's circular buffer here, as the audio mixer still relies on
+         *       previously announced DMA data (via AudioMixerSinkDrainAndStop()) and processes it asynchronously.
+         *       Resetting the buffer here will cause a race condition.  See @bugref{10354}. */
+    }
     else
     {
Index: /trunk/src/VBox/Devices/Audio/DevSB16.cpp
===================================================================
--- /trunk/src/VBox/Devices/Audio/DevSB16.cpp	(revision 103133)
+++ /trunk/src/VBox/Devices/Audio/DevSB16.cpp	(revision 103134)
@@ -111,4 +111,6 @@
 typedef struct SB16STREAMSTATE
 {
+    /** Critical section for this stream. */
+    RTCRITSECT              CritSect;
     /** Flag indicating whether this stream is in enabled state or not. */
     bool                    fEnabled;
@@ -336,4 +338,6 @@
 static int  sb16StreamOpen(PPDMDEVINS pDevIns, PSB16STATE pThis, PSB16STREAM pStream);
 static void sb16StreamClose(PPDMDEVINS pDevIns, PSB16STATE pThis, PSB16STREAM pStream);
+DECLINLINE(void) sb16StreamLock(PSB16STREAM pStream);
+DECLINLINE(void) sb16StreamUnlock(PSB16STREAM pStream);
 DECLINLINE(PAUDMIXSINK) sb16StreamIndexToSink(PSB16STATE pThis, uint8_t uIdx);
 static void sb16StreamTransferScheduleNext(PSB16STATE pThis, PSB16STREAM pStream, uint32_t cSamples);
@@ -1924,4 +1928,17 @@
 *********************************************************************************************************************************/
 
+/**
+ * Reads DMA output data into the internal DMA buffer.
+ *
+ * @returns VBox status code.
+ * @param   pStream     The SB16 stream to read DMA output data from.
+ * @param   uDmaChan    DMA channel to read from.
+ * @param   offDma      DMA offset (in bytes) to start reading from.
+ * @param   cbDma       DMA area size in bytes.
+ * @param   cbToRead    How much bytes to read in total.
+ * @param   pcbRead     Where to return the DMA bytes read.
+ *
+ * @thread  µEMT
+ */
 static int sb16StreamDoDmaOutput(PSB16STATE pThis, PSB16STREAM pStream, int uDmaChan, uint32_t offDma, uint32_t cbDma,
                                  uint32_t cbToRead, uint32_t *pcbRead)
@@ -1930,4 +1947,6 @@
     //Assert(cbToRead <= cbFree); /** @todo Add statistics for overflows. */
     cbToRead = RT_MIN(cbToRead, cbFree);
+
+    sb16StreamLock(pStream);
 
     uint32_t cbReadTotal = 0;
@@ -1949,4 +1968,6 @@
                 break;
             *pcbRead = 0;
+
+            sb16StreamUnlock(pStream);
             return rc;
         }
@@ -1957,5 +1978,9 @@
             AudioHlpFileWrite(pStream->Dbg.Runtime.pFileDMA, pv, cbRead);
 
+        Log3Func(("Writing %#RX32 bytes to DMA buffer\n", cbRead));
+
         RTCircBufReleaseWriteBlock(pStream->State.pCircBuf, cbRead);
+
+        Log3Func(("Now %#RX32 bytes in buffer\n", RTCircBufUsed(pStream->State.pCircBuf)));
 
         Assert(cbToRead >= cbRead);
@@ -1971,4 +1996,7 @@
     pStream->State.StatDmaBufUsed = (uint32_t)RTCircBufUsed(pStream->State.pCircBuf);
 
+    Log3Func(("[SD%RU8] cbCircBufUsed=%#RX64\n", pStream->uIdx, RTCircBufUsed(pStream->State.pCircBuf)));
+
+    sb16StreamUnlock(pStream);
     return VINF_SUCCESS;
 }
@@ -2012,6 +2040,17 @@
     else
     {
-        rc = AudioMixerSinkDrainAndStop(pSink, pStream->State.pCircBuf ? (uint32_t)RTCircBufUsed(pStream->State.pCircBuf) : 0);
+        /* We need to lock the stream here in order to synchronize the amount of bytes we have to drain
+         * here with the mixer's async I/O thread.  See @bugref{10354}. */
+        sb16StreamLock(pStream);
+
+        uint32_t const cbToDrain = pStream->State.pCircBuf ? (uint32_t)RTCircBufUsed(pStream->State.pCircBuf) : 0;
+        Log3Func(("cbToDrain=%#RX32\n", cbToDrain));
+
+        sb16StreamUnlock(pStream);
+
+        rc = AudioMixerSinkDrainAndStop(pSink, cbToDrain);
         AssertRCReturn(rc, rc);
+
+
     }
 
@@ -2068,4 +2107,7 @@
 {
     LogFlowFuncEnter();
+
+    int rc = RTCritSectInit(&pStream->State.CritSect);
+    AssertRCReturn(rc, rc);
 
     pStream->Dbg.Runtime.fEnabled = pThis->Dbg.fEnabled;
@@ -2112,4 +2154,7 @@
     }
 
+    int rc2 = RTCritSectDelete(&pStream->State.CritSect);
+    AssertRC(rc2);
+
     if (pStream->State.pCircBuf)
     {
@@ -2178,4 +2223,26 @@
 
 /**
+ * Locks a SB16 stream for serialized access.
+ *
+ * @param   pStream             The SB16 stream to lock.
+ */
+DECLINLINE(void) sb16StreamLock(PSB16STREAM pStream)
+{
+    int rc2 = RTCritSectEnter(&pStream->State.CritSect);
+    AssertRC(rc2);
+}
+
+/**
+ * Unlocks a formerly locked SB16 stream.
+ *
+ * @param   pStream             The SB16 stream to unlock.
+ */
+DECLINLINE(void) sb16StreamUnlock(PSB16STREAM pStream)
+{
+    int rc2 = RTCritSectLeave(&pStream->State.CritSect);
+    AssertRC(rc2);
+}
+
+/**
  * Opens a SB16 stream with its current mixer settings.
  *
@@ -2210,45 +2277,57 @@
              PDMAudioPropsSampleBits(&pStream->Cfg.Props)));
 
-    /* (Re-)create the stream's internal ring buffer. */
-    if (pStream->State.pCircBuf)
-    {
-        RTCircBufDestroy(pStream->State.pCircBuf);
-        pStream->State.pCircBuf = NULL;
-    }
-
     /** @todo r=bird: two DMA periods is probably too little. */
     const uint32_t cbCircBuf = PDMAudioPropsMilliToBytes(&pStream->Cfg.Props,
                                                          (RT_MS_1SEC / pStream->uTimerHz) * 2 /* Use double buffering here */);
-
-    int rc = RTCircBufCreate(&pStream->State.pCircBuf, cbCircBuf);
-    AssertRCReturn(rc, rc);
-    pStream->State.StatDmaBufSize = (uint32_t)RTCircBufSize(pStream->State.pCircBuf);
-
-    /* Set scheduling hint. */
-    pStream->Cfg.Device.cMsSchedulingHint = RT_MS_1SEC / RT_MIN(pStream->uTimerHz, 1);
-
-    PAUDMIXSINK pMixerSink = sb16StreamIndexToSink(pThis, pStream->uIdx);
-    AssertPtrReturn(pMixerSink, VERR_INVALID_POINTER);
-
-    sb16RemoveDrvStreams(pDevIns, pThis,
-                         sb16StreamIndexToSink(pThis, pStream->uIdx), pStream->Cfg.enmDir, pStream->Cfg.enmPath);
-
-    rc = sb16AddDrvStreams(pDevIns, pThis, pMixerSink, &pStream->Cfg);
-    if (RT_SUCCESS(rc))
-    {
-        if (RT_LIKELY(!pStream->Dbg.Runtime.fEnabled))
-        { /* likely */ }
-        else
+    int rc = VINF_SUCCESS;
+
+    /**
+     * Note: Only re-create the DMA buffer if the size actually has changed.
+     *
+     *       Otherwise do *not* reset the stream's circular buffer here, as the audio mixer still relies on
+     *       previously announced DMA data (via AudioMixerSinkDrainAndStop()) and processes it asynchronously.
+     *       Resetting the buffer here will cause a race condition.  See @bugref{10354}. */
+    if (pStream->State.StatDmaBufSize != cbCircBuf)
+    {
+        Log3Func(("Re-creating ring buffer (%#RX32 -> %#RX32)\n", pStream->State.StatDmaBufSize, cbCircBuf));
+
+        /* (Re-)create the stream's internal ring buffer. */
+        if (pStream->State.pCircBuf)
         {
-            /* Make sure to close + delete a former debug file, as the PCM format has changed (e.g. U8 -> S16). */
-            if (AudioHlpFileIsOpen(pStream->Dbg.Runtime.pFileDMA))
+            RTCircBufDestroy(pStream->State.pCircBuf);
+            pStream->State.pCircBuf = NULL;
+        }
+
+        rc = RTCircBufCreate(&pStream->State.pCircBuf, cbCircBuf);
+        AssertRCReturn(rc, rc);
+        pStream->State.StatDmaBufSize = (uint32_t)RTCircBufSize(pStream->State.pCircBuf);
+
+        /* Set scheduling hint. */
+        pStream->Cfg.Device.cMsSchedulingHint = RT_MS_1SEC / RT_MIN(pStream->uTimerHz, 1);
+
+        PAUDMIXSINK pMixerSink = sb16StreamIndexToSink(pThis, pStream->uIdx);
+        AssertPtrReturn(pMixerSink, VERR_INVALID_POINTER);
+
+        sb16RemoveDrvStreams(pDevIns, pThis,
+                             sb16StreamIndexToSink(pThis, pStream->uIdx), pStream->Cfg.enmDir, pStream->Cfg.enmPath);
+
+        rc = sb16AddDrvStreams(pDevIns, pThis, pMixerSink, &pStream->Cfg);
+        if (RT_SUCCESS(rc))
+        {
+            if (RT_LIKELY(!pStream->Dbg.Runtime.fEnabled))
+            { /* likely */ }
+            else
             {
-                AudioHlpFileClose(pStream->Dbg.Runtime.pFileDMA);
-                AudioHlpFileDelete(pStream->Dbg.Runtime.pFileDMA);
+                /* Make sure to close + delete a former debug file, as the PCM format has changed (e.g. U8 -> S16). */
+                if (AudioHlpFileIsOpen(pStream->Dbg.Runtime.pFileDMA))
+                {
+                    AudioHlpFileClose(pStream->Dbg.Runtime.pFileDMA);
+                    AudioHlpFileDelete(pStream->Dbg.Runtime.pFileDMA);
+                }
+
+                int rc2 = AudioHlpFileOpen(pStream->Dbg.Runtime.pFileDMA, AUDIOHLPFILE_DEFAULT_OPEN_FLAGS,
+                                           &pStream->Cfg.Props);
+                AssertRC(rc2);
             }
-
-            int rc2 = AudioHlpFileOpen(pStream->Dbg.Runtime.pFileDMA, AUDIOHLPFILE_DEFAULT_OPEN_FLAGS,
-                                       &pStream->Cfg.Props);
-            AssertRC(rc2);
         }
     }
@@ -2303,9 +2382,16 @@
  * @param   pStream         The SB16 stream.
  * @param   pSink           The mixer sink to push to.
+ *
+ * @thread  MixAIO-n
+ *
+ * @note    Takes the stream's lock.
  */
 static void sb16StreamPushToMixer(PSB16STREAM pStream, PAUDMIXSINK pSink)
 {
+    sb16StreamLock(pStream);
+
 #ifdef LOG_ENABLED
     uint64_t const offReadOld = pStream->State.offRead;
+    Log3Func(("[SD%RU8] cbCircBufUsed=%#RX64\n", pStream->uIdx, RTCircBufUsed(pStream->State.pCircBuf)));
 #endif
     pStream->State.offRead = AudioMixerSinkTransferFromCircBuf(pSink,
@@ -2316,9 +2402,11 @@
                                                                ? pStream->Dbg.Runtime.pFileStream :*/ NULL);
 
-    Log3Func(("[SD%RU8] transferred=%#RX64 bytes -> @%#RX64\n", pStream->uIdx,
-              pStream->State.offRead - offReadOld, pStream->State.offRead));
+    Log3Func(("[SD%RU8] transferred=%#RX64 bytes -> @%#RX64 (cbCircBufUsed=%#RX64)\n", pStream->uIdx,
+              pStream->State.offRead - offReadOld, pStream->State.offRead, RTCircBufUsed(pStream->State.pCircBuf)));
 
     /* Update buffer stats. */
     pStream->State.StatDmaBufUsed = (uint32_t)RTCircBufUsed(pStream->State.pCircBuf);
+
+    sb16StreamUnlock(pStream);
 }
 
@@ -2328,6 +2416,8 @@
  *
  * For output streams this moves data from the internal DMA buffer (in which
- * ichac97R3StreamUpdateDma put it), thru the mixer and to the various backend
+ * sb16StreamDoDmaOutput put it), thru the mixer and to the various backend
  * audio devices.
+ *
+ * @thread  MixAIO-n
  */
 static DECLCALLBACK(void) sb16StreamUpdateAsyncIoJob(PPDMDEVINS pDevIns, PAUDMIXSINK pSink, void *pvUser)
