1 #include "ProtoToGif.h"
2 
3 using namespace gifProtoFuzzer;
4 using namespace std;
5 
6 constexpr unsigned char ProtoConverter::m_sig[];
7 constexpr unsigned char ProtoConverter::m_ver89a[];
8 constexpr unsigned char ProtoConverter::m_ver87a[];
9 
gifProtoToString(GifProto const & proto)10 string ProtoConverter::gifProtoToString(GifProto const &proto)
11 {
12 	visit(proto);
13 	return m_output.str();
14 }
15 
visit(GifProto const & gif)16 void ProtoConverter::visit(GifProto const &gif)
17 {
18 	visit(gif.header());
19 	visit(gif.lsd());
20 	if (m_hasGCT)
21 		visit(gif.gct());
22 	for (auto const &chunk : gif.chunks())
23 		visit(chunk);
24 	visit(gif.trailer());
25 }
26 
visit(Header const & header)27 void ProtoConverter::visit(Header const &header)
28 {
29 	// Signature GIF
30 	m_output.write((const char *)m_sig, sizeof(m_sig));
31 
32 	switch (header.ver())
33 	{
34 	case Header::ENA:
35 		m_output.write((const char *)m_ver89a, sizeof(m_ver89a));
36 		break;
37 	case Header::ESA:
38 		m_output.write((const char *)m_ver87a, sizeof(m_ver87a));
39 		break;
40 	// We simply don't write anything if it's an invalid version
41 	// Bytes that follow (LSD) will be interpreted as version
42 	case Header::INV:
43 		break;
44 	}
45 }
46 
visit(LogicalScreenDescriptor const & lsd)47 void ProtoConverter::visit(LogicalScreenDescriptor const &lsd)
48 {
49 	writeWord(extractWordFromUInt32(lsd.screenwidth()));
50 	writeWord(extractWordFromUInt32(lsd.screenheight()));
51 
52 	uint8_t packedByte = extractByteFromUInt32(lsd.packed());
53 	// If MSB of packed byte is 1, GCT follows
54 	if (packedByte & 0x80)
55 	{
56 		m_hasGCT = true;
57 		// N: 2^(N+1) colors in GCT
58 		m_globalColorExp = packedByte & 0x07;
59 	}
60 	writeByte(packedByte);
61 	writeByte(extractByteFromUInt32(lsd.backgroundcolor()));
62 	writeByte(extractByteFromUInt32(lsd.aspectratio()));
63 }
64 
visit(GlobalColorTable const & gct)65 void ProtoConverter::visit(GlobalColorTable const &gct)
66 {
67 	//[TODO 27/04/2019 VU]: Should it really be exactly the same size? Or do we want some deterministic randomness here?
68 	// TODO BS: We never overflow expected table size due to the use of min
69 	uint32_t tableSize = min((uint32_t)gct.colors().size(), tableExpToTableSize(m_globalColorExp));
70 	m_output.write(gct.colors().data(), tableSize);
71 }
72 
visit(GraphicControlExtension const & gce)73 void ProtoConverter::visit(GraphicControlExtension const &gce)
74 {
75 	writeByte(0x21); // Extension Introducer
76 	writeByte(0xF9); // Graphic Control Label
77 	writeByte(4);	// Block size
78 	uint8_t packedByte = extractByteFromUInt32(gce.packed());
79 	// packed byte
80 	writeByte(packedByte);
81 	// Delay time is 2 bytes
82 	writeWord(extractWordFromUInt32(gce.delaytime()));
83 	// Transparent color index is 1 byte
84 	writeByte(extractByteFromUInt32(gce.transparentcolorindex()));
85 	writeByte(0x0); // Block Terminator
86 }
87 
visit(ImageChunk const & chunk)88 void ProtoConverter::visit(ImageChunk const &chunk)
89 {
90 	switch (chunk.chunk_oneof_case())
91 	{
92 	case ImageChunk::kBasic:
93 		visit(chunk.basic());
94 		break;
95 	case ImageChunk::kPlaintext:
96 		visit(chunk.plaintext());
97 		break;
98 	case ImageChunk::kAppExt:
99 		visit(chunk.appext());
100 		break;
101 	case ImageChunk::kComExt:
102 		visit(chunk.comext());
103 		break;
104 	case ImageChunk::CHUNK_ONEOF_NOT_SET:
105 		break;
106 	}
107 }
108 
visit(const BasicChunk & chunk)109 void ProtoConverter::visit(const BasicChunk &chunk)
110 {
111 	// Visit GCExt if necessary
112 	if (chunk.has_gcext())
113 		visit(chunk.gcext());
114 	visit(chunk.imdescriptor());
115 	if (m_hasLCT)
116 		visit(chunk.lct());
117 	visit(chunk.img());
118 }
119 
visit(LocalColorTable const & lct)120 void ProtoConverter::visit(LocalColorTable const &lct)
121 {
122 	//[TODO 27/04/2019 VU]: Should it really be exactly the same size? Or do we want some deterministic randomness here?
123 	// TODO BS: We never overflow expected table size due to the use of min
124 	uint32_t tableSize = min((uint32_t)lct.colors().size(), tableExpToTableSize(m_localColorExp));
125 	m_output.write(lct.colors().data(), tableSize);
126 }
127 
visit(ImageDescriptor const & descriptor)128 void ProtoConverter::visit(ImageDescriptor const &descriptor)
129 {
130 	// TODO: Remove seperator from proto since it is always 2C
131 	writeByte(0x2C);
132 	writeWord(extractWordFromUInt32(descriptor.left()));
133 	writeWord(extractWordFromUInt32(descriptor.top()));
134 	writeWord(extractWordFromUInt32(descriptor.height()));
135 	writeWord(extractWordFromUInt32(descriptor.width()));
136 	uint8_t packedByte = extractByteFromUInt32(descriptor.packed());
137 	if (packedByte & 0x80)
138 	{
139 		m_hasLCT = true;
140 		m_localColorExp = packedByte & 0x07;
141 	}
142 	else
143 		m_hasLCT = false;
144 }
145 
visit(SubBlock const & block)146 void ProtoConverter::visit(SubBlock const &block)
147 {
148 	uint8_t len = extractByteFromUInt32(block.len());
149 	if (len == 0)
150 	{
151 		writeByte(0x00);
152 	}
153 	else
154 	{
155 		// TODO BS: We never overflow expected block size due to the use of min
156 		uint32_t write_len = min((uint32_t)len, (uint32_t)block.data().size());
157 		m_output.write(block.data().data(), write_len);
158 	}
159 }
160 
visit(ImageData const & img)161 void ProtoConverter::visit(ImageData const &img)
162 {
163 	// TODO: Verify we are writing the image data correctly
164 	// LZW
165 	writeByte(extractByteFromUInt32(img.lzw()));
166 	// Sub-blocks
167 	for (auto const &block : img.subs())
168 		visit(block);
169 	// NULL sub block signals end of image data
170 	writeByte(0x00);
171 }
172 
visit(PlainTextExtension const & ptExt)173 void ProtoConverter::visit(PlainTextExtension const &ptExt)
174 {
175 	// Visit GCExt if necessary
176 	if (ptExt.has_gcext())
177 		visit(ptExt.gcext());
178 
179 	// First two bytes are 0x21 0x01
180 	writeByte(0x21);
181 	writeByte(0x01);
182 	// Skip zero bytes
183 	writeByte(0x00);
184 	for (auto const &block : ptExt.subs())
185 		visit(block);
186 	// NULL sub block signals end
187 	writeByte(0x00);
188 }
189 
visit(CommentExtension const & comExt)190 void ProtoConverter::visit(CommentExtension const &comExt)
191 {
192 	// First two bytes are 0x21 0xFE
193 	writeByte(0x21);
194 	writeByte(0xFE);
195 	// Sub-blocks
196 	for (auto const &block : comExt.subs())
197 		visit(block);
198 	// NULL sub block signals end of image data
199 	writeByte(0x00);
200 }
201 
visit(ApplicationExtension const & appExt)202 void ProtoConverter::visit(ApplicationExtension const &appExt)
203 {
204 	// First two bytes are 0x21 0xFF
205 	writeByte(0x21);
206 	writeByte(0xFF);
207 	// Next, we write "11" decimal or 0x0B
208 	writeByte(0x0B);
209 	writeLong(appExt.appid());
210 	// We hardcode the auth code to 1.0 or 0x31 0x2E 0x30
211 	writeByte(0x31);
212 	writeByte(0x2E);
213 	writeByte(0x30);
214 	// Sub-blocks
215 	for (auto const &block : appExt.subs())
216 		visit(block);
217 	// NULL sub block signals end of image data
218 	writeByte(0x00);
219 }
220 
visit(Trailer const &)221 void ProtoConverter::visit(Trailer const &)
222 {
223 	writeByte(0x3B);
224 }
225 
226 // =============================================================
227 // Utility functions
228 // =============================================================
writeByte(uint8_t x)229 void ProtoConverter::writeByte(uint8_t x)
230 {
231 	m_output.write((char *)&x, sizeof(x));
232 }
233 
writeWord(uint16_t x)234 void ProtoConverter::writeWord(uint16_t x)
235 {
236 	m_output.write((char *)&x, sizeof(x));
237 }
238 
writeInt(uint32_t x)239 void ProtoConverter::writeInt(uint32_t x)
240 {
241 	m_output.write((char *)&x, sizeof(x));
242 }
243 
writeLong(uint64_t x)244 void ProtoConverter::writeLong(uint64_t x)
245 {
246 	m_output.write((char *)&x, sizeof(x));
247 }
248 
extractWordFromUInt32(uint32_t a)249 uint16_t ProtoConverter::extractWordFromUInt32(uint32_t a)
250 {
251 	uint16_t first_byte = (a & 0xFF);
252 	uint16_t second_byte = ((a >> 8) & 0xFF) << 8;
253 	return first_byte | second_byte;
254 }
255 
extractByteFromUInt32(uint32_t a)256 uint8_t ProtoConverter::extractByteFromUInt32(uint32_t a)
257 {
258 	uint8_t byte = a & 0x80;
259 	return byte;
260 }
261 
262 /**
263  * Given an exponent, returns the global/local color table size, given by 3*2^(exp+1)
264  * @param tableExp The exponent
265  * @return The actual color table size
266  */
tableExpToTableSize(uint32_t tableExp)267 uint32_t ProtoConverter::tableExpToTableSize(uint32_t tableExp)
268 {
269 	// 0 <= tableExp <= 7
270 	// 6 <= tableSize <= 768
271 	uint32_t tableSize = 3 * (pow(2, tableExp + 1));
272 	return tableSize;
273 }
274