diff --git a/libqpdf/QPDF.cc b/libqpdf/QPDF.cc index aab049cb..2a2cc59d 100644 --- a/libqpdf/QPDF.cc +++ b/libqpdf/QPDF.cc @@ -473,7 +473,7 @@ QPDF::parse(char const* password) m->parsed = true; if (m->xref_table.size() > 0 && !getRoot().getKey("/Pages").isDictionary()) { // QPDFs created from JSON have an empty xref table and no root object yet. - throw damagedPDF("", 0, "unable to find page tree"); + throw damagedPDF("", 0, "unable to find page tree"); } } @@ -547,6 +547,9 @@ QPDF::reconstruct_xref(QPDFExc& e) m->file->seek(0, SEEK_END); qpdf_offset_t eof = m->file->tell(); + // Sanity check on object ids. All objects must appear in xref table / stream. In all realistic + // scenarios at leat 3 bytes are required. + auto max_obj_id = eof / 3; m->file->seek(0, SEEK_SET); qpdf_offset_t line_start = 0; // Don't allow very long tokens here during recovery. @@ -564,7 +567,12 @@ QPDF::reconstruct_xref(QPDFExc& e) if ((t2.isInteger()) && (readToken(m->file, MAX_LEN).isWord("obj"))) { int obj = QUtil::string_to_int(t1.getValue().c_str()); int gen = QUtil::string_to_int(t2.getValue().c_str()); - insertReconstructedXrefEntry(obj, token_start, gen); + if (obj <= max_obj_id) { + insertReconstructedXrefEntry(obj, token_start, gen); + } else { + warn(damagedPDF( + "", 0, "ignoring object with impossibly large id " + std::to_string(obj))); + } } } else if (!m->trailer.isInitialized() && t1.isWord("trailer")) { QPDFObjectHandle t = readTrailer(); diff --git a/qpdf/qtest/qpdf/issue-147.out b/qpdf/qtest/qpdf/issue-147.out index 58b61266..e35bdbc2 100644 --- a/qpdf/qtest/qpdf/issue-147.out +++ b/qpdf/qtest/qpdf/issue-147.out @@ -3,6 +3,5 @@ WARNING: issue-147.pdf: file is damaged WARNING: issue-147.pdf: can't find startxref WARNING: issue-147.pdf: Attempting to reconstruct cross-reference table WARNING: issue-147.pdf (trailer, offset 9): expected dictionary key but found non-name object; inserting key /QPDFFake1 -WARNING: issue-147.pdf (object 62 0, offset 88): expected endobj -WARNING: issue-147.pdf (trailer, offset 90): invalid /ID in trailer dictionary -qpdf: issue-147.pdf: invalid password +WARNING: issue-147.pdf: ignoring object with impossibly large id 62 +qpdf: issue-147.pdf: unable to find /Root dictionary