CVE: CVE-2019-3005

Tested Versions:

  • Oracle VirtualBox 6.0.4 revision r128413

Product URL(s): https://virtualbox.org

VirtualBox is a x86 and AMD64/Intel64 virtualization product for enterprise as well as home use. It is a solution commercially supported by Oracle, in addition to being made available as open source software. It runs on various host platforms like Windows, Linux, Mac and Solaris and also supports a large number of guest operating systems.

Vulnerability

Intel HD Audio (HDA) is the default VirtualBox Audio Controller for Windows guests. The vulnerability occurs while processing an audio stream which the guest sends to the host via this emulated device.

The HDA CODEC

Each HD Audio device contains one or more CODECs, and each CODEC contains a list of “nodes” that are connected together in a hierarchical structure, we can send a command to the codec’s root node (node 0) using the CORB ring buffer.

static int hdaR3CORBCmdProcess(PHDASTATE pThis)
{
...    
    uint32_t uCmd  = pThis->pu32CorbBuf[corbRp];
    uint64_t uResp = 0;

    rc = pThis->pCodec->pfnLookup(pThis->pCodec, HDA_CODEC_CMD(uCmd, 0 /* Codec index */), &uResp);
...    
}

The callback for SetStreamId command is vrbProcSetStreamId

static DECLCALLBACK(int) vrbProcSetStreamId(PHDACODEC pThis, uint32_t cmd, uint64_t *pResp)
{
   *pResp = 0;

   uint8_t uSD      = CODEC_F00_06_GET_STREAM_ID(cmd);
...    

   pThis->pfnCbMixerControl(pThis->pHDAState, PDMAUDIOMIXERCTL_LINE_IN, uSD, uChannel);
...    
}

The implementation of pfnCbMixerControl:

static DECLCALLBACK(int) hdaR3MixerControl(PHDASTATE pThis, PDMAUDIOMIXERCTL enmMixerCtl, uint8_t uSD, uint8_t uChannel)
{
...    
    
    /* Detach the existing stream from the sink. */
    if (   pSink->pStream
        && (   pSink->pStream->u8SD      != uSD
            || pSink->pStream->u8Channel != uChannel)
       )
    {
...                    
            pSink->pStream = NULL;
    }

    Assert(uSD < HDA_MAX_STREAMS);

    /* Attach the new stream to the sink.
    * Enabling the stream will be done by the gust via a separate SDnCTL call then. */
    if (pSink->pStream == NULL)
    {
...
        PHDASTREAM pStream = hdaGetStreamFromSD(pThis, uSD);
        if (pStream)
        {
            hdaR3StreamLock(pStream);

            pSink->pStream = pStream;
...                          

            rc = VINF_SUCCESS;
        }
        else
            rc = VERR_NOT_IMPLEMENTED;
...            
}

So we can see the hdaR3MixerControl function tries to:

  • Detach the existing stream from the sink
    • pSink->pStream = NULL;
  • Attach the new stream to the sink
    • PHDASTREAM pStream = hdaGetStreamFromSD(pThis, uSD);

The implementation of hdaGetStreamFromSD function:

PHDASTREAM hdaGetStreamFromSD(PHDASTATE pThis, uint8_t uSD)
{
    AssertPtrReturn(pThis, NULL);
    AssertReturn(uSD < HDA_MAX_STREAMS, NULL);

    if (uSD >= HDA_MAX_STREAMS)
    {
        AssertMsgFailed(("Invalid / non-handled SD%RU8\n", uSD));
        return NULL;
    }

    return &pThis->aStreams[uSD];
}

The stream descriptor number uSD is fully controlled by the guest, so we can make hdaGetStreamFromSD return NULL if we satisfy uSD >= HDA_MAX_STREAMS.

So if pStream is NULL, hdaR3MixerControl doesn’t restore the old stream and leaves pSink->pStream NULL. This will lead to a NULL pointer derefence if this stream is used later.

To trigger the bug:

  • The attacker must first obtain the ability to execute high-privileged code on the target guest system.
  • The guest VM setting must use the default audio controller HDA.

Timeline

  • 2019-09-11 Reported to vendor
  • 2019-10-20 Vendor patched

Vendor Response

The vendor has acknowledged the issue and released an update to address it.

The vendor advisory can be found here: https://www.oracle.com/security-alerts/cpuoct2019.html.