QEMU is a FAST! processor emulator
CentOS Sources
2015-10-27 605027b02ec0651889e07ee5d6f8d75ce60f2f6a
import qemu-kvm-1.5.3-86.el7_1.8
3 files added
1 files modified
431 ■■■■■ changed files
SOURCES/kvm-CVE-2015-1779-incrementally-decode-websocket-frames.patch 253 ●●●●● patch | view | raw | blame | history
SOURCES/kvm-CVE-2015-1779-limit-size-of-HTTP-headers-from-websoc.patch 70 ●●●●● patch | view | raw | blame | history
SOURCES/kvm-qtest-ide-test-disable-flush-test.patch 86 ●●●●● patch | view | raw | blame | history
SPECS/qemu-kvm.spec 22 ●●●●● patch | view | raw | blame | history
SOURCES/kvm-CVE-2015-1779-incrementally-decode-websocket-frames.patch
New file
@@ -0,0 +1,253 @@
From 2eae7bb4e94710164926c670334a83bf9d347c2f Mon Sep 17 00:00:00 2001
From: Gerd Hoffmann <kraxel@redhat.com>
Date: Tue, 22 Sep 2015 17:44:53 +0200
Subject: [PATCH 1/2] CVE-2015-1779: incrementally decode websocket frames
Message-id: <1442943894-7638-2-git-send-email-kraxel@redhat.com>
Patchwork-id: 67884
O-Subject: [RHEL-7.1.z qemu-kvm PATCH 1/2] CVE-2015-1779: incrementally decode websocket frames
Bugzilla: 1205050
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
RH-Acked-by: Thomas Huth <thuth@redhat.com>
RH-Acked-by: Petr Matousek <pmatouse@redhat.com>
From: "Daniel P. Berrange" <berrange@redhat.com>
The logic for decoding websocket frames wants to fully
decode the frame header and payload, before allowing the
VNC server to see any of the payload data. There is no
size limit on websocket payloads, so this allows a
malicious network client to consume 2^64 bytes in memory
in QEMU. It can trigger this denial of service before
the VNC server even performs any authentication.
The fix is to decode the header, and then incrementally
decode the payload data as it is needed. With this fix
the websocket decoder will allow at most 4k of data to
be buffered before decoding and processing payload.
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
[ kraxel: fix frequent spurious disconnects, suggested by Peter Maydell ]
  @@ -361,7 +361,7 @@ int vncws_decode_frame_payload(Buffer *input,
  -        *payload_size = input->offset;
  +        *payload_size = *payload_remain;
[ kraxel: fix 32bit build ]
  @@ -306,7 +306,7 @@ struct VncState
  -    uint64_t ws_payload_remain;
  +    size_t ws_payload_remain;
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
(cherry picked from commit a2bebfd6e09d285aa793cae3fb0fc3a39a9fee6e)
Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
---
 ui/vnc-ws.c | 105 ++++++++++++++++++++++++++++++++++++++++--------------------
 ui/vnc-ws.h |   9 ++++--
 ui/vnc.h    |   2 ++
 3 files changed, 80 insertions(+), 36 deletions(-)
diff --git a/ui/vnc-ws.c b/ui/vnc-ws.c
index df89315..a7d457c 100644
--- a/ui/vnc-ws.c
+++ b/ui/vnc-ws.c
@@ -114,7 +114,7 @@ long vnc_client_read_ws(VncState *vs)
 {
     int ret, err;
     uint8_t *payload;
-    size_t payload_size, frame_size;
+    size_t payload_size, header_size;
     VNC_DEBUG("Read websocket %p size %zd offset %zd\n", vs->ws_input.buffer,
             vs->ws_input.capacity, vs->ws_input.offset);
     buffer_reserve(&vs->ws_input, 4096);
@@ -124,18 +124,39 @@ long vnc_client_read_ws(VncState *vs)
     }
     vs->ws_input.offset += ret;
-    /* make sure that nothing is left in the ws_input buffer */
+    ret = 0;
+    /* consume as much of ws_input buffer as possible */
     do {
-        err = vncws_decode_frame(&vs->ws_input, &payload,
-                              &payload_size, &frame_size);
-        if (err <= 0) {
-            return err;
+        if (vs->ws_payload_remain == 0) {
+            err = vncws_decode_frame_header(&vs->ws_input,
+                                            &header_size,
+                                            &vs->ws_payload_remain,
+                                            &vs->ws_payload_mask);
+            if (err <= 0) {
+                return err;
+            }
+
+            buffer_advance(&vs->ws_input, header_size);
         }
+        if (vs->ws_payload_remain != 0) {
+            err = vncws_decode_frame_payload(&vs->ws_input,
+                                             &vs->ws_payload_remain,
+                                             &vs->ws_payload_mask,
+                                             &payload,
+                                             &payload_size);
+            if (err < 0) {
+                return err;
+            }
+            if (err == 0) {
+                return ret;
+            }
+            ret += err;
-        buffer_reserve(&vs->input, payload_size);
-        buffer_append(&vs->input, payload, payload_size);
+            buffer_reserve(&vs->input, payload_size);
+            buffer_append(&vs->input, payload, payload_size);
-        buffer_advance(&vs->ws_input, frame_size);
+            buffer_advance(&vs->ws_input, payload_size);
+        }
     } while (vs->ws_input.offset > 0);
     return ret;
@@ -273,15 +294,14 @@ void vncws_encode_frame(Buffer *output, const void *payload,
     buffer_append(output, payload, payload_size);
 }
-int vncws_decode_frame(Buffer *input, uint8_t **payload,
-                           size_t *payload_size, size_t *frame_size)
+int vncws_decode_frame_header(Buffer *input,
+                              size_t *header_size,
+                              size_t *payload_remain,
+                              WsMask *payload_mask)
 {
     unsigned char opcode = 0, fin = 0, has_mask = 0;
-    size_t header_size = 0;
-    uint32_t *payload32;
+    size_t payload_len;
     WsHeader *header = (WsHeader *)input->buffer;
-    WsMask mask;
-    int i;
     if (input->offset < WS_HEAD_MIN_LEN + 4) {
         /* header not complete */
@@ -291,7 +311,7 @@ int vncws_decode_frame(Buffer *input, uint8_t **payload,
     fin = (header->b0 & 0x80) >> 7;
     opcode = header->b0 & 0x0f;
     has_mask = (header->b1 & 0x80) >> 7;
-    *payload_size = header->b1 & 0x7f;
+    payload_len = header->b1 & 0x7f;
     if (opcode == WS_OPCODE_CLOSE) {
         /* disconnect */
@@ -308,40 +328,57 @@ int vncws_decode_frame(Buffer *input, uint8_t **payload,
         return -2;
     }
-    if (*payload_size < 126) {
-        header_size = 6;
-        mask = header->u.m;
-    } else if (*payload_size == 126 && input->offset >= 8) {
-        *payload_size = be16_to_cpu(header->u.s16.l16);
-        header_size = 8;
-        mask = header->u.s16.m16;
-    } else if (*payload_size == 127 && input->offset >= 14) {
-        *payload_size = be64_to_cpu(header->u.s64.l64);
-        header_size = 14;
-        mask = header->u.s64.m64;
+    if (payload_len < 126) {
+        *payload_remain = payload_len;
+        *header_size = 6;
+        *payload_mask = header->u.m;
+    } else if (payload_len == 126 && input->offset >= 8) {
+        *payload_remain = be16_to_cpu(header->u.s16.l16);
+        *header_size = 8;
+        *payload_mask = header->u.s16.m16;
+    } else if (payload_len == 127 && input->offset >= 14) {
+        *payload_remain = be64_to_cpu(header->u.s64.l64);
+        *header_size = 14;
+        *payload_mask = header->u.s64.m64;
     } else {
         /* header not complete */
         return 0;
     }
-    *frame_size = header_size + *payload_size;
+    return 1;
+}
+
+int vncws_decode_frame_payload(Buffer *input,
+                               size_t *payload_remain, WsMask *payload_mask,
+                               uint8_t **payload, size_t *payload_size)
+{
+    size_t i;
+    uint32_t *payload32;
-    if (input->offset < *frame_size) {
-        /* frame not complete */
+    *payload = input->buffer;
+    /* If we aren't at the end of the payload, then drop
+     * off the last bytes, so we're always multiple of 4
+     * for purpose of unmasking, except at end of payload
+     */
+    if (input->offset < *payload_remain) {
+        *payload_size = input->offset - (input->offset % 4);
+    } else {
+        *payload_size = *payload_remain;
+    }
+    if (*payload_size == 0) {
         return 0;
     }
-
-    *payload = input->buffer + header_size;
+    *payload_remain -= *payload_size;
     /* unmask frame */
     /* process 1 frame (32 bit op) */
     payload32 = (uint32_t *)(*payload);
     for (i = 0; i < *payload_size / 4; i++) {
-        payload32[i] ^= mask.u;
+        payload32[i] ^= payload_mask->u;
     }
     /* process the remaining bytes (if any) */
     for (i *= 4; i < *payload_size; i++) {
-        (*payload)[i] ^= mask.c[i % 4];
+        (*payload)[i] ^= payload_mask->c[i % 4];
     }
     return 1;
diff --git a/ui/vnc-ws.h b/ui/vnc-ws.h
index 95c1b0a..6e93fa0 100644
--- a/ui/vnc-ws.h
+++ b/ui/vnc-ws.h
@@ -83,7 +83,12 @@ long vnc_client_read_ws(VncState *vs);
 void vncws_process_handshake(VncState *vs, uint8_t *line, size_t size);
 void vncws_encode_frame(Buffer *output, const void *payload,
             const size_t payload_size);
-int vncws_decode_frame(Buffer *input, uint8_t **payload,
-                               size_t *payload_size, size_t *frame_size);
+int vncws_decode_frame_header(Buffer *input,
+                              size_t *header_size,
+                              size_t *payload_remain,
+                              WsMask *payload_mask);
+int vncws_decode_frame_payload(Buffer *input,
+                               size_t *payload_remain, WsMask *payload_mask,
+                               uint8_t **payload, size_t *payload_size);
 #endif /* __QEMU_UI_VNC_WS_H */
diff --git a/ui/vnc.h b/ui/vnc.h
index 6e99213..0efc5c6 100644
--- a/ui/vnc.h
+++ b/ui/vnc.h
@@ -290,6 +290,8 @@ struct VncState
 #ifdef CONFIG_VNC_WS
     Buffer ws_input;
     Buffer ws_output;
+    size_t ws_payload_remain;
+    WsMask ws_payload_mask;
 #endif
     /* current output mode information */
     VncWritePixels *write_pixels;
--
1.8.3.1
SOURCES/kvm-CVE-2015-1779-limit-size-of-HTTP-headers-from-websoc.patch
New file
@@ -0,0 +1,70 @@
From 7721e2e58f7cd2fcf835800622b8a7e1cdeb4557 Mon Sep 17 00:00:00 2001
From: Gerd Hoffmann <kraxel@redhat.com>
Date: Tue, 22 Sep 2015 17:44:54 +0200
Subject: [PATCH 2/2] CVE-2015-1779: limit size of HTTP headers from websockets
 clients
Message-id: <1442943894-7638-3-git-send-email-kraxel@redhat.com>
Patchwork-id: 67885
O-Subject: [RHEL-7.1.z qemu-kvm PATCH 2/2] CVE-2015-1779: limit size of HTTP headers from websockets clients
Bugzilla: 1205050
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
RH-Acked-by: Thomas Huth <thuth@redhat.com>
RH-Acked-by: Petr Matousek <pmatouse@redhat.com>
From: "Daniel P. Berrange" <berrange@redhat.com>
The VNC server websockets decoder will read and buffer data from
websockets clients until it sees the end of the HTTP headers,
as indicated by \r\n\r\n. In theory this allows a malicious to
trick QEMU into consuming an arbitrary amount of RAM. In practice,
because QEMU runs g_strstr_len() across the buffered header data,
it will spend increasingly long burning CPU time searching for
the substring match and less & less time reading data. So while
this does cause arbitrary memory growth, the bigger problem is
that QEMU will be burning 100% of available CPU time.
A novnc websockets client typically sends headers of around
512 bytes in length. As such it is reasonable to place a 4096
byte limit on the amount of data buffered while searching for
the end of HTTP headers.
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
(cherry picked from commit 2cdb5e142fb93e875fa53c52864ef5eb8d5d8b41)
Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
---
 ui/vnc-ws.c | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)
diff --git a/ui/vnc-ws.c b/ui/vnc-ws.c
index a7d457c..7133be9 100644
--- a/ui/vnc-ws.c
+++ b/ui/vnc-ws.c
@@ -88,8 +88,11 @@ void vncws_handshake_read(void *opaque)
     VncState *vs = opaque;
     uint8_t *handshake_end;
     long ret;
-    buffer_reserve(&vs->ws_input, 4096);
-    ret = vnc_client_read_buf(vs, buffer_end(&vs->ws_input), 4096);
+    /* Typical HTTP headers from novnc are 512 bytes, so limiting
+     * total header size to 4096 is easily enough. */
+    size_t want = 4096 - vs->ws_input.offset;
+    buffer_reserve(&vs->ws_input, want);
+    ret = vnc_client_read_buf(vs, buffer_end(&vs->ws_input), want);
     if (!ret) {
         if (vs->csock == -1) {
@@ -106,6 +109,9 @@ void vncws_handshake_read(void *opaque)
         vncws_process_handshake(vs, vs->ws_input.buffer, vs->ws_input.offset);
         buffer_advance(&vs->ws_input, handshake_end - vs->ws_input.buffer +
                 strlen(WS_HANDSHAKE_END));
+    } else if (vs->ws_input.offset >= 4096) {
+        VNC_DEBUG("End of headers not found in first 4096 bytes\n");
+        vnc_client_error(vs);
     }
 }
--
1.8.3.1
SOURCES/kvm-qtest-ide-test-disable-flush-test.patch
New file
@@ -0,0 +1,86 @@
From 699e404550228859f73ce42f36c6da538e1b0fb1 Mon Sep 17 00:00:00 2001
Message-Id: <699e404550228859f73ce42f36c6da538e1b0fb1.1445289321.git.jen@redhat.com>
From: John Snow <jsnow@redhat.com>
Date: Mon, 19 Oct 2015 17:58:34 -0400
Subject: [CHANGE] qtest/ide-test: disable flush-test
To: rhvirt-patches@redhat.com,
    jen@redhat.com
RH-Author: John Snow <jsnow@redhat.com>
Message-id: <1445277514-26179-2-git-send-email-jsnow@redhat.com>
Patchwork-id: 68184
O-Subject: [RHEL-7.1.z qemu-kvm PATCH 1/1] qtest/ide-test: disable flush-test
Bugzilla: 1273098
RH-Acked-by: Jeff Nelson <jenelson@redhat.com>
RH-Acked-by: Wei Huang <wei@redhat.com>
RH-Acked-by: Laszlo Ersek <lersek@redhat.com>
One of the tests downstream causes a race that can result in build
failures. For 7.3, we intend to fix the test properly, but for 7.1.z
and 7.2.*, we disable the test as a workaround.
Signed-off-by: John Snow <jsnow@redhat.com>
Signed-off-by: Jeff E. Nelson <jen@redhat.com>
---
 tests/ide-test.c | 38 --------------------------------------
 1 file changed, 38 deletions(-)
diff --git a/tests/ide-test.c b/tests/ide-test.c
index 51f9239..43b7fd6 100644
--- a/tests/ide-test.c
+++ b/tests/ide-test.c
@@ -425,43 +425,6 @@ static void test_identify(void)
     ide_test_quit();
 }
-static void test_flush(void)
-{
-    uint8_t data;
-
-    ide_test_start(
-        "-vnc none "
-        "-drive file=blkdebug::%s,if=ide,cache=writeback",
-        tmp_path);
-
-    /* Delay the completion of the flush request until we explicitly do it */
-    qmp("{'execute':'human-monitor-command', 'arguments': { "
-        "'command-line': 'qemu-io ide0-hd0 \"break flush_to_os A\"'} }");
-
-    /* FLUSH CACHE command on device 0*/
-    outb(IDE_BASE + reg_device, 0);
-    outb(IDE_BASE + reg_command, CMD_FLUSH_CACHE);
-
-    /* Check status while request is in flight*/
-    data = inb(IDE_BASE + reg_status);
-    assert_bit_set(data, BSY | DRDY);
-    assert_bit_clear(data, DF | ERR | DRQ);
-
-    /* Complete the command */
-    qmp("{'execute':'human-monitor-command', 'arguments': { "
-        "'command-line': 'qemu-io ide0-hd0 \"resume A\"'} }");
-
-    /* Check registers */
-    data = inb(IDE_BASE + reg_device);
-    g_assert_cmpint(data & DEV, ==, 0);
-
-    data = inb(IDE_BASE + reg_status);
-    assert_bit_set(data, DRDY);
-    assert_bit_clear(data, BSY | DF | ERR | DRQ);
-
-    ide_test_quit();
-}
-
 static void test_flush_nodev(void)
 {
     ide_test_start("");
@@ -505,7 +468,6 @@ int main(int argc, char **argv)
     qtest_add_func("/ide/bmdma/long_prdt", test_bmdma_long_prdt);
     qtest_add_func("/ide/bmdma/teardown", test_bmdma_teardown);
-    qtest_add_func("/ide/flush", test_flush);
     qtest_add_func("/ide/flush_nodev", test_flush_nodev);
     ret = g_test_run();
--
2.4.3
SPECS/qemu-kvm.spec
@@ -72,7 +72,7 @@
Summary: QEMU is a FAST! processor emulator
Name: %{pkgname}%{?pkgsuffix}
Version: 1.5.3
Release: 86%{?dist}.6
Release: 86%{?dist}.8
# Epoch because we pushed a qemu-1.0 package. AIUI this can't ever be dropped
Epoch: 10
License: GPLv2+ and LGPLv2+ and BSD
@@ -2859,6 +2859,12 @@
Patch1404: kvm-rtl8139-skip-offload-on-short-TCP-header-CVE-2015-51.patch
# For bz#1248764 - CVE-2015-5165 qemu-kvm: Qemu: rtl8139 uninitialized heap memory information leakage to guest [rhel-7.1.z]
Patch1405: kvm-rtl8139-check-TCP-Data-Offset-field-CVE-2015-5165.patch
# For bz#1205050 - CVE-2015-1779 qemu-kvm: qemu: vnc: insufficient resource limiting in VNC websockets decoder [rhel-7.1.z]
Patch1406: kvm-CVE-2015-1779-incrementally-decode-websocket-frames.patch
# For bz#1205050 - CVE-2015-1779 qemu-kvm: qemu: vnc: insufficient resource limiting in VNC websockets decoder [rhel-7.1.z]
Patch1407: kvm-CVE-2015-1779-limit-size-of-HTTP-headers-from-websoc.patch
# For bz#1273098 - qemu-kvm build failure race condition in tests/ide-test
Patch1408: kvm-qtest-ide-test-disable-flush-test.patch
BuildRequires: zlib-devel
@@ -4472,6 +4478,9 @@
%patch1403 -p1
%patch1404 -p1
%patch1405 -p1
%patch1406 -p1
%patch1407 -p1
%patch1408 -p1
%build
buildarch="%{kvm_target}-softmmu"
@@ -4916,6 +4925,17 @@
%{_libdir}/pkgconfig/libcacard.pc
%changelog
* Mon Oct 19 2015 Jeff E. Nelson <jen@redhat.com> - 1.5.3-86.el7_1.8
- kvm-qtest-ide-test-disable-flush-test.patch [bz#1273098]
- Resolves: bz#1273098
  (qemu-kvm build failure race condition in tests/ide-test)
* Fri Oct 09 2015 Miroslav Rezanina <mrezanin@redhat.com> - 1.5.3-86.el7_1.7
- kvm-CVE-2015-1779-incrementally-decode-websocket-frames.patch [bz#1205050]
- kvm-CVE-2015-1779-limit-size-of-HTTP-headers-from-websoc.patch [bz#1205050]
- Resolves: bz#1205050
  (CVE-2015-1779 qemu-kvm: qemu: vnc: insufficient resource limiting in VNC websockets decoder [rhel-7.1.z])
* Thu Aug 06 2015 Miroslav Rezanina <mrezanin@redhat.com> - 1.5.3-86.el7_1.6
- kvm-rtl8139-avoid-nested-ifs-in-IP-header-parsing-CVE-20.patch [bz#1248764]
- kvm-rtl8139-drop-tautologous-if-ip-.-statement-CVE-2015-.patch [bz#1248764]