Commit 973edb4f2ddecafff11830c1b2f52bcb86d38d22

Authored by Jay Berkenbilt
Committed by GitHub
2 parents 6ae439f1 6f09069f

Merge pull request #1195 from m-holger/fuzz

Fix bug in QPDFWriter::preserveObjectStreams
fuzz/CMakeLists.txt
@@ -116,6 +116,8 @@ set(CORPUS_OTHER @@ -116,6 +116,8 @@ set(CORPUS_OTHER
116 65777.fuzz 116 65777.fuzz
117 68374.fuzz 117 68374.fuzz
118 68377.fuzz 118 68377.fuzz
  119 + 68668.fuzz
  120 + 68915.fuzz
119 ) 121 )
120 122
121 set(CORPUS_DIR ${CMAKE_CURRENT_BINARY_DIR}/qpdf_corpus) 123 set(CORPUS_DIR ${CMAKE_CURRENT_BINARY_DIR}/qpdf_corpus)
fuzz/qpdf_extra/68668.fuzz 0 → 100644
No preview for this file type
fuzz/qpdf_extra/68915.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' => 58], # increment when adding new files 24 + ['qpdf' => 60], # increment when adding new files
25 ); 25 );
26 26
27 my $n_tests = 0; 27 my $n_tests = 0;
libqpdf/QPDFWriter.cc
@@ -1944,28 +1944,30 @@ QPDFWriter::preserveObjectStreams() @@ -1944,28 +1944,30 @@ QPDFWriter::preserveObjectStreams()
1944 // that are not allowed to be in object streams. In addition to removing objects that were 1944 // that are not allowed to be in object streams. In addition to removing objects that were
1945 // erroneously included in object streams in the source PDF, it also prevents unreferenced 1945 // erroneously included in object streams in the source PDF, it also prevents unreferenced
1946 // objects from being included. 1946 // objects from being included.
1947 - auto iter = xref.cbegin();  
1948 auto end = xref.cend(); 1947 auto end = xref.cend();
1949 -  
1950 - // Start by scanning for first compressed object in case we don't have any object streams to  
1951 - // process.  
1952 - for (; iter != end; ++iter) {  
1953 - if (iter->second.getType() == 2) {  
1954 - // Pdf contains object streams.  
1955 - QTC::TC(  
1956 - "qpdf",  
1957 - "QPDFWriter preserve object streams",  
1958 - m->preserve_unreferenced_objects ? 0 : 1);  
1959 -  
1960 - if (m->preserve_unreferenced_objects) {  
1961 - for (; iter != end; ++iter) {  
1962 - if (iter->second.getType() == 2) {  
1963 - m->obj[iter->first].object_stream = iter->second.getObjStreamNumber();  
1964 - }  
1965 - }  
1966 - } else { 1948 + m->obj.streams_empty = true;
  1949 + if (m->preserve_unreferenced_objects) {
  1950 + for (auto iter = xref.cbegin(); iter != end; ++iter) {
  1951 + if (iter->second.getType() == 2) {
  1952 + // Pdf contains object streams.
  1953 + QTC::TC("qpdf", "QPDFWriter preserve object streams preserve unreferenced");
  1954 + m->obj.streams_empty = false;
  1955 + m->obj[iter->first].object_stream = iter->second.getObjStreamNumber();
  1956 + }
  1957 + }
  1958 + } else {
  1959 + // Start by scanning for first compressed object in case we don't have any object streams to
  1960 + // process.
  1961 + for (auto iter = xref.cbegin(); iter != end; ++iter) {
  1962 + if (iter->second.getType() == 2) {
  1963 + // Pdf contains object streams.
  1964 + QTC::TC("qpdf", "QPDFWriter preserve object streams");
  1965 + m->obj.streams_empty = false;
1967 auto eligible = QPDF::Writer::getCompressibleObjSet(m->pdf); 1966 auto eligible = QPDF::Writer::getCompressibleObjSet(m->pdf);
1968 - for (; iter != end; ++iter) { 1967 + // The object pointed to by iter may be a previous generation, in which case it is
  1968 + // removed by getCompressibleObjSet. We need to restart the loop (while the object
  1969 + // table may contain multiple generations of an object).
  1970 + for (iter = xref.cbegin(); iter != end; ++iter) {
1969 if (iter->second.getType() == 2) { 1971 if (iter->second.getType() == 2) {
1970 auto id = static_cast<size_t>(iter->first.getObj()); 1972 auto id = static_cast<size_t>(iter->first.getObj());
1971 if (id < eligible.size() && eligible[id]) { 1973 if (id < eligible.size() && eligible[id]) {
@@ -1975,12 +1977,10 @@ QPDFWriter::preserveObjectStreams() @@ -1975,12 +1977,10 @@ QPDFWriter::preserveObjectStreams()
1975 } 1977 }
1976 } 1978 }
1977 } 1979 }
  1980 + return;
1978 } 1981 }
1979 - return;  
1980 } 1982 }
1981 } 1983 }
1982 - // No compressed objects found.  
1983 - m->obj.streams_empty = true;  
1984 } 1984 }
1985 1985
1986 void 1986 void
libqpdf/QUtil.cc
@@ -1899,7 +1899,8 @@ call_main_from_wmain( @@ -1899,7 +1899,8 @@ call_main_from_wmain(
1899 // strings for compatibility with other systems. That way the rest of qpdf.cc can just act like 1899 // strings for compatibility with other systems. That way the rest of qpdf.cc can just act like
1900 // arguments are UTF-8. 1900 // arguments are UTF-8.
1901 1901
1902 - std::vector<std::unique_ptr<char[]>> utf8_argv; 1902 + std::vector<std::string> utf8_argv;
  1903 + utf8_argv.reserve(QIntC::to_size(argc));
1903 for (int i = 0; i < argc; ++i) { 1904 for (int i = 0; i < argc; ++i) {
1904 std::string utf16; 1905 std::string utf16;
1905 for (size_t j = 0; j < std::wcslen(argv[i]); ++j) { 1906 for (size_t j = 0; j < std::wcslen(argv[i]); ++j) {
@@ -1907,17 +1908,16 @@ call_main_from_wmain( @@ -1907,17 +1908,16 @@ call_main_from_wmain(
1907 utf16.append(1, static_cast<char>(QIntC::to_uchar(codepoint >> 8))); 1908 utf16.append(1, static_cast<char>(QIntC::to_uchar(codepoint >> 8)));
1908 utf16.append(1, static_cast<char>(QIntC::to_uchar(codepoint & 0xff))); 1909 utf16.append(1, static_cast<char>(QIntC::to_uchar(codepoint & 0xff)));
1909 } 1910 }
1910 - std::string utf8 = QUtil::utf16_to_utf8(utf16);  
1911 - utf8_argv.push_back(QUtil::make_unique_cstr(utf8)); 1911 + utf8_argv.emplace_back(QUtil::utf16_to_utf8(utf16));
1912 } 1912 }
1913 - auto utf8_argv_sp = std::make_unique<char*[]>(1 + utf8_argv.size());  
1914 - char** new_argv = utf8_argv_sp.get();  
1915 - for (size_t i = 0; i < utf8_argv.size(); ++i) {  
1916 - new_argv[i] = utf8_argv.at(i).get(); 1913 + std::vector<char*> new_argv;
  1914 + new_argv.reserve(utf8_argv.size() + 1U);
  1915 + for (auto const& arg: utf8_argv) {
  1916 + new_argv.emplace_back(const_cast<char*>(arg.data()));
1917 } 1917 }
1918 argc = QIntC::to_int(utf8_argv.size()); 1918 argc = QIntC::to_int(utf8_argv.size());
1919 - new_argv[argc] = nullptr;  
1920 - return realmain(argc, new_argv); 1919 + new_argv.emplace_back(nullptr);
  1920 + return realmain(argc, new_argv.data());
1921 } 1921 }
1922 1922
1923 int 1923 int
qpdf/qpdf.testcov
@@ -600,7 +600,8 @@ QPDFAcroFormDocumentHelper AP parse error 0 @@ -600,7 +600,8 @@ QPDFAcroFormDocumentHelper AP parse error 0
600 QPDFJob copy fields not this file 0 600 QPDFJob copy fields not this file 0
601 QPDFJob copy fields non-first from orig 0 601 QPDFJob copy fields non-first from orig 0
602 QPDF resolve duplicated page in insert 0 602 QPDF resolve duplicated page in insert 0
603 -QPDFWriter preserve object streams 1 603 +QPDFWriter preserve object streams 0
  604 +QPDFWriter preserve object streams preserve unreferenced 0
604 QPDFWriter exclude from object stream 0 605 QPDFWriter exclude from object stream 0
605 QPDF_pages findPage not found 0 606 QPDF_pages findPage not found 0
606 QPDFObjectHandle check ownership 0 607 QPDFObjectHandle check ownership 0