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 thancFragments
,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 thanpStream->State.cbTransferChunk
andpStream->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.