1 | //======================================================================== |
2 | // |
3 | // Hints.cc |
4 | // |
5 | // This file is licensed under the GPLv2 or later |
6 | // |
7 | // Copyright 2010, 2012, 2013 Hib Eris <hib@hiberis.nl> |
8 | // Copyright 2010, 2011, 2013, 2014, 2016-2019, 2021, 2022 Albert Astals Cid <aacid@kde.org> |
9 | // Copyright 2010, 2013 Pino Toscano <pino@kde.org> |
10 | // Copyright 2013 Adrian Johnson <ajohnson@redneon.com> |
11 | // Copyright 2014 Fabio D'Urso <fabiodurso@hotmail.it> |
12 | // Copyright 2016 Jeffrey Morlan <jmmorlan@sonic.net> |
13 | // Copyright 2019 LE GARREC Vincent <legarrec.vincent@gmail.com> |
14 | // Copyright 2019 Adam Reichold <adam.reichold@t-online.de> |
15 | // |
16 | //======================================================================== |
17 | |
18 | #include <config.h> |
19 | |
20 | #include "Hints.h" |
21 | |
22 | #include "Linearization.h" |
23 | #include "Object.h" |
24 | #include "Stream.h" |
25 | #include "XRef.h" |
26 | #include "Parser.h" |
27 | #include "Lexer.h" |
28 | #include "SecurityHandler.h" |
29 | |
30 | #include <climits> |
31 | |
32 | class StreamBitReader |
33 | { |
34 | public: |
35 | explicit StreamBitReader(Stream *strA) : str(strA), inputBits(0), isAtEof(false) { } |
36 | |
37 | void resetInputBits() { inputBits = 0; } |
38 | |
39 | bool atEOF() const { return isAtEof; } |
40 | |
41 | unsigned int readBit() |
42 | { |
43 | unsigned int bit; |
44 | int c; |
45 | |
46 | if (inputBits == 0) { |
47 | if ((c = str->getChar()) == EOF) { |
48 | isAtEof = true; |
49 | return (unsigned int)-1; |
50 | } |
51 | bitsBuffer = c; |
52 | inputBits = 8; |
53 | } |
54 | bit = (bitsBuffer >> (inputBits - 1)) & 1; |
55 | --inputBits; |
56 | return bit; |
57 | } |
58 | |
59 | unsigned int readBits(int n) |
60 | { |
61 | unsigned int bit, bits; |
62 | |
63 | if (n < 0) { |
64 | return -1; |
65 | } |
66 | if (n == 0) { |
67 | return 0; |
68 | } |
69 | |
70 | if (n == 1) { |
71 | return readBit(); |
72 | } |
73 | |
74 | bit = readBit(); |
75 | if (bit == (unsigned int)-1) { |
76 | return -1; |
77 | } |
78 | |
79 | bit = bit << (n - 1); |
80 | |
81 | bits = readBits(n: n - 1); |
82 | if (bits == (unsigned int)-1) { |
83 | return -1; |
84 | } |
85 | |
86 | return bit | bits; |
87 | } |
88 | |
89 | private: |
90 | Stream *str; |
91 | int inputBits; |
92 | char bitsBuffer; |
93 | bool isAtEof; |
94 | }; |
95 | |
96 | //------------------------------------------------------------------------ |
97 | // Hints |
98 | //------------------------------------------------------------------------ |
99 | |
100 | Hints::Hints(BaseStream *str, Linearization *linearization, XRef *xref, SecurityHandler *secHdlr) |
101 | { |
102 | mainXRefEntriesOffset = linearization->getMainXRefEntriesOffset(); |
103 | nPages = linearization->getNumPages(); |
104 | pageFirst = linearization->getPageFirst(); |
105 | pageEndFirst = linearization->getEndFirst(); |
106 | pageObjectFirst = linearization->getObjectNumberFirst(); |
107 | if (pageObjectFirst < 0 || pageObjectFirst >= xref->getNumObjects()) { |
108 | error(category: errSyntaxWarning, pos: -1, msg: "Invalid reference for first page object ({0:d}) in linearization table " , pageObjectFirst); |
109 | pageObjectFirst = 0; |
110 | } |
111 | XRefEntry *pageObjectFirstXRefEntry = xref->getEntry(i: pageObjectFirst); |
112 | if (!pageObjectFirstXRefEntry) { |
113 | error(category: errSyntaxWarning, pos: -1, msg: "No XRef entry for first page object" ); |
114 | pageOffsetFirst = 0; |
115 | } else { |
116 | pageOffsetFirst = pageObjectFirstXRefEntry->offset; |
117 | } |
118 | |
119 | if (nPages >= INT_MAX / (int)sizeof(unsigned int)) { |
120 | error(category: errSyntaxWarning, pos: -1, msg: "Invalid number of pages ({0:d}) for hints table" , nPages); |
121 | nPages = 0; |
122 | } |
123 | nObjects = (unsigned int *)gmallocn_checkoverflow(count: nPages, size: sizeof(unsigned int)); |
124 | pageObjectNum = (int *)gmallocn_checkoverflow(count: nPages, size: sizeof(int)); |
125 | xRefOffset = (unsigned int *)gmallocn_checkoverflow(count: nPages, size: sizeof(unsigned int)); |
126 | pageLength = (unsigned int *)gmallocn_checkoverflow(count: nPages, size: sizeof(unsigned int)); |
127 | pageOffset = (Goffset *)gmallocn_checkoverflow(count: nPages, size: sizeof(Goffset)); |
128 | numSharedObject = (unsigned int *)gmallocn_checkoverflow(count: nPages, size: sizeof(unsigned int)); |
129 | sharedObjectId = (unsigned int **)gmallocn_checkoverflow(count: nPages, size: sizeof(unsigned int *)); |
130 | if (!nObjects || !pageObjectNum || !xRefOffset || !pageLength || !pageOffset || !numSharedObject || !sharedObjectId) { |
131 | error(category: errSyntaxWarning, pos: -1, msg: "Failed to allocate memory for hints table" ); |
132 | nPages = 0; |
133 | } |
134 | |
135 | if (nPages != 0) { |
136 | memset(s: pageLength, c: 0, n: nPages * sizeof(unsigned int)); |
137 | memset(s: pageOffset, c: 0, n: nPages * sizeof(unsigned int)); |
138 | memset(s: numSharedObject, c: 0, n: nPages * sizeof(unsigned int)); |
139 | memset(s: pageObjectNum, c: 0, n: nPages * sizeof(int)); |
140 | } |
141 | |
142 | groupLength = nullptr; |
143 | groupOffset = nullptr; |
144 | groupHasSignature = nullptr; |
145 | groupNumObjects = nullptr; |
146 | groupXRefOffset = nullptr; |
147 | |
148 | ok = true; |
149 | readTables(str, linearization, xref, secHdlr); |
150 | } |
151 | |
152 | Hints::~Hints() |
153 | { |
154 | gfree(p: nObjects); |
155 | gfree(p: pageObjectNum); |
156 | gfree(p: xRefOffset); |
157 | gfree(p: pageLength); |
158 | gfree(p: pageOffset); |
159 | for (int i = 0; i < nPages; i++) { |
160 | if (numSharedObject[i]) { |
161 | gfree(p: sharedObjectId[i]); |
162 | } |
163 | } |
164 | gfree(p: sharedObjectId); |
165 | gfree(p: numSharedObject); |
166 | |
167 | gfree(p: groupLength); |
168 | gfree(p: groupOffset); |
169 | gfree(p: groupHasSignature); |
170 | gfree(p: groupNumObjects); |
171 | gfree(p: groupXRefOffset); |
172 | } |
173 | |
174 | void Hints::readTables(BaseStream *str, Linearization *linearization, XRef *xref, SecurityHandler *secHdlr) |
175 | { |
176 | hintsOffset = linearization->getHintsOffset(); |
177 | hintsLength = linearization->getHintsLength(); |
178 | hintsOffset2 = linearization->getHintsOffset2(); |
179 | hintsLength2 = linearization->getHintsLength2(); |
180 | |
181 | const unsigned int bufLength = hintsLength + hintsLength2; |
182 | |
183 | if (bufLength == 0) { |
184 | ok = false; |
185 | return; |
186 | } |
187 | |
188 | std::vector<char> buf(bufLength); |
189 | char *p = &buf[0]; |
190 | |
191 | if (hintsOffset && hintsLength) { |
192 | std::unique_ptr<Stream> s(str->makeSubStream(start: hintsOffset, limited: false, length: hintsLength, dict: Object(objNull))); |
193 | s->reset(); |
194 | for (unsigned int i = 0; i < hintsLength; i++) { |
195 | const int c = s->getChar(); |
196 | if (unlikely(c == EOF)) { |
197 | error(category: errSyntaxWarning, pos: -1, msg: "Found EOF while reading hints" ); |
198 | ok = false; |
199 | return; |
200 | } |
201 | *p++ = c; |
202 | } |
203 | } |
204 | |
205 | if (hintsOffset2 && hintsLength2) { |
206 | std::unique_ptr<Stream> s(str->makeSubStream(start: hintsOffset2, limited: false, length: hintsLength2, dict: Object(objNull))); |
207 | s->reset(); |
208 | for (unsigned int i = 0; i < hintsLength2; i++) { |
209 | const int c = s->getChar(); |
210 | if (unlikely(c == EOF)) { |
211 | error(category: errSyntaxWarning, pos: -1, msg: "Found EOF while reading hints2" ); |
212 | ok = false; |
213 | return; |
214 | } |
215 | *p++ = c; |
216 | } |
217 | } |
218 | |
219 | MemStream *memStream = new MemStream(&buf[0], 0, bufLength, Object(objNull)); |
220 | |
221 | Parser *parser = new Parser(xref, memStream, true); |
222 | |
223 | int num, gen; |
224 | Object obj; |
225 | if ((obj = parser->getObj(), obj.isInt()) && (num = obj.getInt(), obj = parser->getObj(), obj.isInt()) && (gen = obj.getInt(), obj = parser->getObj(), obj.isCmd(cmdA: "obj" )) |
226 | && (obj = parser->getObj(simpleOnly: false, fileKey: secHdlr ? secHdlr->getFileKey() : nullptr, encAlgorithm: secHdlr ? secHdlr->getEncAlgorithm() : cryptRC4, keyLength: secHdlr ? secHdlr->getFileKeyLength() : 0, objNum: num, objGen: gen, recursion: 0, strict: true), obj.isStream())) { |
227 | Stream *hintsStream = obj.getStream(); |
228 | Dict *hintsDict = obj.streamGetDict(); |
229 | |
230 | int sharedStreamOffset = 0; |
231 | if (hintsDict->lookupInt(key: "S" , alt_key: nullptr, value: &sharedStreamOffset) && sharedStreamOffset > 0) { |
232 | |
233 | hintsStream->reset(); |
234 | ok = readPageOffsetTable(str: hintsStream); |
235 | |
236 | if (ok) { |
237 | hintsStream->reset(); |
238 | for (int i = 0; i < sharedStreamOffset; i++) { |
239 | hintsStream->getChar(); |
240 | } |
241 | ok = readSharedObjectsTable(str: hintsStream); |
242 | } |
243 | } else { |
244 | error(category: errSyntaxWarning, pos: -1, msg: "Invalid shared object hint table offset" ); |
245 | ok = false; |
246 | } |
247 | } else { |
248 | error(category: errSyntaxWarning, pos: -1, msg: "Failed parsing hints table object" ); |
249 | ok = false; |
250 | } |
251 | |
252 | delete parser; |
253 | } |
254 | |
255 | bool Hints::readPageOffsetTable(Stream *str) |
256 | { |
257 | if (nPages < 1) { |
258 | error(category: errSyntaxWarning, pos: -1, msg: "Invalid number of pages reading page offset hints table" ); |
259 | return false; |
260 | } |
261 | |
262 | StreamBitReader sbr(str); |
263 | |
264 | nObjectLeast = sbr.readBits(n: 32); |
265 | if (nObjectLeast < 1) { |
266 | error(category: errSyntaxWarning, pos: -1, msg: "Invalid least number of objects reading page offset hints table" ); |
267 | nPages = 0; |
268 | return false; |
269 | } |
270 | |
271 | objectOffsetFirst = sbr.readBits(n: 32); |
272 | if (objectOffsetFirst >= hintsOffset) { |
273 | objectOffsetFirst += hintsLength; |
274 | } |
275 | |
276 | nBitsDiffObjects = sbr.readBits(n: 16); |
277 | if (nBitsDiffObjects > 32) { |
278 | error(category: errSyntaxWarning, pos: -1, msg: "Invalid number of bits needed to represent the difference between the greatest and least number of objects in a page" ); |
279 | nPages = 0; |
280 | return false; |
281 | } |
282 | |
283 | pageLengthLeast = sbr.readBits(n: 32); |
284 | |
285 | nBitsDiffPageLength = sbr.readBits(n: 16); |
286 | |
287 | OffsetStreamLeast = sbr.readBits(n: 32); |
288 | |
289 | nBitsOffsetStream = sbr.readBits(n: 16); |
290 | |
291 | lengthStreamLeast = sbr.readBits(n: 32); |
292 | |
293 | nBitsLengthStream = sbr.readBits(n: 16); |
294 | |
295 | nBitsNumShared = sbr.readBits(n: 16); |
296 | |
297 | nBitsShared = sbr.readBits(n: 16); |
298 | |
299 | nBitsNumerator = sbr.readBits(n: 16); |
300 | |
301 | denominator = sbr.readBits(n: 16); |
302 | |
303 | if ((nBitsDiffPageLength > 32) || (nBitsOffsetStream > 32) || (nBitsLengthStream > 32) || (nBitsNumShared > 32) || (nBitsShared > 32) || (nBitsNumerator > 32)) { |
304 | error(category: errSyntaxWarning, pos: -1, msg: "Invalid number of bits reading page offset hints table" ); |
305 | return false; |
306 | } |
307 | |
308 | for (int i = 0; i < nPages && !sbr.atEOF(); i++) { |
309 | nObjects[i] = nObjectLeast + sbr.readBits(n: nBitsDiffObjects); |
310 | } |
311 | if (sbr.atEOF()) { |
312 | return false; |
313 | } |
314 | |
315 | nObjects[0] = 0; |
316 | xRefOffset[0] = mainXRefEntriesOffset + 20; |
317 | for (int i = 1; i < nPages; i++) { |
318 | xRefOffset[i] = xRefOffset[i - 1] + 20 * nObjects[i - 1]; |
319 | } |
320 | |
321 | pageObjectNum[0] = 1; |
322 | for (int i = 1; i < nPages; i++) { |
323 | pageObjectNum[i] = pageObjectNum[i - 1] + nObjects[i - 1]; |
324 | } |
325 | pageObjectNum[0] = pageObjectFirst; |
326 | |
327 | sbr.resetInputBits(); // reset on byte boundary. Not in specs! |
328 | for (int i = 0; i < nPages && !sbr.atEOF(); i++) { |
329 | pageLength[i] = pageLengthLeast + sbr.readBits(n: nBitsDiffPageLength); |
330 | } |
331 | if (sbr.atEOF()) { |
332 | return false; |
333 | } |
334 | |
335 | sbr.resetInputBits(); // reset on byte boundary. Not in specs! |
336 | numSharedObject[0] = sbr.readBits(n: nBitsNumShared); |
337 | numSharedObject[0] = 0; // Do not trust the read value to be 0. |
338 | sharedObjectId[0] = nullptr; |
339 | for (int i = 1; i < nPages && !sbr.atEOF(); i++) { |
340 | numSharedObject[i] = sbr.readBits(n: nBitsNumShared); |
341 | if (numSharedObject[i] >= INT_MAX / (int)sizeof(unsigned int)) { |
342 | error(category: errSyntaxWarning, pos: -1, msg: "Invalid number of shared objects" ); |
343 | numSharedObject[i] = 0; |
344 | return false; |
345 | } |
346 | sharedObjectId[i] = (unsigned int *)gmallocn_checkoverflow(count: numSharedObject[i], size: sizeof(unsigned int)); |
347 | if (numSharedObject[i] && !sharedObjectId[i]) { |
348 | error(category: errSyntaxWarning, pos: -1, msg: "Failed to allocate memory for shared object IDs" ); |
349 | numSharedObject[i] = 0; |
350 | return false; |
351 | } |
352 | } |
353 | if (sbr.atEOF()) { |
354 | return false; |
355 | } |
356 | |
357 | sbr.resetInputBits(); // reset on byte boundary. Not in specs! |
358 | for (int i = 1; i < nPages; i++) { |
359 | for (unsigned int j = 0; j < numSharedObject[i] && !sbr.atEOF(); j++) { |
360 | sharedObjectId[i][j] = sbr.readBits(n: nBitsShared); |
361 | } |
362 | } |
363 | |
364 | pageOffset[0] = pageOffsetFirst; |
365 | // find pageOffsets. |
366 | for (int i = 1; i < nPages; i++) { |
367 | pageOffset[i] = pageOffset[i - 1] + pageLength[i - 1]; |
368 | } |
369 | |
370 | return !sbr.atEOF(); |
371 | } |
372 | |
373 | bool Hints::readSharedObjectsTable(Stream *str) |
374 | { |
375 | StreamBitReader sbr(str); |
376 | |
377 | const unsigned int firstSharedObjectNumber = sbr.readBits(n: 32); |
378 | |
379 | const unsigned int firstSharedObjectOffset = sbr.readBits(n: 32) + hintsLength; |
380 | |
381 | const unsigned int nSharedGroupsFirst = sbr.readBits(n: 32); |
382 | |
383 | const unsigned int nSharedGroups = sbr.readBits(n: 32); |
384 | |
385 | const unsigned int nBitsNumObjects = sbr.readBits(n: 16); |
386 | |
387 | const unsigned int groupLengthLeast = sbr.readBits(n: 32); |
388 | |
389 | const unsigned int nBitsDiffGroupLength = sbr.readBits(n: 16); |
390 | |
391 | if ((!nSharedGroups) || (nSharedGroups >= INT_MAX / (int)sizeof(unsigned int))) { |
392 | error(category: errSyntaxWarning, pos: -1, msg: "Invalid number of shared object groups" ); |
393 | return false; |
394 | } |
395 | if ((!nSharedGroupsFirst) || (nSharedGroupsFirst > nSharedGroups)) { |
396 | error(category: errSyntaxWarning, pos: -1, msg: "Invalid number of first page shared object groups" ); |
397 | return false; |
398 | } |
399 | if (nBitsNumObjects > 32 || nBitsDiffGroupLength > 32) { |
400 | error(category: errSyntaxWarning, pos: -1, msg: "Invalid shared object groups bit length" ); |
401 | return false; |
402 | } |
403 | |
404 | groupLength = (unsigned int *)gmallocn_checkoverflow(count: nSharedGroups, size: sizeof(unsigned int)); |
405 | groupOffset = (unsigned int *)gmallocn_checkoverflow(count: nSharedGroups, size: sizeof(unsigned int)); |
406 | groupHasSignature = (unsigned int *)gmallocn_checkoverflow(count: nSharedGroups, size: sizeof(unsigned int)); |
407 | groupNumObjects = (unsigned int *)gmallocn_checkoverflow(count: nSharedGroups, size: sizeof(unsigned int)); |
408 | groupXRefOffset = (unsigned int *)gmallocn_checkoverflow(count: nSharedGroups, size: sizeof(unsigned int)); |
409 | if (!groupLength || !groupOffset || !groupHasSignature || !groupNumObjects || !groupXRefOffset) { |
410 | error(category: errSyntaxWarning, pos: -1, msg: "Failed to allocate memory for shared object groups" ); |
411 | return false; |
412 | } |
413 | |
414 | sbr.resetInputBits(); // reset on byte boundary. Not in specs! |
415 | for (unsigned int i = 0; i < nSharedGroups && !sbr.atEOF(); i++) { |
416 | groupLength[i] = groupLengthLeast + sbr.readBits(n: nBitsDiffGroupLength); |
417 | } |
418 | if (sbr.atEOF()) { |
419 | return false; |
420 | } |
421 | |
422 | groupOffset[0] = objectOffsetFirst; |
423 | for (unsigned int i = 1; i < nSharedGroupsFirst; i++) { |
424 | groupOffset[i] = groupOffset[i - 1] + groupLength[i - 1]; |
425 | } |
426 | if (nSharedGroups > nSharedGroupsFirst) { |
427 | groupOffset[nSharedGroupsFirst] = firstSharedObjectOffset; |
428 | for (unsigned int i = nSharedGroupsFirst + 1; i < nSharedGroups; i++) { |
429 | groupOffset[i] = groupOffset[i - 1] + groupLength[i - 1]; |
430 | } |
431 | } |
432 | |
433 | sbr.resetInputBits(); // reset on byte boundary. Not in specs! |
434 | for (unsigned int i = 0; i < nSharedGroups && !sbr.atEOF(); i++) { |
435 | groupHasSignature[i] = sbr.readBits(n: 1); |
436 | } |
437 | if (sbr.atEOF()) { |
438 | return false; |
439 | } |
440 | |
441 | sbr.resetInputBits(); // reset on byte boundary. Not in specs! |
442 | for (unsigned int i = 0; i < nSharedGroups && !sbr.atEOF(); i++) { |
443 | if (groupHasSignature[i]) { |
444 | // readBits doesn't supports more than 32 bits. |
445 | sbr.readBits(n: 32); |
446 | sbr.readBits(n: 32); |
447 | sbr.readBits(n: 32); |
448 | sbr.readBits(n: 32); |
449 | } |
450 | } |
451 | if (sbr.atEOF()) { |
452 | return false; |
453 | } |
454 | |
455 | sbr.resetInputBits(); // reset on byte boundary. Not in specs! |
456 | for (unsigned int i = 0; i < nSharedGroups && !sbr.atEOF(); i++) { |
457 | groupNumObjects[i] = nBitsNumObjects ? 1 + sbr.readBits(n: nBitsNumObjects) : 1; |
458 | } |
459 | |
460 | for (unsigned int i = 0; i < nSharedGroupsFirst; i++) { |
461 | groupNumObjects[i] = 0; |
462 | groupXRefOffset[i] = 0; |
463 | } |
464 | if (nSharedGroups > nSharedGroupsFirst) { |
465 | groupXRefOffset[nSharedGroupsFirst] = mainXRefEntriesOffset + 20 * firstSharedObjectNumber; |
466 | for (unsigned int i = nSharedGroupsFirst + 1; i < nSharedGroups; i++) { |
467 | groupXRefOffset[i] = groupXRefOffset[i - 1] + 20 * groupNumObjects[i - 1]; |
468 | } |
469 | } |
470 | |
471 | return !sbr.atEOF(); |
472 | } |
473 | |
474 | bool Hints::isOk() const |
475 | { |
476 | return ok; |
477 | } |
478 | |
479 | Goffset Hints::getPageOffset(int page) |
480 | { |
481 | if ((page < 1) || (page > nPages)) { |
482 | return 0; |
483 | } |
484 | |
485 | if (page - 1 > pageFirst) { |
486 | return pageOffset[page - 1]; |
487 | } else if (page - 1 < pageFirst) { |
488 | return pageOffset[page]; |
489 | } else { |
490 | return pageOffset[0]; |
491 | } |
492 | } |
493 | |
494 | int Hints::getPageObjectNum(int page) |
495 | { |
496 | if ((page < 1) || (page > nPages)) { |
497 | return 0; |
498 | } |
499 | |
500 | if (page - 1 > pageFirst) { |
501 | return pageObjectNum[page - 1]; |
502 | } else if (page - 1 < pageFirst) { |
503 | return pageObjectNum[page]; |
504 | } else { |
505 | return pageObjectNum[0]; |
506 | } |
507 | } |
508 | |