Out-of-bounds read/write in QEMU PC SMBus emulation

Author: Michael Hanselmann. Updated: December 6, 2018.

QEMU is a virtual machine emulator. In early December 2018 I identified an out-of-bounds read and write vulnerability in its “PC SMBus” emulation by reading the source code. The responsible source code was added in August 2018 and is part of QEMU 3.1. After producing proof-of-concept exploits I sent a report on the vulnerability to the project while applying responsible disclosure principles. As the source code wasn't part of any final release it was directly fixed in QEMU v3.1.0-rc5 release (commit f2609ffdf3) and no CVE was assigned.

Reproduction environment

QEMU master branch as of December 5, 2018, commit 80422b0019 (v3.1.0-rc4). The file in question, hw/i2c/pm_smbus.c, was last modified in commit 45726b6e2c (August 2018). Built and running on Debian 9 (Stretch) with gcc version 6.3.0 20170516 (Debian 6.3.0-18+deb9u1). Configure command:

Reading arbitrary memory beyond the buffer

My proof-of-concept exploit starts off with getting the bus emulation into a defined state (index to zero, various registers set to known values). While testing it may be necessary to unload any kernel module using the I²C bus (usually i2c_*). To cut the chase we'll read some memory:

What we're seeing are strings allocated by g_strdup for QEMU object properties. Reading the 1000 bytes at the offset of 6600 took just over 5 minutes in my test environment. It's not particularly fast, but it's very reliable if no other in-guest software such as a kernel module tries to interact with the bus in the meantime.

The function pointer stored in reset by pm_smbus_init is readily available:

Writing beyond buffer

I've tried to find ways to find arbitrary bytes beyond the buffer, but was only successful in reliably writing individual bytes within 255 bytes. Repeating a single byte read from smb_blkdata up to a specific offset would also be possible, though less interesting. Setting smb_blkdata while the index is equal or greater than PM_SMBUS_MAX_MSG_SIZE (32) leads to the index being reset and tracking the uint32_t index in smb_data0 is only possible up to 255 as the latter is of type uint8_t. Moving the index by reading overwrites smb_blkdata.

What the proof-of-concept exploit instead does is corrupt a function pointer, namely set_irq. smb_ioport_writeb will try to invoke the function before returning, leading to a segmentation fault.

Virtual machines using the ICH9 chipset emulation can trigger the reset function from the guest system (ICH9_SMB_HOSTC_SSRESET). The pointer is well within the 255 bytes and if written from MSB to LSB could be easily controlled by a guest (implementation left as an exercise for the reader).