1 #include "egif_fuzz_common.h"
2 #define GIF_IMAGE_WIDTH 100
3 // This is rgb byte stream length per horizontal line = GIF_IMAGE_WIDTH * 3
4 #define GIF_IMAGE_LINE 300
5
6 extern "C" void PrintGifError(int ErrorCode);
7
stub_output_writer(GifFileType * gifFileType,const uint8_t * buf,int len)8 int stub_output_writer(GifFileType *gifFileType, const uint8_t *buf, int len)
9 {
10 struct gifUserData *gud = (struct gifUserData *)gifFileType->UserData;
11
12 if (gud == NULL || gud->gifData == NULL || len == 0)
13 return 0;
14 if (gud->allocatedSize < (gud->gifLen + len))
15 {
16 // Reallocate gifFileType
17 int newSize = (gud->gifLen + len) * 2;
18 uint8_t *oldGud = gud->gifData;
19 gud->gifData = (uint8_t *)realloc(oldGud, newSize);
20 // Assert when realloc fails.
21 assert(gud->gifData != NULL);
22 gud->allocatedSize = newSize;
23 }
24 memcpy(gud->gifData + gud->gifLen, buf, len);
25 gud->gifLen += len;
26 return len;
27 }
28
29 // RGB to GIF converter
rgb_to_gif(const uint8_t * data,size_t size)30 static bool rgb_to_gif(const uint8_t *data, size_t size)
31 {
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 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 GifByteType *mem = (GifByteType *)malloc(sizeof(GifByteType) * height * GIF_IMAGE_WIDTH * 3);
43 if (!mem)
44 return false;
45
46 // Copy RGB data to mem
47 memcpy(mem, data, size);
48
49 GifByteType *red_buf = mem;
50 GifByteType *green_buf = mem + (GIF_IMAGE_WIDTH * height);
51 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 int color_map_size = 256;
61 ColorMapObject *output_color_map = GifMakeMapObject(color_map_size, NULL);
62 if (!output_color_map)
63 {
64 free(mem);
65 return false;
66 }
67
68 // gif output will be written to output_buf
69 size_t out_size = sizeof(GifByteType) * GIF_IMAGE_WIDTH * height;
70 GifByteType *output_buf = (GifByteType *)malloc(out_size);
71 if (!output_buf)
72 {
73 GifFreeMapObject(output_color_map);
74 free(mem);
75 return false;
76 }
77
78 if (GifQuantizeBuffer(GIF_IMAGE_WIDTH, height, &color_map_size,
79 red_buf, green_buf, blue_buf,
80 output_buf, output_color_map->Colors) == GIF_ERROR)
81 {
82 GifFreeMapObject(output_color_map);
83 free(output_buf);
84 free(mem);
85 return false;
86 }
87
88 // Now that raw RGB data has been quantized, we no longer need it.
89 free(mem);
90
91 GifFileType *GifFile;
92 int Error;
93 // We start with 1024, but resize dynamically
94 // see stub_output_writer
95 uint8_t *gifData = (uint8_t *)malloc(1024);
96 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 GifFile = EGifOpen((void *)&gUData, stub_output_writer, &Error);
105 if (GifFile == NULL)
106 {
107 PrintGifError(GifFile->Error);
108 GifFreeMapObject(output_color_map);
109 free(output_buf);
110 free(gUData.gifData);
111 return false;
112 }
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 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 if (EGifPutScreenDesc(GifFile, GIF_IMAGE_WIDTH, height, color_map_size, 0, output_color_map) == GIF_ERROR)
132 {
133 PrintGifError(GifFile->Error);
134 GifFreeMapObject(output_color_map);
135 free(output_buf);
136 EGifCloseFile(GifFile, &Error);
137 free(gUData.gifData);
138 return false;
139 }
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 if (EGifPutImageDesc(GifFile, 0, 0, GIF_IMAGE_WIDTH, height, false, NULL) == GIF_ERROR)
149 {
150 PrintGifError(GifFile->Error);
151 GifFreeMapObject(output_color_map);
152 free(output_buf);
153 EGifCloseFile(GifFile, &Error);
154 free(gUData.gifData);
155 return false;
156 }
157
158 GifByteType *output_bufp = output_buf;
159 for (int i = 0; i < height; i++)
160 {
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 if (EGifPutLine(GifFile, output_bufp, GIF_IMAGE_WIDTH) == GIF_ERROR)
169 {
170 PrintGifError(GifFile->Error);
171 GifFreeMapObject(output_color_map);
172 free(output_buf);
173 EGifCloseFile(GifFile, &Error);
174 free(gUData.gifData);
175 return false;
176 }
177 output_bufp += GIF_IMAGE_WIDTH;
178 }
179
180 /* void GifFreeMapObject(ColorMapObject *Object)
181 * Description
182 * Free the storage occupied by a ColorMapObject that is no longer needed.
183 */
184 GifFreeMapObject(output_color_map);
185 free(output_buf);
186 EGifCloseFile(GifFile, &Error);
187 free(gUData.gifData);
188 return true;
189 }
190
fuzz_egif(const uint8_t * Data,size_t Size)191 int fuzz_egif(const uint8_t *Data, size_t Size)
192 {
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 if ((Size == 0) || ((Size % GIF_IMAGE_LINE) != 0))
199 return 0;
200 bool status = rgb_to_gif(Data, Size);
201 return 0;
202 }
203