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
32class StreamBitReader
33{
34public:
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
89private:
90 Stream *str;
91 int inputBits;
92 char bitsBuffer;
93 bool isAtEof;
94};
95
96//------------------------------------------------------------------------
97// Hints
98//------------------------------------------------------------------------
99
100Hints::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
152Hints::~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
174void 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
255bool 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
373bool 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
474bool Hints::isOk() const
475{
476 return ok;
477}
478
479Goffset 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
494int 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

source code of poppler/poppler/Hints.cc