Commit c02cb9a7203d6b30557b317153aabd974af4d637
1 parent
42cd7a98
Fix QPDF::recoverStreamLength
Ensure the the recovered stream end is not part of a different object. Test file is bad24.pdf with stream 4 'endstream' corrupted.
Showing
9 changed files
with
121 additions
and
18 deletions
.idea/codeStyles/Project.xml
| @@ -3,7 +3,7 @@ | @@ -3,7 +3,7 @@ | ||
| 3 | <RiderCodeStyleSettings> | 3 | <RiderCodeStyleSettings> |
| 4 | <option name="/Default/CodeStyle/CodeFormatting/CppClangFormat/EnableClangFormatSupport/@EntryValue" value="true" type="bool" /> | 4 | <option name="/Default/CodeStyle/CodeFormatting/CppClangFormat/EnableClangFormatSupport/@EntryValue" value="true" type="bool" /> |
| 5 | </RiderCodeStyleSettings> | 5 | </RiderCodeStyleSettings> |
| 6 | - <SqlCodeStyleSettings version="7"> | 6 | + <SqlCodeStyleSettings version="6"> |
| 7 | <option name="KEYWORD_CASE" value="2" /> | 7 | <option name="KEYWORD_CASE" value="2" /> |
| 8 | </SqlCodeStyleSettings> | 8 | </SqlCodeStyleSettings> |
| 9 | <clangFormatSettings> | 9 | <clangFormatSettings> |
libqpdf/QPDF.cc
| @@ -1649,27 +1649,28 @@ QPDF::recoverStreamLength( | @@ -1649,27 +1649,28 @@ QPDF::recoverStreamLength( | ||
| 1649 | } | 1649 | } |
| 1650 | 1650 | ||
| 1651 | if (length) { | 1651 | if (length) { |
| 1652 | - qpdf_offset_t this_obj_offset = 0; | ||
| 1653 | - QPDFObjGen this_og; | 1652 | + auto end = stream_offset + toO(length); |
| 1653 | + qpdf_offset_t found_offset = 0; | ||
| 1654 | + QPDFObjGen found_og; | ||
| 1654 | 1655 | ||
| 1655 | // Make sure this is inside this object | 1656 | // Make sure this is inside this object |
| 1656 | - for (auto const& iter: m->xref_table) { | ||
| 1657 | - QPDFXRefEntry const& entry = iter.second; | 1657 | + for (auto const& [current_og, entry]: m->xref_table) { |
| 1658 | if (entry.getType() == 1) { | 1658 | if (entry.getType() == 1) { |
| 1659 | qpdf_offset_t obj_offset = entry.getOffset(); | 1659 | qpdf_offset_t obj_offset = entry.getOffset(); |
| 1660 | - if ((obj_offset > stream_offset) && | ||
| 1661 | - ((this_obj_offset == 0) || (this_obj_offset > obj_offset))) { | ||
| 1662 | - this_obj_offset = obj_offset; | ||
| 1663 | - this_og = iter.first; | 1660 | + if (found_offset < obj_offset && obj_offset < end) { |
| 1661 | + found_offset = obj_offset; | ||
| 1662 | + found_og = current_og; | ||
| 1664 | } | 1663 | } |
| 1665 | } | 1664 | } |
| 1666 | } | 1665 | } |
| 1667 | - if (this_obj_offset && (this_og == og)) { | ||
| 1668 | - // Well, we found endstream\nendobj within the space allowed for this object, so we're | ||
| 1669 | - // probably in good shape. | ||
| 1670 | - throw std::logic_error("unreachable success"); | 1666 | + if (!found_offset || found_og == og) { |
| 1667 | + // If we are trying to recover an XRef stream the xref table will not contain and | ||
| 1668 | + // won't contain any entries, therefore we cannot check the found length. Otherwise we | ||
| 1669 | + // found endstream\nendobj within the space allowed for this object, so we're probably | ||
| 1670 | + // in good shape. | ||
| 1671 | } else { | 1671 | } else { |
| 1672 | QTC::TC("qpdf", "QPDF found wrong endstream in recovery"); | 1672 | QTC::TC("qpdf", "QPDF found wrong endstream in recovery"); |
| 1673 | + length = 0; | ||
| 1673 | } | 1674 | } |
| 1674 | } | 1675 | } |
| 1675 | 1676 |
qpdf/qtest/error-condition.test
| @@ -55,6 +55,7 @@ my @badfiles = ("not a PDF file", # 1 | @@ -55,6 +55,7 @@ my @badfiles = ("not a PDF file", # 1 | ||
| 55 | "bad dictionary key", # 36 | 55 | "bad dictionary key", # 36 |
| 56 | "space before xref", # 37 | 56 | "space before xref", # 37 |
| 57 | "startxref to space then eof", # 38 | 57 | "startxref to space then eof", # 38 |
| 58 | + "stream lenth revocery overlapping", # 39 | ||
| 58 | ); | 59 | ); |
| 59 | 60 | ||
| 60 | $n_tests += @badfiles + 8; | 61 | $n_tests += @badfiles + 8; |
| @@ -65,7 +66,7 @@ $n_tests += @badfiles + 8; | @@ -65,7 +66,7 @@ $n_tests += @badfiles + 8; | ||
| 65 | # have error conditions that used to be fatal but are now considered | 66 | # have error conditions that used to be fatal but are now considered |
| 66 | # non-fatal. | 67 | # non-fatal. |
| 67 | my %badtest_overrides = (); | 68 | my %badtest_overrides = (); |
| 68 | -for(6, 12..15, 17, 18..32, 34..37) | 69 | +for(6, 12..15, 17, 18..32, 34..37, 39) |
| 69 | { | 70 | { |
| 70 | $badtest_overrides{$_} = 0; | 71 | $badtest_overrides{$_} = 0; |
| 71 | } | 72 | } |
qpdf/qtest/parsing.test
| @@ -17,7 +17,7 @@ my $td = new TestDriver('parsing'); | @@ -17,7 +17,7 @@ my $td = new TestDriver('parsing'); | ||
| 17 | my $n_tests = 17; | 17 | my $n_tests = 17; |
| 18 | 18 | ||
| 19 | $td->runtest("parse objects from string", | 19 | $td->runtest("parse objects from string", |
| 20 | - {$td->COMMAND => "test_driver 31 bad39.qdf"}, | 20 | + {$td->COMMAND => "test_driver 31 bad-parse.qdf"}, |
| 21 | {$td->FILE => "parse-object.out", $td->EXIT_STATUS => 0}, | 21 | {$td->FILE => "parse-object.out", $td->EXIT_STATUS => 0}, |
| 22 | $td->NORMALIZE_NEWLINES); | 22 | $td->NORMALIZE_NEWLINES); |
| 23 | $td->runtest("EOF terminating literal tokens", | 23 | $td->runtest("EOF terminating literal tokens", |
qpdf/qtest/qpdf/bad39.qdf renamed to qpdf/qtest/qpdf/bad-parse.qdf
qpdf/qtest/qpdf/bad39-recover.out
0 โ 100644
| 1 | +WARNING: bad39.pdf (object 4 0, offset 385): expected endstream | ||
| 2 | +WARNING: bad39.pdf (object 4 0, offset 341): attempting to recover stream length | ||
| 3 | +WARNING: bad39.pdf (object 4 0, offset 341): unable to recover stream data; treating stream as empty | ||
| 4 | +/QTest is indirect and has type stream (10) | ||
| 5 | +/QTest is a stream. Dictionary: << /Length 44 >> | ||
| 6 | +Raw stream data: | ||
| 7 | + | ||
| 8 | +Uncompressed stream data: | ||
| 9 | + | ||
| 10 | +End of stream data | ||
| 11 | +unparse: 4 0 R | ||
| 12 | +unparseResolved: 4 0 R | ||
| 13 | +test 1 done |
qpdf/qtest/qpdf/bad39.out
0 โ 100644
qpdf/qtest/qpdf/bad39.pdf
0 โ 100644
| 1 | +%PDF-1.3 | ||
| 2 | +1 0 obj | ||
| 3 | +<< | ||
| 4 | + /Type /Catalog | ||
| 5 | + /Pages 2 0 R | ||
| 6 | +>> | ||
| 7 | +endobj | ||
| 8 | + | ||
| 9 | +2 0 obj | ||
| 10 | +<< | ||
| 11 | + /Type /Pages | ||
| 12 | + /Kids [ | ||
| 13 | + 3 0 R | ||
| 14 | + ] | ||
| 15 | + /Count 1 | ||
| 16 | +>> | ||
| 17 | +endobj | ||
| 18 | + | ||
| 19 | +3 0 obj | ||
| 20 | +<< | ||
| 21 | + /Type /Page | ||
| 22 | + /Parent 2 0 R | ||
| 23 | + /MediaBox [0 0 612 792] | ||
| 24 | + /Contents 4 0 R | ||
| 25 | + /Resources << | ||
| 26 | + /ProcSet 5 0 R | ||
| 27 | + /Font << | ||
| 28 | + /F1 6 0 R | ||
| 29 | + >> | ||
| 30 | + >> | ||
| 31 | +>> | ||
| 32 | +endobj | ||
| 33 | + | ||
| 34 | +4 0 obj | ||
| 35 | +<< | ||
| 36 | + /Length 44 | ||
| 37 | +>> | ||
| 38 | +stream | ||
| 39 | +BT | ||
| 40 | + /F1 24 Tf | ||
| 41 | + 72 720 Td | ||
| 42 | + (Potato) Tj | ||
| 43 | +ET | ||
| 44 | +enxstream | ||
| 45 | +enxobj | ||
| 46 | + | ||
| 47 | +5 0 obj | ||
| 48 | +[ | ||
| 49 | |||
| 50 | + /Text | ||
| 51 | +] | ||
| 52 | +endobj | ||
| 53 | + | ||
| 54 | +6 0 obj | ||
| 55 | +<< | ||
| 56 | + /Type /Font | ||
| 57 | + /Subtype /Type1 | ||
| 58 | + /Name /F1 | ||
| 59 | + /BaseFont /Helvetica | ||
| 60 | + /Encoding /Winng | ||
| 61 | +>> | ||
| 62 | +endstream | ||
| 63 | +endobj | ||
| 64 | + | ||
| 65 | +xref | ||
| 66 | +0 7 | ||
| 67 | +0000000000 65535 f | ||
| 68 | +0000000009 00000 n | ||
| 69 | +0000000063 00000 n | ||
| 70 | +0000000135 00000 n | ||
| 71 | +0000000307 00000 n | ||
| 72 | +0000000403 00000 n | ||
| 73 | +0000000438 00000 n | ||
| 74 | +trailer << | ||
| 75 | + /Size 7 | ||
| 76 | + /Root 1 0 R | ||
| 77 | + /QTest 4 0 R | ||
| 78 | +>> | ||
| 79 | +startxref | ||
| 80 | +556 | ||
| 81 | +%%EOF |
qpdf/qtest/qpdf/parse-object.out
| @@ -5,7 +5,7 @@ WARNING: parsed object (offset 9): unknown token while reading object; treating | @@ -5,7 +5,7 @@ WARNING: parsed object (offset 9): unknown token while reading object; treating | ||
| 5 | WARNING: parsed object: treating unexpected brace token as null | 5 | WARNING: parsed object: treating unexpected brace token as null |
| 6 | WARNING: parsed object: treating unexpected brace token as null | 6 | WARNING: parsed object: treating unexpected brace token as null |
| 7 | WARNING: parsed object: unexpected dictionary close token | 7 | WARNING: parsed object: unexpected dictionary close token |
| 8 | -WARNING: bad39.qdf (object 7 0, offset 1121): unexpected EOF | ||
| 9 | -WARNING: bad39.qdf (object 7 0, offset 1121): expected endobj | ||
| 10 | -WARNING: bad39.qdf (object 7 0, offset 1121): EOF after endobj | 8 | +WARNING: bad-parse.qdf (object 7 0, offset 1121): unexpected EOF |
| 9 | +WARNING: bad-parse.qdf (object 7 0, offset 1121): expected endobj | ||
| 10 | +WARNING: bad-parse.qdf (object 7 0, offset 1121): EOF after endobj | ||
| 11 | test 31 done | 11 | test 31 done |