From: Francis Dupont Date: Mon, 8 Jun 2026 19:50:36 +0000 (+0200) Subject: [#4565] Checkpoint: built recursion chain X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=fc6555317cd7a3dc8f35192b34c19bb72e43cdb1;p=thirdparty%2Fkea.git [#4565] Checkpoint: built recursion chain --- diff --git a/src/lib/dhcp/libdhcp++.cc b/src/lib/dhcp/libdhcp++.cc index 0789186e5f..ea3fa7b72c 100644 --- a/src/lib/dhcp/libdhcp++.cc +++ b/src/lib/dhcp/libdhcp++.cc @@ -313,11 +313,19 @@ LibDHCP::optionFactory(Option::Universe u, return (it->second(u, type, buf)); } +size_t +LibDHCP::MAX_RECUSION_LEVEL = 10; + size_t LibDHCP::unpackOptions6(const OptionBuffer& buf, const string& option_space, OptionCollection& options, size_t* relay_msg_offset /* = 0 */, - size_t* relay_msg_len /* = 0 */) { + size_t* relay_msg_len /* = 0 */, + size_t rec_level /* = 0 */) { + ++rec_level; + if (rec_level >= MAX_RECUSION_LEVEL) { + isc_throw(isc::Unexpected, "Too deep recursion in unpacking options"); + } size_t offset = 0; size_t length = buf.size(); size_t last_offset = 0; @@ -443,7 +451,8 @@ LibDHCP::unpackOptions6(const OptionBuffer& buf, const string& option_space, isc_throw_assert(def); opt = def->optionFactory(Option::V6, opt_type, buf.begin() + offset, - buf.begin() + offset + opt_len); + buf.begin() + offset + opt_len, + rec_level); } catch (const SkipThisOptionError&) { opt.reset(); } catch (const SkipRemainingOptionsError&) { diff --git a/src/lib/dhcp/libdhcp++.h b/src/lib/dhcp/libdhcp++.h index 32ebcec121..cb61f8c488 100644 --- a/src/lib/dhcp/libdhcp++.h +++ b/src/lib/dhcp/libdhcp++.h @@ -285,6 +285,7 @@ public: /// offset to beginning of relay_msg option will be stored in it. /// @param relay_msg_len reference to a size_t structure. If specified, /// length of the relay_msg option will be stored in it. + /// @param rec_level recursion level. /// @return offset to the first byte after the last successfully /// parsed option /// @@ -296,7 +297,8 @@ public: const std::string& option_space, isc::dhcp::OptionCollection& options, size_t* relay_msg_offset = 0, - size_t* relay_msg_len = 0); + size_t* relay_msg_len = 0, + size_t rec_level = 0); /// @brief Extend vendor options from fused options in multiple OptionVendor /// or OptionVendorClass options and add respective suboptions or values. @@ -467,6 +469,13 @@ public: /// @brief Get definition of D6O_IAADDR option. static const OptionDefinition& D6O_IAADDR_DEF(); + /// @brief Maximum level of recursion unpacking options. + /// + /// @note: avoid to blow the stack when unpacking recursive + /// embedded v6 space options from a thread (so not using + /// system stack) on a TCP (so possibly large) received packet. + static size_t MAX_RECUSION_LEVEL; + private: /// Initialize DHCP option definitions. diff --git a/src/lib/dhcp/option.cc b/src/lib/dhcp/option.cc index 1b6fb639b3..1897d77b62 100644 --- a/src/lib/dhcp/option.cc +++ b/src/lib/dhcp/option.cc @@ -152,7 +152,7 @@ void Option::unpack(OptionBufferConstIter begin, } void -Option::unpackOptions(const OptionBuffer& buf) { +Option::unpackOptions(const OptionBuffer& buf, size_t rec_level /* = 0 */) { list deferred; switch (universe_) { case V4: @@ -161,7 +161,8 @@ Option::unpackOptions(const OptionBuffer& buf) { getType() == DHO_VENDOR_ENCAPSULATED_OPTIONS); return; case V6: - LibDHCP::unpackOptions6(buf, getEncapsulatedSpace(), options_); + LibDHCP::unpackOptions6(buf, getEncapsulatedSpace(), options_, + 0, 0, rec_level); return; default: isc_throw(isc::BadValue, "Invalid universe type " << universe_); diff --git a/src/lib/dhcp/option.h b/src/lib/dhcp/option.h index 68b422556f..5f491062c4 100644 --- a/src/lib/dhcp/option.h +++ b/src/lib/dhcp/option.h @@ -549,12 +549,13 @@ protected: /// different exceptions when option assembly fails. /// /// @param buf buffer to be parsed. + /// @param rec_level recursion level. /// /// @todo The set of exceptions thrown by this function depend on /// exceptions thrown by unpack methods invoked on objects /// representing sub options. We should consider whether to aggregate /// those into one exception which can be documented here. - void unpackOptions(const OptionBuffer& buf); + void unpackOptions(const OptionBuffer& buf, size_t rec_level = 0); /// @brief Returns option header in the textual format. /// diff --git a/src/lib/dhcp/option_custom.cc b/src/lib/dhcp/option_custom.cc index 29ad3cbcd6..2aaf0880f6 100644 --- a/src/lib/dhcp/option_custom.cc +++ b/src/lib/dhcp/option_custom.cc @@ -45,6 +45,17 @@ OptionCustom::OptionCustom(const OptionDefinition& def, createBuffers(getData()); } +OptionCustom::OptionCustom(const OptionDefinition& def, + Universe u, + OptionBufferConstIter first, + OptionBufferConstIter last, + size_t rec_level) + : Option(u, def.getCode(), first, last), + definition_(def) { + setEncapsulatedSpace(def.getEncapsulatedSpace()); + createBuffers(getData(), rec_level); +} + OptionPtr OptionCustom::clone() const { return (cloneInternal()); @@ -285,7 +296,7 @@ OptionCustom::bufferLength(const OptionDataType data_type, bool in_array, } void -OptionCustom::createBuffers(const OptionBuffer& data_buf) { +OptionCustom::createBuffers(const OptionBuffer& data_buf, size_t rec_level) { // Check that the option definition is correct as we are going // to use it to split the data_ buffer into set of sub buffers. definition_.validate(); @@ -335,7 +346,7 @@ OptionCustom::createBuffers(const OptionBuffer& data_buf) { // Unpack suboptions if any. else if (data != data_buf.end() && !getEncapsulatedSpace().empty()) { - unpackOptions(OptionBuffer(data, data_buf.end())); + unpackOptions(OptionBuffer(data, data_buf.end()), rec_level); } } else if (data_type != OPT_EMPTY_TYPE) { @@ -390,13 +401,13 @@ OptionCustom::createBuffers(const OptionBuffer& data_buf) { // Unpack suboptions if any. if (data != data_buf.end() && !getEncapsulatedSpace().empty()) { - unpackOptions(OptionBuffer(data, data_buf.end())); + unpackOptions(OptionBuffer(data, data_buf.end()), rec_level); } } } else { // Unpack suboptions if any. if (data != data_buf.end() && !getEncapsulatedSpace().empty()) { - unpackOptions(OptionBuffer(data, data_buf.end())); + unpackOptions(OptionBuffer(data, data_buf.end()), rec_level); } } // If everything went ok we can replace old buffer set with new ones. diff --git a/src/lib/dhcp/option_custom.h b/src/lib/dhcp/option_custom.h index e5f1167b74..61c143befc 100644 --- a/src/lib/dhcp/option_custom.h +++ b/src/lib/dhcp/option_custom.h @@ -80,6 +80,22 @@ public: OptionCustom(const OptionDefinition& def, Universe u, OptionBufferConstIter first, OptionBufferConstIter last); + /// @brief Constructor, used for received options with limited recursion. + /// + /// @param def option definition. + /// @param u specifies universe (V4 or V6). + /// @param first iterator to the first element that should be copied. + /// @param last iterator to the next element after the last one + /// to be copied. + /// @param rec_level recursion level. + /// + /// @throw OutOfRange if option buffer is truncated. + /// + /// @todo list all exceptions thrown by ctor. + OptionCustom(const OptionDefinition& def, Universe u, + OptionBufferConstIter first, OptionBufferConstIter last, + size_t rec_level); + /// @brief Copies this option and returns a pointer to the copy. virtual OptionPtr clone() const; @@ -436,7 +452,8 @@ private: /// @brief Create collection of buffers representing data field values. /// /// @param data_buf a buffer to be parsed. - void createBuffers(const OptionBuffer& data_buf); + /// @param rec_level recursion level. + void createBuffers(const OptionBuffer& data_buf, size_t rec_level = 0); /// @brief Return a text representation of a data field. /// diff --git a/src/lib/dhcp/option_definition.cc b/src/lib/dhcp/option_definition.cc index d3665e2b7b..29d47af5fa 100644 --- a/src/lib/dhcp/option_definition.cc +++ b/src/lib/dhcp/option_definition.cc @@ -187,7 +187,8 @@ OptionDefinition::optionFactory(Option::Universe u, uint16_t type, OptionBufferConstIter begin, OptionBufferConstIter end, - bool convenient_notation) const { + bool convenient_notation, + size_t rec_level) const { try { // Some of the options are represented by the specialized classes derived @@ -206,7 +207,8 @@ OptionDefinition::optionFactory(Option::Universe u, if (getEncapsulatedSpace().empty()) { return (factoryEmpty(u, type)); } else { - return (OptionPtr(new OptionCustom(*this, u, begin, end))); + return (OptionPtr(new OptionCustom(*this, u, begin, end, + rec_level))); } case OPT_BINARY_TYPE: @@ -285,7 +287,7 @@ OptionDefinition::optionFactory(Option::Universe u, // Do nothing. We will return generic option a few lines down. ; } - return (OptionPtr(new OptionCustom(*this, u, begin, end))); + return (OptionPtr(new OptionCustom(*this, u, begin, end, rec_level))); } catch (const SkipThisOptionError&) { // We need to throw this one as is. throw; diff --git a/src/lib/dhcp/option_definition.h b/src/lib/dhcp/option_definition.h index 558bd6af6b..3bbede8dee 100644 --- a/src/lib/dhcp/option_definition.h +++ b/src/lib/dhcp/option_definition.h @@ -437,6 +437,7 @@ public: /// as a string formatted in user-friendly, convenient way. /// The flag is propagated to the option constructor, so that /// the data could be parsed properly. Defaults to false. + /// @param rec_level recursion level. /// /// @return instance of the DHCP option. /// @throw InvalidOptionValue if data for the option is invalid. @@ -444,7 +445,8 @@ public: uint16_t type, OptionBufferConstIter begin, OptionBufferConstIter end, - bool convenient_notation = false) const; + bool convenient_notation = false, + size_t rec_level = 0) const; /// @brief Option factory. ///