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