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