LCOV - code coverage report
Current view: top level - tests - test-quic.c (source / functions) Hit Total Coverage
Test: out.info Lines: 172 208 82.7 %
Date: 2022-01-27 10:43:00 Functions: 20 25 80.0 %

          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             : }

Generated by: LCOV version 1.14