All of lore.kernel.org
 help / color / mirror / Atom feed
From: Laszlo Ersek <lersek@redhat.com>
To: Gerd Hoffmann <kraxel@redhat.com>, qemu-devel@nongnu.org
Cc: Petr Matousek <pmatouse@redhat.com>,
	secalert@redhat.com, qemu-stable@nongnu.org,
	P J P <ppandit@redhat.com>, Anthony Liguori <aliguori@amazon.com>,
	spice-devel@lists.freedesktop.org
Subject: Re: [Qemu-devel] [CVE-2014-3615 PATCH v2 3/3] spice: make sure we don't overflow ssd->buf
Date: Fri, 05 Sep 2014 09:52:34 +0200	[thread overview]
Message-ID: <54096BC2.7070102@redhat.com> (raw)
In-Reply-To: <1409814273-11463-4-git-send-email-kraxel@redhat.com>

On 09/04/14 09:04, Gerd Hoffmann wrote:
> Related spice-only bug.  We have a fixed 16 MB buffer here, being
> presented to the spice-server as qxl video memory in case spice is
> used with a non-qxl card.  It's also used with qxl in vga mode.
> 
> When using display resolutions requiring more than 16 MB of memory we
> are going to overflow that buffer.  In theory the guest can write,
> indirectly via spice-server.  The spice-server clears the memory after
> setting a new video mode though, triggering a segfault in the overflow
> case, so qemu crashes before the guest has a chance to do something
> evil.
> 
> Fix that by switching to dynamic allocation for the buffer.
> 
> CVE-2014-3615
> 
> Cc: qemu-stable@nongnu.org
> Cc: secalert@redhat.com
> Cc: Laszlo Ersek <lersek@redhat.com>
> Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
> ---
>  ui/spice-display.c | 16 ++++++++++++----
>  1 file changed, 12 insertions(+), 4 deletions(-)
> 
> diff --git a/ui/spice-display.c b/ui/spice-display.c
> index 66e2578..57a8fd0 100644
> --- a/ui/spice-display.c
> +++ b/ui/spice-display.c
> @@ -334,6 +334,7 @@ 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));
>  
> @@ -347,9 +348,18 @@ void qemu_spice_create_host_primary(SimpleSpiceDisplay *ssd)
>      surface.mouse_mode = true;
>      surface.flags      = 0;
>      surface.type       = 0;
> -    surface.mem        = (uintptr_t)ssd->buf;
>      surface.group_id   = MEMSLOT_GROUP_HOST;
>  
> +    surface_size = surface.width * surface.height * 4;

(1) surface.width and surface.height are uint32_t fields
[spice-server/server/spice.h]:

struct QXLDevSurfaceCreate {
    uint32_t width;
    uint32_t height;

uint32_t equals "unsigned int" in our case, hence the multiplication
will be carried out in "unsigned int" -- 32-bits. Given that the
dimensions here are under guest control, I suggest to write it as

    surface_size = (uint64_t)surface.width * surface.height * 4;

(2) However, I have a concern even that way.

The above multiplication (due to the *4) can overflow in uint64_t as well.

I can see that "surface.width" and "surface.height" come from
pixman_image_get_width() and pixman_image_get_height(), which seem to
return "int" values. Hence,

    (uint64_t)0x7FFF_FFFF * 0x7FFF_FFFF * 4 == 0xFFFF_FFFC_0000_0004

which is OK. But, what if pixman returns a negative value? Can we create
an image that big?

>From qemu_spice_create_one_update():

    int bw, bh;

    bw       = rect->right - rect->left;
    bh       = rect->bottom - rect->top;

    dest = pixman_image_create_bits(PIXMAN_x8r8g8b8, bw, bh,
                                    (void *)update->bitmap, bw * 4);

where

typedef struct SPICE_ATTR_PACKED QXLRect {
    int32_t top;
    int32_t left;
    int32_t bottom;
    int32_t right;
} QXLRect;

....

I can't track this back far enough. I'd feel safer if you checked that
the multiplication can't overflow even in uint64_t.

(3) In addition:

> +    if (ssd->bufsize < surface_size) {
> +        ssd->bufsize = surface_size;

struct SimpleSpiceDisplay {
[...]
    int bufsize;

Meaning, even if the multiplication fits okay in an uint64_t, the above
assignment can overflow ssd->bufsize, because that's just an int.

> +        fprintf(stderr, "%s: %" PRId64 " (%dx%d)\n", __func__,
> +                surface_size, surface.width, surface.height);

(4) surface_size is uint64_t, it should be printed with PRIu64, not
PRId64. Similarly, surface.width and surface.height are uint32_t, they
should be printed with either %u or PRIu32, not %d.

> +        g_free(ssd->buf);
> +        ssd->buf = g_malloc(ssd->bufsize);

Then, g_malloc() takes a "gsize", which means "unsigned long":

https://developer.gnome.org/glib/2.28/glib-Memory-Allocation.html#g-malloc
https://developer.gnome.org/glib/2.28/glib-Basic-Types.html#gsize

which has the range of uint32_t in an ILP32 (i686) build. Therefore even
changing the type of ssd->bufsize to uint64_t wouldn't help.

(5) Instead, you really need to make sure that the very first
multiplication fits in a signed int:

    int width, height;

    width = surface_width(ssd->ds);
    height = surface_height(ssd->ds);

    if (width <= 0 || height <= 0 ||
        width > INT_MAX / 4 ||
        height > INT_MAX / (width * 4)) {
        /* won't ever fit */
        abort();
    }

    if (ssd->bufsize < width * height * 4) {
        ssd->bufsize = width * height * 4;
        /* do the rest of the realloc */
    }

and do everything else after, even the population of "surface"'s fields.

> +    }
> +    surface.mem = (uintptr_t)ssd->buf;
> +
>      qemu_spice_create_primary_surface(ssd, 0, &surface, QXL_SYNC);
>  }
>  
> @@ -369,8 +379,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);
>  }

I'm okay with this as long as you guarantee that the dimensions the
guest specifies will be strictly greater than zero. Otherwise, the
product could be zero, and I quite dislike g_malloc(0) calls -- "If
n_bytes is 0 it returns NULL", which could be problematic elsewhere.

The code I proposed above makes sure that both dimensions are positive.

>  
>  /* display listener callbacks */
> @@ -495,7 +503,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;
>  }

Is it safe to detach these two from each other? You could actually leave
the initial 16MB alloc in place.

Thanks
Laszlo

  reply	other threads:[~2014-09-05  7:52 UTC|newest]

Thread overview: 9+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2014-09-04  7:04 [Qemu-devel] [CVE-2014-3615 PATCH v2 0/3] vbe: bochs dispi interface fixes Gerd Hoffmann
2014-09-04  7:04 ` [Qemu-devel] [CVE-2014-3615 PATCH v2 1/3] vbe: make bochs dispi interface return the correct memory size with qxl Gerd Hoffmann
2014-09-04  7:04 ` [Qemu-devel] [CVE-2014-3615 PATCH v2 2/3] vbe: rework sanity checks Gerd Hoffmann
2014-09-04  7:04 ` [Qemu-devel] [CVE-2014-3615 PATCH v2 3/3] spice: make sure we don't overflow ssd->buf Gerd Hoffmann
2014-09-05  7:52   ` Laszlo Ersek [this message]
2014-09-05  8:58     ` Gerd Hoffmann
2014-09-05  9:06       ` Laszlo Ersek
2014-09-05  9:33         ` Gerd Hoffmann
2014-09-05 10:15           ` Laszlo Ersek

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=54096BC2.7070102@redhat.com \
    --to=lersek@redhat.com \
    --cc=aliguori@amazon.com \
    --cc=kraxel@redhat.com \
    --cc=pmatouse@redhat.com \
    --cc=ppandit@redhat.com \
    --cc=qemu-devel@nongnu.org \
    --cc=qemu-stable@nongnu.org \
    --cc=secalert@redhat.com \
    --cc=spice-devel@lists.freedesktop.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.