From 4490c23f64ede6e5e2268b7e5d86e7f69600ff3e Mon Sep 17 00:00:00 2001 From: m-holger Date: Wed, 27 Aug 2025 17:36:00 +0100 Subject: [PATCH] Add `value_validator` to `NNTree` implementations for enhanced validation. --- include/qpdf/QPDFNameTreeObjectHelper.hh | 13 ++++++++++++- include/qpdf/QPDFNumberTreeObjectHelper.hh | 13 ++++++++++++- libqpdf/NNTree.cc | 40 ++++++++++++++++++++++++++++++++-------- libqpdf/QPDFNameTreeObjectHelper.cc | 21 ++++++++++++++++++--- libqpdf/QPDFNumberTreeObjectHelper.cc | 21 ++++++++++++++++++--- libqpdf/qpdf/NNTree.hh | 8 +++++++- 6 files changed, 99 insertions(+), 17 deletions(-) diff --git a/include/qpdf/QPDFNameTreeObjectHelper.hh b/include/qpdf/QPDFNameTreeObjectHelper.hh index 9405942..12950d0 100644 --- a/include/qpdf/QPDFNameTreeObjectHelper.hh +++ b/include/qpdf/QPDFNameTreeObjectHelper.hh @@ -45,6 +45,13 @@ class QPDF_DLL_CLASS QPDFNameTreeObjectHelper: public QPDFObjectHelper QPDF_DLL QPDFNameTreeObjectHelper(QPDFObjectHandle, QPDF&, bool auto_repair = true); + QPDF_DLL + QPDFNameTreeObjectHelper( + QPDFObjectHandle, + QPDF&, + std::function value_validator, + bool auto_repair = true); + // Create an empty name tree QPDF_DLL static QPDFNameTreeObjectHelper newEmpty(QPDF&, bool auto_repair = true); @@ -171,7 +178,11 @@ class QPDF_DLL_CLASS QPDFNameTreeObjectHelper: public QPDFObjectHelper ~Members() = default; private: - Members(QPDFObjectHandle& oh, QPDF&, bool auto_repair); + Members( + QPDFObjectHandle& oh, + QPDF&, + std::function value_validator, + bool auto_repair); Members(Members const&) = delete; std::shared_ptr impl; diff --git a/include/qpdf/QPDFNumberTreeObjectHelper.hh b/include/qpdf/QPDFNumberTreeObjectHelper.hh index 3cc7a89..ec06fd3 100644 --- a/include/qpdf/QPDFNumberTreeObjectHelper.hh +++ b/include/qpdf/QPDFNumberTreeObjectHelper.hh @@ -44,6 +44,13 @@ class QPDF_DLL_CLASS QPDFNumberTreeObjectHelper: public QPDFObjectHelper QPDFNumberTreeObjectHelper(QPDFObjectHandle, QPDF&, bool auto_repair = true); QPDF_DLL + QPDFNumberTreeObjectHelper( + QPDFObjectHandle, + QPDF&, + std::function value_validator, + bool auto_repair = true); + + QPDF_DLL ~QPDFNumberTreeObjectHelper() override; // Create an empty number tree @@ -188,7 +195,11 @@ class QPDF_DLL_CLASS QPDFNumberTreeObjectHelper: public QPDFObjectHelper ~Members() = default; private: - Members(QPDFObjectHandle& oh, QPDF&, bool auto_repair); + Members( + QPDFObjectHandle& oh, + QPDF&, + std::function value_validator, + bool auto_repair); Members(Members const&) = delete; std::shared_ptr impl; diff --git a/libqpdf/NNTree.cc b/libqpdf/NNTree.cc index 751bd72..f22dbc2 100644 --- a/libqpdf/NNTree.cc +++ b/libqpdf/NNTree.cc @@ -141,8 +141,8 @@ NNTreeIterator::increment(bool backward) impl.warn(node, "items array doesn't have enough elements"); } else if (!impl.details.keyValid(items[item_number])) { impl.warn(node, ("item " + std::to_string(item_number) + " has the wrong type")); - } else if (!items[item_number + 1]) { - impl.warn(node, "item " + std::to_string(item_number) + " is null"); + } else if (!impl.value_valid(items[item_number + 1])) { + impl.warn(node, "item " + std::to_string(item_number + 1) + " is invalid"); } else { return; } @@ -386,6 +386,9 @@ NNTreeIterator::insertAfter(QPDFObjectHandle const& key, QPDFObjectHandle const& if (!(key && value)) { impl.error(node, "insert: key or value is null"); } + if (!impl.value_valid(value)) { + impl.error(node, "insert: value is invalid"); + } items.insert(item_number + 2, key); items.insert(item_number + 3, value); resetLimits(node, lastPathElement()); @@ -621,10 +624,15 @@ NNTreeIterator::deepen(QPDFObjectHandle a_node, bool first, bool allow_empty) } NNTreeImpl::NNTreeImpl( - NNTreeDetails const& details, QPDF& qpdf, QPDFObjectHandle& oh, bool auto_repair) : + NNTreeDetails const& details, + QPDF& qpdf, + QPDFObjectHandle& oh, + std::function value_validator, + bool auto_repair) : details(details), qpdf(qpdf), oh(oh), + value_valid(value_validator), auto_repair(auto_repair) { } @@ -740,7 +748,7 @@ NNTreeImpl::repair() { auto new_node = QPDFObjectHandle::newDictionary(); new_node.replaceKey(details.itemsKey(), Array()); - NNTreeImpl repl(details, qpdf, new_node, false); + NNTreeImpl repl(details, qpdf, new_node, value_valid, false); for (auto const& [key, value]: *this) { if (key && value) { repl.insert(key, value); @@ -774,10 +782,17 @@ NNTreeImpl::findInternal(QPDFObjectHandle const& key, bool return_prev_if_not_fo if (first_item == end()) { return end(); } - if (first_item.valid() && details.keyValid(first_item->first) && - details.compareKeys(key, first_item->first) < 0) { - // Before the first key - return end(); + if (first_item.valid()) { + if (!details.keyValid(first_item->first)) { + error(oh, "encountered invalid key in find"); + } + if (!value_valid(first_item->second)) { + error(oh, "encountered invalid value in find"); + } + if (details.compareKeys(key, first_item->first) < 0) { + // Before the first key + return end(); + } } qpdf_assert_debug(!last_item.valid()); @@ -797,6 +812,12 @@ NNTreeImpl::findInternal(QPDFObjectHandle const& key, bool return_prev_if_not_fo key, items, nitems / 2, return_prev_if_not_found, &NNTreeImpl::compareKeyItem); if (idx >= 0) { result.setItemNumber(node, 2 * idx); + if (!result.impl.details.keyValid(result.ivalue.first)) { + error(node, "encountered invalid key in find"); + } + if (!result.impl.value_valid(result.ivalue.second)) { + error(oh, "encountered invalid value in find"); + } } return result; } @@ -830,6 +851,9 @@ NNTreeImpl::insertFirst(QPDFObjectHandle const& key, QPDFObjectHandle const& val if (!(key && value)) { error(oh, "unable to insert null key or value"); } + if (!value_valid(value)) { + error(oh, "attempting to insert an invalid value"); + } items.insert(0, key); items.insert(1, value); iter.setItemNumber(iter.node, 0); diff --git a/libqpdf/QPDFNameTreeObjectHelper.cc b/libqpdf/QPDFNameTreeObjectHelper.cc index 16c0822..b6344ec 100644 --- a/libqpdf/QPDFNameTreeObjectHelper.cc +++ b/libqpdf/QPDFNameTreeObjectHelper.cc @@ -40,14 +40,29 @@ QPDFNameTreeObjectHelper::~QPDFNameTreeObjectHelper() // NOLINT (modernize-use-e // class, see github issue #745. } -QPDFNameTreeObjectHelper::Members::Members(QPDFObjectHandle& oh, QPDF& q, bool auto_repair) : - impl(std::make_shared(name_tree_details, q, oh, auto_repair)) +QPDFNameTreeObjectHelper::Members::Members( + QPDFObjectHandle& oh, + QPDF& q, + std::function value_validator, + bool auto_repair) : + impl(std::make_shared(name_tree_details, q, oh, value_validator, auto_repair)) { } QPDFNameTreeObjectHelper::QPDFNameTreeObjectHelper(QPDFObjectHandle oh, QPDF& q, bool auto_repair) : QPDFObjectHelper(oh), - m(new Members(oh, q, auto_repair)) + m(new Members( + oh, q, [](QPDFObjectHandle const& o) -> bool { return static_cast(o); }, auto_repair)) +{ +} + +QPDFNameTreeObjectHelper::QPDFNameTreeObjectHelper( + QPDFObjectHandle oh, + QPDF& q, + std::function value_validator, + bool auto_repair) : + QPDFObjectHelper(oh), + m(new Members(oh, q, value_validator, auto_repair)) { } diff --git a/libqpdf/QPDFNumberTreeObjectHelper.cc b/libqpdf/QPDFNumberTreeObjectHelper.cc index adf3ddd..297c67d 100644 --- a/libqpdf/QPDFNumberTreeObjectHelper.cc +++ b/libqpdf/QPDFNumberTreeObjectHelper.cc @@ -41,15 +41,30 @@ QPDFNumberTreeObjectHelper::~QPDFNumberTreeObjectHelper() // NOLINT (modernize-u // class, see github issue #745. } -QPDFNumberTreeObjectHelper::Members::Members(QPDFObjectHandle& oh, QPDF& q, bool auto_repair) : - impl(std::make_shared(number_tree_details, q, oh, auto_repair)) +QPDFNumberTreeObjectHelper::Members::Members( + QPDFObjectHandle& oh, + QPDF& q, + std::function value_validator, + bool auto_repair) : + impl(std::make_shared(number_tree_details, q, oh, value_validator, auto_repair)) { } QPDFNumberTreeObjectHelper::QPDFNumberTreeObjectHelper( QPDFObjectHandle oh, QPDF& q, bool auto_repair) : QPDFObjectHelper(oh), - m(new Members(oh, q, auto_repair)) + m(new Members( + oh, q, [](QPDFObjectHandle const& o) -> bool { return static_cast(o); }, auto_repair)) +{ +} + +QPDFNumberTreeObjectHelper::QPDFNumberTreeObjectHelper( + QPDFObjectHandle oh, + QPDF& q, + std::function value_validator, + bool auto_repair) : + QPDFObjectHelper(oh), + m(new Members(oh, q, value_validator, auto_repair)) { } diff --git a/libqpdf/qpdf/NNTree.hh b/libqpdf/qpdf/NNTree.hh index 96d954e..ac266db 100644 --- a/libqpdf/qpdf/NNTree.hh +++ b/libqpdf/qpdf/NNTree.hh @@ -95,7 +95,12 @@ class NNTreeImpl public: typedef NNTreeIterator iterator; - NNTreeImpl(NNTreeDetails const&, QPDF&, QPDFObjectHandle&, bool auto_repair = true); + NNTreeImpl( + NNTreeDetails const&, + QPDF&, + QPDFObjectHandle&, + std::function value_validator, + bool auto_repair = true); iterator begin(); iterator end(); iterator last(); @@ -127,6 +132,7 @@ class NNTreeImpl QPDF& qpdf; int split_threshold{32}; QPDFObjectHandle oh; + const std::function value_valid; bool auto_repair{true}; size_t error_count{0}; }; -- libgit2 0.21.4