Commit e62973d277c57d8cc0a70c27afe0eb4958b78c42
1 parent
fff205dc
In QPDF check for page tree after reading xref table
Also add new fuzz test case.
Showing
26 changed files
with
31 additions
and
38 deletions
fuzz/CMakeLists.txt
| @@ -118,6 +118,7 @@ set(CORPUS_OTHER | @@ -118,6 +118,7 @@ set(CORPUS_OTHER | ||
| 118 | 68377.fuzz | 118 | 68377.fuzz |
| 119 | 68668.fuzz | 119 | 68668.fuzz |
| 120 | 68915.fuzz | 120 | 68915.fuzz |
| 121 | + 69857.fuzz | ||
| 121 | ) | 122 | ) |
| 122 | 123 | ||
| 123 | set(CORPUS_DIR ${CMAKE_CURRENT_BINARY_DIR}/qpdf_corpus) | 124 | set(CORPUS_DIR ${CMAKE_CURRENT_BINARY_DIR}/qpdf_corpus) |
fuzz/qpdf_extra/69857.fuzz
0 → 100644
No preview for this file type
fuzz/qtest/fuzz.test
| @@ -21,7 +21,7 @@ my @fuzzers = ( | @@ -21,7 +21,7 @@ my @fuzzers = ( | ||
| 21 | ['pngpredictor' => 1], | 21 | ['pngpredictor' => 1], |
| 22 | ['runlength' => 6], | 22 | ['runlength' => 6], |
| 23 | ['tiffpredictor' => 2], | 23 | ['tiffpredictor' => 2], |
| 24 | - ['qpdf' => 60], # increment when adding new files | 24 | + ['qpdf' => 61], # increment when adding new files |
| 25 | ); | 25 | ); |
| 26 | 26 | ||
| 27 | my $n_tests = 0; | 27 | my $n_tests = 0; |
libqpdf/QPDF.cc
| @@ -471,6 +471,10 @@ QPDF::parse(char const* password) | @@ -471,6 +471,10 @@ QPDF::parse(char const* password) | ||
| 471 | 471 | ||
| 472 | initializeEncryption(); | 472 | initializeEncryption(); |
| 473 | m->parsed = true; | 473 | m->parsed = true; |
| 474 | + if (m->xref_table.size() > 0 && !getRoot().getKey("/Pages").isDictionary()) { | ||
| 475 | + // QPDFs created from JSON have an empty xref table and no root object yet. | ||
| 476 | + throw damagedPDF("", 0, "unable to find page tree"); | ||
| 477 | + } | ||
| 474 | } | 478 | } |
| 475 | 479 | ||
| 476 | void | 480 | void |
libqpdf/QPDFWriter.cc
| @@ -1121,7 +1121,6 @@ QPDFWriter::enqueueObject(QPDFObjectHandle object) | @@ -1121,7 +1121,6 @@ QPDFWriter::enqueueObject(QPDFObjectHandle object) | ||
| 1121 | } else if (obj.renumber == -1) { | 1121 | } else if (obj.renumber == -1) { |
| 1122 | // This can happen if a specially constructed file indicates that an object stream is | 1122 | // This can happen if a specially constructed file indicates that an object stream is |
| 1123 | // inside itself. | 1123 | // inside itself. |
| 1124 | - QTC::TC("qpdf", "QPDFWriter ignore self-referential object stream"); | ||
| 1125 | } | 1124 | } |
| 1126 | return; | 1125 | return; |
| 1127 | } else if (!m->linearized) { | 1126 | } else if (!m->linearized) { |
qpdf/qpdf.testcov
| @@ -277,7 +277,6 @@ QPDF ignore first extra space in xref entry 0 | @@ -277,7 +277,6 @@ QPDF ignore first extra space in xref entry 0 | ||
| 277 | QPDF ignore second extra space in xref entry 0 | 277 | QPDF ignore second extra space in xref entry 0 |
| 278 | QPDF ignore length error xref entry 0 | 278 | QPDF ignore length error xref entry 0 |
| 279 | QPDF_encryption pad short parameter 0 | 279 | QPDF_encryption pad short parameter 0 |
| 280 | -QPDFWriter ignore self-referential object stream 0 | ||
| 281 | QPDFObjectHandle found old angle 1 | 280 | QPDFObjectHandle found old angle 1 |
| 282 | QPDF_Stream special filters 3 | 281 | QPDF_Stream special filters 3 |
| 283 | QPDFTokenizer block long token 0 | 282 | QPDFTokenizer block long token 0 |
qpdf/qtest/invalid-objects.test
| @@ -19,7 +19,7 @@ my $n_tests = 4; | @@ -19,7 +19,7 @@ my $n_tests = 4; | ||
| 19 | $td->runtest("closed input source", | 19 | $td->runtest("closed input source", |
| 20 | {$td->COMMAND => "test_driver 73 minimal.pdf"}, | 20 | {$td->COMMAND => "test_driver 73 minimal.pdf"}, |
| 21 | {$td->FILE => "test73.out", | 21 | {$td->FILE => "test73.out", |
| 22 | - $td->EXIT_STATUS => 2}, | 22 | + $td->EXIT_STATUS => 0}, |
| 23 | $td->NORMALIZE_NEWLINES); | 23 | $td->NORMALIZE_NEWLINES); |
| 24 | 24 | ||
| 25 | $td->runtest("empty object", | 25 | $td->runtest("empty object", |
qpdf/qtest/qpdf/bad11-recover.out
| 1 | WARNING: bad11.pdf: file is damaged | 1 | WARNING: bad11.pdf: file is damaged |
| 2 | WARNING: bad11.pdf (trailer, offset 905): /Prev key in trailer dictionary is not an integer | 2 | WARNING: bad11.pdf (trailer, offset 905): /Prev key in trailer dictionary is not an integer |
| 3 | WARNING: bad11.pdf: Attempting to reconstruct cross-reference table | 3 | WARNING: bad11.pdf: Attempting to reconstruct cross-reference table |
| 4 | +WARNING: bad11.pdf (object 2 0, offset 128): expected endobj | ||
| 4 | /QTest is implicit | 5 | /QTest is implicit |
| 5 | /QTest is direct and has type null (2) | 6 | /QTest is direct and has type null (2) |
| 6 | /QTest is null | 7 | /QTest is null |
qpdf/qtest/qpdf/bad12-recover.out
| 1 | WARNING: bad12.pdf: reported number of objects (9) is not one plus the highest object number (7) | 1 | WARNING: bad12.pdf: reported number of objects (9) is not one plus the highest object number (7) |
| 2 | +WARNING: bad12.pdf (object 2 0, offset 128): expected endobj | ||
| 2 | /QTest is implicit | 3 | /QTest is implicit |
| 3 | /QTest is direct and has type null (2) | 4 | /QTest is direct and has type null (2) |
| 4 | /QTest is null | 5 | /QTest is null |
qpdf/qtest/qpdf/bad12.out
| 1 | WARNING: bad12.pdf: reported number of objects (9) is not one plus the highest object number (7) | 1 | WARNING: bad12.pdf: reported number of objects (9) is not one plus the highest object number (7) |
| 2 | +WARNING: bad12.pdf (object 2 0, offset 128): expected endobj | ||
| 2 | /QTest is implicit | 3 | /QTest is implicit |
| 3 | /QTest is direct and has type null (2) | 4 | /QTest is direct and has type null (2) |
| 4 | /QTest is null | 5 | /QTest is null |
qpdf/qtest/qpdf/bad2-recover.out
| 1 | WARNING: bad2.pdf: file is damaged | 1 | WARNING: bad2.pdf: file is damaged |
| 2 | WARNING: bad2.pdf: can't find startxref | 2 | WARNING: bad2.pdf: can't find startxref |
| 3 | WARNING: bad2.pdf: Attempting to reconstruct cross-reference table | 3 | WARNING: bad2.pdf: Attempting to reconstruct cross-reference table |
| 4 | +WARNING: bad2.pdf (object 2 0, offset 128): expected endobj | ||
| 4 | /QTest is implicit | 5 | /QTest is implicit |
| 5 | /QTest is direct and has type null (2) | 6 | /QTest is direct and has type null (2) |
| 6 | /QTest is null | 7 | /QTest is null |
qpdf/qtest/qpdf/bad3-recover.out
| 1 | WARNING: bad3.pdf: file is damaged | 1 | WARNING: bad3.pdf: file is damaged |
| 2 | WARNING: bad3.pdf (offset 542): xref not found | 2 | WARNING: bad3.pdf (offset 542): xref not found |
| 3 | WARNING: bad3.pdf: Attempting to reconstruct cross-reference table | 3 | WARNING: bad3.pdf: Attempting to reconstruct cross-reference table |
| 4 | +WARNING: bad3.pdf (object 2 0, offset 128): expected endobj | ||
| 4 | /QTest is implicit | 5 | /QTest is implicit |
| 5 | /QTest is direct and has type null (2) | 6 | /QTest is direct and has type null (2) |
| 6 | /QTest is null | 7 | /QTest is null |
qpdf/qtest/qpdf/bad4-recover.out
| 1 | WARNING: bad4.pdf: file is damaged | 1 | WARNING: bad4.pdf: file is damaged |
| 2 | WARNING: bad4.pdf (xref table, offset 547): xref syntax invalid | 2 | WARNING: bad4.pdf (xref table, offset 547): xref syntax invalid |
| 3 | WARNING: bad4.pdf: Attempting to reconstruct cross-reference table | 3 | WARNING: bad4.pdf: Attempting to reconstruct cross-reference table |
| 4 | +WARNING: bad4.pdf (object 2 0, offset 128): expected endobj | ||
| 4 | /QTest is implicit | 5 | /QTest is implicit |
| 5 | /QTest is direct and has type null (2) | 6 | /QTest is direct and has type null (2) |
| 6 | /QTest is null | 7 | /QTest is null |
qpdf/qtest/qpdf/bad5-recover.out
| 1 | WARNING: bad5.pdf: file is damaged | 1 | WARNING: bad5.pdf: file is damaged |
| 2 | WARNING: bad5.pdf (xref table, offset 591): invalid xref entry (obj=2) | 2 | WARNING: bad5.pdf (xref table, offset 591): invalid xref entry (obj=2) |
| 3 | WARNING: bad5.pdf: Attempting to reconstruct cross-reference table | 3 | WARNING: bad5.pdf: Attempting to reconstruct cross-reference table |
| 4 | +WARNING: bad5.pdf (object 2 0, offset 128): expected endobj | ||
| 4 | /QTest is implicit | 5 | /QTest is implicit |
| 5 | /QTest is direct and has type null (2) | 6 | /QTest is direct and has type null (2) |
| 6 | /QTest is null | 7 | /QTest is null |
qpdf/qtest/qpdf/bad6-recover.out
qpdf/qtest/qpdf/bad6.out
qpdf/qtest/qpdf/bad8-recover.out
| 1 | WARNING: bad8.pdf: file is damaged | 1 | WARNING: bad8.pdf: file is damaged |
| 2 | WARNING: bad8.pdf (offset 543): xref not found | 2 | WARNING: bad8.pdf (offset 543): xref not found |
| 3 | WARNING: bad8.pdf: Attempting to reconstruct cross-reference table | 3 | WARNING: bad8.pdf: Attempting to reconstruct cross-reference table |
| 4 | +WARNING: bad8.pdf (object 2 0, offset 128): expected endobj | ||
| 4 | /QTest is implicit | 5 | /QTest is implicit |
| 5 | /QTest is direct and has type null (2) | 6 | /QTest is direct and has type null (2) |
| 6 | /QTest is null | 7 | /QTest is null |
qpdf/qtest/qpdf/fuzz-16214.out
| @@ -11,8 +11,8 @@ WARNING: fuzz-16214.pdf (object 1 0, offset 7189): expected n n obj | @@ -11,8 +11,8 @@ WARNING: fuzz-16214.pdf (object 1 0, offset 7189): expected n n obj | ||
| 11 | WARNING: fuzz-16214.pdf: Attempting to reconstruct cross-reference table | 11 | WARNING: fuzz-16214.pdf: Attempting to reconstruct cross-reference table |
| 12 | WARNING: fuzz-16214.pdf (offset 7207): error decoding stream data for object 2 0: stream inflate: inflate: data: invalid code lengths set | 12 | WARNING: fuzz-16214.pdf (offset 7207): error decoding stream data for object 2 0: stream inflate: inflate: data: invalid code lengths set |
| 13 | WARNING: fuzz-16214.pdf (offset 7207): getStreamData called on unfilterable stream | 13 | WARNING: fuzz-16214.pdf (offset 7207): getStreamData called on unfilterable stream |
| 14 | -WARNING: fuzz-16214.pdf (object 11 0, offset 11551): supposed object stream 5 has wrong type | ||
| 15 | -WARNING: fuzz-16214.pdf (object 11 0, offset 11551): object stream 5 has incorrect keys | 14 | +WARNING: fuzz-16214.pdf (object 8 0, offset 7207): supposed object stream 5 has wrong type |
| 15 | +WARNING: fuzz-16214.pdf (object 8 0, offset 7207): object stream 5 has incorrect keys | ||
| 16 | WARNING: fuzz-16214.pdf (object 21 0, offset 3639): expected endstream | 16 | WARNING: fuzz-16214.pdf (object 21 0, offset 3639): expected endstream |
| 17 | WARNING: fuzz-16214.pdf (object 21 0, offset 3112): attempting to recover stream length | 17 | WARNING: fuzz-16214.pdf (object 21 0, offset 3112): attempting to recover stream length |
| 18 | WARNING: fuzz-16214.pdf (object 21 0, offset 3112): recovered stream length: 340 | 18 | WARNING: fuzz-16214.pdf (object 21 0, offset 3112): recovered stream length: 340 |
qpdf/qtest/qpdf/issue-119.out
| 1 | -WARNING: issue-119.pdf (object 4 0, offset 298): expected dictionary key but found non-name object; inserting key /QPDFFake1 | ||
| 2 | -WARNING: issue-119.pdf (object 4 0, offset 298): expected dictionary key but found non-name object; inserting key /QPDFFake2 | ||
| 3 | -qpdf: operation succeeded with warnings; resulting file may have some problems | 1 | +qpdf: issue-119.pdf: unable to find page tree |
qpdf/qtest/qpdf/issue-120.out
| 1 | -WARNING: issue-120.pdf (offset 85): loop detected resolving object 3 0 | ||
| 2 | -WARNING: issue-120.pdf (object 6 0, offset 85): supposed object stream 3 is not a stream | ||
| 3 | -WARNING: issue-120.pdf: file is damaged | ||
| 4 | -WARNING: issue-120.pdf (object 8 10, offset 26880): expected n n obj | ||
| 5 | -WARNING: issue-120.pdf: Attempting to reconstruct cross-reference table | ||
| 6 | -WARNING: issue-120.pdf: object 8 10 not found in file after regenerating cross reference table | ||
| 7 | -qpdf: operation succeeded with warnings; resulting file may have some problems | 1 | +qpdf: issue-120.pdf: unable to find page tree |
qpdf/qtest/qpdf/issue-143.out
| @@ -14,6 +14,4 @@ WARNING: issue-143.pdf (object 1 0, offset 21): stream dictionary lacks /Length | @@ -14,6 +14,4 @@ WARNING: issue-143.pdf (object 1 0, offset 21): stream dictionary lacks /Length | ||
| 14 | WARNING: issue-143.pdf (object 1 0, offset 84): attempting to recover stream length | 14 | WARNING: issue-143.pdf (object 1 0, offset 84): attempting to recover stream length |
| 15 | WARNING: issue-143.pdf (object 1 0, offset 84): recovered stream length: 606 | 15 | WARNING: issue-143.pdf (object 1 0, offset 84): recovered stream length: 606 |
| 16 | WARNING: issue-143.pdf object stream 1 (object 2 0, offset 33): expected dictionary key but found non-name object; inserting key /QPDFFake1 | 16 | WARNING: issue-143.pdf object stream 1 (object 2 0, offset 33): expected dictionary key but found non-name object; inserting key /QPDFFake1 |
| 17 | -WARNING: issue-143.pdf: object 0/0 has unexpected xref entry type | ||
| 18 | -WARNING: issue-143.pdf (object 2 0, offset 84): supposed object stream 12336 is not a stream | ||
| 19 | -qpdf: operation succeeded with warnings; resulting file may have some problems | 17 | +qpdf: issue-143.pdf: unable to find page tree |
qpdf/qtest/qpdf/issue-51.out
| @@ -2,15 +2,4 @@ WARNING: issue-51.pdf: can't find PDF header | @@ -2,15 +2,4 @@ WARNING: issue-51.pdf: can't find PDF header | ||
| 2 | WARNING: issue-51.pdf: reported number of objects (0) is not one plus the highest object number (8) | 2 | WARNING: issue-51.pdf: reported number of objects (0) is not one plus the highest object number (8) |
| 3 | WARNING: issue-51.pdf (object 7 0, offset 476): dictionary has duplicated key /0000; last occurrence overrides earlier ones | 3 | WARNING: issue-51.pdf (object 7 0, offset 476): dictionary has duplicated key /0000; last occurrence overrides earlier ones |
| 4 | WARNING: issue-51.pdf (object 7 0, offset 553): expected endobj | 4 | WARNING: issue-51.pdf (object 7 0, offset 553): expected endobj |
| 5 | -WARNING: issue-51.pdf (object 1 0, offset 236): dictionary has duplicated key /00000000; last occurrence overrides earlier ones | ||
| 6 | -WARNING: issue-51.pdf (object 1 0, offset 359): expected endobj | ||
| 7 | -WARNING: issue-51.pdf (offset 70): loop detected resolving object 2 0 | ||
| 8 | -WARNING: issue-51.pdf (object 2 0, offset 26): stream dictionary lacks /Length key | ||
| 9 | -WARNING: issue-51.pdf (object 2 0, offset 71): attempting to recover stream length | ||
| 10 | -WARNING: issue-51.pdf (object 2 0, offset 71): unable to recover stream data; treating stream as empty | ||
| 11 | -WARNING: issue-51.pdf (object 2 0, offset 977): expected endobj | ||
| 12 | -WARNING: issue-51.pdf (object 3 0): object has offset 0 | ||
| 13 | -WARNING: issue-51.pdf (object 4 0): object has offset 0 | ||
| 14 | -WARNING: issue-51.pdf (object 5 0): object has offset 0 | ||
| 15 | -WARNING: issue-51.pdf (object 6 0): object has offset 0 | ||
| 16 | -WARNING: issue-51.pdf (object 8 0): object has offset 0 | 5 | +issue-51.pdf: unable to find page tree |
qpdf/qtest/qpdf/job-api.out
| @@ -21,6 +21,7 @@ captured stderr | @@ -21,6 +21,7 @@ captured stderr | ||
| 21 | WARNING: bad2.pdf: file is damaged | 21 | WARNING: bad2.pdf: file is damaged |
| 22 | WARNING: bad2.pdf: can't find startxref | 22 | WARNING: bad2.pdf: can't find startxref |
| 23 | WARNING: bad2.pdf: Attempting to reconstruct cross-reference table | 23 | WARNING: bad2.pdf: Attempting to reconstruct cross-reference table |
| 24 | +WARNING: bad2.pdf (object 2 0, offset 128): expected endobj | ||
| 24 | WARNING: bad2.pdf (object 4 0, offset 389): expected endobj | 25 | WARNING: bad2.pdf (object 4 0, offset 389): expected endobj |
| 25 | qpdf: operation succeeded with warnings | 26 | qpdf: operation succeeded with warnings |
| 26 | test 84 done | 27 | test 84 done |
qpdf/qtest/qpdf/test73.out
| 1 | getRoot: attempted to dereference an uninitialized QPDFObjectHandle | 1 | getRoot: attempted to dereference an uninitialized QPDFObjectHandle |
| 2 | -WARNING: closed input source: object 1/0: error reading object: QPDF operation attempted on a QPDF object with no input source. QPDF operations are invalid before processFile (or another process method) or after closeInputSource | ||
| 3 | -closed input source: unable to find /Root dictionary | 2 | +WARNING: closed input source: object 4/0: error reading object: QPDF operation attempted on a QPDF object with no input source. QPDF operations are invalid before processFile (or another process method) or after closeInputSource |
| 3 | +test 73 done |
qpdf/qtest/specific-bugs.test
| @@ -16,19 +16,19 @@ my $td = new TestDriver('specific-bugs'); | @@ -16,19 +16,19 @@ my $td = new TestDriver('specific-bugs'); | ||
| 16 | 16 | ||
| 17 | # The number is the github issue number in which the bug was reported. | 17 | # The number is the github issue number in which the bug was reported. |
| 18 | my @bug_tests = ( | 18 | my @bug_tests = ( |
| 19 | - ["51", "resolve loop", 3], | 19 | + ["51", "resolve loop", 2], |
| 20 | ["99", "object 0", 2], | 20 | ["99", "object 0", 2], |
| 21 | ["99b", "object 0", 2], | 21 | ["99b", "object 0", 2], |
| 22 | ["100", "xref reconstruction loop", 2], | 22 | ["100", "xref reconstruction loop", 2], |
| 23 | ["101", "resolve for exception text", 2], | 23 | ["101", "resolve for exception text", 2], |
| 24 | ["117", "other infinite loop", 3], | 24 | ["117", "other infinite loop", 3], |
| 25 | ["118", "other infinite loop", 2], | 25 | ["118", "other infinite loop", 2], |
| 26 | - ["119", "other infinite loop", 3], | ||
| 27 | - ["120", "other infinite loop", 3], | 26 | + ["119", "other infinite loop", 2], |
| 27 | + ["120", "other infinite loop", 2], | ||
| 28 | ["106", "zlib data error", 3], | 28 | ["106", "zlib data error", 3], |
| 29 | ["141a", "/W entry size 0", 2], | 29 | ["141a", "/W entry size 0", 2], |
| 30 | ["141b", "/W entry size 0", 2], | 30 | ["141b", "/W entry size 0", 2], |
| 31 | - ["143", "self-referential ostream", 3, "--preserve-unreferenced"], | 31 | + ["143", "self-referential ostream", 2, "--preserve-unreferenced"], |
| 32 | ["146", "very deeply nested array", 2], | 32 | ["146", "very deeply nested array", 2], |
| 33 | ["147", "previously caused memory error", 2], | 33 | ["147", "previously caused memory error", 2], |
| 34 | ["148", "free memory on bad flate", 2], | 34 | ["148", "free memory on bad flate", 2], |
qpdf/test_driver.cc
| @@ -2496,7 +2496,7 @@ test_73(QPDF& pdf, char const* arg2) | @@ -2496,7 +2496,7 @@ test_73(QPDF& pdf, char const* arg2) | ||
| 2496 | } | 2496 | } |
| 2497 | 2497 | ||
| 2498 | pdf.closeInputSource(); | 2498 | pdf.closeInputSource(); |
| 2499 | - pdf.getRoot().getKey("/Pages").unparseResolved(); | 2499 | + pdf.getObject(4, 0).unparseResolved(); |
| 2500 | } | 2500 | } |
| 2501 | 2501 | ||
| 2502 | static void | 2502 | static void |