From: Stuart Henderson Subject: Re: devel/boost: backport a regression fix to 1.91.0 To: Sergii Rudchenko , Cc: , Brad Smith Date: Mon, 14 Sep 2026 09:44:37 +0100 please use "make update-patches" to generate the patch files; from what you have now, "make clean; make patch; rm patches/patch-libs_url_base_edit_params; make update-patches". -- Sent from a phone, apologies for poor formatting. On 13 September 2026 20:34:36 Sergii Rudchenko wrote: > The current version of the Boost C++ library in OpenBSD ports (1.91.0) > has an > annoying regression in Boost.URL: https://github.com/boostorg/url/issues/989 > > The issue is fixed upstream and released in in 1.92.0: > https://github.com/boostorg/url/commit/41b467a389a68321608138a432b15024b6c11918 > > After talking to Marc Espie at EuroBSDCon I understood that that > updating boost > to 1.92.0 is at this point in the release cycle is risky and will most > likely > be postponed until 8.0 is out. He suggested to try backporting the fix > for the > time being. > > Below is the patch taken as-is from the upstream and it luckily applies > cleanly > on top of 1.91.0. I have verified the fix on the resulting build from > the port. > > Can we please apply this stopgap measure until we move to 1.92.0? > > Index: devel/boost/Makefile > =================================================================== > RCS file: /cvs/ports/devel/boost/Makefile,v > diff -u -p -u -r1.163 Makefile > --- devel/boost/Makefile 18 May 2026 05:48:17 -0000 1.163 > +++ devel/boost/Makefile 13 Sep 2026 18:07:29 -0000 > @@ -6,6 +6,7 @@ COMMENT-main= free peer-reviewed portabl > COMMENT-md= machine-dependent libraries for boost > > VERSION= 1.91.0 > +REVISION= 0 > DISTNAME= boost_${VERSION:S/./_/g} > PKGNAME= boost-${VERSION} > EPOCH= 0 > Index: devel/boost/patches/patch-libs_url_base_edit_params > =================================================================== > RCS file: devel/boost/patches/patch-libs_url_base_edit_params > diff -N devel/boost/patches/patch-libs_url_base_edit_params > --- /dev/null 1 Jan 1970 00:00:00 -0000 > +++ devel/boost/patches/patch-libs_url_base_edit_params 13 Sep 2026 > 18:07:29 -0000 > @@ -0,0 +1,140 @@ > +Backport the fix for the URL corruption bug on url.params().append() in > Boost 1.91.0 > + > +Fixed upstream in 1.92.0: 41b467a389a68321608138a432b15024b6c11918 > +Index: boost/include/boost/url/impl/url_base.hpp > +--- boost/url/impl/url_base.hpp.orig > ++++ boost/url/impl/url_base.hpp > +@@ -3159,7 +3159,12 @@ edit_params( > + BOOST_ASSERT(pos1 <= impl_.offset(id_frag)); > + > + // calc decoded size of old range, > +- // minus one if '?' or '&' prefixed > ++ // minus one for the leading '?' which is > ++ // not counted in decoded_[id_query]. > ++ // dn0 may be -1 here when the old range is > ++ // empty and the query was non-empty; the > ++ // matching subtraction on dn below cancels > ++ // that out when the delta is taken. > + auto dn0 = > + static_cast( > + detail::decode_bytes_unsafe( > +@@ -3168,8 +3173,6 @@ edit_params( > + pos1 - pos0))); > + if(impl_.len(id_query) > 0) > + dn0 -= 1; > +- if(dn0 < 0) > +- dn0 = 0; > + > + //------------------------------------------------ > + // > +@@ -3267,16 +3270,13 @@ edit_params( > + } > + } > + > +- // calc decoded size of new range, > +- // minus one if '?' or '&' prefixed > ++ // calc decoded size of new range; see dn0. > + auto dn = > + static_cast( > + detail::decode_bytes_unsafe( > + core::string_view(dest0, dest - dest0))); > + if(impl_.len(id_query) > 0) > + dn -= 1; > +- if(dn < 0) > +- dn = 0; > + > + if(dn >= dn0) > + impl_.decoded_[id_query] += > +Index: libs/url/test/unit/params_encoded_ref.cpp > +--- libs/url/test/unit/params_encoded_ref.cpp.orig > ++++ libs/url/test/unit/params_encoded_ref.cpp > +@@ -121,6 +121,11 @@ struct params_encoded_ref_test > + params_encoded_ref ps(u.encoded_params()); > + f(ps); > + BOOST_TEST_EQ(u.encoded_query(), s1); > ++ BOOST_TEST_EQ( > ++ u.encoded_query().decoded_size(), > ++ pct_string_view(s1).decoded_size()); > ++ BOOST_TEST_NO_THROW(u.encoded_target()); > ++ BOOST_TEST_NO_THROW(u.encoded_resource()); > + if(! BOOST_TEST_EQ( > + ps.size(), init.size())) > + return; > +Index: libs/url/test/unit/params_ref.cpp > +--- libs/url/test/unit/params_ref.cpp.orig > ++++ libs/url/test/unit/params_ref.cpp > +@@ -142,6 +142,11 @@ struct params_ref_test > + params_ref ps(u.params()); > + f(ps); > + BOOST_TEST_EQ(u.encoded_query(), s1); > ++ BOOST_TEST_EQ( > ++ u.encoded_query().decoded_size(), > ++ pct_string_view(s1).decoded_size()); > ++ BOOST_TEST_NO_THROW(u.encoded_target()); > ++ BOOST_TEST_NO_THROW(u.encoded_resource()); > + if(! BOOST_TEST_EQ( > + ps.size(), init.size())) > + return; > +@@ -814,6 +819,62 @@ struct params_ref_test > + check(f, "?k0&k1=&k2=key", "k0&k1=" BIGSTR "&k2=key", > + { {"k0",no_value}, {"k1",BIGSTR}, {"k2","key"} }); > + } > ++ > ++ // issue #989: encoded_target() asserts after > ++ // successive params().set() on a fresh url. > ++ // Regression for broken decoded_[id_query] > ++ // bookkeeping in url_base::edit_params. > ++ { > ++ url u; > ++ u.params().set("a", "b"); > ++ BOOST_TEST_EQ(u.encoded_query(), "a=b"); > ++ BOOST_TEST_EQ(u.encoded_query().decoded_size(), 3u); > ++ BOOST_TEST_EQ(u.encoded_target(), "?a=b"); > ++ > ++ u.params().set("c", "d"); > ++ BOOST_TEST_EQ(u.encoded_query(), "a=b&c=d"); > ++ BOOST_TEST_EQ(u.encoded_query().decoded_size(), 7u); > ++ BOOST_TEST_EQ(u.encoded_target(), "?a=b&c=d"); > ++ } > ++ > ++ // Related: erase first param must leave a > ++ // consistent decoded_[id_query]. > ++ { > ++ url u("?a=b&c=d"); > ++ BOOST_TEST_EQ(u.encoded_query().decoded_size(), 7u); > ++ u.params().erase(u.params().begin()); > ++ BOOST_TEST_EQ(u.encoded_query(), "c=d"); > ++ BOOST_TEST_EQ(u.encoded_query().decoded_size(), 3u); > ++ BOOST_TEST_EQ(u.encoded_target(), "?c=d"); > ++ } > ++ > ++ // Related: erase last param must leave a > ++ // consistent decoded_[id_query]. > ++ { > ++ url u("?a=b&c=d"); > ++ u.params().erase(std::next(u.params().begin())); > ++ BOOST_TEST_EQ(u.encoded_query(), "a=b"); > ++ BOOST_TEST_EQ(u.encoded_query().decoded_size(), 3u); > ++ BOOST_TEST_EQ(u.encoded_target(), "?a=b"); > ++ } > ++ > ++ // Related: insert at front of non-empty query. > ++ { > ++ url u("?x=y"); > ++ u.params().insert(u.params().begin(), {"a", "b"}); > ++ BOOST_TEST_EQ(u.encoded_query(), "a=b&x=y"); > ++ BOOST_TEST_EQ(u.encoded_query().decoded_size(), 7u); > ++ BOOST_TEST_EQ(u.encoded_target(), "?a=b&x=y"); > ++ } > ++ > ++ // Related: append to non-empty query. > ++ { > ++ url u("?x=y"); > ++ u.params().append({"a", "b"}); > ++ BOOST_TEST_EQ(u.encoded_query(), "x=y&a=b"); > ++ BOOST_TEST_EQ(u.encoded_query().decoded_size(), 7u); > ++ BOOST_TEST_EQ(u.encoded_target(), "?x=y&a=b"); > ++ } > + } > + > + static