Line data Source code
1 : /*
2 : Copyright (C) 2017 Red Hat, Inc.
3 :
4 : This library is free software; you can redistribute it and/or
5 : modify it under the terms of the GNU Lesser General Public
6 : License as published by the Free Software Foundation; either
7 : version 2.1 of the License, or (at your option) any later version.
8 :
9 : This library is distributed in the hope that it will be useful,
10 : but WITHOUT ANY WARRANTY; without even the implied warranty of
11 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 : Lesser General Public License for more details.
13 :
14 : You should have received a copy of the GNU Lesser General Public
15 : License along with this library; if not, see <http://www.gnu.org/licenses/>.
16 : */
17 :
18 : /* Test QUIC encoding and decoding. This test can also be used to fuzz the decoding.
19 : *
20 : * To use for the fuzzer you should:
21 : * 1- build enabling AFL.
22 : * $ make clean
23 : * $ make CC=afl-gcc CFLAGS='-O2 -fno-omit-frame-pointer'
24 : * 2- run AFL, the export is to use ElectricFence to detect some additional
25 : * possible buffer overflow, AFL required the program to crash in case of errors
26 : * $ cd tests
27 : * $ mkdir afl_findings
28 : * $ export AFL_PRELOAD=/usr/lib64/libefence.so.0.0
29 : * $ afl-fuzz -i fuzzer-quic-testcases -o afl_findings -m 100 -- ./test_quic --fuzzer-decode @@
30 : */
31 : #include <config.h>
32 :
33 : #include <stdlib.h>
34 : #include <stdbool.h>
35 : #include <string.h>
36 : #include <glib.h>
37 : #include <gdk-pixbuf/gdk-pixbuf.h>
38 :
39 : #include "common/quic.h"
40 :
41 : typedef enum {
42 : COLOR_MODE_RGB,
43 : COLOR_MODE_RGB16,
44 : COLOR_MODE_GRAY,
45 :
46 : COLOR_MODE_END
47 : } color_mode_t;
48 :
49 : static color_mode_t color_mode = COLOR_MODE_RGB;
50 : static bool fuzzying = false;
51 :
52 : typedef struct {
53 : QuicUsrContext usr;
54 : GByteArray *dest;
55 : } QuicData;
56 :
57 : static SPICE_GNUC_NORETURN SPICE_GNUC_PRINTF(2, 3) void
58 0 : quic_usr_error(QuicUsrContext *usr, const char *fmt, ...)
59 : {
60 0 : if (fuzzying) {
61 0 : exit(1);
62 : }
63 :
64 : va_list ap;
65 :
66 0 : va_start(ap, fmt);
67 0 : g_logv(G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, fmt, ap);
68 0 : va_end(ap);
69 :
70 0 : g_assert_not_reached();
71 : }
72 :
73 : static SPICE_GNUC_PRINTF(2, 3) void
74 0 : quic_usr_warn(QuicUsrContext *usr, const char *fmt, ...)
75 : {
76 : va_list ap;
77 :
78 0 : va_start(ap, fmt);
79 0 : g_logv(G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, fmt, ap);
80 0 : va_end(ap);
81 0 : }
82 :
83 444 : static void *quic_usr_malloc(QuicUsrContext *usr, int size)
84 : {
85 444 : return g_malloc(size);
86 : }
87 :
88 :
89 444 : static void quic_usr_free(QuicUsrContext *usr, void *ptr)
90 : {
91 444 : g_free(ptr);
92 444 : }
93 :
94 38 : static int quic_usr_more_space_encode(QuicUsrContext *usr, uint32_t **io_ptr, int rows_completed)
95 : {
96 38 : QuicData *quic_data = (QuicData *)usr;
97 38 : int initial_len = quic_data->dest->len;
98 :
99 38 : g_byte_array_set_size(quic_data->dest, quic_data->dest->len*2);
100 :
101 38 : *io_ptr = (uint32_t *)(quic_data->dest->data + initial_len);
102 38 : return (quic_data->dest->len - initial_len)/4;
103 : }
104 :
105 0 : static int quic_usr_more_space_decode(QuicUsrContext *usr, uint32_t **io_ptr, int rows_completed)
106 : {
107 : // currently all data are passed at initialization, decoder are not expected to
108 : // read more data, beside during fuzzing, in this case quic_usr_error will
109 : // be called
110 0 : return 0;
111 : }
112 :
113 :
114 0 : static int quic_usr_more_lines(QuicUsrContext *usr, uint8_t **lines)
115 : {
116 0 : g_return_val_if_reached(0);
117 : }
118 :
119 :
120 16 : static void init_quic_data(QuicData *quic_data, bool encode)
121 : {
122 16 : quic_data->usr.error = quic_usr_error;
123 16 : quic_data->usr.warn = quic_usr_warn;
124 16 : quic_data->usr.info = quic_usr_warn;
125 16 : quic_data->usr.malloc = quic_usr_malloc;
126 16 : quic_data->usr.free = quic_usr_free;
127 16 : quic_data->usr.more_space = encode ? quic_usr_more_space_encode : quic_usr_more_space_decode;
128 16 : quic_data->usr.more_lines = quic_usr_more_lines;
129 16 : quic_data->dest = g_byte_array_new();
130 16 : }
131 :
132 : // RGB luminosity (sum 256) 54, 184, 18
133 572520 : static inline uint8_t pixel_to_gray(uint8_t r, uint8_t g, uint8_t b)
134 : {
135 572520 : return (54u * r + 184u * g + 18u * b) / 256u;
136 : }
137 :
138 1145040 : static inline void gray_to_pixel(uint8_t gray, uint8_t *p)
139 : {
140 1145040 : p[0] = p[1] = p[2] = gray;
141 1145040 : }
142 :
143 175120 : static inline uint16_t pixel_to_rgb16(uint8_t r, uint8_t g, uint8_t b)
144 : {
145 175120 : r = (r >> 3) & 0x1Fu;
146 175120 : g = (g >> 3) & 0x1Fu;
147 175120 : b = (b >> 3) & 0x1Fu;
148 175120 : return r * (32u*32u) + g * 32u + b;
149 : }
150 :
151 350240 : static inline void rgb16_to_pixel(uint16_t color, uint8_t *p)
152 : {
153 : uint8_t comp;
154 350240 : comp = (color >> 10) & 0x1Fu;
155 350240 : *p++ = (comp << 3) | (comp >> 2);
156 350240 : color <<= 5;
157 350240 : comp = (color >> 10) & 0x1Fu;
158 350240 : *p++ = (comp << 3) | (comp >> 2);
159 350240 : color <<= 5;
160 350240 : comp = (color >> 10) & 0x1Fu;
161 350240 : *p++ = (comp << 3) | (comp >> 2);
162 350240 : }
163 :
164 : #define CONVERT_PROC(TYPE, FUNC) \
165 : static void pixbuf_convert_to_##FUNC(GdkPixbuf *pixbuf, TYPE *dest_line, int dest_stride) \
166 : { \
167 : int width = gdk_pixbuf_get_width(pixbuf); \
168 : int height = gdk_pixbuf_get_height(pixbuf); \
169 : int n_channels = gdk_pixbuf_get_n_channels (pixbuf); \
170 : int stride = gdk_pixbuf_get_rowstride(pixbuf); \
171 : uint8_t *line = gdk_pixbuf_get_pixels(pixbuf); \
172 : \
173 : for (int y = 0; y < height; y++) { \
174 : uint8_t *p = line; \
175 : TYPE *dest = dest_line; \
176 : for (int x = 0; x < width; x++) { \
177 : \
178 : *dest = pixel_to_##FUNC(p[0], p[1], p[2]); \
179 : FUNC##_to_pixel(*dest, p); \
180 : ++dest; \
181 : p += n_channels; \
182 : } \
183 : line += stride; \
184 : dest_line = (TYPE*)((char*) dest_line + dest_stride); \
185 : } \
186 : }
187 :
188 573275 : CONVERT_PROC(uint8_t, gray)
189 175491 : CONVERT_PROC(uint16_t, rgb16)
190 :
191 : #define UNCONVERT_PROC(TYPE, FUNC) \
192 : static void pixbuf_unconvert_to_##FUNC(GdkPixbuf *pixbuf) \
193 : { \
194 : const int width = gdk_pixbuf_get_width(pixbuf); \
195 : const int height = gdk_pixbuf_get_height(pixbuf); \
196 : const int n_channels = gdk_pixbuf_get_n_channels(pixbuf); \
197 : const int stride = gdk_pixbuf_get_rowstride(pixbuf); \
198 : uint8_t *line = gdk_pixbuf_get_pixels(pixbuf) + stride*height; \
199 : \
200 : for (int y = 0; y < height; y++) { \
201 : line -= stride; \
202 : uint8_t *p = line + width*n_channels; \
203 : const TYPE *dest = (TYPE*) line + width; \
204 : for (int x = 0; x < width; x++) { \
205 : --dest; \
206 : p -= n_channels; \
207 : FUNC##_to_pixel(*dest, p); \
208 : if (n_channels == 4) { \
209 : p[3] = 255; \
210 : } \
211 : } \
212 : } \
213 : g_assert(line == gdk_pixbuf_get_pixels(pixbuf)); \
214 : }
215 :
216 573275 : UNCONVERT_PROC(uint8_t, gray)
217 175491 : UNCONVERT_PROC(uint16_t, rgb16)
218 :
219 : typedef struct {
220 : QuicImageType quic_type;
221 : uint8_t *pixels;
222 : int stride;
223 : GdkPixbuf *pixbuf;
224 : } ImageBuf;
225 :
226 8 : static ImageBuf *image_buf_init(ImageBuf *imgbuf, GdkPixbuf *pixbuf)
227 : {
228 8 : imgbuf->pixbuf = pixbuf;
229 8 : switch (gdk_pixbuf_get_n_channels(pixbuf)) {
230 6 : case 3:
231 6 : imgbuf->quic_type = QUIC_IMAGE_TYPE_RGB24;
232 6 : break;
233 2 : case 4:
234 2 : imgbuf->quic_type = QUIC_IMAGE_TYPE_RGBA;
235 2 : break;
236 0 : default:
237 0 : g_assert_not_reached();
238 : }
239 8 : imgbuf->pixels = gdk_pixbuf_get_pixels(pixbuf);
240 8 : imgbuf->stride = gdk_pixbuf_get_rowstride(pixbuf);
241 :
242 8 : if (color_mode == COLOR_MODE_GRAY) {
243 2 : int stride = gdk_pixbuf_get_width(pixbuf);
244 2 : uint8_t *pixels = g_malloc(stride * gdk_pixbuf_get_height(pixbuf));
245 2 : pixbuf_convert_to_gray(pixbuf, pixels, stride);
246 2 : imgbuf->stride = stride;
247 2 : imgbuf->pixels = pixels;
248 2 : imgbuf->quic_type = QUIC_IMAGE_TYPE_GRAY;
249 6 : } else if (color_mode == COLOR_MODE_RGB16) {
250 2 : int stride = gdk_pixbuf_get_width(pixbuf)*2;
251 2 : uint16_t *pixels = g_malloc(stride * gdk_pixbuf_get_height(pixbuf));
252 2 : pixbuf_convert_to_rgb16(pixbuf, pixels, stride);
253 2 : imgbuf->stride = stride;
254 2 : imgbuf->pixels = (uint8_t*)pixels;
255 2 : imgbuf->quic_type = QUIC_IMAGE_TYPE_RGB16;
256 : }
257 :
258 8 : return imgbuf;
259 : }
260 :
261 8 : static void image_buf_free(ImageBuf *imgbuf, GdkPixbuf *pixbuf)
262 : {
263 8 : if (imgbuf->quic_type == QUIC_IMAGE_TYPE_GRAY) {
264 2 : pixbuf_unconvert_to_gray(pixbuf);
265 : }
266 :
267 8 : if (imgbuf->quic_type == QUIC_IMAGE_TYPE_RGB16) {
268 2 : pixbuf_unconvert_to_rgb16(pixbuf);
269 : }
270 :
271 8 : if (imgbuf->pixels != gdk_pixbuf_get_pixels(imgbuf->pixbuf)) {
272 4 : g_free(imgbuf->pixels);
273 : }
274 8 : }
275 :
276 8 : static GByteArray *quic_encode_from_pixbuf(GdkPixbuf *pixbuf, const ImageBuf *imgbuf)
277 : {
278 : QuicData quic_data;
279 : QuicContext *quic;
280 : int encoded_size;
281 :
282 8 : init_quic_data(&quic_data, true);
283 8 : g_byte_array_set_size(quic_data.dest, 1024);
284 :
285 8 : quic = quic_create(&quic_data.usr);
286 8 : g_assert(quic != NULL);
287 8 : encoded_size = quic_encode(quic, imgbuf->quic_type,
288 : gdk_pixbuf_get_width(pixbuf),
289 : gdk_pixbuf_get_height(pixbuf),
290 8 : imgbuf->pixels,
291 8 : gdk_pixbuf_get_height(pixbuf),
292 8 : imgbuf->stride,
293 8 : (uint32_t *)quic_data.dest->data,
294 8 : quic_data.dest->len/sizeof(uint32_t));
295 8 : g_assert(encoded_size > 0);
296 8 : encoded_size *= 4;
297 8 : g_byte_array_set_size(quic_data.dest, encoded_size);
298 8 : quic_destroy(quic);
299 :
300 8 : return quic_data.dest;
301 : }
302 :
303 8 : static GdkPixbuf *quic_decode_to_pixbuf(GByteArray *compressed_data)
304 : {
305 : QuicData quic_data;
306 : QuicContext *quic;
307 : GdkPixbuf *pixbuf;
308 : QuicImageType type;
309 : int width;
310 : int height;
311 : int status;
312 :
313 8 : init_quic_data(&quic_data, false);
314 8 : g_byte_array_free(quic_data.dest, TRUE);
315 8 : quic_data.dest = NULL;
316 :
317 8 : quic = quic_create(&quic_data.usr);
318 8 : g_assert(quic != NULL);
319 :
320 8 : status = quic_decode_begin(quic,
321 8 : (uint32_t *)compressed_data->data, compressed_data->len/4,
322 : &type, &width, &height);
323 : /* limit size for fuzzer, he restrict virtual memory */
324 8 : if (fuzzying && (status != QUIC_OK || (width * height) > 16 * 1024 * 1024 / 4)) {
325 0 : exit(1);
326 : }
327 8 : g_assert(status == QUIC_OK);
328 :
329 8 : pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB,
330 8 : (type == QUIC_IMAGE_TYPE_RGBA || type == QUIC_IMAGE_TYPE_RGB32), 8,
331 : width, height);
332 16 : status = quic_decode(quic, type,
333 8 : gdk_pixbuf_get_pixels(pixbuf),
334 : gdk_pixbuf_get_rowstride(pixbuf));
335 8 : g_assert(status == QUIC_OK);
336 8 : quic_destroy(quic);
337 :
338 8 : return pixbuf;
339 : }
340 :
341 8 : static void pixbuf_compare(GdkPixbuf *pixbuf_a, GdkPixbuf *pixbuf_b)
342 : {
343 8 : int width = gdk_pixbuf_get_width(pixbuf_a);
344 8 : int height = gdk_pixbuf_get_height(pixbuf_a);
345 8 : int n_channels_a = gdk_pixbuf_get_n_channels(pixbuf_a);
346 8 : int n_channels_b = gdk_pixbuf_get_n_channels(pixbuf_b);
347 : int x;
348 : int y;
349 8 : guint8 *pixels_a = gdk_pixbuf_get_pixels(pixbuf_a);
350 8 : guint8 *pixels_b = gdk_pixbuf_get_pixels(pixbuf_b);
351 8 : bool check_alpha = gdk_pixbuf_get_has_alpha(pixbuf_a);
352 :
353 8 : g_assert(width == gdk_pixbuf_get_width(pixbuf_b));
354 8 : g_assert(height == gdk_pixbuf_get_height(pixbuf_b));
355 8 : if (color_mode != COLOR_MODE_RGB) {
356 4 : check_alpha = false;
357 : } else {
358 4 : g_assert(n_channels_a == n_channels_b);
359 4 : g_assert(gdk_pixbuf_get_byte_length(pixbuf_a) == gdk_pixbuf_get_byte_length(pixbuf_b));
360 : }
361 1886 : for (y = 0; y < height; y++) {
362 1623246 : for (x = 0; x < width; x++) {
363 1621368 : guint8 *p_a = pixels_a + y*gdk_pixbuf_get_rowstride(pixbuf_a) + x*n_channels_a;
364 1621368 : guint8 *p_b = pixels_b + y*gdk_pixbuf_get_rowstride(pixbuf_b) + x*n_channels_b;
365 :
366 1621368 : g_assert(p_a[0] == p_b[0]);
367 1621368 : g_assert(p_a[1] == p_b[1]);
368 1621368 : g_assert(p_a[2] == p_b[2]);
369 1621368 : if (check_alpha) {
370 176882 : g_assert(p_a[3] == p_b[3]);
371 : }
372 : }
373 : }
374 8 : }
375 :
376 8 : static GdkPixbuf *pixbuf_new_random(int alpha, bool fixed)
377 : {
378 8 : gboolean has_alpha = alpha >= 0 ? alpha : g_random_boolean();
379 8 : gint width = g_random_int_range(100, 2000);
380 8 : gint height = g_random_int_range(100, 500);
381 : GdkPixbuf *random_pixbuf;
382 : guint i, size;
383 : guint8 *pixels;
384 :
385 8 : random_pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, has_alpha, 8, width, height);
386 8 : pixels = gdk_pixbuf_get_pixels(random_pixbuf);
387 8 : size = gdk_pixbuf_get_byte_length(random_pixbuf);
388 5043428 : for (i = 0; i < size; i++) {
389 5043420 : pixels[i] = fixed ? (gint)((i%(has_alpha?4:3))) << 5 : g_random_int_range(0, 256);
390 : }
391 :
392 8 : return random_pixbuf;
393 : }
394 :
395 8 : static void test_pixbuf(GdkPixbuf *pixbuf)
396 : {
397 : GdkPixbuf *uncompressed_pixbuf;
398 : GByteArray *compressed_data;
399 8 : g_assert(pixbuf != NULL);
400 8 : g_assert(gdk_pixbuf_get_colorspace(pixbuf) == GDK_COLORSPACE_RGB);
401 8 : g_assert(gdk_pixbuf_get_bits_per_sample(pixbuf) == 8);
402 :
403 : ImageBuf imgbuf[1];
404 8 : image_buf_init(imgbuf, pixbuf);
405 8 : compressed_data = quic_encode_from_pixbuf(pixbuf, imgbuf);
406 :
407 8 : uncompressed_pixbuf = quic_decode_to_pixbuf(compressed_data);
408 8 : image_buf_free(imgbuf, uncompressed_pixbuf);
409 :
410 : //g_assert(memcmp(gdk_pixbuf_get_pixels(pixbuf), gdk_pixbuf_get_pixels(uncompressed_pixbuf), gdk_pixbuf_get_byte_length(uncompressed_pixbuf)));
411 8 : pixbuf_compare(pixbuf, uncompressed_pixbuf);
412 :
413 8 : g_byte_array_free(compressed_data, TRUE);
414 8 : g_object_unref(uncompressed_pixbuf);
415 :
416 8 : }
417 :
418 : static int
419 0 : fuzzer_decode(const char *fn)
420 : {
421 : GdkPixbuf *uncompressed_pixbuf;
422 : GByteArray compressed_data[1];
423 0 : gchar *contents = NULL;
424 : gsize length;
425 :
426 0 : fuzzying = true;
427 0 : if (!g_file_get_contents(fn, &contents, &length, NULL)) {
428 0 : exit(1);
429 : }
430 0 : compressed_data->data = (void*) contents;
431 0 : compressed_data->len = length;
432 0 : uncompressed_pixbuf = quic_decode_to_pixbuf(compressed_data);
433 :
434 0 : g_object_unref(uncompressed_pixbuf);
435 0 : g_free(contents);
436 :
437 0 : return 0;
438 : }
439 :
440 1 : int main(int argc, char **argv)
441 : {
442 1 : if (argc >= 3 && strcmp(argv[1], "--fuzzer-decode") == 0) {
443 0 : return fuzzer_decode(argv[2]);
444 : }
445 :
446 1 : if (argc >= 2) {
447 0 : for (int i = 1; i < argc; ++i) {
448 : GdkPixbuf *source_pixbuf;
449 :
450 0 : source_pixbuf = gdk_pixbuf_new_from_file(argv[i], NULL);
451 0 : test_pixbuf(source_pixbuf);
452 0 : g_object_unref(source_pixbuf);
453 : }
454 1 : } else if (argc == 1) {
455 : int test;
456 5 : for (test = 0; test < 4; test++) {
457 4 : int alpha = test % 2;
458 4 : bool fixed = (test / 2) % 2;
459 :
460 16 : for (color_mode = COLOR_MODE_RGB; color_mode < COLOR_MODE_END; color_mode++) {
461 : /* alpha affects only COLOR_MODE_RGB more, reduce number of tests */
462 12 : if (color_mode != COLOR_MODE_RGB && alpha) {
463 4 : continue;
464 : }
465 8 : GdkPixbuf *pixbuf = pixbuf_new_random(alpha, fixed);
466 8 : test_pixbuf(pixbuf);
467 8 : g_object_unref(pixbuf);
468 : }
469 : }
470 : } else {
471 0 : g_assert_not_reached();
472 : }
473 :
474 1 : return 0;
475 : }
|