Commit 973edb4f2ddecafff11830c1b2f52bcb86d38d22
Committed by
GitHub
Merge pull request #1195 from m-holger/fuzz
Fix bug in QPDFWriter::preserveObjectStreams
Showing
7 changed files
with
37 additions
and
34 deletions
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 |