1 /* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /*
3 Copyright (C) 2009,2010 Red Hat, Inc.
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
9
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with this library; if not, see <http://www.gnu.org/licenses/>.
17 */
18 #include <config.h>
19
20 #include <inttypes.h>
21 #include <glib.h>
22 #include <common/lz_common.h>
23 #include "spice-bitmap-utils.h"
24 #include "red-common.h"
25 #include "red-qxl.h"
26 #include "memslot.h"
27 #include "red-parse-qxl.h"
28
29 /* Max size in bytes for any data field used in a QXL command.
30 * This will for example be useful to prevent the guest from saturating the
31 * host memory if it tries to send overlapping chunks.
32 * This value should be big enough for all requests but limited
33 * to 32 bits. Even better if it fits on 31 bits to detect integer overflows.
34 */
35 #define MAX_DATA_CHUNK 0x7ffffffflu
36
37 verify(MAX_DATA_CHUNK <= G_MAXINT32);
38
39 /* Limit number of chunks.
40 * The guest can attempt to make host allocate too much memory
41 * just with a large number of small chunks.
42 * Prevent that the chunk list take more memory than the data itself.
43 */
44 #define MAX_CHUNKS (MAX_DATA_CHUNK/1024u)
45
46 #define INVALID_SIZE ((size_t) -1)
47
48 struct RedDataChunk {
49 uint32_t data_size;
50 RedDataChunk *prev_chunk;
51 RedDataChunk *next_chunk;
52 uint8_t *data;
53 };
54
55 #if 0
56 static void hexdump_qxl(RedMemSlotInfo *slots, int group_id,
57 QXLPHYSICAL addr, uint8_t bytes)
58 {
59 uint8_t *hex;
60 int i;
61
62 hex = (uint8_t*)memslot_get_virt(slots, addr, bytes, group_id);
63 for (i = 0; i < bytes; i++) {
64 if (0 == i % 16) {
65 fprintf(stderr, "%lx: ", addr+i);
66 }
67 if (0 == i % 4) {
68 fprintf(stderr, " ");
69 }
70 fprintf(stderr, " %02x", hex[i]);
71 if (15 == i % 16) {
72 fprintf(stderr, "\n");
73 }
74 }
75 }
76 #endif
77
color_16_to_32(uint32_t color)78 static inline uint32_t color_16_to_32(uint32_t color)
79 {
80 uint32_t ret;
81
82 ret = ((color & 0x001f) << 3) | ((color & 0x001c) >> 2);
83 ret |= ((color & 0x03e0) << 6) | ((color & 0x0380) << 1);
84 ret |= ((color & 0x7c00) << 9) | ((color & 0x7000) << 4);
85
86 return ret;
87 }
88
red_linearize_chunk(RedDataChunk * head,size_t size,bool * free_chunk)89 static uint8_t *red_linearize_chunk(RedDataChunk *head, size_t size, bool *free_chunk)
90 {
91 uint8_t *data, *ptr;
92 RedDataChunk *chunk;
93 uint32_t copy;
94
95 if (head->next_chunk == nullptr) {
96 spice_assert(size <= head->data_size);
97 *free_chunk = false;
98 return head->data;
99 }
100
101 ptr = data = (uint8_t*) g_malloc(size);
102 *free_chunk = true;
103 for (chunk = head; chunk != nullptr && size > 0; chunk = chunk->next_chunk) {
104 copy = MIN(chunk->data_size, size);
105 memcpy(ptr, chunk->data, copy);
106 ptr += copy;
107 size -= copy;
108 }
109 spice_assert(size == 0);
110 return data;
111 }
112
red_get_data_chunks_ptr(RedMemSlotInfo * slots,int group_id,int memslot_id,RedDataChunk * red,QXLDataChunk * qxl)113 static size_t red_get_data_chunks_ptr(RedMemSlotInfo *slots, int group_id,
114 int memslot_id,
115 RedDataChunk *red, QXLDataChunk *qxl)
116 {
117 RedDataChunk *red_prev;
118 uint64_t data_size = 0;
119 uint32_t chunk_data_size;
120 QXLPHYSICAL next_chunk;
121 unsigned num_chunks = 0;
122
123 red->data_size = qxl->data_size;
124 data_size += red->data_size;
125 red->data = qxl->data;
126 red->prev_chunk = red->next_chunk = nullptr;
127 if (!memslot_validate_virt(slots, (intptr_t)red->data, memslot_id, red->data_size, group_id)) {
128 red->data = nullptr;
129 return INVALID_SIZE;
130 }
131
132 while ((next_chunk = qxl->next_chunk) != 0) {
133 /* somebody is trying to use too much memory using a lot of chunks.
134 * Or made a circular list of chunks
135 */
136 if (++num_chunks >= MAX_CHUNKS) {
137 spice_warning("data split in too many chunks, avoiding DoS");
138 goto error;
139 }
140
141 memslot_id = memslot_get_id(slots, next_chunk);
142 qxl = (QXLDataChunk *)memslot_get_virt(slots, next_chunk, sizeof(*qxl), group_id);
143 if (qxl == nullptr) {
144 goto error;
145 }
146
147 /* do not waste space for empty chunks.
148 * This could be just a driver issue or an attempt
149 * to allocate too much memory or a circular list.
150 * All above cases are handled by the check for number
151 * of chunks.
152 */
153 chunk_data_size = qxl->data_size;
154 if (chunk_data_size == 0)
155 continue;
156
157 red_prev = red;
158 red = g_new0(RedDataChunk, 1);
159 red->data_size = chunk_data_size;
160 red->prev_chunk = red_prev;
161 red->data = qxl->data;
162 red_prev->next_chunk = red;
163
164 data_size += chunk_data_size;
165 /* this can happen if client is sending nested chunks */
166 if (data_size > MAX_DATA_CHUNK) {
167 spice_warning("too much data inside chunks, avoiding DoS");
168 goto error;
169 }
170 if (!memslot_validate_virt(slots, (intptr_t)red->data, memslot_id, red->data_size, group_id))
171 goto error;
172 }
173
174 red->next_chunk = nullptr;
175 return data_size;
176
177 error:
178 while (red->prev_chunk) {
179 red_prev = red->prev_chunk;
180 g_free(red);
181 red = red_prev;
182 }
183 red->data_size = 0;
184 red->next_chunk = nullptr;
185 red->data = nullptr;
186 return INVALID_SIZE;
187 }
188
red_get_data_chunks(RedMemSlotInfo * slots,int group_id,RedDataChunk * red,QXLPHYSICAL addr)189 static size_t red_get_data_chunks(RedMemSlotInfo *slots, int group_id,
190 RedDataChunk *red, QXLPHYSICAL addr)
191 {
192 QXLDataChunk *qxl;
193 int memslot_id = memslot_get_id(slots, addr);
194
195 qxl = (QXLDataChunk *)memslot_get_virt(slots, addr, sizeof(*qxl), group_id);
196 if (qxl == nullptr) {
197 return INVALID_SIZE;
198 }
199 return red_get_data_chunks_ptr(slots, group_id, memslot_id, red, qxl);
200 }
201
red_put_data_chunks(RedDataChunk * red)202 static void red_put_data_chunks(RedDataChunk *red)
203 {
204 RedDataChunk *tmp;
205
206 red = red->next_chunk;
207 while (red) {
208 tmp = red;
209 red = red->next_chunk;
210 g_free(tmp);
211 }
212 }
213
red_get_point_ptr(SpicePoint * red,QXLPoint * qxl)214 static void red_get_point_ptr(SpicePoint *red, QXLPoint *qxl)
215 {
216 red->x = qxl->x;
217 red->y = qxl->y;
218 }
219
red_get_point16_ptr(SpicePoint16 * red,QXLPoint16 * qxl)220 static void red_get_point16_ptr(SpicePoint16 *red, QXLPoint16 *qxl)
221 {
222 red->x = qxl->x;
223 red->y = qxl->y;
224 }
225
red_get_rect_ptr(SpiceRect * red,const QXLRect * qxl)226 void red_get_rect_ptr(SpiceRect *red, const QXLRect *qxl)
227 {
228 red->top = qxl->top;
229 red->left = qxl->left;
230 red->bottom = qxl->bottom;
231 red->right = qxl->right;
232 }
233
red_get_path(RedMemSlotInfo * slots,int group_id,QXLPHYSICAL addr)234 static SpicePath *red_get_path(RedMemSlotInfo *slots, int group_id,
235 QXLPHYSICAL addr)
236 {
237 RedDataChunk chunks;
238 QXLPathSeg *start, *end;
239 SpicePathSeg *seg;
240 uint8_t *data;
241 bool free_data;
242 QXLPath *qxl;
243 SpicePath *red;
244 size_t size;
245 uint64_t mem_size, mem_size2, segment_size;
246 int n_segments;
247 int i;
248 uint32_t count;
249
250 qxl = (QXLPath *)memslot_get_virt(slots, addr, sizeof(*qxl), group_id);
251 if (qxl == nullptr) {
252 return nullptr;
253 }
254 size = red_get_data_chunks_ptr(slots, group_id,
255 memslot_get_id(slots, addr),
256 &chunks, &qxl->chunk);
257 if (size == INVALID_SIZE) {
258 return nullptr;
259 }
260 data = red_linearize_chunk(&chunks, size, &free_data);
261 red_put_data_chunks(&chunks);
262
263 n_segments = 0;
264 mem_size = sizeof(*red);
265
266 start = (QXLPathSeg*)data;
267 end = (QXLPathSeg*)(data + size);
268 while (start+1 < end) {
269 n_segments++;
270 count = start->count;
271 segment_size = sizeof(SpicePathSeg) + (uint64_t) count * sizeof(SpicePointFix);
272 mem_size += sizeof(SpicePathSeg *) + SPICE_ALIGN(segment_size, 4);
273 /* avoid going backward with 32 bit architectures */
274 spice_assert((uint64_t) count * sizeof(QXLPointFix)
275 <= (char*) end - (char*) &start->points[0]);
276 start = (QXLPathSeg*)(&start->points[count]);
277 }
278
279 red = (SpicePath*) g_malloc(mem_size);
280 red->num_segments = n_segments;
281
282 start = (QXLPathSeg*)data;
283 end = (QXLPathSeg*)(data + size);
284 seg = (SpicePathSeg*)&red->segments[n_segments];
285 n_segments = 0;
286 mem_size2 = sizeof(*red);
287 while (start+1 < end && n_segments < red->num_segments) {
288 red->segments[n_segments++] = seg;
289 count = start->count;
290
291 /* Protect against overflow in size calculations before
292 writing to memory */
293 /* Verify that we didn't overflow due to guest changing data */
294 mem_size2 += sizeof(SpicePathSeg) + (uint64_t) count * sizeof(SpicePointFix);
295 spice_assert(mem_size2 <= mem_size);
296
297 seg->flags = start->flags;
298 seg->count = count;
299 for (i = 0; i < seg->count; i++) {
300 seg->points[i].x = start->points[i].x;
301 seg->points[i].y = start->points[i].y;
302 }
303 start = (QXLPathSeg*)(&start->points[i]);
304 seg = (SpicePathSeg*)(&seg->points[i]);
305 }
306 /* Ensure guest didn't tamper with segment count */
307 spice_assert(n_segments == red->num_segments);
308
309 if (free_data) {
310 g_free(data);
311 }
312 return red;
313 }
314
red_get_clip_rects(RedMemSlotInfo * slots,int group_id,QXLPHYSICAL addr)315 static SpiceClipRects *red_get_clip_rects(RedMemSlotInfo *slots, int group_id,
316 QXLPHYSICAL addr)
317 {
318 RedDataChunk chunks;
319 QXLClipRects *qxl;
320 SpiceClipRects *red;
321 QXLRect *start;
322 uint8_t *data;
323 bool free_data;
324 size_t size;
325 int i;
326 uint32_t num_rects;
327
328 qxl = (QXLClipRects *)memslot_get_virt(slots, addr, sizeof(*qxl), group_id);
329 if (qxl == nullptr) {
330 return nullptr;
331 }
332 size = red_get_data_chunks_ptr(slots, group_id,
333 memslot_get_id(slots, addr),
334 &chunks, &qxl->chunk);
335 if (size == INVALID_SIZE) {
336 return nullptr;
337 }
338 data = red_linearize_chunk(&chunks, size, &free_data);
339 red_put_data_chunks(&chunks);
340
341 num_rects = qxl->num_rects;
342 /* The cast is needed to prevent 32 bit integer overflows.
343 * This check is enough as size is limited to 31 bit
344 * by red_get_data_chunks_ptr checks.
345 */
346 spice_assert((uint64_t) num_rects * sizeof(QXLRect) == size);
347 SPICE_VERIFY(sizeof(SpiceRect) == sizeof(QXLRect));
348 red = (SpiceClipRects*) g_malloc(sizeof(*red) + num_rects * sizeof(SpiceRect));
349 red->num_rects = num_rects;
350
351 start = (QXLRect*)data;
352 for (i = 0; i < red->num_rects; i++) {
353 red_get_rect_ptr(red->rects + i, start++);
354 }
355
356 if (free_data) {
357 g_free(data);
358 }
359 return red;
360 }
361
red_get_image_data_flat(RedMemSlotInfo * slots,int group_id,QXLPHYSICAL addr,size_t size)362 static SpiceChunks *red_get_image_data_flat(RedMemSlotInfo *slots, int group_id,
363 QXLPHYSICAL addr, size_t size)
364 {
365 SpiceChunks *data;
366 void *bitmap_virt;
367
368 bitmap_virt = memslot_get_virt(slots, addr, size, group_id);
369 if (bitmap_virt == nullptr) {
370 return nullptr;
371 }
372
373 data = spice_chunks_new(1);
374 data->data_size = size;
375 data->chunk[0].data = (uint8_t*) bitmap_virt;
376 data->chunk[0].len = size;
377 return data;
378 }
379
red_get_image_data_chunked(RedMemSlotInfo * slots,int group_id,RedDataChunk * head)380 static SpiceChunks *red_get_image_data_chunked(RedMemSlotInfo *slots, int group_id,
381 RedDataChunk *head)
382 {
383 SpiceChunks *data;
384 RedDataChunk *chunk;
385 int i;
386
387 for (i = 0, chunk = head; chunk != nullptr; chunk = chunk->next_chunk) {
388 i++;
389 }
390
391 data = spice_chunks_new(i);
392 data->data_size = 0;
393 for (i = 0, chunk = head;
394 chunk != nullptr && i < data->num_chunks;
395 chunk = chunk->next_chunk, i++) {
396 data->chunk[i].data = chunk->data;
397 data->chunk[i].len = chunk->data_size;
398 data->data_size += chunk->data_size;
399 }
400 spice_assert(i == data->num_chunks);
401 return data;
402 }
403
bitmap_format_to_string(int format)404 static const char *bitmap_format_to_string(int format)
405 {
406 switch (format) {
407 case SPICE_BITMAP_FMT_INVALID: return "SPICE_BITMAP_FMT_INVALID";
408 case SPICE_BITMAP_FMT_1BIT_LE: return "SPICE_BITMAP_FMT_1BIT_LE";
409 case SPICE_BITMAP_FMT_1BIT_BE: return "SPICE_BITMAP_FMT_1BIT_BE";
410 case SPICE_BITMAP_FMT_4BIT_LE: return "SPICE_BITMAP_FMT_4BIT_LE";
411 case SPICE_BITMAP_FMT_4BIT_BE: return "SPICE_BITMAP_FMT_4BIT_BE";
412 case SPICE_BITMAP_FMT_8BIT: return "SPICE_BITMAP_FMT_8BIT";
413 case SPICE_BITMAP_FMT_16BIT: return "SPICE_BITMAP_FMT_16BIT";
414 case SPICE_BITMAP_FMT_24BIT: return "SPICE_BITMAP_FMT_24BIT";
415 case SPICE_BITMAP_FMT_32BIT: return "SPICE_BITMAP_FMT_32BIT";
416 case SPICE_BITMAP_FMT_RGBA: return "SPICE_BITMAP_FMT_RGBA";
417 case SPICE_BITMAP_FMT_8BIT_A: return "SPICE_BITMAP_FMT_8BIT_A";
418 }
419 return "unknown";
420 }
421
422 static const unsigned int MAP_BITMAP_FMT_TO_BITS_PER_PIXEL[] =
423 {0, 1, 1, 4, 4, 8, 16, 24, 32, 32, 8};
424
bitmap_consistent(SpiceBitmap * bitmap)425 static bool bitmap_consistent(SpiceBitmap *bitmap)
426 {
427 unsigned int bpp;
428
429 if (bitmap->format >= SPICE_N_ELEMENTS(MAP_BITMAP_FMT_TO_BITS_PER_PIXEL)) {
430 spice_warning("wrong format specified for image");
431 return false;
432 }
433
434 bpp = MAP_BITMAP_FMT_TO_BITS_PER_PIXEL[bitmap->format];
435
436 if (bitmap->stride < (((uint64_t) bitmap->x * bpp + 7u) / 8u)) {
437 spice_warning("image stride too small for width: %d < ((%d * %d + 7) / 8) (%s=%d)",
438 bitmap->stride, bitmap->x, bpp,
439 bitmap_format_to_string(bitmap->format),
440 bitmap->format);
441 return false;
442 }
443 return true;
444 }
445
red_get_image(RedMemSlotInfo * slots,int group_id,QXLPHYSICAL addr,uint32_t flags,bool is_mask)446 static SpiceImage *red_get_image(RedMemSlotInfo *slots, int group_id,
447 QXLPHYSICAL addr, uint32_t flags, bool is_mask)
448 {
449 RedDataChunk chunks;
450 QXLImage *qxl;
451 SpiceImage *red = nullptr;
452 SpicePalette *rp = nullptr;
453 uint64_t bitmap_size, size;
454 uint8_t qxl_flags;
455 QXLPHYSICAL palette;
456
457 if (addr == 0) {
458 return nullptr;
459 }
460
461 qxl = (QXLImage *)memslot_get_virt(slots, addr, sizeof(*qxl), group_id);
462 if (qxl == nullptr) {
463 return nullptr;
464 }
465 red = g_new0(SpiceImage, 1);
466 red->descriptor.id = qxl->descriptor.id;
467 red->descriptor.type = qxl->descriptor.type;
468 red->descriptor.flags = 0;
469 if (qxl->descriptor.flags & QXL_IMAGE_HIGH_BITS_SET) {
470 red->descriptor.flags |= SPICE_IMAGE_FLAGS_HIGH_BITS_SET;
471 }
472 if (qxl->descriptor.flags & QXL_IMAGE_CACHE) {
473 red->descriptor.flags |= SPICE_IMAGE_FLAGS_CACHE_ME;
474 }
475 red->descriptor.width = qxl->descriptor.width;
476 red->descriptor.height = qxl->descriptor.height;
477
478 switch (red->descriptor.type) {
479 case SPICE_IMAGE_TYPE_BITMAP:
480 red->u.bitmap.format = qxl->bitmap.format;
481 red->u.bitmap.x = qxl->bitmap.x;
482 red->u.bitmap.y = qxl->bitmap.y;
483 red->u.bitmap.stride = qxl->bitmap.stride;
484 palette = qxl->bitmap.palette;
485 if (!bitmap_fmt_is_rgb(red->u.bitmap.format) && !palette && !is_mask) {
486 spice_warning("guest error: missing palette on bitmap format=%d",
487 red->u.bitmap.format);
488 goto error;
489 }
490 if (red->u.bitmap.x == 0 || red->u.bitmap.y == 0) {
491 spice_warning("guest error: zero area bitmap");
492 goto error;
493 }
494 qxl_flags = qxl->bitmap.flags;
495 if (qxl_flags & QXL_BITMAP_TOP_DOWN) {
496 red->u.bitmap.flags = SPICE_BITMAP_FLAGS_TOP_DOWN;
497 }
498 if (!bitmap_consistent(&red->u.bitmap)) {
499 goto error;
500 }
501 if (palette) {
502 QXLPalette *qp;
503 int i, num_ents;
504 qp = (QXLPalette *)memslot_get_virt(slots, palette,
505 sizeof(*qp), group_id);
506 if (qp == nullptr) {
507 goto error;
508 }
509 num_ents = qp->num_ents;
510 if (!memslot_validate_virt(slots, (intptr_t)qp->ents,
511 memslot_get_id(slots, palette),
512 num_ents * sizeof(qp->ents[0]), group_id)) {
513 goto error;
514 }
515 rp = (SpicePalette*) g_malloc(num_ents * sizeof(rp->ents[0]) + sizeof(*rp));
516 rp->unique = qp->unique;
517 rp->num_ents = num_ents;
518 if (flags & QXL_COMMAND_FLAG_COMPAT_16BPP) {
519 for (i = 0; i < num_ents; i++) {
520 rp->ents[i] = color_16_to_32(qp->ents[i]);
521 }
522 } else {
523 for (i = 0; i < num_ents; i++) {
524 rp->ents[i] = qp->ents[i];
525 }
526 }
527 red->u.bitmap.palette = rp;
528 red->u.bitmap.palette_id = rp->unique;
529 }
530 bitmap_size = (uint64_t) red->u.bitmap.y * red->u.bitmap.stride;
531 if (bitmap_size > MAX_DATA_CHUNK) {
532 goto error;
533 }
534 if (qxl_flags & QXL_BITMAP_DIRECT) {
535 red->u.bitmap.data = red_get_image_data_flat(slots, group_id,
536 qxl->bitmap.data,
537 bitmap_size);
538 } else {
539 size = red_get_data_chunks(slots, group_id,
540 &chunks, qxl->bitmap.data);
541 if (size == INVALID_SIZE || size != bitmap_size) {
542 red_put_data_chunks(&chunks);
543 goto error;
544 }
545 red->u.bitmap.data = red_get_image_data_chunked(slots, group_id,
546 &chunks);
547 red_put_data_chunks(&chunks);
548 }
549 if (qxl_flags & QXL_BITMAP_UNSTABLE) {
550 red->u.bitmap.data->flags |= SPICE_CHUNKS_FLAGS_UNSTABLE;
551 }
552 break;
553 case SPICE_IMAGE_TYPE_SURFACE:
554 red->u.surface.surface_id = qxl->surface_image.surface_id;
555 break;
556 case SPICE_IMAGE_TYPE_QUIC:
557 red->u.quic.data_size = qxl->quic.data_size;
558 size = red_get_data_chunks_ptr(slots, group_id,
559 memslot_get_id(slots, addr),
560 &chunks, (QXLDataChunk *)qxl->quic.data);
561 if (size == INVALID_SIZE || size != red->u.quic.data_size) {
562 red_put_data_chunks(&chunks);
563 goto error;
564 }
565 red->u.quic.data = red_get_image_data_chunked(slots, group_id,
566 &chunks);
567 red_put_data_chunks(&chunks);
568 break;
569 default:
570 spice_warning("unknown type %d", red->descriptor.type);
571 goto error;
572 }
573 return red;
574 error:
575 g_free(red);
576 g_free(rp);
577 return nullptr;
578 }
579
red_put_image(SpiceImage * red)580 static void red_put_image(SpiceImage *red)
581 {
582 if (red == nullptr)
583 return;
584
585 switch (red->descriptor.type) {
586 case SPICE_IMAGE_TYPE_BITMAP:
587 g_free(red->u.bitmap.palette);
588 spice_chunks_destroy(red->u.bitmap.data);
589 break;
590 case SPICE_IMAGE_TYPE_QUIC:
591 spice_chunks_destroy(red->u.quic.data);
592 break;
593 }
594 g_free(red);
595 }
596
red_get_brush_ptr(RedMemSlotInfo * slots,int group_id,SpiceBrush * red,QXLBrush * qxl,uint32_t flags)597 static void red_get_brush_ptr(RedMemSlotInfo *slots, int group_id,
598 SpiceBrush *red, QXLBrush *qxl, uint32_t flags)
599 {
600 red->type = qxl->type;
601 switch (red->type) {
602 case SPICE_BRUSH_TYPE_SOLID:
603 if (flags & QXL_COMMAND_FLAG_COMPAT_16BPP) {
604 red->u.color = color_16_to_32(qxl->u.color);
605 } else {
606 red->u.color = qxl->u.color;
607 }
608 break;
609 case SPICE_BRUSH_TYPE_PATTERN:
610 red->u.pattern.pat = red_get_image(slots, group_id, qxl->u.pattern.pat, flags, false);
611 red_get_point_ptr(&red->u.pattern.pos, &qxl->u.pattern.pos);
612 break;
613 }
614 }
615
red_put_brush(SpiceBrush * red)616 static void red_put_brush(SpiceBrush *red)
617 {
618 switch (red->type) {
619 case SPICE_BRUSH_TYPE_PATTERN:
620 red_put_image(red->u.pattern.pat);
621 break;
622 }
623 }
624
red_get_qmask_ptr(RedMemSlotInfo * slots,int group_id,SpiceQMask * red,QXLQMask * qxl,uint32_t flags)625 static void red_get_qmask_ptr(RedMemSlotInfo *slots, int group_id,
626 SpiceQMask *red, QXLQMask *qxl, uint32_t flags)
627 {
628 red->bitmap = red_get_image(slots, group_id, qxl->bitmap, flags, true);
629 if (red->bitmap) {
630 red->flags = qxl->flags;
631 red_get_point_ptr(&red->pos, &qxl->pos);
632 } else {
633 red->flags = 0;
634 red->pos.x = 0;
635 red->pos.y = 0;
636 }
637 }
638
red_put_qmask(SpiceQMask * red)639 static void red_put_qmask(SpiceQMask *red)
640 {
641 red_put_image(red->bitmap);
642 }
643
red_get_fill_ptr(RedMemSlotInfo * slots,int group_id,SpiceFill * red,QXLFill * qxl,uint32_t flags)644 static void red_get_fill_ptr(RedMemSlotInfo *slots, int group_id,
645 SpiceFill *red, QXLFill *qxl, uint32_t flags)
646 {
647 red_get_brush_ptr(slots, group_id, &red->brush, &qxl->brush, flags);
648 red->rop_descriptor = qxl->rop_descriptor;
649 red_get_qmask_ptr(slots, group_id, &red->mask, &qxl->mask, flags);
650 }
651
red_put_fill(SpiceFill * red)652 static void red_put_fill(SpiceFill *red)
653 {
654 red_put_brush(&red->brush);
655 red_put_qmask(&red->mask);
656 }
657
red_get_opaque_ptr(RedMemSlotInfo * slots,int group_id,SpiceOpaque * red,QXLOpaque * qxl,uint32_t flags)658 static void red_get_opaque_ptr(RedMemSlotInfo *slots, int group_id,
659 SpiceOpaque *red, QXLOpaque *qxl, uint32_t flags)
660 {
661 red->src_bitmap = red_get_image(slots, group_id, qxl->src_bitmap, flags, false);
662 red_get_rect_ptr(&red->src_area, &qxl->src_area);
663 red_get_brush_ptr(slots, group_id, &red->brush, &qxl->brush, flags);
664 red->rop_descriptor = qxl->rop_descriptor;
665 red->scale_mode = qxl->scale_mode;
666 red_get_qmask_ptr(slots, group_id, &red->mask, &qxl->mask, flags);
667 }
668
red_put_opaque(SpiceOpaque * red)669 static void red_put_opaque(SpiceOpaque *red)
670 {
671 red_put_image(red->src_bitmap);
672 red_put_brush(&red->brush);
673 red_put_qmask(&red->mask);
674 }
675
red_get_copy_ptr(RedMemSlotInfo * slots,int group_id,RedDrawable * red_drawable,QXLCopy * qxl,uint32_t flags)676 static bool red_get_copy_ptr(RedMemSlotInfo *slots, int group_id,
677 RedDrawable *red_drawable, QXLCopy *qxl, uint32_t flags)
678 {
679 /* there's no sense to have this true, this will just waste CPU and reduce optimizations
680 * for this command. Due to some bugs however some driver set self_bitmap field for this
681 * command so reset it. */
682 red_drawable->self_bitmap = false;
683
684 SpiceCopy *red = &red_drawable->u.copy;
685
686 red->src_bitmap = red_get_image(slots, group_id, qxl->src_bitmap, flags, false);
687 if (!red->src_bitmap) {
688 return false;
689 }
690 red_get_rect_ptr(&red->src_area, &qxl->src_area);
691 /* The source area should not extend outside the source bitmap or have
692 * swapped coordinates.
693 */
694 if (red->src_area.left < 0 ||
695 red->src_area.left > red->src_area.right ||
696 red->src_area.top < 0 ||
697 red->src_area.top > red->src_area.bottom) {
698 return false;
699 }
700 if (red->src_bitmap->descriptor.type == SPICE_IMAGE_TYPE_BITMAP &&
701 (red->src_area.right > red->src_bitmap->u.bitmap.x ||
702 red->src_area.bottom > red->src_bitmap->u.bitmap.y)) {
703 return false;
704 }
705 red->rop_descriptor = qxl->rop_descriptor;
706 red->scale_mode = qxl->scale_mode;
707 red_get_qmask_ptr(slots, group_id, &red->mask, &qxl->mask, flags);
708 return true;
709 }
710
red_put_copy(SpiceCopy * red)711 static void red_put_copy(SpiceCopy *red)
712 {
713 red_put_image(red->src_bitmap);
714 red_put_qmask(&red->mask);
715 }
716
717 // these types are really the same thing
718 #define red_get_blend_ptr red_get_copy_ptr
719 #define red_put_blend red_put_copy
720
red_get_transparent_ptr(RedMemSlotInfo * slots,int group_id,SpiceTransparent * red,QXLTransparent * qxl,uint32_t flags)721 static void red_get_transparent_ptr(RedMemSlotInfo *slots, int group_id,
722 SpiceTransparent *red, QXLTransparent *qxl,
723 uint32_t flags)
724 {
725 red->src_bitmap = red_get_image(slots, group_id, qxl->src_bitmap, flags, false);
726 red_get_rect_ptr(&red->src_area, &qxl->src_area);
727 red->src_color = qxl->src_color;
728 red->true_color = qxl->true_color;
729 }
730
red_put_transparent(SpiceTransparent * red)731 static void red_put_transparent(SpiceTransparent *red)
732 {
733 red_put_image(red->src_bitmap);
734 }
735
red_get_alpha_blend_ptr(RedMemSlotInfo * slots,int group_id,SpiceAlphaBlend * red,QXLAlphaBlend * qxl,uint32_t flags)736 static void red_get_alpha_blend_ptr(RedMemSlotInfo *slots, int group_id,
737 SpiceAlphaBlend *red, QXLAlphaBlend *qxl,
738 uint32_t flags)
739 {
740 red->alpha_flags = qxl->alpha_flags;
741 red->alpha = qxl->alpha;
742 red->src_bitmap = red_get_image(slots, group_id, qxl->src_bitmap, flags, false);
743 red_get_rect_ptr(&red->src_area, &qxl->src_area);
744 }
745
red_get_alpha_blend_ptr_compat(RedMemSlotInfo * slots,int group_id,SpiceAlphaBlend * red,QXLCompatAlphaBlend * qxl,uint32_t flags)746 static void red_get_alpha_blend_ptr_compat(RedMemSlotInfo *slots, int group_id,
747 SpiceAlphaBlend *red, QXLCompatAlphaBlend *qxl,
748 uint32_t flags)
749 {
750 red->alpha = qxl->alpha;
751 red->src_bitmap = red_get_image(slots, group_id, qxl->src_bitmap, flags, false);
752 red_get_rect_ptr(&red->src_area, &qxl->src_area);
753 }
754
red_put_alpha_blend(SpiceAlphaBlend * red)755 static void red_put_alpha_blend(SpiceAlphaBlend *red)
756 {
757 red_put_image(red->src_bitmap);
758 }
759
get_transform(RedMemSlotInfo * slots,int group_id,QXLPHYSICAL qxl_transform,SpiceTransform * dst_transform)760 static bool get_transform(RedMemSlotInfo *slots,
761 int group_id,
762 QXLPHYSICAL qxl_transform,
763 SpiceTransform *dst_transform)
764 {
765 const uint32_t *t = nullptr;
766
767 if (qxl_transform == 0)
768 return false;
769
770 t = (uint32_t *)memslot_get_virt(slots, qxl_transform, sizeof(*dst_transform), group_id);
771
772 if (t == nullptr)
773 return false;
774
775 memcpy(dst_transform, t, sizeof(*dst_transform));
776 return true;
777 }
778
red_get_composite_ptr(RedMemSlotInfo * slots,int group_id,SpiceComposite * red,QXLComposite * qxl,uint32_t flags)779 static void red_get_composite_ptr(RedMemSlotInfo *slots, int group_id,
780 SpiceComposite *red, QXLComposite *qxl, uint32_t flags)
781 {
782 red->flags = qxl->flags;
783
784 red->src_bitmap = red_get_image(slots, group_id, qxl->src, flags, false);
785 if (get_transform(slots, group_id, qxl->src_transform, &red->src_transform))
786 red->flags |= SPICE_COMPOSITE_HAS_SRC_TRANSFORM;
787
788 if (qxl->mask) {
789 red->mask_bitmap = red_get_image(slots, group_id, qxl->mask, flags, false);
790 red->flags |= SPICE_COMPOSITE_HAS_MASK;
791 if (get_transform(slots, group_id, qxl->mask_transform, &red->mask_transform))
792 red->flags |= SPICE_COMPOSITE_HAS_MASK_TRANSFORM;
793 } else {
794 red->mask_bitmap = nullptr;
795 }
796 red->src_origin.x = qxl->src_origin.x;
797 red->src_origin.y = qxl->src_origin.y;
798 red->mask_origin.x = qxl->mask_origin.x;
799 red->mask_origin.y = qxl->mask_origin.y;
800 }
801
red_put_composite(SpiceComposite * red)802 static void red_put_composite(SpiceComposite *red)
803 {
804 red_put_image(red->src_bitmap);
805 if (red->mask_bitmap)
806 red_put_image(red->mask_bitmap);
807 }
808
red_get_rop3_ptr(RedMemSlotInfo * slots,int group_id,SpiceRop3 * red,QXLRop3 * qxl,uint32_t flags)809 static void red_get_rop3_ptr(RedMemSlotInfo *slots, int group_id,
810 SpiceRop3 *red, QXLRop3 *qxl, uint32_t flags)
811 {
812 red->src_bitmap = red_get_image(slots, group_id, qxl->src_bitmap, flags, false);
813 red_get_rect_ptr(&red->src_area, &qxl->src_area);
814 red_get_brush_ptr(slots, group_id, &red->brush, &qxl->brush, flags);
815 red->rop3 = qxl->rop3;
816 red->scale_mode = qxl->scale_mode;
817 red_get_qmask_ptr(slots, group_id, &red->mask, &qxl->mask, flags);
818 }
819
red_put_rop3(SpiceRop3 * red)820 static void red_put_rop3(SpiceRop3 *red)
821 {
822 red_put_image(red->src_bitmap);
823 red_put_brush(&red->brush);
824 red_put_qmask(&red->mask);
825 }
826
red_get_stroke_ptr(RedMemSlotInfo * slots,int group_id,SpiceStroke * red,QXLStroke * qxl,uint32_t flags)827 static bool red_get_stroke_ptr(RedMemSlotInfo *slots, int group_id,
828 SpiceStroke *red, QXLStroke *qxl, uint32_t flags)
829 {
830 red->path = red_get_path(slots, group_id, qxl->path);
831 if (!red->path) {
832 return false;
833 }
834 red->attr.flags = qxl->attr.flags;
835 if (red->attr.flags & SPICE_LINE_FLAGS_STYLED) {
836 int style_nseg;
837 uint8_t *buf;
838
839 style_nseg = qxl->attr.style_nseg;
840 red->attr.style = (SPICE_FIXED28_4*) g_malloc_n(style_nseg, sizeof(SPICE_FIXED28_4));
841 red->attr.style_nseg = style_nseg;
842 spice_assert(qxl->attr.style);
843 buf = (uint8_t *)memslot_get_virt(slots, qxl->attr.style,
844 style_nseg * sizeof(QXLFIXED), group_id);
845 if (buf == nullptr) {
846 return false;
847 }
848 memcpy(red->attr.style, buf, style_nseg * sizeof(QXLFIXED));
849 } else {
850 red->attr.style_nseg = 0;
851 red->attr.style = nullptr;
852 }
853 red_get_brush_ptr(slots, group_id, &red->brush, &qxl->brush, flags);
854 red->fore_mode = qxl->fore_mode;
855 red->back_mode = qxl->back_mode;
856 return true;
857 }
858
red_put_stroke(SpiceStroke * red)859 static void red_put_stroke(SpiceStroke *red)
860 {
861 red_put_brush(&red->brush);
862 g_free(red->path);
863 if (red->attr.flags & SPICE_LINE_FLAGS_STYLED) {
864 g_free(red->attr.style);
865 }
866 }
867
red_get_string(RedMemSlotInfo * slots,int group_id,QXLPHYSICAL addr)868 static SpiceString *red_get_string(RedMemSlotInfo *slots, int group_id,
869 QXLPHYSICAL addr)
870 {
871 RedDataChunk chunks;
872 QXLString *qxl;
873 QXLRasterGlyph *start, *end;
874 SpiceString *red;
875 SpiceRasterGlyph *glyph;
876 uint8_t *data;
877 bool free_data;
878 size_t chunk_size, qxl_size, red_size, glyph_size;
879 int glyphs, i;
880 /* use unsigned to prevent integer overflow in multiplication below */
881 unsigned int bpp = 0;
882 uint16_t qxl_flags, qxl_length;
883
884 qxl = (QXLString *)memslot_get_virt(slots, addr, sizeof(*qxl), group_id);
885 if (qxl == nullptr) {
886 return nullptr;
887 }
888 chunk_size = red_get_data_chunks_ptr(slots, group_id,
889 memslot_get_id(slots, addr),
890 &chunks, &qxl->chunk);
891 if (chunk_size == INVALID_SIZE) {
892 return nullptr;
893 }
894 data = red_linearize_chunk(&chunks, chunk_size, &free_data);
895 red_put_data_chunks(&chunks);
896
897 qxl_size = qxl->data_size;
898 qxl_flags = qxl->flags;
899 qxl_length = qxl->length;
900 spice_assert(chunk_size == qxl_size);
901
902 if (qxl_flags & SPICE_STRING_FLAGS_RASTER_A1) {
903 bpp = 1;
904 } else if (qxl_flags & SPICE_STRING_FLAGS_RASTER_A4) {
905 bpp = 4;
906 } else if (qxl_flags & SPICE_STRING_FLAGS_RASTER_A8) {
907 bpp = 8;
908 }
909 spice_assert(bpp != 0);
910
911 start = (QXLRasterGlyph*)data;
912 end = (QXLRasterGlyph*)(data + chunk_size);
913 red_size = sizeof(SpiceString);
914 glyphs = 0;
915 while (start < end) {
916 spice_assert((QXLRasterGlyph*)(&start->data[0]) <= end);
917 glyphs++;
918 glyph_size = start->height * ((start->width * bpp + 7u) / 8u);
919 red_size += sizeof(SpiceRasterGlyph *) + SPICE_ALIGN(sizeof(SpiceRasterGlyph) + glyph_size, 4);
920 /* do the test correctly, we know end - start->data[0] cannot
921 * overflow, don't use start->data[glyph_size] to test for
922 * buffer overflow as this on 32 bit can cause overflow
923 * on the pointer arithmetic */
924 spice_assert(glyph_size <= (char*) end - (char*) &start->data[0]);
925 start = (QXLRasterGlyph*)(&start->data[glyph_size]);
926 }
927 spice_assert(start <= end);
928 spice_assert(glyphs == qxl_length);
929
930 red = (SpiceString*) g_malloc(red_size);
931 red->length = qxl_length;
932 red->flags = qxl_flags;
933
934 start = (QXLRasterGlyph*)data;
935 end = (QXLRasterGlyph*)(data + chunk_size);
936 glyph = (SpiceRasterGlyph *)&red->glyphs[red->length];
937 for (i = 0; i < red->length; i++) {
938 spice_assert((QXLRasterGlyph*)(&start->data[0]) <= end);
939 red->glyphs[i] = glyph;
940 glyph->width = start->width;
941 glyph->height = start->height;
942 red_get_point_ptr(&glyph->render_pos, &start->render_pos);
943 red_get_point_ptr(&glyph->glyph_origin, &start->glyph_origin);
944 glyph_size = glyph->height * ((glyph->width * bpp + 7u) / 8u);
945 /* see above for similar test */
946 spice_assert(glyph_size <= (char*) end - (char*) &start->data[0]);
947 memcpy(glyph->data, start->data, glyph_size);
948 start = (QXLRasterGlyph*)(&start->data[glyph_size]);
949 glyph = SPICE_ALIGNED_CAST(SpiceRasterGlyph*,
950 (((uint8_t *)glyph) +
951 SPICE_ALIGN(sizeof(SpiceRasterGlyph) + glyph_size, 4)));
952 }
953
954 if (free_data) {
955 g_free(data);
956 }
957 return red;
958 }
959
red_get_text_ptr(RedMemSlotInfo * slots,int group_id,SpiceText * red,QXLText * qxl,uint32_t flags)960 static void red_get_text_ptr(RedMemSlotInfo *slots, int group_id,
961 SpiceText *red, QXLText *qxl, uint32_t flags)
962 {
963 red->str = red_get_string(slots, group_id, qxl->str);
964 red_get_rect_ptr(&red->back_area, &qxl->back_area);
965 red_get_brush_ptr(slots, group_id, &red->fore_brush, &qxl->fore_brush, flags);
966 red_get_brush_ptr(slots, group_id, &red->back_brush, &qxl->back_brush, flags);
967 red->fore_mode = qxl->fore_mode;
968 red->back_mode = qxl->back_mode;
969 }
970
red_put_text_ptr(SpiceText * red)971 static void red_put_text_ptr(SpiceText *red)
972 {
973 g_free(red->str);
974 red_put_brush(&red->fore_brush);
975 red_put_brush(&red->back_brush);
976 }
977
red_get_whiteness_ptr(RedMemSlotInfo * slots,int group_id,SpiceWhiteness * red,QXLWhiteness * qxl,uint32_t flags)978 static void red_get_whiteness_ptr(RedMemSlotInfo *slots, int group_id,
979 SpiceWhiteness *red, QXLWhiteness *qxl, uint32_t flags)
980 {
981 red_get_qmask_ptr(slots, group_id, &red->mask, &qxl->mask, flags);
982 }
983
red_put_whiteness(SpiceWhiteness * red)984 static void red_put_whiteness(SpiceWhiteness *red)
985 {
986 red_put_qmask(&red->mask);
987 }
988
989 #define red_get_invers_ptr red_get_whiteness_ptr
990 #define red_get_blackness_ptr red_get_whiteness_ptr
991 #define red_put_invers red_put_whiteness
992 #define red_put_blackness red_put_whiteness
993
red_get_clip_ptr(RedMemSlotInfo * slots,int group_id,SpiceClip * red,QXLClip * qxl)994 static void red_get_clip_ptr(RedMemSlotInfo *slots, int group_id,
995 SpiceClip *red, QXLClip *qxl)
996 {
997 red->type = qxl->type;
998 switch (red->type) {
999 case SPICE_CLIP_TYPE_RECTS:
1000 red->rects = red_get_clip_rects(slots, group_id, qxl->data);
1001 break;
1002 }
1003 }
1004
red_put_clip(SpiceClip * red)1005 static void red_put_clip(SpiceClip *red)
1006 {
1007 switch (red->type) {
1008 case SPICE_CLIP_TYPE_RECTS:
1009 g_free(red->rects);
1010 break;
1011 }
1012 }
1013
red_get_native_drawable(QXLInstance * qxl_instance,RedMemSlotInfo * slots,int group_id,RedDrawable * red,QXLPHYSICAL addr,uint32_t flags)1014 static bool red_get_native_drawable(QXLInstance *qxl_instance, RedMemSlotInfo *slots, int group_id,
1015 RedDrawable *red, QXLPHYSICAL addr, uint32_t flags)
1016 {
1017 QXLDrawable *qxl;
1018 int i;
1019
1020 qxl = (QXLDrawable *)memslot_get_virt(slots, addr, sizeof(*qxl), group_id);
1021 if (qxl == nullptr) {
1022 return false;
1023 }
1024 red->qxl = qxl_instance;
1025 red->release_info_ext.info = &qxl->release_info;
1026 red->release_info_ext.group_id = group_id;
1027
1028 red_get_rect_ptr(&red->bbox, &qxl->bbox);
1029 red_get_clip_ptr(slots, group_id, &red->clip, &qxl->clip);
1030 red->effect = qxl->effect;
1031 red->mm_time = qxl->mm_time;
1032 red->self_bitmap = qxl->self_bitmap;
1033 red_get_rect_ptr(&red->self_bitmap_area, &qxl->self_bitmap_area);
1034 red->surface_id = qxl->surface_id;
1035
1036 for (i = 0; i < 3; i++) {
1037 red->surface_deps[i] = qxl->surfaces_dest[i];
1038 red_get_rect_ptr(&red->surfaces_rects[i], &qxl->surfaces_rects[i]);
1039 }
1040
1041 red->type = qxl->type;
1042 switch (red->type) {
1043 case QXL_DRAW_ALPHA_BLEND:
1044 red_get_alpha_blend_ptr(slots, group_id,
1045 &red->u.alpha_blend, &qxl->u.alpha_blend, flags);
1046 break;
1047 case QXL_DRAW_BLACKNESS:
1048 red_get_blackness_ptr(slots, group_id,
1049 &red->u.blackness, &qxl->u.blackness, flags);
1050 break;
1051 case QXL_DRAW_BLEND:
1052 return red_get_blend_ptr(slots, group_id, red, &qxl->u.blend, flags);
1053 case QXL_DRAW_COPY:
1054 return red_get_copy_ptr(slots, group_id, red, &qxl->u.copy, flags);
1055 case QXL_COPY_BITS:
1056 red_get_point_ptr(&red->u.copy_bits.src_pos, &qxl->u.copy_bits.src_pos);
1057 break;
1058 case QXL_DRAW_FILL:
1059 red_get_fill_ptr(slots, group_id, &red->u.fill, &qxl->u.fill, flags);
1060 break;
1061 case QXL_DRAW_OPAQUE:
1062 red_get_opaque_ptr(slots, group_id, &red->u.opaque, &qxl->u.opaque, flags);
1063 break;
1064 case QXL_DRAW_INVERS:
1065 red_get_invers_ptr(slots, group_id, &red->u.invers, &qxl->u.invers, flags);
1066 break;
1067 case QXL_DRAW_NOP:
1068 break;
1069 case QXL_DRAW_ROP3:
1070 red_get_rop3_ptr(slots, group_id, &red->u.rop3, &qxl->u.rop3, flags);
1071 break;
1072 case QXL_DRAW_COMPOSITE:
1073 red_get_composite_ptr(slots, group_id, &red->u.composite, &qxl->u.composite, flags);
1074 break;
1075 case QXL_DRAW_STROKE:
1076 return red_get_stroke_ptr(slots, group_id, &red->u.stroke, &qxl->u.stroke, flags);
1077 case QXL_DRAW_TEXT:
1078 red_get_text_ptr(slots, group_id, &red->u.text, &qxl->u.text, flags);
1079 break;
1080 case QXL_DRAW_TRANSPARENT:
1081 red_get_transparent_ptr(slots, group_id,
1082 &red->u.transparent, &qxl->u.transparent, flags);
1083 break;
1084 case QXL_DRAW_WHITENESS:
1085 red_get_whiteness_ptr(slots, group_id,
1086 &red->u.whiteness, &qxl->u.whiteness, flags);
1087 break;
1088 default:
1089 spice_warning("unknown type %d", red->type);
1090 return false;
1091 };
1092 return true;
1093 }
1094
red_get_compat_drawable(QXLInstance * qxl_instance,RedMemSlotInfo * slots,int group_id,RedDrawable * red,QXLPHYSICAL addr,uint32_t flags)1095 static bool red_get_compat_drawable(QXLInstance *qxl_instance, RedMemSlotInfo *slots, int group_id,
1096 RedDrawable *red, QXLPHYSICAL addr, uint32_t flags)
1097 {
1098 QXLCompatDrawable *qxl;
1099
1100 qxl = (QXLCompatDrawable *)memslot_get_virt(slots, addr, sizeof(*qxl), group_id);
1101 if (qxl == nullptr) {
1102 return false;
1103 }
1104 red->qxl = qxl_instance;
1105 red->release_info_ext.info = &qxl->release_info;
1106 red->release_info_ext.group_id = group_id;
1107
1108 red_get_rect_ptr(&red->bbox, &qxl->bbox);
1109 red_get_clip_ptr(slots, group_id, &red->clip, &qxl->clip);
1110 red->effect = qxl->effect;
1111 red->mm_time = qxl->mm_time;
1112
1113 red->self_bitmap = (qxl->bitmap_offset != 0);
1114 red_get_rect_ptr(&red->self_bitmap_area, &qxl->bitmap_area);
1115
1116 red->surface_deps[0] = -1;
1117 red->surface_deps[1] = -1;
1118 red->surface_deps[2] = -1;
1119
1120 red->type = qxl->type;
1121 switch (red->type) {
1122 case QXL_DRAW_ALPHA_BLEND:
1123 red_get_alpha_blend_ptr_compat(slots, group_id,
1124 &red->u.alpha_blend, &qxl->u.alpha_blend, flags);
1125 break;
1126 case QXL_DRAW_BLACKNESS:
1127 red_get_blackness_ptr(slots, group_id,
1128 &red->u.blackness, &qxl->u.blackness, flags);
1129 break;
1130 case QXL_DRAW_BLEND:
1131 return red_get_blend_ptr(slots, group_id, red, &qxl->u.blend, flags);
1132 case QXL_DRAW_COPY:
1133 return red_get_copy_ptr(slots, group_id, red, &qxl->u.copy, flags);
1134 case QXL_COPY_BITS:
1135 red_get_point_ptr(&red->u.copy_bits.src_pos, &qxl->u.copy_bits.src_pos);
1136 red->surface_deps[0] = 0;
1137 red->surfaces_rects[0].left = red->u.copy_bits.src_pos.x;
1138 red->surfaces_rects[0].right = red->u.copy_bits.src_pos.x +
1139 (red->bbox.right - red->bbox.left);
1140 red->surfaces_rects[0].top = red->u.copy_bits.src_pos.y;
1141 red->surfaces_rects[0].bottom = red->u.copy_bits.src_pos.y +
1142 (red->bbox.bottom - red->bbox.top);
1143 break;
1144 case QXL_DRAW_FILL:
1145 red_get_fill_ptr(slots, group_id, &red->u.fill, &qxl->u.fill, flags);
1146 break;
1147 case QXL_DRAW_OPAQUE:
1148 red_get_opaque_ptr(slots, group_id, &red->u.opaque, &qxl->u.opaque, flags);
1149 break;
1150 case QXL_DRAW_INVERS:
1151 red_get_invers_ptr(slots, group_id, &red->u.invers, &qxl->u.invers, flags);
1152 break;
1153 case QXL_DRAW_NOP:
1154 break;
1155 case QXL_DRAW_ROP3:
1156 red_get_rop3_ptr(slots, group_id, &red->u.rop3, &qxl->u.rop3, flags);
1157 break;
1158 case QXL_DRAW_STROKE:
1159 return red_get_stroke_ptr(slots, group_id, &red->u.stroke, &qxl->u.stroke, flags);
1160 case QXL_DRAW_TEXT:
1161 red_get_text_ptr(slots, group_id, &red->u.text, &qxl->u.text, flags);
1162 break;
1163 case QXL_DRAW_TRANSPARENT:
1164 red_get_transparent_ptr(slots, group_id,
1165 &red->u.transparent, &qxl->u.transparent, flags);
1166 break;
1167 case QXL_DRAW_WHITENESS:
1168 red_get_whiteness_ptr(slots, group_id,
1169 &red->u.whiteness, &qxl->u.whiteness, flags);
1170 break;
1171 default:
1172 spice_warning("unknown type %d", red->type);
1173 return false;
1174 };
1175 return true;
1176 }
1177
red_get_drawable(QXLInstance * qxl,RedMemSlotInfo * slots,int group_id,RedDrawable * red,QXLPHYSICAL addr,uint32_t flags)1178 static bool red_get_drawable(QXLInstance *qxl, RedMemSlotInfo *slots, int group_id,
1179 RedDrawable *red, QXLPHYSICAL addr, uint32_t flags)
1180 {
1181 bool ret;
1182
1183 if (flags & QXL_COMMAND_FLAG_COMPAT) {
1184 ret = red_get_compat_drawable(qxl, slots, group_id, red, addr, flags);
1185 } else {
1186 ret = red_get_native_drawable(qxl, slots, group_id, red, addr, flags);
1187 }
1188 return ret;
1189 }
1190
red_put_drawable(RedDrawable * red)1191 static void red_put_drawable(RedDrawable *red)
1192 {
1193 red_put_clip(&red->clip);
1194 if (red->self_bitmap_image) {
1195 red_put_image(red->self_bitmap_image);
1196 }
1197 switch (red->type) {
1198 case QXL_DRAW_ALPHA_BLEND:
1199 red_put_alpha_blend(&red->u.alpha_blend);
1200 break;
1201 case QXL_DRAW_BLACKNESS:
1202 red_put_blackness(&red->u.blackness);
1203 break;
1204 case QXL_DRAW_BLEND:
1205 red_put_blend(&red->u.blend);
1206 break;
1207 case QXL_DRAW_COPY:
1208 red_put_copy(&red->u.copy);
1209 break;
1210 case QXL_DRAW_FILL:
1211 red_put_fill(&red->u.fill);
1212 break;
1213 case QXL_DRAW_OPAQUE:
1214 red_put_opaque(&red->u.opaque);
1215 break;
1216 case QXL_DRAW_INVERS:
1217 red_put_invers(&red->u.invers);
1218 break;
1219 case QXL_DRAW_ROP3:
1220 red_put_rop3(&red->u.rop3);
1221 break;
1222 case QXL_DRAW_COMPOSITE:
1223 red_put_composite(&red->u.composite);
1224 break;
1225 case QXL_DRAW_STROKE:
1226 red_put_stroke(&red->u.stroke);
1227 break;
1228 case QXL_DRAW_TEXT:
1229 red_put_text_ptr(&red->u.text);
1230 break;
1231 case QXL_DRAW_TRANSPARENT:
1232 red_put_transparent(&red->u.transparent);
1233 break;
1234 case QXL_DRAW_WHITENESS:
1235 red_put_whiteness(&red->u.whiteness);
1236 break;
1237 }
1238 if (red->qxl != nullptr) {
1239 red_qxl_release_resource(red->qxl, red->release_info_ext);
1240 }
1241 }
1242
red_drawable_new(QXLInstance * qxl,RedMemSlotInfo * slots,int group_id,QXLPHYSICAL addr,uint32_t flags)1243 RedDrawable *red_drawable_new(QXLInstance *qxl, RedMemSlotInfo *slots,
1244 int group_id, QXLPHYSICAL addr,
1245 uint32_t flags)
1246 {
1247 auto red = g_new0(RedDrawable, 1);
1248
1249 red->refs = 1;
1250
1251 if (!red_get_drawable(qxl, slots, group_id, red, addr, flags)) {
1252 red_drawable_unref(red);
1253 return nullptr;
1254 }
1255
1256 return red;
1257 }
1258
red_drawable_ref(RedDrawable * drawable)1259 RedDrawable *red_drawable_ref(RedDrawable *drawable)
1260 {
1261 drawable->refs++;
1262 return drawable;
1263 }
1264
red_drawable_unref(RedDrawable * red_drawable)1265 void red_drawable_unref(RedDrawable *red_drawable)
1266 {
1267 if (--red_drawable->refs) {
1268 return;
1269 }
1270 red_put_drawable(red_drawable);
1271 g_free(red_drawable);
1272 }
1273
red_get_update_cmd(QXLInstance * qxl_instance,RedMemSlotInfo * slots,int group_id,RedUpdateCmd * red,QXLPHYSICAL addr)1274 static bool red_get_update_cmd(QXLInstance *qxl_instance, RedMemSlotInfo *slots, int group_id,
1275 RedUpdateCmd *red, QXLPHYSICAL addr)
1276 {
1277 QXLUpdateCmd *qxl;
1278
1279 qxl = (QXLUpdateCmd *)memslot_get_virt(slots, addr, sizeof(*qxl), group_id);
1280 if (qxl == nullptr) {
1281 return false;
1282 }
1283 red->qxl = qxl_instance;
1284 red->release_info_ext.info = &qxl->release_info;
1285 red->release_info_ext.group_id = group_id;
1286
1287 red_get_rect_ptr(&red->area, &qxl->area);
1288 red->update_id = qxl->update_id;
1289 red->surface_id = qxl->surface_id;
1290 return true;
1291 }
1292
red_put_update_cmd(RedUpdateCmd * red)1293 static void red_put_update_cmd(RedUpdateCmd *red)
1294 {
1295 if (red->qxl != nullptr) {
1296 red_qxl_release_resource(red->qxl, red->release_info_ext);
1297 }
1298 }
1299
red_update_cmd_new(QXLInstance * qxl,RedMemSlotInfo * slots,int group_id,QXLPHYSICAL addr)1300 RedUpdateCmd *red_update_cmd_new(QXLInstance *qxl, RedMemSlotInfo *slots,
1301 int group_id, QXLPHYSICAL addr)
1302 {
1303 RedUpdateCmd *red;
1304
1305 red = g_new0(RedUpdateCmd, 1);
1306
1307 red->refs = 1;
1308
1309 if (!red_get_update_cmd(qxl, slots, group_id, red, addr)) {
1310 red_update_cmd_unref(red);
1311 return nullptr;
1312 }
1313
1314 return red;
1315 }
1316
red_update_cmd_ref(RedUpdateCmd * red)1317 RedUpdateCmd *red_update_cmd_ref(RedUpdateCmd *red)
1318 {
1319 red->refs++;
1320 return red;
1321 }
1322
red_update_cmd_unref(RedUpdateCmd * red)1323 void red_update_cmd_unref(RedUpdateCmd *red)
1324 {
1325 if (--red->refs) {
1326 return;
1327 }
1328 red_put_update_cmd(red);
1329 g_free(red);
1330 }
1331
red_get_message(QXLInstance * qxl_instance,RedMemSlotInfo * slots,int group_id,RedMessage * red,QXLPHYSICAL addr)1332 static bool red_get_message(QXLInstance *qxl_instance, RedMemSlotInfo *slots, int group_id,
1333 RedMessage *red, QXLPHYSICAL addr)
1334 {
1335 QXLMessage *qxl;
1336 int memslot_id;
1337 uintptr_t len;
1338 uint8_t *end;
1339
1340 /*
1341 * security alert:
1342 * qxl->data[0] size isn't specified anywhere -> can't verify
1343 * luckily this is for debug logging only,
1344 * so we can just ignore it by default.
1345 */
1346 qxl = (QXLMessage *)memslot_get_virt(slots, addr, sizeof(*qxl), group_id);
1347 if (qxl == nullptr) {
1348 return false;
1349 }
1350 red->qxl = qxl_instance;
1351 red->release_info_ext.info = &qxl->release_info;
1352 red->release_info_ext.group_id = group_id;
1353 red->data = qxl->data;
1354 memslot_id = memslot_get_id(slots, addr+sizeof(*qxl));
1355 len = memslot_max_size_virt(slots, ((intptr_t) qxl)+sizeof(*qxl), memslot_id, group_id);
1356 len = MIN(len, 100000);
1357 end = (uint8_t *)memchr(qxl->data, 0, len);
1358 if (end == nullptr) {
1359 return false;
1360 }
1361 red->len = end - qxl->data;
1362 return true;
1363 }
1364
red_put_message(RedMessage * red)1365 static void red_put_message(RedMessage *red)
1366 {
1367 if (red->qxl != nullptr) {
1368 red_qxl_release_resource(red->qxl, red->release_info_ext);
1369 }
1370 }
1371
red_message_new(QXLInstance * qxl,RedMemSlotInfo * slots,int group_id,QXLPHYSICAL addr)1372 RedMessage *red_message_new(QXLInstance *qxl, RedMemSlotInfo *slots,
1373 int group_id, QXLPHYSICAL addr)
1374 {
1375 RedMessage *red;
1376
1377 red = g_new0(RedMessage, 1);
1378
1379 red->refs = 1;
1380
1381 if (!red_get_message(qxl, slots, group_id, red, addr)) {
1382 red_message_unref(red);
1383 return nullptr;
1384 }
1385
1386 return red;
1387 }
1388
red_message_ref(RedMessage * red)1389 RedMessage *red_message_ref(RedMessage *red)
1390 {
1391 red->refs++;
1392 return red;
1393 }
1394
red_message_unref(RedMessage * red)1395 void red_message_unref(RedMessage *red)
1396 {
1397 if (--red->refs) {
1398 return;
1399 }
1400 red_put_message(red);
1401 g_free(red);
1402 }
1403
surface_format_to_bpp(uint32_t format)1404 static unsigned int surface_format_to_bpp(uint32_t format)
1405 {
1406 switch (format) {
1407 case SPICE_SURFACE_FMT_1_A:
1408 return 1;
1409 case SPICE_SURFACE_FMT_8_A:
1410 return 8;
1411 case SPICE_SURFACE_FMT_16_555:
1412 case SPICE_SURFACE_FMT_16_565:
1413 return 16;
1414 case SPICE_SURFACE_FMT_32_xRGB:
1415 case SPICE_SURFACE_FMT_32_ARGB:
1416 return 32;
1417 }
1418 return 0;
1419 }
1420
red_validate_surface(uint32_t width,uint32_t height,int32_t stride,uint32_t format)1421 bool red_validate_surface(uint32_t width, uint32_t height,
1422 int32_t stride, uint32_t format)
1423 {
1424 unsigned int bpp;
1425 uint64_t size;
1426
1427 bpp = surface_format_to_bpp(format);
1428
1429 /* check if format is valid */
1430 if (!bpp) {
1431 return false;
1432 }
1433
1434 /* check stride is larger than required bytes */
1435 size = ((uint64_t) width * bpp + 7u) / 8u;
1436 /* the uint32_t conversion is here to avoid problems with -2^31 value */
1437 if (stride == G_MININT32 || size > (uint32_t) abs(stride)) {
1438 return false;
1439 }
1440
1441 /* the multiplication can overflow, also abs(-2^31) may return a negative value */
1442 size = (uint64_t) height * abs(stride);
1443 return size <= MAX_DATA_CHUNK;
1444 }
1445
red_get_surface_cmd(QXLInstance * qxl_instance,RedMemSlotInfo * slots,int group_id,RedSurfaceCmd * red,QXLPHYSICAL addr)1446 static bool red_get_surface_cmd(QXLInstance *qxl_instance, RedMemSlotInfo *slots, int group_id,
1447 RedSurfaceCmd *red, QXLPHYSICAL addr)
1448
1449 {
1450 QXLSurfaceCmd *qxl;
1451 uint64_t size;
1452
1453 qxl = (QXLSurfaceCmd *)memslot_get_virt(slots, addr, sizeof(*qxl), group_id);
1454 if (qxl == nullptr) {
1455 return false;
1456 }
1457 red->qxl = qxl_instance;
1458 red->release_info_ext.info = &qxl->release_info;
1459 red->release_info_ext.group_id = group_id;
1460
1461 red->surface_id = qxl->surface_id;
1462 red->type = qxl->type;
1463 red->flags = qxl->flags;
1464
1465 switch (red->type) {
1466 case QXL_SURFACE_CMD_CREATE:
1467 red->u.surface_create.format = qxl->u.surface_create.format;
1468 red->u.surface_create.width = qxl->u.surface_create.width;
1469 red->u.surface_create.height = qxl->u.surface_create.height;
1470 red->u.surface_create.stride = qxl->u.surface_create.stride;
1471
1472 if (!red_validate_surface(red->u.surface_create.width, red->u.surface_create.height,
1473 red->u.surface_create.stride, red->u.surface_create.format)) {
1474 return false;
1475 }
1476
1477 size = red->u.surface_create.height * abs(red->u.surface_create.stride);
1478 red->u.surface_create.data =
1479 (uint8_t*)memslot_get_virt(slots, qxl->u.surface_create.data, size, group_id);
1480 if (red->u.surface_create.data == nullptr) {
1481 return false;
1482 }
1483 break;
1484 }
1485 return true;
1486 }
1487
red_put_surface_cmd(RedSurfaceCmd * red)1488 static void red_put_surface_cmd(RedSurfaceCmd *red)
1489 {
1490 if (red->qxl) {
1491 red_qxl_release_resource(red->qxl, red->release_info_ext);
1492 }
1493 }
1494
red_surface_cmd_new(QXLInstance * qxl_instance,RedMemSlotInfo * slots,int group_id,QXLPHYSICAL addr)1495 RedSurfaceCmd *red_surface_cmd_new(QXLInstance *qxl_instance, RedMemSlotInfo *slots,
1496 int group_id, QXLPHYSICAL addr)
1497 {
1498 RedSurfaceCmd *cmd;
1499
1500 cmd = g_new0(RedSurfaceCmd, 1);
1501
1502 cmd->refs = 1;
1503
1504 if (!red_get_surface_cmd(qxl_instance, slots, group_id, cmd, addr)) {
1505 red_surface_cmd_unref(cmd);
1506 return nullptr;
1507 }
1508
1509 return cmd;
1510 }
1511
red_surface_cmd_ref(RedSurfaceCmd * cmd)1512 RedSurfaceCmd *red_surface_cmd_ref(RedSurfaceCmd *cmd)
1513 {
1514 cmd->refs++;
1515 return cmd;
1516 }
1517
red_surface_cmd_unref(RedSurfaceCmd * cmd)1518 void red_surface_cmd_unref(RedSurfaceCmd *cmd)
1519 {
1520 if (--cmd->refs) {
1521 return;
1522 }
1523 red_put_surface_cmd(cmd);
1524 g_free(cmd);
1525 }
1526
red_get_cursor(RedMemSlotInfo * slots,int group_id,SpiceCursor * red,QXLPHYSICAL addr)1527 static bool red_get_cursor(RedMemSlotInfo *slots, int group_id,
1528 SpiceCursor *red, QXLPHYSICAL addr)
1529 {
1530 QXLCursor *qxl;
1531 RedDataChunk chunks;
1532 size_t size;
1533 uint8_t *data;
1534 bool free_data;
1535
1536 qxl = (QXLCursor *)memslot_get_virt(slots, addr, sizeof(*qxl), group_id);
1537 if (qxl == nullptr) {
1538 return false;
1539 }
1540
1541 red->header.unique = qxl->header.unique;
1542 red->header.type = qxl->header.type;
1543 red->header.width = qxl->header.width;
1544 red->header.height = qxl->header.height;
1545 red->header.hot_spot_x = qxl->header.hot_spot_x;
1546 red->header.hot_spot_y = qxl->header.hot_spot_y;
1547
1548 red->flags = 0;
1549 red->data_size = qxl->data_size;
1550 size = red_get_data_chunks_ptr(slots, group_id,
1551 memslot_get_id(slots, addr),
1552 &chunks, &qxl->chunk);
1553 if (size == INVALID_SIZE) {
1554 return false;
1555 }
1556 red->data_size = MIN(red->data_size, size);
1557 data = red_linearize_chunk(&chunks, size, &free_data);
1558 red_put_data_chunks(&chunks);
1559 if (free_data) {
1560 red->data = data;
1561 } else {
1562 red->data = (uint8_t*) g_memdup(data, size);
1563 }
1564 // Arrived here we could note that we are not going to use anymore cursor data
1565 // and we could be tempted to release resource back to QXL. Don't do that!
1566 // If machine is migrated we will get cursor data back so we need to hold this
1567 // data for migration
1568 return true;
1569 }
1570
red_put_cursor(SpiceCursor * red)1571 static void red_put_cursor(SpiceCursor *red)
1572 {
1573 g_free(red->data);
1574 }
1575
red_get_cursor_cmd(QXLInstance * qxl_instance,RedMemSlotInfo * slots,int group_id,RedCursorCmd * red,QXLPHYSICAL addr)1576 static bool red_get_cursor_cmd(QXLInstance *qxl_instance, RedMemSlotInfo *slots,
1577 int group_id, RedCursorCmd *red,
1578 QXLPHYSICAL addr)
1579 {
1580 QXLCursorCmd *qxl;
1581
1582 qxl = (QXLCursorCmd *)memslot_get_virt(slots, addr, sizeof(*qxl), group_id);
1583 if (qxl == nullptr) {
1584 return false;
1585 }
1586 red->qxl = qxl_instance;
1587 red->release_info_ext.info = &qxl->release_info;
1588 red->release_info_ext.group_id = group_id;
1589
1590 red->type = qxl->type;
1591 switch (red->type) {
1592 case QXL_CURSOR_SET:
1593 red_get_point16_ptr(&red->u.set.position, &qxl->u.set.position);
1594 red->u.set.visible = qxl->u.set.visible;
1595 return red_get_cursor(slots, group_id, &red->u.set.shape, qxl->u.set.shape);
1596 case QXL_CURSOR_MOVE:
1597 red_get_point16_ptr(&red->u.position, &qxl->u.position);
1598 break;
1599 case QXL_CURSOR_TRAIL:
1600 red->u.trail.length = qxl->u.trail.length;
1601 red->u.trail.frequency = qxl->u.trail.frequency;
1602 break;
1603 }
1604 return true;
1605 }
1606
red_cursor_cmd_new(QXLInstance * qxl,RedMemSlotInfo * slots,int group_id,QXLPHYSICAL addr)1607 RedCursorCmd *red_cursor_cmd_new(QXLInstance *qxl, RedMemSlotInfo *slots,
1608 int group_id, QXLPHYSICAL addr)
1609 {
1610 RedCursorCmd *cmd;
1611
1612 cmd = g_new0(RedCursorCmd, 1);
1613
1614 cmd->refs = 1;
1615
1616 if (!red_get_cursor_cmd(qxl, slots, group_id, cmd, addr)) {
1617 red_cursor_cmd_unref(cmd);
1618 return nullptr;
1619 }
1620
1621 return cmd;
1622 }
1623
red_put_cursor_cmd(RedCursorCmd * red)1624 static void red_put_cursor_cmd(RedCursorCmd *red)
1625 {
1626 switch (red->type) {
1627 case QXL_CURSOR_SET:
1628 red_put_cursor(&red->u.set.shape);
1629 break;
1630 }
1631 if (red->qxl) {
1632 red_qxl_release_resource(red->qxl, red->release_info_ext);
1633 }
1634 }
1635
red_cursor_cmd_ref(RedCursorCmd * red)1636 RedCursorCmd *red_cursor_cmd_ref(RedCursorCmd *red)
1637 {
1638 red->refs++;
1639 return red;
1640 }
1641
red_cursor_cmd_unref(RedCursorCmd * red)1642 void red_cursor_cmd_unref(RedCursorCmd *red)
1643 {
1644 if (--red->refs) {
1645 return;
1646 }
1647 red_put_cursor_cmd(red);
1648 g_free(red);
1649 }
1650