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