]> git.proxmox.com Git - pve-qemu-kvm.git/blob - debian/patches/CVE-2015-1779-incrementally-decode-websocket-frames.patch
Fix CVE-2016-2841, CVE-2016-2857, CVE-2016-2858
[pve-qemu-kvm.git] / debian / patches / CVE-2015-1779-incrementally-decode-websocket-frames.patch
1 From a2bebfd6e09d285aa793cae3fb0fc3a39a9fee6e Mon Sep 17 00:00:00 2001
2 From: "Daniel P. Berrange" <berrange@redhat.com>
3 Date: Mon, 23 Mar 2015 22:58:21 +0000
4 Subject: [PATCH 1/2] CVE-2015-1779: incrementally decode websocket frames
5
6 The logic for decoding websocket frames wants to fully
7 decode the frame header and payload, before allowing the
8 VNC server to see any of the payload data. There is no
9 size limit on websocket payloads, so this allows a
10 malicious network client to consume 2^64 bytes in memory
11 in QEMU. It can trigger this denial of service before
12 the VNC server even performs any authentication.
13
14 The fix is to decode the header, and then incrementally
15 decode the payload data as it is needed. With this fix
16 the websocket decoder will allow at most 4k of data to
17 be buffered before decoding and processing payload.
18
19 Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
20
21 [ kraxel: fix frequent spurious disconnects, suggested by Peter Maydell ]
22
23 @@ -361,7 +361,7 @@ int vncws_decode_frame_payload(Buffer *input,
24 - *payload_size = input->offset;
25 + *payload_size = *payload_remain;
26
27 [ kraxel: fix 32bit build ]
28
29 @@ -306,7 +306,7 @@ struct VncState
30 - uint64_t ws_payload_remain;
31 + size_t ws_payload_remain;
32
33 Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
34 ---
35 ui/vnc-ws.c | 105 ++++++++++++++++++++++++++++++++++++++++--------------------
36 ui/vnc-ws.h | 9 ++++--
37 ui/vnc.h | 2 ++
38 3 files changed, 80 insertions(+), 36 deletions(-)
39
40 diff --git a/ui/vnc-ws.c b/ui/vnc-ws.c
41 index 85dbb7e..0b7de4e 100644
42 --- a/ui/vnc-ws.c
43 +++ b/ui/vnc-ws.c
44 @@ -107,7 +107,7 @@ long vnc_client_read_ws(VncState *vs)
45 {
46 int ret, err;
47 uint8_t *payload;
48 - size_t payload_size, frame_size;
49 + size_t payload_size, header_size;
50 VNC_DEBUG("Read websocket %p size %zd offset %zd\n", vs->ws_input.buffer,
51 vs->ws_input.capacity, vs->ws_input.offset);
52 buffer_reserve(&vs->ws_input, 4096);
53 @@ -117,18 +117,39 @@ long vnc_client_read_ws(VncState *vs)
54 }
55 vs->ws_input.offset += ret;
56
57 - /* make sure that nothing is left in the ws_input buffer */
58 + ret = 0;
59 + /* consume as much of ws_input buffer as possible */
60 do {
61 - err = vncws_decode_frame(&vs->ws_input, &payload,
62 - &payload_size, &frame_size);
63 - if (err <= 0) {
64 - return err;
65 + if (vs->ws_payload_remain == 0) {
66 + err = vncws_decode_frame_header(&vs->ws_input,
67 + &header_size,
68 + &vs->ws_payload_remain,
69 + &vs->ws_payload_mask);
70 + if (err <= 0) {
71 + return err;
72 + }
73 +
74 + buffer_advance(&vs->ws_input, header_size);
75 }
76 + if (vs->ws_payload_remain != 0) {
77 + err = vncws_decode_frame_payload(&vs->ws_input,
78 + &vs->ws_payload_remain,
79 + &vs->ws_payload_mask,
80 + &payload,
81 + &payload_size);
82 + if (err < 0) {
83 + return err;
84 + }
85 + if (err == 0) {
86 + return ret;
87 + }
88 + ret += err;
89
90 - buffer_reserve(&vs->input, payload_size);
91 - buffer_append(&vs->input, payload, payload_size);
92 + buffer_reserve(&vs->input, payload_size);
93 + buffer_append(&vs->input, payload, payload_size);
94
95 - buffer_advance(&vs->ws_input, frame_size);
96 + buffer_advance(&vs->ws_input, payload_size);
97 + }
98 } while (vs->ws_input.offset > 0);
99
100 return ret;
101 @@ -265,15 +286,14 @@ void vncws_encode_frame(Buffer *output, const void *payload,
102 buffer_append(output, payload, payload_size);
103 }
104
105 -int vncws_decode_frame(Buffer *input, uint8_t **payload,
106 - size_t *payload_size, size_t *frame_size)
107 +int vncws_decode_frame_header(Buffer *input,
108 + size_t *header_size,
109 + size_t *payload_remain,
110 + WsMask *payload_mask)
111 {
112 unsigned char opcode = 0, fin = 0, has_mask = 0;
113 - size_t header_size = 0;
114 - uint32_t *payload32;
115 + size_t payload_len;
116 WsHeader *header = (WsHeader *)input->buffer;
117 - WsMask mask;
118 - int i;
119
120 if (input->offset < WS_HEAD_MIN_LEN + 4) {
121 /* header not complete */
122 @@ -283,7 +303,7 @@ int vncws_decode_frame(Buffer *input, uint8_t **payload,
123 fin = (header->b0 & 0x80) >> 7;
124 opcode = header->b0 & 0x0f;
125 has_mask = (header->b1 & 0x80) >> 7;
126 - *payload_size = header->b1 & 0x7f;
127 + payload_len = header->b1 & 0x7f;
128
129 if (opcode == WS_OPCODE_CLOSE) {
130 /* disconnect */
131 @@ -300,40 +320,57 @@ int vncws_decode_frame(Buffer *input, uint8_t **payload,
132 return -2;
133 }
134
135 - if (*payload_size < 126) {
136 - header_size = 6;
137 - mask = header->u.m;
138 - } else if (*payload_size == 126 && input->offset >= 8) {
139 - *payload_size = be16_to_cpu(header->u.s16.l16);
140 - header_size = 8;
141 - mask = header->u.s16.m16;
142 - } else if (*payload_size == 127 && input->offset >= 14) {
143 - *payload_size = be64_to_cpu(header->u.s64.l64);
144 - header_size = 14;
145 - mask = header->u.s64.m64;
146 + if (payload_len < 126) {
147 + *payload_remain = payload_len;
148 + *header_size = 6;
149 + *payload_mask = header->u.m;
150 + } else if (payload_len == 126 && input->offset >= 8) {
151 + *payload_remain = be16_to_cpu(header->u.s16.l16);
152 + *header_size = 8;
153 + *payload_mask = header->u.s16.m16;
154 + } else if (payload_len == 127 && input->offset >= 14) {
155 + *payload_remain = be64_to_cpu(header->u.s64.l64);
156 + *header_size = 14;
157 + *payload_mask = header->u.s64.m64;
158 } else {
159 /* header not complete */
160 return 0;
161 }
162
163 - *frame_size = header_size + *payload_size;
164 + return 1;
165 +}
166 +
167 +int vncws_decode_frame_payload(Buffer *input,
168 + size_t *payload_remain, WsMask *payload_mask,
169 + uint8_t **payload, size_t *payload_size)
170 +{
171 + size_t i;
172 + uint32_t *payload32;
173
174 - if (input->offset < *frame_size) {
175 - /* frame not complete */
176 + *payload = input->buffer;
177 + /* If we aren't at the end of the payload, then drop
178 + * off the last bytes, so we're always multiple of 4
179 + * for purpose of unmasking, except at end of payload
180 + */
181 + if (input->offset < *payload_remain) {
182 + *payload_size = input->offset - (input->offset % 4);
183 + } else {
184 + *payload_size = *payload_remain;
185 + }
186 + if (*payload_size == 0) {
187 return 0;
188 }
189 -
190 - *payload = input->buffer + header_size;
191 + *payload_remain -= *payload_size;
192
193 /* unmask frame */
194 /* process 1 frame (32 bit op) */
195 payload32 = (uint32_t *)(*payload);
196 for (i = 0; i < *payload_size / 4; i++) {
197 - payload32[i] ^= mask.u;
198 + payload32[i] ^= payload_mask->u;
199 }
200 /* process the remaining bytes (if any) */
201 for (i *= 4; i < *payload_size; i++) {
202 - (*payload)[i] ^= mask.c[i % 4];
203 + (*payload)[i] ^= payload_mask->c[i % 4];
204 }
205
206 return 1;
207 diff --git a/ui/vnc-ws.h b/ui/vnc-ws.h
208 index ef229b7..14d4230 100644
209 --- a/ui/vnc-ws.h
210 +++ b/ui/vnc-ws.h
211 @@ -83,7 +83,12 @@ long vnc_client_read_ws(VncState *vs);
212 void vncws_process_handshake(VncState *vs, uint8_t *line, size_t size);
213 void vncws_encode_frame(Buffer *output, const void *payload,
214 const size_t payload_size);
215 -int vncws_decode_frame(Buffer *input, uint8_t **payload,
216 - size_t *payload_size, size_t *frame_size);
217 +int vncws_decode_frame_header(Buffer *input,
218 + size_t *header_size,
219 + size_t *payload_remain,
220 + WsMask *payload_mask);
221 +int vncws_decode_frame_payload(Buffer *input,
222 + size_t *payload_remain, WsMask *payload_mask,
223 + uint8_t **payload, size_t *payload_size);
224
225 #endif /* __QEMU_UI_VNC_WS_H */
226 diff --git a/ui/vnc.h b/ui/vnc.h
227 index e19ac39..3f7c6a9 100644
228 --- a/ui/vnc.h
229 +++ b/ui/vnc.h
230 @@ -306,6 +306,8 @@ struct VncState
231 #ifdef CONFIG_VNC_WS
232 Buffer ws_input;
233 Buffer ws_output;
234 + size_t ws_payload_remain;
235 + WsMask ws_payload_mask;
236 #endif
237 /* current output mode information */
238 VncWritePixels *write_pixels;
239 --
240 2.1.4
241