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->u32CBLis less thancFragments,pStream->State.cbTransferSizewill be 0 - The
Assertmacro will be stripped out in the release build so it has no effect. cFragments,pStream->State.cbTransferSizeis 0, so it will less thanpStream->State.cbTransferChunkandpStream->State.cbTransferChunkwill be set to 0.- The expression
cTicksPerHz / pStream->State.cbTransferChunkwill 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.