CVE: CVE-2019-3002

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 stream

The HDA stream can be reinitialized with a controlled value from the guest by modifying the Stream Descriptor Control register:

static int hdaRegWriteSDCTL(PHDASTATE pThis, uint32_t iReg, uint32_t u32Value)
{
#ifdef IN_RING3
    /* Get the stream descriptor. */
    const uint8_t uSD = HDA_SD_NUM_FROM_REG(pThis, CTL, iReg);
    
....
    
    PHDASTREAM pStream = hdaGetStreamFromSD(pThis, uSD);
....    
                /* (Re-)initialize the stream with current values. */
                rc2 = hdaR3StreamInit(pStream, pStream->u8SD);
....                
                
    }

hdaR3StreamInit()

The hdaR3StreamInit() function initializes an HDA stream with some other configured registers (BDPL, BDPU, LVI, CBL, FIFOS, FMT) which also controlled by the guest:

int hdaR3StreamInit(PHDASTREAM pStream, uint8_t uSD)
{
    AssertPtrReturn(pStream, VERR_INVALID_POINTER);

    PHDASTATE pThis = pStream->pHDAState;
    AssertPtr(pThis);

    const uint64_t u64BDLBase = RT_MAKE_U64(HDA_STREAM_REG(pThis, BDPL, uSD),
                                            HDA_STREAM_REG(pThis, BDPU, uSD));
    const uint16_t u16LVI     = HDA_STREAM_REG(pThis, LVI, uSD);
    const uint32_t u32CBL     = HDA_STREAM_REG(pThis, CBL, uSD);
    const uint16_t u16FIFOS   = HDA_STREAM_REG(pThis, FIFOS, uSD) + 1;
    const uint16_t u16FMT     = HDA_STREAM_REG(pThis, FMT, uSD);
    
....    
    
    pStream->u8SD       = uSD;

    /* Update all register copies so that we later know that something has changed. */
    pStream->u64BDLBase = u64BDLBase;
    pStream->u16LVI     = u16LVI;
    pStream->u32CBL     = u32CBL;
    pStream->u16FIFOS   = u16FIFOS;
    pStream->u16FMT     = u16FMT;
    
....    

Set those registers properly, we can reach to the following code:

int hdaR3StreamInit(PHDASTREAM pStream, uint8_t uSD)
{
    
....

        pStream->State.cbTransferSize = pStream->u32CBL / cFragments;
        Assert(pStream->State.cbTransferSize);

....   

        if (pStream->State.cbTransferChunk > pStream->State.cbTransferSize)
            pStream->State.cbTransferChunk = pStream->State.cbTransferSize;    

....
        
        pStream->State.cTicksPerByte = cTicksPerHz / pStream->State.cbTransferChunk;
        Assert(pStream->State.cTicksPerByte);
    
....    

pStream->u32CBL is also controlled by the guest so:

  • If pStream->u32CBL is less than cFragments, pStream->State.cbTransferSize will be 0
  • The Assert macro will be stripped out in the release build so it has no effect.
  • cFragments, pStream->State.cbTransferSize is 0, so it will less than pStream->State.cbTransferChunk and pStream->State.cbTransferChunk will be set to 0.
  • The expression cTicksPerHz / pStream->State.cbTransferChunk will thus result in a Divide by Zero exception and crashes the VM

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.