From 3f2864c35c678ced27afbb2b89dbda1fcf5f4008 Mon Sep 17 00:00:00 2001 From: Ramya Eliger Date: Thu, 27 Aug 2026 15:01:44 +0530 Subject: [PATCH] buffer initializer_list before reallocation in array::insert The initializer_list overload constructed revert_insert before reading the list. On the growth path revert_insert frees the old table, so value_refs pointing into the array itself were read after the storage was freed: a heap use-after-free, e.g. a.insert(a.begin(), {a[0], a[1]}) when size() == capacity(). Materialise the list into a temporary array before relocating, matching the input-iterator insert path. --- include/boost/json/impl/array.ipp | 17 +++++++++++--- test/array.cpp | 39 ++++++++++++++++++++++--------- 2 files changed, 42 insertions(+), 14 deletions(-) diff --git a/include/boost/json/impl/array.ipp b/include/boost/json/impl/array.ipp index 259f422b4..bc4641d47 100644 --- a/include/boost/json/impl/array.ipp +++ b/include/boost/json/impl/array.ipp @@ -516,10 +516,21 @@ insert( value_ref> init) -> iterator { + BOOST_ASSERT( + pos >= begin() && pos <= end()); + if(init.size() == 0) + return data() + (pos - data()); + // the value_refs in init may point into this + // array, whose storage revert_insert can + // relocate and free, so buffer them first + array temp(init, sp_); revert_insert r( - pos, init.size(), *this); - value_ref::write_array( - r.p, init, sp_); + pos, temp.size(), *this); + relocate( + r.p, + temp.data(), + temp.size()); + temp.t_->size = 0; return r.commit(); } diff --git a/test/array.cpp b/test/array.cpp index cc4bea6fe..d6cd732e3 100644 --- a/test/array.cpp +++ b/test/array.cpp @@ -1035,18 +1035,35 @@ class array_test } // insert(const_iterator, init_list) - fail_loop([&](storage_ptr const& sp) { - array a({0, 3, 4}, sp); - auto it = a.insert( - a.begin() + 1, {1, str_}); - BOOST_TEST(it == a.begin() + 1); - BOOST_TEST(a[0].as_int64() == 0); - BOOST_TEST(a[1].as_int64() == 1); - BOOST_TEST(a[2].as_string() == str_); - BOOST_TEST(a[3].as_int64() == 3); - BOOST_TEST(a[4].as_int64() == 4); - }); + fail_loop([&](storage_ptr const& sp) + { + array a({0, 3, 4}, sp); + auto it = a.insert( + a.begin() + 1, {1, str_}); + BOOST_TEST(it == a.begin() + 1); + BOOST_TEST(a[0].as_int64() == 0); + BOOST_TEST(a[1].as_int64() == 1); + BOOST_TEST(a[2].as_string() == str_); + BOOST_TEST(a[3].as_int64() == 3); + BOOST_TEST(a[4].as_int64() == 4); + }); + + // elements of init alias *this and + // insertion reallocates + fail_loop([&](storage_ptr const& sp) + { + array a({1, str_}, sp); + BOOST_TEST(a.capacity() == a.size()); + a.insert(a.begin(), {a[0], a[1]}); + BOOST_TEST(a.size() == 4); + BOOST_TEST(a[0].as_int64() == 1); + BOOST_TEST(a[1].as_string() == str_); + BOOST_TEST(a[2].as_int64() == 1); + BOOST_TEST(a[3].as_string() == str_); + check_storage(a, sp); + }); + } // emplace(const_iterator, arg) fail_loop([&](storage_ptr const& sp)