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