//======================================================================== // // Hints.cc // // This file is licensed under the GPLv2 or later // // Copyright 2010, 2012, 2013 Hib Eris // Copyright 2010, 2011, 2013, 2014, 2016-2019, 2021, 2022 Albert Astals Cid // Copyright 2010, 2013 Pino Toscano // Copyright 2013 Adrian Johnson // Copyright 2014 Fabio D'Urso // Copyright 2016 Jeffrey Morlan // Copyright 2019 LE GARREC Vincent // Copyright 2019 Adam Reichold // //======================================================================== #include #include "Hints.h" #include "Linearization.h" #include "Object.h" #include "Stream.h" #include "XRef.h" #include "Parser.h" #include "Lexer.h" #include "SecurityHandler.h" #include class StreamBitReader { public: explicit StreamBitReader(Stream *strA) : str(strA), inputBits(0), isAtEof(false) { } void resetInputBits() { inputBits = 0; } bool atEOF() const { return isAtEof; } unsigned int readBit() { unsigned int bit; int c; if (inputBits == 0) { if ((c = str->getChar()) == EOF) { isAtEof = true; return (unsigned int)-1; } bitsBuffer = c; inputBits = 8; } bit = (bitsBuffer >> (inputBits - 1)) & 1; --inputBits; return bit; } unsigned int readBits(int n) { unsigned int bit, bits; if (n < 0) { return -1; } if (n == 0) { return 0; } if (n == 1) { return readBit(); } bit = readBit(); if (bit == (unsigned int)-1) { return -1; } bit = bit << (n - 1); bits = readBits(n - 1); if (bits == (unsigned int)-1) { return -1; } return bit | bits; } private: Stream *str; int inputBits; char bitsBuffer; bool isAtEof; }; //------------------------------------------------------------------------ // Hints //------------------------------------------------------------------------ Hints::Hints(BaseStream *str, Linearization *linearization, XRef *xref, SecurityHandler *secHdlr) { mainXRefEntriesOffset = linearization->getMainXRefEntriesOffset(); nPages = linearization->getNumPages(); pageFirst = linearization->getPageFirst(); pageEndFirst = linearization->getEndFirst(); pageObjectFirst = linearization->getObjectNumberFirst(); if (pageObjectFirst < 0 || pageObjectFirst >= xref->getNumObjects()) { error(errSyntaxWarning, -1, "Invalid reference for first page object ({0:d}) in linearization table ", pageObjectFirst); pageObjectFirst = 0; } XRefEntry *pageObjectFirstXRefEntry = xref->getEntry(pageObjectFirst); if (!pageObjectFirstXRefEntry) { error(errSyntaxWarning, -1, "No XRef entry for first page object"); pageOffsetFirst = 0; } else { pageOffsetFirst = pageObjectFirstXRefEntry->offset; } if (nPages >= INT_MAX / (int)sizeof(unsigned int)) { error(errSyntaxWarning, -1, "Invalid number of pages ({0:d}) for hints table", nPages); nPages = 0; } nObjects = (unsigned int *)gmallocn_checkoverflow(nPages, sizeof(unsigned int)); pageObjectNum = (int *)gmallocn_checkoverflow(nPages, sizeof(int)); xRefOffset = (unsigned int *)gmallocn_checkoverflow(nPages, sizeof(unsigned int)); pageLength = (unsigned int *)gmallocn_checkoverflow(nPages, sizeof(unsigned int)); pageOffset = (Goffset *)gmallocn_checkoverflow(nPages, sizeof(Goffset)); numSharedObject = (unsigned int *)gmallocn_checkoverflow(nPages, sizeof(unsigned int)); sharedObjectId = (unsigned int **)gmallocn_checkoverflow(nPages, sizeof(unsigned int *)); if (!nObjects || !pageObjectNum || !xRefOffset || !pageLength || !pageOffset || !numSharedObject || !sharedObjectId) { error(errSyntaxWarning, -1, "Failed to allocate memory for hints table"); nPages = 0; } if (nPages != 0) { memset(pageLength, 0, nPages * sizeof(unsigned int)); memset(pageOffset, 0, nPages * sizeof(unsigned int)); memset(numSharedObject, 0, nPages * sizeof(unsigned int)); memset(pageObjectNum, 0, nPages * sizeof(int)); } groupLength = nullptr; groupOffset = nullptr; groupHasSignature = nullptr; groupNumObjects = nullptr; groupXRefOffset = nullptr; ok = true; readTables(str, linearization, xref, secHdlr); } Hints::~Hints() { gfree(nObjects); gfree(pageObjectNum); gfree(xRefOffset); gfree(pageLength); gfree(pageOffset); for (int i = 0; i < nPages; i++) { if (numSharedObject[i]) { gfree(sharedObjectId[i]); } } gfree(sharedObjectId); gfree(numSharedObject); gfree(groupLength); gfree(groupOffset); gfree(groupHasSignature); gfree(groupNumObjects); gfree(groupXRefOffset); } void Hints::readTables(BaseStream *str, Linearization *linearization, XRef *xref, SecurityHandler *secHdlr) { hintsOffset = linearization->getHintsOffset(); hintsLength = linearization->getHintsLength(); hintsOffset2 = linearization->getHintsOffset2(); hintsLength2 = linearization->getHintsLength2(); const unsigned int bufLength = hintsLength + hintsLength2; if (bufLength == 0) { ok = false; return; } std::vector buf(bufLength); char *p = &buf[0]; if (hintsOffset && hintsLength) { std::unique_ptr s(str->makeSubStream(hintsOffset, false, hintsLength, Object(objNull))); s->reset(); for (unsigned int i = 0; i < hintsLength; i++) { const int c = s->getChar(); if (unlikely(c == EOF)) { error(errSyntaxWarning, -1, "Found EOF while reading hints"); ok = false; return; } *p++ = c; } } if (hintsOffset2 && hintsLength2) { std::unique_ptr s(str->makeSubStream(hintsOffset2, false, hintsLength2, Object(objNull))); s->reset(); for (unsigned int i = 0; i < hintsLength2; i++) { const int c = s->getChar(); if (unlikely(c == EOF)) { error(errSyntaxWarning, -1, "Found EOF while reading hints2"); ok = false; return; } *p++ = c; } } MemStream *memStream = new MemStream(&buf[0], 0, bufLength, Object(objNull)); Parser *parser = new Parser(xref, memStream, true); int num, gen; Object obj; if ((obj = parser->getObj(), obj.isInt()) && (num = obj.getInt(), obj = parser->getObj(), obj.isInt()) && (gen = obj.getInt(), obj = parser->getObj(), obj.isCmd("obj")) && (obj = parser->getObj(false, secHdlr ? secHdlr->getFileKey() : nullptr, secHdlr ? secHdlr->getEncAlgorithm() : cryptRC4, secHdlr ? secHdlr->getFileKeyLength() : 0, num, gen, 0, true), obj.isStream())) { Stream *hintsStream = obj.getStream(); Dict *hintsDict = obj.streamGetDict(); int sharedStreamOffset = 0; if (hintsDict->lookupInt("S", nullptr, &sharedStreamOffset) && sharedStreamOffset > 0) { hintsStream->reset(); ok = readPageOffsetTable(hintsStream); if (ok) { hintsStream->reset(); for (int i = 0; i < sharedStreamOffset; i++) { hintsStream->getChar(); } ok = readSharedObjectsTable(hintsStream); } } else { error(errSyntaxWarning, -1, "Invalid shared object hint table offset"); ok = false; } } else { error(errSyntaxWarning, -1, "Failed parsing hints table object"); ok = false; } delete parser; } bool Hints::readPageOffsetTable(Stream *str) { if (nPages < 1) { error(errSyntaxWarning, -1, "Invalid number of pages reading page offset hints table"); return false; } StreamBitReader sbr(str); nObjectLeast = sbr.readBits(32); if (nObjectLeast < 1) { error(errSyntaxWarning, -1, "Invalid least number of objects reading page offset hints table"); nPages = 0; return false; } objectOffsetFirst = sbr.readBits(32); if (objectOffsetFirst >= hintsOffset) { objectOffsetFirst += hintsLength; } nBitsDiffObjects = sbr.readBits(16); if (nBitsDiffObjects > 32) { error(errSyntaxWarning, -1, "Invalid number of bits needed to represent the difference between the greatest and least number of objects in a page"); nPages = 0; return false; } pageLengthLeast = sbr.readBits(32); nBitsDiffPageLength = sbr.readBits(16); OffsetStreamLeast = sbr.readBits(32); nBitsOffsetStream = sbr.readBits(16); lengthStreamLeast = sbr.readBits(32); nBitsLengthStream = sbr.readBits(16); nBitsNumShared = sbr.readBits(16); nBitsShared = sbr.readBits(16); nBitsNumerator = sbr.readBits(16); denominator = sbr.readBits(16); if ((nBitsDiffPageLength > 32) || (nBitsOffsetStream > 32) || (nBitsLengthStream > 32) || (nBitsNumShared > 32) || (nBitsShared > 32) || (nBitsNumerator > 32)) { error(errSyntaxWarning, -1, "Invalid number of bits reading page offset hints table"); return false; } for (int i = 0; i < nPages && !sbr.atEOF(); i++) { nObjects[i] = nObjectLeast + sbr.readBits(nBitsDiffObjects); } if (sbr.atEOF()) { return false; } nObjects[0] = 0; xRefOffset[0] = mainXRefEntriesOffset + 20; for (int i = 1; i < nPages; i++) { xRefOffset[i] = xRefOffset[i - 1] + 20 * nObjects[i - 1]; } pageObjectNum[0] = 1; for (int i = 1; i < nPages; i++) { pageObjectNum[i] = pageObjectNum[i - 1] + nObjects[i - 1]; } pageObjectNum[0] = pageObjectFirst; sbr.resetInputBits(); // reset on byte boundary. Not in specs! for (int i = 0; i < nPages && !sbr.atEOF(); i++) { pageLength[i] = pageLengthLeast + sbr.readBits(nBitsDiffPageLength); } if (sbr.atEOF()) { return false; } sbr.resetInputBits(); // reset on byte boundary. Not in specs! numSharedObject[0] = sbr.readBits(nBitsNumShared); numSharedObject[0] = 0; // Do not trust the read value to be 0. sharedObjectId[0] = nullptr; for (int i = 1; i < nPages && !sbr.atEOF(); i++) { numSharedObject[i] = sbr.readBits(nBitsNumShared); if (numSharedObject[i] >= INT_MAX / (int)sizeof(unsigned int)) { error(errSyntaxWarning, -1, "Invalid number of shared objects"); numSharedObject[i] = 0; return false; } sharedObjectId[i] = (unsigned int *)gmallocn_checkoverflow(numSharedObject[i], sizeof(unsigned int)); if (numSharedObject[i] && !sharedObjectId[i]) { error(errSyntaxWarning, -1, "Failed to allocate memory for shared object IDs"); numSharedObject[i] = 0; return false; } } if (sbr.atEOF()) { return false; } sbr.resetInputBits(); // reset on byte boundary. Not in specs! for (int i = 1; i < nPages; i++) { for (unsigned int j = 0; j < numSharedObject[i] && !sbr.atEOF(); j++) { sharedObjectId[i][j] = sbr.readBits(nBitsShared); } } pageOffset[0] = pageOffsetFirst; // find pageOffsets. for (int i = 1; i < nPages; i++) { pageOffset[i] = pageOffset[i - 1] + pageLength[i - 1]; } return !sbr.atEOF(); } bool Hints::readSharedObjectsTable(Stream *str) { StreamBitReader sbr(str); const unsigned int firstSharedObjectNumber = sbr.readBits(32); const unsigned int firstSharedObjectOffset = sbr.readBits(32) + hintsLength; const unsigned int nSharedGroupsFirst = sbr.readBits(32); const unsigned int nSharedGroups = sbr.readBits(32); const unsigned int nBitsNumObjects = sbr.readBits(16); const unsigned int groupLengthLeast = sbr.readBits(32); const unsigned int nBitsDiffGroupLength = sbr.readBits(16); if ((!nSharedGroups) || (nSharedGroups >= INT_MAX / (int)sizeof(unsigned int))) { error(errSyntaxWarning, -1, "Invalid number of shared object groups"); return false; } if ((!nSharedGroupsFirst) || (nSharedGroupsFirst > nSharedGroups)) { error(errSyntaxWarning, -1, "Invalid number of first page shared object groups"); return false; } if (nBitsNumObjects > 32 || nBitsDiffGroupLength > 32) { error(errSyntaxWarning, -1, "Invalid shared object groups bit length"); return false; } groupLength = (unsigned int *)gmallocn_checkoverflow(nSharedGroups, sizeof(unsigned int)); groupOffset = (unsigned int *)gmallocn_checkoverflow(nSharedGroups, sizeof(unsigned int)); groupHasSignature = (unsigned int *)gmallocn_checkoverflow(nSharedGroups, sizeof(unsigned int)); groupNumObjects = (unsigned int *)gmallocn_checkoverflow(nSharedGroups, sizeof(unsigned int)); groupXRefOffset = (unsigned int *)gmallocn_checkoverflow(nSharedGroups, sizeof(unsigned int)); if (!groupLength || !groupOffset || !groupHasSignature || !groupNumObjects || !groupXRefOffset) { error(errSyntaxWarning, -1, "Failed to allocate memory for shared object groups"); return false; } sbr.resetInputBits(); // reset on byte boundary. Not in specs! for (unsigned int i = 0; i < nSharedGroups && !sbr.atEOF(); i++) { groupLength[i] = groupLengthLeast + sbr.readBits(nBitsDiffGroupLength); } if (sbr.atEOF()) { return false; } groupOffset[0] = objectOffsetFirst; for (unsigned int i = 1; i < nSharedGroupsFirst; i++) { groupOffset[i] = groupOffset[i - 1] + groupLength[i - 1]; } if (nSharedGroups > nSharedGroupsFirst) { groupOffset[nSharedGroupsFirst] = firstSharedObjectOffset; for (unsigned int i = nSharedGroupsFirst + 1; i < nSharedGroups; i++) { groupOffset[i] = groupOffset[i - 1] + groupLength[i - 1]; } } sbr.resetInputBits(); // reset on byte boundary. Not in specs! for (unsigned int i = 0; i < nSharedGroups && !sbr.atEOF(); i++) { groupHasSignature[i] = sbr.readBits(1); } if (sbr.atEOF()) { return false; } sbr.resetInputBits(); // reset on byte boundary. Not in specs! for (unsigned int i = 0; i < nSharedGroups && !sbr.atEOF(); i++) { if (groupHasSignature[i]) { // readBits doesn't supports more than 32 bits. sbr.readBits(32); sbr.readBits(32); sbr.readBits(32); sbr.readBits(32); } } if (sbr.atEOF()) { return false; } sbr.resetInputBits(); // reset on byte boundary. Not in specs! for (unsigned int i = 0; i < nSharedGroups && !sbr.atEOF(); i++) { groupNumObjects[i] = nBitsNumObjects ? 1 + sbr.readBits(nBitsNumObjects) : 1; } for (unsigned int i = 0; i < nSharedGroupsFirst; i++) { groupNumObjects[i] = 0; groupXRefOffset[i] = 0; } if (nSharedGroups > nSharedGroupsFirst) { groupXRefOffset[nSharedGroupsFirst] = mainXRefEntriesOffset + 20 * firstSharedObjectNumber; for (unsigned int i = nSharedGroupsFirst + 1; i < nSharedGroups; i++) { groupXRefOffset[i] = groupXRefOffset[i - 1] + 20 * groupNumObjects[i - 1]; } } return !sbr.atEOF(); } bool Hints::isOk() const { return ok; } Goffset Hints::getPageOffset(int page) { if ((page < 1) || (page > nPages)) { return 0; } if (page - 1 > pageFirst) { return pageOffset[page - 1]; } else if (page - 1 < pageFirst) { return pageOffset[page]; } else { return pageOffset[0]; } } int Hints::getPageObjectNum(int page) { if ((page < 1) || (page > nPages)) { return 0; } if (page - 1 > pageFirst) { return pageObjectNum[page - 1]; } else if (page - 1 < pageFirst) { return pageObjectNum[page]; } else { return pageObjectNum[0]; } }