Coverage Report

Created: 2025-06-13 06:08

/src/egif_fuzz_common.cc
Line
Count
Source (jump to first uncovered line)
1
#include "egif_fuzz_common.h"
2
151k
#define GIF_IMAGE_WIDTH 100
3
// This is rgb byte stream length per horizontal line = GIF_IMAGE_WIDTH * 3
4
728
#define GIF_IMAGE_LINE 300
5
6
extern "C" void PrintGifError(int ErrorCode);
7
8
int stub_output_writer(GifFileType *gifFileType, const uint8_t *buf, int len)
9
117k
{
10
117k
  struct gifUserData *gud = (struct gifUserData *)gifFileType->UserData;
11
12
117k
  if (gud == NULL || gud->gifData == NULL || len == 0)
13
0
    return 0;
14
117k
  if (gud->allocatedSize < (gud->gifLen + len))
15
705
  {
16
    // Reallocate gifFileType
17
705
    int newSize = (gud->gifLen + len) * 2;
18
705
    uint8_t *oldGud = gud->gifData;
19
705
    gud->gifData = (uint8_t *)realloc(oldGud, newSize);
20
    // Assert when realloc fails.
21
705
    assert(gud->gifData != NULL);
22
705
    gud->allocatedSize = newSize;
23
705
  }
24
117k
  memcpy(gud->gifData + gud->gifLen, buf, len);
25
117k
  gud->gifLen += len;
26
117k
  return len;
27
117k
}
28
29
// RGB to GIF converter
30
static bool rgb_to_gif(const uint8_t *data, size_t size)
31
350
{
32
  // Bail if total size is not a multiple of GIF_IMAGE_LINE (see below)
33
  // Keep a fixed width e.g., GIF_IMAGE_WIDTH
34
  // size/3 = GIF_IMAGE_WIDTH * height
35
  // height = size/GIF_IMAGE_LINE
36
37
  // Extract height
38
350
  int height = size / GIF_IMAGE_LINE;
39
40
  // GifByteType is unsigned char (raw byte)
41
  // mem holds the raw RGB byte stream for the entire image
42
350
  GifByteType *mem = (GifByteType *)malloc(sizeof(GifByteType) * height * GIF_IMAGE_WIDTH * 3);
43
350
  if (!mem)
44
0
    return false;
45
46
  // Copy RGB data to mem
47
350
  memcpy(mem, data, size);
48
49
350
  GifByteType *red_buf = mem;
50
350
  GifByteType *green_buf = mem + (GIF_IMAGE_WIDTH * height);
51
350
  GifByteType *blue_buf = mem + (GIF_IMAGE_WIDTH * height * 2);
52
53
  // ColorMapObject *GifMakeMapObject(int ColorCount, GifColorType *ColorMap)
54
  // Allocate storage for a color map object with the given number of RGB triplet slots.
55
  // If the second argument is non-NULL, initialize the color table portion of
56
  // the new map from it. Returns NULL if memory is exhausted or if the size is
57
  // not a power of 2 <= 256.
58
  // TODO: Fuzz color map size (has to be a power of 2 less than equal to 256)
59
  // TODO: Fuzz color table initialization
60
350
  int color_map_size = 256;
61
350
  ColorMapObject *output_color_map = GifMakeMapObject(color_map_size, NULL);
62
350
  if (!output_color_map)
63
0
  {
64
0
    free(mem);
65
0
    return false;
66
0
  }
67
68
  // gif output will be written to output_buf
69
350
  size_t out_size = sizeof(GifByteType) * GIF_IMAGE_WIDTH * height;
70
350
  GifByteType *output_buf = (GifByteType *)malloc(out_size);
71
350
  if (!output_buf)
72
0
  {
73
0
    GifFreeMapObject(output_color_map);
74
0
    free(mem);
75
0
    return false;
76
0
  }
77
78
350
  if (GifQuantizeBuffer(GIF_IMAGE_WIDTH, height, &color_map_size,
79
350
              red_buf, green_buf, blue_buf,
80
350
              output_buf, output_color_map->Colors) == GIF_ERROR)
81
0
  {
82
0
    GifFreeMapObject(output_color_map);
83
0
    free(output_buf);
84
0
    free(mem);
85
0
    return false;
86
0
  }
87
88
  // Now that raw RGB data has been quantized, we no longer need it.
89
350
  free(mem);
90
91
350
  GifFileType *GifFile;
92
350
  int Error;
93
  // We start with 1024, but resize dynamically
94
  // see stub_output_writer
95
350
  uint8_t *gifData = (uint8_t *)malloc(1024);
96
350
  struct gifUserData gUData = {0, 1024, gifData};
97
98
  /* GifFileType *EGifOpen(void *userPtr, OutputFunc writeFunc, int *ErrorCode)
99
   * Description:
100
   *  Open a new GIF file using the given userPtr (in binary mode, if under Windows).
101
   *  writeFunc is a function pointer that writes to output gif file.
102
   *  If any error occurs, NULL is returned and the ErrorCode is set.
103
   */
104
350
  GifFile = EGifOpen((void *)&gUData, stub_output_writer, &Error);
105
350
  if (GifFile == NULL)
106
0
  {
107
0
    PrintGifError(GifFile->Error);
108
0
    GifFreeMapObject(output_color_map);
109
0
    free(output_buf);
110
0
    free(gUData.gifData);
111
0
    return false;
112
0
  }
113
114
  /* void EGifSetGifVersion(GifFileType *GifFile, bool gif89)
115
   * Description:
116
   *  Set the GIF type, to GIF89 if the argument is true and GIF87 if it is false.
117
   *  The default type is GIF87. This function may be called after the GifFile
118
   *  record is allocated but before EGifPutScreenDesc().
119
   */
120
350
  EGifSetGifVersion(GifFile, false);
121
122
  /* int EGifPutScreenDesc(GifFileType *GifFile,
123
     *   const int GifWidth, const GifHeight,
124
     *   const int GifColorRes, const int GifBackGround,
125
     *   ColorMapObject *GifColorMap)
126
     *
127
   *  Update the GifFile Screen parameters, in GifFile structure and in the real file.
128
   *  If error occurs, returns GIF_ERROR (see gif_lib.h), otherwise GIF_OK.
129
   *  This routine should be called immediately after the GIF file was opened.
130
   */
131
350
  if (EGifPutScreenDesc(GifFile, GIF_IMAGE_WIDTH, height, color_map_size, 0, output_color_map) == GIF_ERROR)
132
0
  {
133
0
    PrintGifError(GifFile->Error);
134
0
    GifFreeMapObject(output_color_map);
135
0
    free(output_buf);
136
0
    EGifCloseFile(GifFile, &Error);
137
0
    free(gUData.gifData);
138
0
    return false;
139
0
  }
140
141
  /* int EGifPutImageDesc(GifFileType *GifFile, const int GifLeft, const int GifTop,
142
   * const int GifWidth, const GifHeight, const bool GifInterlace, ColorMapObject *GifColorMap)
143
   * Description
144
   *  Update GifFile Image parameters, in GifFile structure and in the real file.
145
   *  if error occurs returns GIF_ERROR (see gif_lib.h), otherwise GIF_OK.
146
   *  This routine should be called each time a new image must be dumped to the file.
147
   */
148
350
  if (EGifPutImageDesc(GifFile, 0, 0, GIF_IMAGE_WIDTH, height, false, NULL) == GIF_ERROR)
149
0
  {
150
0
    PrintGifError(GifFile->Error);
151
0
    GifFreeMapObject(output_color_map);
152
0
    free(output_buf);
153
0
    EGifCloseFile(GifFile, &Error);
154
0
    free(gUData.gifData);
155
0
    return false;
156
0
  }
157
158
350
  GifByteType *output_bufp = output_buf;
159
74.7k
  for (int i = 0; i < height; i++)
160
74.4k
  {
161
    /* int EGifPutLine(GifFileType *GifFile, PixelType *GifLine, int GifLineLen)
162
     * Description:
163
     *  Dumps a block of pixels out to the GIF file. The slab can be of any length.
164
     *  More than that, this routine may be interleaved with EGifPutPixel(),
165
     *  until all pixels have been sent.
166
     *  Returns GIF_ERROR if something went wrong, GIF_OK otherwise.
167
     */
168
74.4k
    if (EGifPutLine(GifFile, output_bufp, GIF_IMAGE_WIDTH) == GIF_ERROR)
169
0
    {
170
0
      PrintGifError(GifFile->Error);
171
0
      GifFreeMapObject(output_color_map);
172
0
      free(output_buf);
173
0
      EGifCloseFile(GifFile, &Error);
174
0
      free(gUData.gifData);
175
0
      return false;
176
0
    }
177
74.4k
    output_bufp += GIF_IMAGE_WIDTH;
178
74.4k
  }
179
180
  /* void GifFreeMapObject(ColorMapObject *Object)
181
   * Description
182
   *  Free the storage occupied by a ColorMapObject that is no longer needed.
183
   */
184
350
  GifFreeMapObject(output_color_map);
185
350
  free(output_buf);
186
350
  EGifCloseFile(GifFile, &Error);
187
350
  free(gUData.gifData);
188
350
  return true;
189
350
}
190
191
int fuzz_egif(const uint8_t *Data, size_t Size)
192
378
{
193
  // We treat fuzzed data as a raw RGB stream for a picture
194
  // with a fixed width of GIF_IMAGE_WIDTH.
195
  // Since we need 3 color bytes per pixel (RGB), height = size/GIF_IMAGE_LINE
196
  //      where GIF_IMAGE_LINE = GIF_IMAGE_WIDTH * 3
197
  // For integral height, we need Size to be a multiple of GIF_IMAGE_LINE
198
378
  if ((Size == 0) || ((Size % GIF_IMAGE_LINE) != 0))
199
28
    return 0;
200
350
  bool status = rgb_to_gif(Data, Size);
201
350
  return 0;
202
378
}