diff --git a/fuzz/build.mk b/fuzz/build.mk index ec3bd61e..d38fb909 100644 --- a/fuzz/build.mk +++ b/fuzz/build.mk @@ -41,6 +41,7 @@ CORPUS_FROM_TEST = \ outlines-with-old-root-dests.pdf \ page-labels-and-outlines.pdf \ page-labels-num-tree.pdf \ + fuzz-16214.pdf \ issue-99b.pdf \ issue-99.pdf \ issue-100.pdf \ diff --git a/fuzz/qtest/fuzz.test b/fuzz/qtest/fuzz.test index 26ae4f10..df850c45 100644 --- a/fuzz/qtest/fuzz.test +++ b/fuzz/qtest/fuzz.test @@ -9,7 +9,7 @@ require TestDriver; my $td = new TestDriver('fuzz'); -my $qpdf_n_test_files = 29; +my $qpdf_n_test_files = 30; my @extra = glob("../qpdf_extra/*.fuzz"); my $qpdf_n_extra_files = scalar(@extra); my $qpdf_n_orig_files = 2559; diff --git a/libqpdf/QPDFWriter.cc b/libqpdf/QPDFWriter.cc index 895f98ce..f5fa2bc9 100644 --- a/libqpdf/QPDFWriter.cc +++ b/libqpdf/QPDFWriter.cc @@ -2012,7 +2012,19 @@ QPDFWriter::writeObjectStream(QPDFObjectHandle object) // pass 1. indicateProgress(true, false); } - writeObject(this->m->pdf.getObjectByObjGen(obj), count); + QPDFObjectHandle obj_to_write = + this->m->pdf.getObjectByObjGen(obj); + if (obj_to_write.isStream()) + { + // This condition occurred in a fuzz input. Ideally we + // should block it at at parse time, but it's not + // clear to me how to construct a case for this. + QTC::TC("qpdf", "QPDFWriter stream in ostream"); + obj_to_write.warnIfPossible( + "stream found inside object stream; treating as null"); + obj_to_write = QPDFObjectHandle::newNull(); + } + writeObject(obj_to_write, count); this->m->xref[new_obj] = QPDFXRefEntry(2, new_id, count); } diff --git a/libqpdf/QPDF_Stream.cc b/libqpdf/QPDF_Stream.cc index 4f20c604..dd2796e8 100644 --- a/libqpdf/QPDF_Stream.cc +++ b/libqpdf/QPDF_Stream.cc @@ -105,7 +105,8 @@ QPDF_Stream::setStreamDescription() { setDescription( this->qpdf, - "stream object " + QUtil::int_to_string(this->objid) + " " + + this->qpdf->getFilename() + + ", stream object " + QUtil::int_to_string(this->objid) + " " + QUtil::int_to_string(this->generation)); } diff --git a/qpdf/qpdf.testcov b/qpdf/qpdf.testcov index c8ad0bdd..f327d464 100644 --- a/qpdf/qpdf.testcov +++ b/qpdf/qpdf.testcov @@ -444,3 +444,4 @@ QPDF xref skipped space 0 QPDF eof skipping spaces before xref 1 QPDF_encryption user matches owner V < 5 0 QPDF_encryption same password 1 +QPDFWriter stream in ostream 0 diff --git a/qpdf/qtest/qpdf.test b/qpdf/qtest/qpdf.test index 00f29c72..41c94519 100644 --- a/qpdf/qtest/qpdf.test +++ b/qpdf/qtest/qpdf.test @@ -643,6 +643,7 @@ my @bug_tests = ( ["263", "empty xref stream", 2], ["335a", "ozz-fuzz-12152", 2], ["335b", "ozz-fuzz-14845", 2], + ["fuzz-16214", "stream in object stream", 3], # When adding to this list, consider adding to SEED_CORPUS_FILES # in fuzz/build.mk and updating the count in fuzz/qtest/fuzz.test. ); @@ -669,9 +670,10 @@ foreach my $d (@bug_tests) } else { + my $base = (-f "issue-$n.pdf") ? "issue-$n" : "$n"; $td->runtest($description, - {$td->COMMAND => "qpdf issue-$n.pdf a.pdf"}, - {$td->FILE => "issue-$n.out", + {$td->COMMAND => "qpdf $base.pdf a.pdf"}, + {$td->FILE => "$base.out", $td->EXIT_STATUS => $exit_status}, $td->NORMALIZE_NEWLINES); } diff --git a/qpdf/qtest/qpdf/fuzz-16214.out b/qpdf/qtest/qpdf/fuzz-16214.out new file mode 100644 index 00000000..55862b1a --- /dev/null +++ b/qpdf/qtest/qpdf/fuzz-16214.out @@ -0,0 +1,22 @@ +WARNING: fuzz-16214.pdf: can't find PDF header +WARNING: fuzz-16214.pdf (xref stream: object 5 0, offset 124): stream dictionary lacks /Length key +WARNING: fuzz-16214.pdf (xref stream: object 5 0, offset 353): attempting to recover stream length +WARNING: fuzz-16214.pdf (xref stream: object 5 0, offset 353): recovered stream length: 722 +WARNING: fuzz-16214.pdf (xref stream, offset 116): Cross-reference stream data has the wrong size; expected = 92; actual = 196 +WARNING: fuzz-16214.pdf: reported number of objects (6) is not one plus the highest object number (35) +WARNING: fuzz-16214.pdf (object 14 0, offset 652): expected dictionary key but found non-name object; inserting key /QPDFFake1 +WARNING: fuzz-16214.pdf (object 14 0, offset 734): expected endobj +WARNING: fuzz-16214.pdf: file is damaged +WARNING: fuzz-16214.pdf (object 1 0, offset 7189): expected n n obj +WARNING: fuzz-16214.pdf: Attempting to reconstruct cross-reference table +WARNING: fuzz-16214.pdf (offset 7207): error decoding stream data for object 2 0: stream inflate: inflate: data: invalid code lengths set +WARNING: fuzz-16214.pdf (offset 7207): getStreamData called on unfilterable stream +WARNING: fuzz-16214.pdf (offset 7207): error decoding stream data for object 2 0: stream inflate: inflate: data: invalid code lengths set +WARNING: fuzz-16214.pdf (offset 7207): getStreamData called on unfilterable stream +WARNING: fuzz-16214.pdf (object 11 0, offset 11551): supposed object stream 5 has wrong type +WARNING: fuzz-16214.pdf (object 21 0, offset 3639): expected endstream +WARNING: fuzz-16214.pdf (object 21 0, offset 3112): attempting to recover stream length +WARNING: fuzz-16214.pdf (object 21 0, offset 3112): recovered stream length: 340 +WARNING: fuzz-16214.pdf, stream object 8 0: stream found inside object stream; treating as null +WARNING: fuzz-16214.pdf, stream object 8 0: stream found inside object stream; treating as null +qpdf: operation succeeded with warnings; resulting file may have some problems diff --git a/qpdf/qtest/qpdf/fuzz-16214.pdf b/qpdf/qtest/qpdf/fuzz-16214.pdf new file mode 100644 index 00000000..c84a8eba Binary files /dev/null and b/qpdf/qtest/qpdf/fuzz-16214.pdf differ