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