mirror of https://github.com/xemu-project/xemu.git
hw/display/qxl: Avoid buffer overrun in qxl_phys2virt (CVE-2022-4144)
Have qxl_get_check_slot_offset() return false if the requested buffer size does not fit within the slot memory region. Similarly qxl_phys2virt() now returns NULL in such case, and qxl_dirty_one_surface() aborts. This avoids buffer overrun in the host pointer returned by memory_region_get_ram_ptr(). Fixes: CVE-2022-4144 (out-of-bounds read) Reported-by: Wenxu Yin (@awxylitol) Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1336 Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com> Message-Id: <20221128202741.4945-5-philmd@linaro.org>
This commit is contained in:
parent
8efec0ef8b
commit
6dbbf05514
|
@ -1424,11 +1424,13 @@ static void qxl_reset_surfaces(PCIQXLDevice *d)
|
||||||
|
|
||||||
/* can be also called from spice server thread context */
|
/* can be also called from spice server thread context */
|
||||||
static bool qxl_get_check_slot_offset(PCIQXLDevice *qxl, QXLPHYSICAL pqxl,
|
static bool qxl_get_check_slot_offset(PCIQXLDevice *qxl, QXLPHYSICAL pqxl,
|
||||||
uint32_t *s, uint64_t *o)
|
uint32_t *s, uint64_t *o,
|
||||||
|
size_t size_requested)
|
||||||
{
|
{
|
||||||
uint64_t phys = le64_to_cpu(pqxl);
|
uint64_t phys = le64_to_cpu(pqxl);
|
||||||
uint32_t slot = (phys >> (64 - 8)) & 0xff;
|
uint32_t slot = (phys >> (64 - 8)) & 0xff;
|
||||||
uint64_t offset = phys & 0xffffffffffff;
|
uint64_t offset = phys & 0xffffffffffff;
|
||||||
|
uint64_t size_available;
|
||||||
|
|
||||||
if (slot >= NUM_MEMSLOTS) {
|
if (slot >= NUM_MEMSLOTS) {
|
||||||
qxl_set_guest_bug(qxl, "slot too large %d >= %d", slot,
|
qxl_set_guest_bug(qxl, "slot too large %d >= %d", slot,
|
||||||
|
@ -1452,6 +1454,23 @@ static bool qxl_get_check_slot_offset(PCIQXLDevice *qxl, QXLPHYSICAL pqxl,
|
||||||
slot, offset, qxl->guest_slots[slot].size);
|
slot, offset, qxl->guest_slots[slot].size);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
size_available = memory_region_size(qxl->guest_slots[slot].mr);
|
||||||
|
if (qxl->guest_slots[slot].offset + offset >= size_available) {
|
||||||
|
qxl_set_guest_bug(qxl,
|
||||||
|
"slot %d offset %"PRIu64" > region size %"PRIu64"\n",
|
||||||
|
slot, qxl->guest_slots[slot].offset + offset,
|
||||||
|
size_available);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
size_available -= qxl->guest_slots[slot].offset + offset;
|
||||||
|
if (size_requested > size_available) {
|
||||||
|
qxl_set_guest_bug(qxl,
|
||||||
|
"slot %d offset %"PRIu64" size %zu: "
|
||||||
|
"overrun by %"PRIu64" bytes\n",
|
||||||
|
slot, offset, size_requested,
|
||||||
|
size_requested - size_available);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
*s = slot;
|
*s = slot;
|
||||||
*o = offset;
|
*o = offset;
|
||||||
|
@ -1471,7 +1490,7 @@ void *qxl_phys2virt(PCIQXLDevice *qxl, QXLPHYSICAL pqxl, int group_id,
|
||||||
offset = le64_to_cpu(pqxl) & 0xffffffffffff;
|
offset = le64_to_cpu(pqxl) & 0xffffffffffff;
|
||||||
return (void *)(intptr_t)offset;
|
return (void *)(intptr_t)offset;
|
||||||
case MEMSLOT_GROUP_GUEST:
|
case MEMSLOT_GROUP_GUEST:
|
||||||
if (!qxl_get_check_slot_offset(qxl, pqxl, &slot, &offset)) {
|
if (!qxl_get_check_slot_offset(qxl, pqxl, &slot, &offset, size)) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
ptr = memory_region_get_ram_ptr(qxl->guest_slots[slot].mr);
|
ptr = memory_region_get_ram_ptr(qxl->guest_slots[slot].mr);
|
||||||
|
@ -1937,9 +1956,9 @@ static void qxl_dirty_one_surface(PCIQXLDevice *qxl, QXLPHYSICAL pqxl,
|
||||||
uint32_t slot;
|
uint32_t slot;
|
||||||
bool rc;
|
bool rc;
|
||||||
|
|
||||||
rc = qxl_get_check_slot_offset(qxl, pqxl, &slot, &offset);
|
|
||||||
assert(rc == true);
|
|
||||||
size = (uint64_t)height * abs(stride);
|
size = (uint64_t)height * abs(stride);
|
||||||
|
rc = qxl_get_check_slot_offset(qxl, pqxl, &slot, &offset, size);
|
||||||
|
assert(rc == true);
|
||||||
trace_qxl_surfaces_dirty(qxl->id, offset, size);
|
trace_qxl_surfaces_dirty(qxl->id, offset, size);
|
||||||
qxl_set_dirty(qxl->guest_slots[slot].mr,
|
qxl_set_dirty(qxl->guest_slots[slot].mr,
|
||||||
qxl->guest_slots[slot].offset + offset,
|
qxl->guest_slots[slot].offset + offset,
|
||||||
|
|
|
@ -157,7 +157,7 @@ OBJECT_DECLARE_SIMPLE_TYPE(PCIQXLDevice, PCI_QXL)
|
||||||
*
|
*
|
||||||
* Returns a host pointer to a buffer placed at offset @phys within the
|
* Returns a host pointer to a buffer placed at offset @phys within the
|
||||||
* active slot @group_id of the PCI VGA RAM memory region associated with
|
* active slot @group_id of the PCI VGA RAM memory region associated with
|
||||||
* the @qxl device. If the slot is inactive, or the offset is out
|
* the @qxl device. If the slot is inactive, or the offset + size are out
|
||||||
* of the memory region, returns NULL.
|
* of the memory region, returns NULL.
|
||||||
*
|
*
|
||||||
* Use with care; by the time this function returns, the returned pointer is
|
* Use with care; by the time this function returns, the returned pointer is
|
||||||
|
|
Loading…
Reference in New Issue