CVE-2014-3615: fix sanity checks in vbe (bochs dispi) and spice.
-----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.22 (GNU/Linux) iQIcBAABAgAGBQJUCZvsAAoJEEy22O7T6HE4D80P/j64duoXcM9G3tWq2L3ki+dA t46OnRdMXSb5O5vJkoJ0ZzujgRleHLbV2D5rfFuhWK0slPdDw3dN9aH2FCerI1lD 5iXGrIsZ6hhxRTYCVTQ8ibttnCtFxhq0YZcJ/8XbHiUH/EaHPHMXWEgxG4B2FgRB QXsjQsh+1nIKN41rsNzsCVyxFeTo1pU2aKFbFDbOvZpU0I/hksR/jXhg02Om0zCc l8XgLpQfJmhORgA1dptWFLBnsE3ILs+0nfhNt3HBrWn32lLG0bnIFMfcUbn+T5Do 97aIv68/qFntcNeO/cFouV+3hsl8QE3Qg9bayOWT5ZutCEtOy8wEAtCx4bzep46a PM2NbvTBDjPAK0D8Bkr8wvgYeL2ROtuskcLgfcWlbutx3qGlJ1aj7a+OxlUD1yUM C24FR2sd3UYl9OX78Vn4DuCR094uILWcNq/5Ym2hi8aWF3TdempcOiQWOJq8Fnbs Y0j9O5FxFGVJ0Vt43yjqqwpZMZNqx8zR4UFOx/GuYlRdImz8W4+VeY+/sbVQrMxN pwnlQmx+IRp3TuLdnjwU/+7tqRgGyiMqaqLW07fVCSD8glGB60wlYh1ZydZPPy4K Ve71qT420p7zjMmlKTBU9IVsJKgSBMovd7M1oLrslxbnnBYxxetO4rMRTEoHxSui rT88qImcGxI33oJcOeoq =fF3a -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/kraxel/tags/pull-cve-2014-3615-20140905-1' into staging CVE-2014-3615: fix sanity checks in vbe (bochs dispi) and spice. # gpg: Signature made Fri 05 Sep 2014 12:18:04 BST using RSA key ID D3E87138 # gpg: Good signature from "Gerd Hoffmann (work) <kraxel@redhat.com>" # gpg: aka "Gerd Hoffmann <gerd@kraxel.org>" # gpg: aka "Gerd Hoffmann (private) <kraxel@gmail.com>" * remotes/kraxel/tags/pull-cve-2014-3615-20140905-1: spice: make sure we don't overflow ssd->buf vbe: rework sanity checks vbe: make bochs dispi interface return the correct memory size with qxl Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
5fd7fc8db9
@ -2063,6 +2063,7 @@ static int qxl_init_primary(PCIDevice *dev)
|
||||
|
||||
qxl->id = 0;
|
||||
qxl_init_ramsize(qxl);
|
||||
vga->vbe_size = qxl->vgamem_size;
|
||||
vga->vram_size_mb = qxl->vga.vram_size >> 20;
|
||||
vga_common_init(vga, OBJECT(dev), true);
|
||||
vga_init(vga, OBJECT(dev),
|
||||
|
159
hw/display/vga.c
159
hw/display/vga.c
@ -576,6 +576,93 @@ void vga_ioport_write(void *opaque, uint32_t addr, uint32_t val)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Sanity check vbe register writes.
|
||||
*
|
||||
* As we don't have a way to signal errors to the guest in the bochs
|
||||
* dispi interface we'll go adjust the registers to the closest valid
|
||||
* value.
|
||||
*/
|
||||
static void vbe_fixup_regs(VGACommonState *s)
|
||||
{
|
||||
uint16_t *r = s->vbe_regs;
|
||||
uint32_t bits, linelength, maxy, offset;
|
||||
|
||||
if (!(r[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED)) {
|
||||
/* vbe is turned off -- nothing to do */
|
||||
return;
|
||||
}
|
||||
|
||||
/* check depth */
|
||||
switch (r[VBE_DISPI_INDEX_BPP]) {
|
||||
case 4:
|
||||
case 8:
|
||||
case 16:
|
||||
case 24:
|
||||
case 32:
|
||||
bits = r[VBE_DISPI_INDEX_BPP];
|
||||
break;
|
||||
case 15:
|
||||
bits = 16;
|
||||
break;
|
||||
default:
|
||||
bits = r[VBE_DISPI_INDEX_BPP] = 8;
|
||||
break;
|
||||
}
|
||||
|
||||
/* check width */
|
||||
r[VBE_DISPI_INDEX_XRES] &= ~7u;
|
||||
if (r[VBE_DISPI_INDEX_XRES] == 0) {
|
||||
r[VBE_DISPI_INDEX_XRES] = 8;
|
||||
}
|
||||
if (r[VBE_DISPI_INDEX_XRES] > VBE_DISPI_MAX_XRES) {
|
||||
r[VBE_DISPI_INDEX_XRES] = VBE_DISPI_MAX_XRES;
|
||||
}
|
||||
r[VBE_DISPI_INDEX_VIRT_WIDTH] &= ~7u;
|
||||
if (r[VBE_DISPI_INDEX_VIRT_WIDTH] > VBE_DISPI_MAX_XRES) {
|
||||
r[VBE_DISPI_INDEX_VIRT_WIDTH] = VBE_DISPI_MAX_XRES;
|
||||
}
|
||||
if (r[VBE_DISPI_INDEX_VIRT_WIDTH] < r[VBE_DISPI_INDEX_XRES]) {
|
||||
r[VBE_DISPI_INDEX_VIRT_WIDTH] = r[VBE_DISPI_INDEX_XRES];
|
||||
}
|
||||
|
||||
/* check height */
|
||||
linelength = r[VBE_DISPI_INDEX_VIRT_WIDTH] * bits / 8;
|
||||
maxy = s->vbe_size / linelength;
|
||||
if (r[VBE_DISPI_INDEX_YRES] == 0) {
|
||||
r[VBE_DISPI_INDEX_YRES] = 1;
|
||||
}
|
||||
if (r[VBE_DISPI_INDEX_YRES] > VBE_DISPI_MAX_YRES) {
|
||||
r[VBE_DISPI_INDEX_YRES] = VBE_DISPI_MAX_YRES;
|
||||
}
|
||||
if (r[VBE_DISPI_INDEX_YRES] > maxy) {
|
||||
r[VBE_DISPI_INDEX_YRES] = maxy;
|
||||
}
|
||||
|
||||
/* check offset */
|
||||
if (r[VBE_DISPI_INDEX_X_OFFSET] > VBE_DISPI_MAX_XRES) {
|
||||
r[VBE_DISPI_INDEX_X_OFFSET] = VBE_DISPI_MAX_XRES;
|
||||
}
|
||||
if (r[VBE_DISPI_INDEX_Y_OFFSET] > VBE_DISPI_MAX_YRES) {
|
||||
r[VBE_DISPI_INDEX_Y_OFFSET] = VBE_DISPI_MAX_YRES;
|
||||
}
|
||||
offset = r[VBE_DISPI_INDEX_X_OFFSET] * bits / 8;
|
||||
offset += r[VBE_DISPI_INDEX_Y_OFFSET] * linelength;
|
||||
if (offset + r[VBE_DISPI_INDEX_YRES] * linelength > s->vbe_size) {
|
||||
r[VBE_DISPI_INDEX_Y_OFFSET] = 0;
|
||||
offset = r[VBE_DISPI_INDEX_X_OFFSET] * bits / 8;
|
||||
if (offset + r[VBE_DISPI_INDEX_YRES] * linelength > s->vbe_size) {
|
||||
r[VBE_DISPI_INDEX_X_OFFSET] = 0;
|
||||
offset = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* update vga state */
|
||||
r[VBE_DISPI_INDEX_VIRT_HEIGHT] = maxy;
|
||||
s->vbe_line_offset = linelength;
|
||||
s->vbe_start_addr = offset / 4;
|
||||
}
|
||||
|
||||
static uint32_t vbe_ioport_read_index(void *opaque, uint32_t addr)
|
||||
{
|
||||
VGACommonState *s = opaque;
|
||||
@ -610,7 +697,7 @@ uint32_t vbe_ioport_read_data(void *opaque, uint32_t addr)
|
||||
val = s->vbe_regs[s->vbe_index];
|
||||
}
|
||||
} else if (s->vbe_index == VBE_DISPI_INDEX_VIDEO_MEMORY_64K) {
|
||||
val = s->vram_size / (64 * 1024);
|
||||
val = s->vbe_size / (64 * 1024);
|
||||
} else {
|
||||
val = 0;
|
||||
}
|
||||
@ -645,22 +732,13 @@ void vbe_ioport_write_data(void *opaque, uint32_t addr, uint32_t val)
|
||||
}
|
||||
break;
|
||||
case VBE_DISPI_INDEX_XRES:
|
||||
if ((val <= VBE_DISPI_MAX_XRES) && ((val & 7) == 0)) {
|
||||
s->vbe_regs[s->vbe_index] = val;
|
||||
}
|
||||
break;
|
||||
case VBE_DISPI_INDEX_YRES:
|
||||
if (val <= VBE_DISPI_MAX_YRES) {
|
||||
s->vbe_regs[s->vbe_index] = val;
|
||||
}
|
||||
break;
|
||||
case VBE_DISPI_INDEX_BPP:
|
||||
if (val == 0)
|
||||
val = 8;
|
||||
if (val == 4 || val == 8 || val == 15 ||
|
||||
val == 16 || val == 24 || val == 32) {
|
||||
s->vbe_regs[s->vbe_index] = val;
|
||||
}
|
||||
case VBE_DISPI_INDEX_VIRT_WIDTH:
|
||||
case VBE_DISPI_INDEX_X_OFFSET:
|
||||
case VBE_DISPI_INDEX_Y_OFFSET:
|
||||
s->vbe_regs[s->vbe_index] = val;
|
||||
vbe_fixup_regs(s);
|
||||
break;
|
||||
case VBE_DISPI_INDEX_BANK:
|
||||
if (s->vbe_regs[VBE_DISPI_INDEX_BPP] == 4) {
|
||||
@ -677,19 +755,11 @@ void vbe_ioport_write_data(void *opaque, uint32_t addr, uint32_t val)
|
||||
!(s->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED)) {
|
||||
int h, shift_control;
|
||||
|
||||
s->vbe_regs[VBE_DISPI_INDEX_VIRT_WIDTH] =
|
||||
s->vbe_regs[VBE_DISPI_INDEX_XRES];
|
||||
s->vbe_regs[VBE_DISPI_INDEX_VIRT_HEIGHT] =
|
||||
s->vbe_regs[VBE_DISPI_INDEX_YRES];
|
||||
s->vbe_regs[VBE_DISPI_INDEX_VIRT_WIDTH] = 0;
|
||||
s->vbe_regs[VBE_DISPI_INDEX_X_OFFSET] = 0;
|
||||
s->vbe_regs[VBE_DISPI_INDEX_Y_OFFSET] = 0;
|
||||
|
||||
if (s->vbe_regs[VBE_DISPI_INDEX_BPP] == 4)
|
||||
s->vbe_line_offset = s->vbe_regs[VBE_DISPI_INDEX_XRES] >> 1;
|
||||
else
|
||||
s->vbe_line_offset = s->vbe_regs[VBE_DISPI_INDEX_XRES] *
|
||||
((s->vbe_regs[VBE_DISPI_INDEX_BPP] + 7) >> 3);
|
||||
s->vbe_start_addr = 0;
|
||||
s->vbe_regs[VBE_DISPI_INDEX_ENABLE] |= VBE_DISPI_ENABLED;
|
||||
vbe_fixup_regs(s);
|
||||
|
||||
/* clear the screen (should be done in BIOS) */
|
||||
if (!(val & VBE_DISPI_NOCLEARMEM)) {
|
||||
@ -738,40 +808,6 @@ void vbe_ioport_write_data(void *opaque, uint32_t addr, uint32_t val)
|
||||
s->vbe_regs[s->vbe_index] = val;
|
||||
vga_update_memory_access(s);
|
||||
break;
|
||||
case VBE_DISPI_INDEX_VIRT_WIDTH:
|
||||
{
|
||||
int w, h, line_offset;
|
||||
|
||||
if (val < s->vbe_regs[VBE_DISPI_INDEX_XRES])
|
||||
return;
|
||||
w = val;
|
||||
if (s->vbe_regs[VBE_DISPI_INDEX_BPP] == 4)
|
||||
line_offset = w >> 1;
|
||||
else
|
||||
line_offset = w * ((s->vbe_regs[VBE_DISPI_INDEX_BPP] + 7) >> 3);
|
||||
h = s->vram_size / line_offset;
|
||||
/* XXX: support weird bochs semantics ? */
|
||||
if (h < s->vbe_regs[VBE_DISPI_INDEX_YRES])
|
||||
return;
|
||||
s->vbe_regs[VBE_DISPI_INDEX_VIRT_WIDTH] = w;
|
||||
s->vbe_regs[VBE_DISPI_INDEX_VIRT_HEIGHT] = h;
|
||||
s->vbe_line_offset = line_offset;
|
||||
}
|
||||
break;
|
||||
case VBE_DISPI_INDEX_X_OFFSET:
|
||||
case VBE_DISPI_INDEX_Y_OFFSET:
|
||||
{
|
||||
int x;
|
||||
s->vbe_regs[s->vbe_index] = val;
|
||||
s->vbe_start_addr = s->vbe_line_offset * s->vbe_regs[VBE_DISPI_INDEX_Y_OFFSET];
|
||||
x = s->vbe_regs[VBE_DISPI_INDEX_X_OFFSET];
|
||||
if (s->vbe_regs[VBE_DISPI_INDEX_BPP] == 4)
|
||||
s->vbe_start_addr += x >> 1;
|
||||
else
|
||||
s->vbe_start_addr += x * ((s->vbe_regs[VBE_DISPI_INDEX_BPP] + 7) >> 3);
|
||||
s->vbe_start_addr >>= 2;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -2285,6 +2321,9 @@ void vga_common_init(VGACommonState *s, Object *obj, bool global_vmstate)
|
||||
s->vram_size <<= 1;
|
||||
}
|
||||
s->vram_size_mb = s->vram_size >> 20;
|
||||
if (!s->vbe_size) {
|
||||
s->vbe_size = s->vram_size;
|
||||
}
|
||||
|
||||
s->is_vbe_vmstate = 1;
|
||||
memory_region_init_ram(&s->vram, obj, "vga.vram", s->vram_size);
|
||||
|
@ -93,6 +93,7 @@ typedef struct VGACommonState {
|
||||
MemoryRegion vram_vbe;
|
||||
uint32_t vram_size;
|
||||
uint32_t vram_size_mb; /* property */
|
||||
uint32_t vbe_size;
|
||||
uint32_t latch;
|
||||
bool has_chain4_alias;
|
||||
MemoryRegion chain4_alias;
|
||||
|
@ -334,11 +334,23 @@ void qemu_spice_create_host_memslot(SimpleSpiceDisplay *ssd)
|
||||
void qemu_spice_create_host_primary(SimpleSpiceDisplay *ssd)
|
||||
{
|
||||
QXLDevSurfaceCreate surface;
|
||||
uint64_t surface_size;
|
||||
|
||||
memset(&surface, 0, sizeof(surface));
|
||||
|
||||
dprint(1, "%s/%d: %dx%d\n", __func__, ssd->qxl.id,
|
||||
surface_width(ssd->ds), surface_height(ssd->ds));
|
||||
surface_size = (uint64_t) surface_width(ssd->ds) *
|
||||
surface_height(ssd->ds) * 4;
|
||||
assert(surface_size > 0);
|
||||
assert(surface_size < INT_MAX);
|
||||
if (ssd->bufsize < surface_size) {
|
||||
ssd->bufsize = surface_size;
|
||||
g_free(ssd->buf);
|
||||
ssd->buf = g_malloc(ssd->bufsize);
|
||||
}
|
||||
|
||||
dprint(1, "%s/%d: %ux%u (size %" PRIu64 "/%d)\n", __func__, ssd->qxl.id,
|
||||
surface_width(ssd->ds), surface_height(ssd->ds),
|
||||
surface_size, ssd->bufsize);
|
||||
|
||||
surface.format = SPICE_SURFACE_FMT_32_xRGB;
|
||||
surface.width = surface_width(ssd->ds);
|
||||
@ -369,8 +381,6 @@ void qemu_spice_display_init_common(SimpleSpiceDisplay *ssd)
|
||||
if (ssd->num_surfaces == 0) {
|
||||
ssd->num_surfaces = 1024;
|
||||
}
|
||||
ssd->bufsize = (16 * 1024 * 1024);
|
||||
ssd->buf = g_malloc(ssd->bufsize);
|
||||
}
|
||||
|
||||
/* display listener callbacks */
|
||||
@ -495,7 +505,7 @@ static void interface_get_init_info(QXLInstance *sin, QXLDevInitInfo *info)
|
||||
info->num_memslots = NUM_MEMSLOTS;
|
||||
info->num_memslots_groups = NUM_MEMSLOTS_GROUPS;
|
||||
info->internal_groupslot_id = 0;
|
||||
info->qxl_ram_size = ssd->bufsize;
|
||||
info->qxl_ram_size = 16 * 1024 * 1024;
|
||||
info->n_surfaces = ssd->num_surfaces;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user