diff options
author | Peter Maydell <peter.maydell@linaro.org> | 2014-09-05 12:26:33 +0100 |
---|---|---|
committer | Peter Maydell <peter.maydell@linaro.org> | 2014-09-05 12:26:33 +0100 |
commit | 5fd7fc8db92adea953ecf5917168989a28e8871f (patch) | |
tree | 53bca29f0f0ea334fe450da08df509cc5ff99e91 | |
parent | fd884c07658d02a96a882b8457d6d7a5cd71a407 (diff) | |
parent | ab9509cceabef28071e41bdfa073083859c949a7 (diff) | |
download | qemu-5fd7fc8db92adea953ecf5917168989a28e8871f.zip |
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>
-rw-r--r-- | hw/display/qxl.c | 1 | ||||
-rw-r--r-- | hw/display/vga.c | 159 | ||||
-rw-r--r-- | hw/display/vga_int.h | 1 | ||||
-rw-r--r-- | ui/spice-display.c | 20 |
4 files changed, 116 insertions, 65 deletions
diff --git a/hw/display/qxl.c b/hw/display/qxl.c index d43aa49eb8..652af99001 100644 --- a/hw/display/qxl.c +++ b/hw/display/qxl.c @@ -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), diff --git a/hw/display/vga.c b/hw/display/vga.c index 65dab8dbed..62e6243c6f 100644 --- a/hw/display/vga.c +++ b/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); diff --git a/hw/display/vga_int.h b/hw/display/vga_int.h index 641f8f41d2..bbc0cb2ad8 100644 --- a/hw/display/vga_int.h +++ b/hw/display/vga_int.h @@ -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; diff --git a/ui/spice-display.c b/ui/spice-display.c index 66e25788ce..def7b52e9c 100644 --- a/ui/spice-display.c +++ b/ui/spice-display.c @@ -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; } |