ab42d9
From: Gerd Hoffmann <kraxel@redhat.com>
ab42d9
Date: Mon, 17 Aug 2015 19:56:53 +0200
ab42d9
Subject: [PATCH] vnc: fix memory corruption (CVE-2015-5225)
ab42d9
MIME-Version: 1.0
ab42d9
Content-Type: text/plain; charset=UTF-8
ab42d9
Content-Transfer-Encoding: 8bit
ab42d9
ab42d9
The _cmp_bytes variable added by commit "bea60dd ui/vnc: fix potential
ab42d9
memory corruption issues" can become negative.  Result is (possibly
ab42d9
exploitable) memory corruption.  Reason for that is it uses the stride
ab42d9
instead of bytes per scanline to apply limits.
ab42d9
ab42d9
For the server surface is is actually fine.  vnc creates that itself,
ab42d9
there is never any padding and thus scanline length always equals stride.
ab42d9
ab42d9
For the guest surface scanline length and stride are typically identical
ab42d9
too, but it doesn't has to be that way.  So add and use a new variable
ab42d9
(guest_ll) for the guest scanline length.  Also rename min_stride to
ab42d9
line_bytes to make more clear what it actually is.  Finally sprinkle
ab42d9
in an assert() to make sure we never use a negative _cmp_bytes again.
ab42d9
ab42d9
Reported-by: 范祚至(库特) <zuozhi.fzz@alibaba-inc.com>
ab42d9
Reviewed-by: P J P <ppandit@redhat.com>
ab42d9
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
ab42d9
(cherry picked from commit eb8934b0418b3b1d125edddc4fc334a54334a49b)
ab42d9
---
ab42d9
 ui/vnc.c | 15 ++++++++++-----
ab42d9
 1 file changed, 10 insertions(+), 5 deletions(-)
ab42d9
ab42d9
diff --git a/ui/vnc.c b/ui/vnc.c
ab42d9
index f989dfb..472c30e 100644
ab42d9
--- a/ui/vnc.c
ab42d9
+++ b/ui/vnc.c
ab42d9
@@ -2863,7 +2863,7 @@ static int vnc_refresh_server_surface(VncDisplay *vd)
ab42d9
                     pixman_image_get_width(vd->server));
ab42d9
     int height = MIN(pixman_image_get_height(vd->guest.fb),
ab42d9
                      pixman_image_get_height(vd->server));
ab42d9
-    int cmp_bytes, server_stride, min_stride, guest_stride, y = 0;
ab42d9
+    int cmp_bytes, server_stride, line_bytes, guest_ll, guest_stride, y = 0;
ab42d9
     uint8_t *guest_row0 = NULL, *server_row0;
ab42d9
     VncState *vs;
ab42d9
     int has_dirty = 0;
ab42d9
@@ -2882,17 +2882,21 @@ static int vnc_refresh_server_surface(VncDisplay *vd)
ab42d9
      * Update server dirty map.
ab42d9
      */
ab42d9
     server_row0 = (uint8_t *)pixman_image_get_data(vd->server);
ab42d9
-    server_stride = guest_stride = pixman_image_get_stride(vd->server);
ab42d9
+    server_stride = guest_stride = guest_ll =
ab42d9
+        pixman_image_get_stride(vd->server);
ab42d9
     cmp_bytes = MIN(VNC_DIRTY_PIXELS_PER_BIT * VNC_SERVER_FB_BYTES,
ab42d9
                     server_stride);
ab42d9
     if (vd->guest.format != VNC_SERVER_FB_FORMAT) {
ab42d9
         int width = pixman_image_get_width(vd->server);
ab42d9
         tmpbuf = qemu_pixman_linebuf_create(VNC_SERVER_FB_FORMAT, width);
ab42d9
     } else {
ab42d9
+        int guest_bpp =
ab42d9
+            PIXMAN_FORMAT_BPP(pixman_image_get_format(vd->guest.fb));
ab42d9
         guest_row0 = (uint8_t *)pixman_image_get_data(vd->guest.fb);
ab42d9
         guest_stride = pixman_image_get_stride(vd->guest.fb);
ab42d9
+        guest_ll = pixman_image_get_width(vd->guest.fb) * ((guest_bpp + 7) / 8);
ab42d9
     }
ab42d9
-    min_stride = MIN(server_stride, guest_stride);
ab42d9
+    line_bytes = MIN(server_stride, guest_ll);
ab42d9
 
ab42d9
     for (;;) {
ab42d9
         int x;
ab42d9
@@ -2923,9 +2927,10 @@ static int vnc_refresh_server_surface(VncDisplay *vd)
ab42d9
             if (!test_and_clear_bit(x, vd->guest.dirty[y])) {
ab42d9
                 continue;
ab42d9
             }
ab42d9
-            if ((x + 1) * cmp_bytes > min_stride) {
ab42d9
-                _cmp_bytes = min_stride - x * cmp_bytes;
ab42d9
+            if ((x + 1) * cmp_bytes > line_bytes) {
ab42d9
+                _cmp_bytes = line_bytes - x * cmp_bytes;
ab42d9
             }
ab42d9
+            assert(_cmp_bytes >= 0);
ab42d9
             if (memcmp(server_ptr, guest_ptr, _cmp_bytes) == 0) {
ab42d9
                 continue;
ab42d9
             }