Blame SOURCES/kvm-CVE-2015-1779-incrementally-decode-websocket-frames.patch

605027
From 2eae7bb4e94710164926c670334a83bf9d347c2f Mon Sep 17 00:00:00 2001
605027
From: Gerd Hoffmann <kraxel@redhat.com>
605027
Date: Tue, 22 Sep 2015 17:44:53 +0200
605027
Subject: [PATCH 1/2] CVE-2015-1779: incrementally decode websocket frames
605027
605027
Message-id: <1442943894-7638-2-git-send-email-kraxel@redhat.com>
605027
Patchwork-id: 67884
605027
O-Subject: [RHEL-7.1.z qemu-kvm PATCH 1/2] CVE-2015-1779: incrementally decode websocket frames
605027
Bugzilla: 1205050
605027
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
605027
RH-Acked-by: Thomas Huth <thuth@redhat.com>
605027
RH-Acked-by: Petr Matousek <pmatouse@redhat.com>
605027
605027
From: "Daniel P. Berrange" <berrange@redhat.com>
605027
605027
The logic for decoding websocket frames wants to fully
605027
decode the frame header and payload, before allowing the
605027
VNC server to see any of the payload data. There is no
605027
size limit on websocket payloads, so this allows a
605027
malicious network client to consume 2^64 bytes in memory
605027
in QEMU. It can trigger this denial of service before
605027
the VNC server even performs any authentication.
605027
605027
The fix is to decode the header, and then incrementally
605027
decode the payload data as it is needed. With this fix
605027
the websocket decoder will allow at most 4k of data to
605027
be buffered before decoding and processing payload.
605027
605027
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
605027
605027
[ kraxel: fix frequent spurious disconnects, suggested by Peter Maydell ]
605027
605027
  @@ -361,7 +361,7 @@ int vncws_decode_frame_payload(Buffer *input,
605027
  -        *payload_size = input->offset;
605027
  +        *payload_size = *payload_remain;
605027
605027
[ kraxel: fix 32bit build ]
605027
605027
  @@ -306,7 +306,7 @@ struct VncState
605027
  -    uint64_t ws_payload_remain;
605027
  +    size_t ws_payload_remain;
605027
605027
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
605027
(cherry picked from commit a2bebfd6e09d285aa793cae3fb0fc3a39a9fee6e)
605027
Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
605027
---
605027
 ui/vnc-ws.c | 105 ++++++++++++++++++++++++++++++++++++++++--------------------
605027
 ui/vnc-ws.h |   9 ++++--
605027
 ui/vnc.h    |   2 ++
605027
 3 files changed, 80 insertions(+), 36 deletions(-)
605027
605027
diff --git a/ui/vnc-ws.c b/ui/vnc-ws.c
605027
index df89315..a7d457c 100644
605027
--- a/ui/vnc-ws.c
605027
+++ b/ui/vnc-ws.c
605027
@@ -114,7 +114,7 @@ long vnc_client_read_ws(VncState *vs)
605027
 {
605027
     int ret, err;
605027
     uint8_t *payload;
605027
-    size_t payload_size, frame_size;
605027
+    size_t payload_size, header_size;
605027
     VNC_DEBUG("Read websocket %p size %zd offset %zd\n", vs->ws_input.buffer,
605027
             vs->ws_input.capacity, vs->ws_input.offset);
605027
     buffer_reserve(&vs->ws_input, 4096);
605027
@@ -124,18 +124,39 @@ long vnc_client_read_ws(VncState *vs)
605027
     }
605027
     vs->ws_input.offset += ret;
605027
 
605027
-    /* make sure that nothing is left in the ws_input buffer */
605027
+    ret = 0;
605027
+    /* consume as much of ws_input buffer as possible */
605027
     do {
605027
-        err = vncws_decode_frame(&vs->ws_input, &payload,
605027
-                              &payload_size, &frame_size);
605027
-        if (err <= 0) {
605027
-            return err;
605027
+        if (vs->ws_payload_remain == 0) {
605027
+            err = vncws_decode_frame_header(&vs->ws_input,
605027
+                                            &header_size,
605027
+                                            &vs->ws_payload_remain,
605027
+                                            &vs->ws_payload_mask);
605027
+            if (err <= 0) {
605027
+                return err;
605027
+            }
605027
+
605027
+            buffer_advance(&vs->ws_input, header_size);
605027
         }
605027
+        if (vs->ws_payload_remain != 0) {
605027
+            err = vncws_decode_frame_payload(&vs->ws_input,
605027
+                                             &vs->ws_payload_remain,
605027
+                                             &vs->ws_payload_mask,
605027
+                                             &payload,
605027
+                                             &payload_size);
605027
+            if (err < 0) {
605027
+                return err;
605027
+            }
605027
+            if (err == 0) {
605027
+                return ret;
605027
+            }
605027
+            ret += err;
605027
 
605027
-        buffer_reserve(&vs->input, payload_size);
605027
-        buffer_append(&vs->input, payload, payload_size);
605027
+            buffer_reserve(&vs->input, payload_size);
605027
+            buffer_append(&vs->input, payload, payload_size);
605027
 
605027
-        buffer_advance(&vs->ws_input, frame_size);
605027
+            buffer_advance(&vs->ws_input, payload_size);
605027
+        }
605027
     } while (vs->ws_input.offset > 0);
605027
 
605027
     return ret;
605027
@@ -273,15 +294,14 @@ void vncws_encode_frame(Buffer *output, const void *payload,
605027
     buffer_append(output, payload, payload_size);
605027
 }
605027
 
605027
-int vncws_decode_frame(Buffer *input, uint8_t **payload,
605027
-                           size_t *payload_size, size_t *frame_size)
605027
+int vncws_decode_frame_header(Buffer *input,
605027
+                              size_t *header_size,
605027
+                              size_t *payload_remain,
605027
+                              WsMask *payload_mask)
605027
 {
605027
     unsigned char opcode = 0, fin = 0, has_mask = 0;
605027
-    size_t header_size = 0;
605027
-    uint32_t *payload32;
605027
+    size_t payload_len;
605027
     WsHeader *header = (WsHeader *)input->buffer;
605027
-    WsMask mask;
605027
-    int i;
605027
 
605027
     if (input->offset < WS_HEAD_MIN_LEN + 4) {
605027
         /* header not complete */
605027
@@ -291,7 +311,7 @@ int vncws_decode_frame(Buffer *input, uint8_t **payload,
605027
     fin = (header->b0 & 0x80) >> 7;
605027
     opcode = header->b0 & 0x0f;
605027
     has_mask = (header->b1 & 0x80) >> 7;
605027
-    *payload_size = header->b1 & 0x7f;
605027
+    payload_len = header->b1 & 0x7f;
605027
 
605027
     if (opcode == WS_OPCODE_CLOSE) {
605027
         /* disconnect */
605027
@@ -308,40 +328,57 @@ int vncws_decode_frame(Buffer *input, uint8_t **payload,
605027
         return -2;
605027
     }
605027
 
605027
-    if (*payload_size < 126) {
605027
-        header_size = 6;
605027
-        mask = header->u.m;
605027
-    } else if (*payload_size == 126 && input->offset >= 8) {
605027
-        *payload_size = be16_to_cpu(header->u.s16.l16);
605027
-        header_size = 8;
605027
-        mask = header->u.s16.m16;
605027
-    } else if (*payload_size == 127 && input->offset >= 14) {
605027
-        *payload_size = be64_to_cpu(header->u.s64.l64);
605027
-        header_size = 14;
605027
-        mask = header->u.s64.m64;
605027
+    if (payload_len < 126) {
605027
+        *payload_remain = payload_len;
605027
+        *header_size = 6;
605027
+        *payload_mask = header->u.m;
605027
+    } else if (payload_len == 126 && input->offset >= 8) {
605027
+        *payload_remain = be16_to_cpu(header->u.s16.l16);
605027
+        *header_size = 8;
605027
+        *payload_mask = header->u.s16.m16;
605027
+    } else if (payload_len == 127 && input->offset >= 14) {
605027
+        *payload_remain = be64_to_cpu(header->u.s64.l64);
605027
+        *header_size = 14;
605027
+        *payload_mask = header->u.s64.m64;
605027
     } else {
605027
         /* header not complete */
605027
         return 0;
605027
     }
605027
 
605027
-    *frame_size = header_size + *payload_size;
605027
+    return 1;
605027
+}
605027
+
605027
+int vncws_decode_frame_payload(Buffer *input,
605027
+                               size_t *payload_remain, WsMask *payload_mask,
605027
+                               uint8_t **payload, size_t *payload_size)
605027
+{
605027
+    size_t i;
605027
+    uint32_t *payload32;
605027
 
605027
-    if (input->offset < *frame_size) {
605027
-        /* frame not complete */
605027
+    *payload = input->buffer;
605027
+    /* If we aren't at the end of the payload, then drop
605027
+     * off the last bytes, so we're always multiple of 4
605027
+     * for purpose of unmasking, except at end of payload
605027
+     */
605027
+    if (input->offset < *payload_remain) {
605027
+        *payload_size = input->offset - (input->offset % 4);
605027
+    } else {
605027
+        *payload_size = *payload_remain;
605027
+    }
605027
+    if (*payload_size == 0) {
605027
         return 0;
605027
     }
605027
-
605027
-    *payload = input->buffer + header_size;
605027
+    *payload_remain -= *payload_size;
605027
 
605027
     /* unmask frame */
605027
     /* process 1 frame (32 bit op) */
605027
     payload32 = (uint32_t *)(*payload);
605027
     for (i = 0; i < *payload_size / 4; i++) {
605027
-        payload32[i] ^= mask.u;
605027
+        payload32[i] ^= payload_mask->u;
605027
     }
605027
     /* process the remaining bytes (if any) */
605027
     for (i *= 4; i < *payload_size; i++) {
605027
-        (*payload)[i] ^= mask.c[i % 4];
605027
+        (*payload)[i] ^= payload_mask->c[i % 4];
605027
     }
605027
 
605027
     return 1;
605027
diff --git a/ui/vnc-ws.h b/ui/vnc-ws.h
605027
index 95c1b0a..6e93fa0 100644
605027
--- a/ui/vnc-ws.h
605027
+++ b/ui/vnc-ws.h
605027
@@ -83,7 +83,12 @@ long vnc_client_read_ws(VncState *vs);
605027
 void vncws_process_handshake(VncState *vs, uint8_t *line, size_t size);
605027
 void vncws_encode_frame(Buffer *output, const void *payload,
605027
             const size_t payload_size);
605027
-int vncws_decode_frame(Buffer *input, uint8_t **payload,
605027
-                               size_t *payload_size, size_t *frame_size);
605027
+int vncws_decode_frame_header(Buffer *input,
605027
+                              size_t *header_size,
605027
+                              size_t *payload_remain,
605027
+                              WsMask *payload_mask);
605027
+int vncws_decode_frame_payload(Buffer *input,
605027
+                               size_t *payload_remain, WsMask *payload_mask,
605027
+                               uint8_t **payload, size_t *payload_size);
605027
 
605027
 #endif /* __QEMU_UI_VNC_WS_H */
605027
diff --git a/ui/vnc.h b/ui/vnc.h
605027
index 6e99213..0efc5c6 100644
605027
--- a/ui/vnc.h
605027
+++ b/ui/vnc.h
605027
@@ -290,6 +290,8 @@ struct VncState
605027
 #ifdef CONFIG_VNC_WS
605027
     Buffer ws_input;
605027
     Buffer ws_output;
605027
+    size_t ws_payload_remain;
605027
+    WsMask ws_payload_mask;
605027
 #endif
605027
     /* current output mode information */
605027
     VncWritePixels *write_pixels;
605027
-- 
605027
1.8.3.1
605027