From 24196c08cb2457ab3e7a6d0226e6c28ac55bb832 Mon Sep 17 00:00:00 2001 From: Jay Berkenbilt Date: Thu, 22 Oct 2020 05:11:58 -0400 Subject: [PATCH] Fix loop detection error (fuzz issue 23172) --- ChangeLog | 5 +++++ TODO | 1 - fuzz/qpdf_extra/23172.fuzz | Bin 0 -> 26638 bytes include/qpdf/QPDF.hh | 3 ++- libqpdf/QPDF_optimization.cc | 17 ++++++++++------- 5 files changed, 17 insertions(+), 9 deletions(-) create mode 100644 fuzz/qpdf_extra/23172.fuzz diff --git a/ChangeLog b/ChangeLog index 4a59b6a7..b217b8f3 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2020-10-22 Jay Berkenbilt + + * Fix loop detection problem when traversing page thumbnails + during optimization (fuzz issue 23172). + 2020-10-21 Jay Berkenbilt * Bug fix: properly handle copying foreign streams that have diff --git a/TODO b/TODO index 3f4e0976..a2854a2e 100644 --- a/TODO +++ b/TODO @@ -65,7 +65,6 @@ Fuzz Errors * https://bugs.chromium.org/p/oss-fuzz/issues/detail?id= * New: - * 23172: stack overflow (https://oss-fuzz.com/testcase-detail/5719543787028480) * 23599: integer overflow: https://oss-fuzz.com/testcase?key=6290807920525312 * 23642: leak: https://oss-fuzz.com/testcase-detail/4906569690251264 diff --git a/fuzz/qpdf_extra/23172.fuzz b/fuzz/qpdf_extra/23172.fuzz new file mode 100644 index 0000000000000000000000000000000000000000..802711cb9e336d4c496343a540bc183368ffc614 GIT binary patch literal 26638 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 PZf1Ogne@LVu1QV5 literal 0 HcmV?d00001 diff --git a/include/qpdf/QPDF.hh b/include/qpdf/QPDF.hh index 720c1bc8..2482b232 100644 --- a/include/qpdf/QPDF.hh +++ b/include/qpdf/QPDF.hh @@ -1341,7 +1341,8 @@ class QPDF std::set& visited); void updateObjectMaps(ObjUser const& ou, QPDFObjectHandle oh); void updateObjectMapsInternal(ObjUser const& ou, QPDFObjectHandle oh, - std::set& visited, bool top); + std::set& visited, bool top, + int depth); void filterCompressedObjects(std::map const& object_stream_data); // Type conversion helper methods diff --git a/libqpdf/QPDF_optimization.cc b/libqpdf/QPDF_optimization.cc index afa7ccbd..05cac415 100644 --- a/libqpdf/QPDF_optimization.cc +++ b/libqpdf/QPDF_optimization.cc @@ -354,12 +354,13 @@ void QPDF::updateObjectMaps(ObjUser const& ou, QPDFObjectHandle oh) { std::set visited; - updateObjectMapsInternal(ou, oh, visited, true); + updateObjectMapsInternal(ou, oh, visited, true, 0); } void QPDF::updateObjectMapsInternal(ObjUser const& ou, QPDFObjectHandle oh, - std::set& visited, bool top) + std::set& visited, bool top, + int depth) { // Traverse the object tree from this point taking care to avoid // crossing page boundaries. @@ -397,7 +398,8 @@ QPDF::updateObjectMapsInternal(ObjUser const& ou, QPDFObjectHandle oh, int n = oh.getArrayNItems(); for (int i = 0; i < n; ++i) { - updateObjectMapsInternal(ou, oh.getArrayItem(i), visited, false); + updateObjectMapsInternal( + ou, oh.getArrayItem(i), visited, false, 1 + depth); } } else if (oh.isDictionary() || oh.isStream()) @@ -417,8 +419,9 @@ QPDF::updateObjectMapsInternal(ObjUser const& ou, QPDFObjectHandle oh, { // Traverse page thumbnail dictionaries as a special // case. - updateObjectMaps(ObjUser(ObjUser::ou_thumb, ou.pageno), - dict.getKey(key)); + updateObjectMapsInternal( + ObjUser(ObjUser::ou_thumb, ou.pageno), + dict.getKey(key), visited, false, 1 + depth); } else if (is_page_node && (key == "/Parent")) { @@ -426,8 +429,8 @@ QPDF::updateObjectMapsInternal(ObjUser const& ou, QPDFObjectHandle oh, } else { - updateObjectMapsInternal(ou, dict.getKey(key), - visited, false); + updateObjectMapsInternal( + ou, dict.getKey(key), visited, false, 1 + depth); } } }