mirror of
https://github.com/nlohmann/json.git
synced 2026-09-29 11:10:30 +00:00
Create a value before giving it its type
Several functions set the type of a value before creating the string, array, object, or binary value it stands for. When that creation threw - std::bad_alloc from the allocator, say - the value was left behind with the new type but nothing behind it: - json::binary() returned no value, but its destructor asserted that a binary value has a binary array, aborting debug builds. - operator[], push_back, emplace_back, emplace, and update turned a null value into an array or object that did not exist; any later access dereferenced a null pointer. - The to_json conversions destroyed the old value first. A failed creation of the new one left a pointer to the destroyed old value, which was then used and freed again (heap-use-after-free). The value is now created first and the type set after it, and to_json destroys the old value only once the new one exists, so a failed allocation leaves the value unchanged. Signed-off-by: Niels Lohmann <mail@nlohmann.me>
This commit is contained in:
@@ -6571,6 +6571,10 @@ namespace detail
|
||||
* j.m_data.m_value.destroy(j.m_data.m_type) to avoid a memory leak in case j contains an
|
||||
* allocated value (e.g., a string). See bug issue
|
||||
* https://github.com/nlohmann/json/issues/2865 for more information.
|
||||
*
|
||||
* A value that has to be allocated is created before the old one is destroyed:
|
||||
* were it the other way around, an exception while creating the new value would
|
||||
* leave j with the type of the new value, but the pointer to the destroyed old one.
|
||||
*/
|
||||
|
||||
template<value_t> struct external_constructor;
|
||||
@@ -6594,18 +6598,20 @@ struct external_constructor<value_t::string>
|
||||
template<typename BasicJsonType>
|
||||
static void construct(BasicJsonType& j, const typename BasicJsonType::string_t& s)
|
||||
{
|
||||
const typename BasicJsonType::json_value value(s);
|
||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||
j.m_data.m_type = value_t::string;
|
||||
j.m_data.m_value = s;
|
||||
j.m_data.m_value = value;
|
||||
j.assert_invariant();
|
||||
}
|
||||
|
||||
template<typename BasicJsonType>
|
||||
static void construct(BasicJsonType& j, typename BasicJsonType::string_t&& s)
|
||||
{
|
||||
const typename BasicJsonType::json_value value(std::move(s));
|
||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||
j.m_data.m_type = value_t::string;
|
||||
j.m_data.m_value = std::move(s);
|
||||
j.m_data.m_value = value;
|
||||
j.assert_invariant();
|
||||
}
|
||||
|
||||
@@ -6614,9 +6620,11 @@ struct external_constructor<value_t::string>
|
||||
int > = 0 >
|
||||
static void construct(BasicJsonType& j, const CompatibleStringType& str)
|
||||
{
|
||||
typename BasicJsonType::json_value value;
|
||||
value.string = j.template create<typename BasicJsonType::string_t>(str);
|
||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||
j.m_data.m_type = value_t::string;
|
||||
j.m_data.m_value.string = j.template create<typename BasicJsonType::string_t>(str);
|
||||
j.m_data.m_value = value;
|
||||
j.assert_invariant();
|
||||
}
|
||||
};
|
||||
@@ -6627,18 +6635,20 @@ struct external_constructor<value_t::binary>
|
||||
template<typename BasicJsonType>
|
||||
static void construct(BasicJsonType& j, const typename BasicJsonType::binary_t& b)
|
||||
{
|
||||
const typename BasicJsonType::json_value value(b);
|
||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||
j.m_data.m_type = value_t::binary;
|
||||
j.m_data.m_value = typename BasicJsonType::binary_t(b);
|
||||
j.m_data.m_value = value;
|
||||
j.assert_invariant();
|
||||
}
|
||||
|
||||
template<typename BasicJsonType>
|
||||
static void construct(BasicJsonType& j, typename BasicJsonType::binary_t&& b)
|
||||
{
|
||||
const typename BasicJsonType::json_value value(std::move(b));
|
||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||
j.m_data.m_type = value_t::binary;
|
||||
j.m_data.m_value = typename BasicJsonType::binary_t(std::move(b));
|
||||
j.m_data.m_value = value;
|
||||
j.assert_invariant();
|
||||
}
|
||||
};
|
||||
@@ -6688,9 +6698,10 @@ struct external_constructor<value_t::array>
|
||||
template<typename BasicJsonType>
|
||||
static void construct(BasicJsonType& j, const typename BasicJsonType::array_t& arr)
|
||||
{
|
||||
const typename BasicJsonType::json_value value(arr);
|
||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||
j.m_data.m_type = value_t::array;
|
||||
j.m_data.m_value = arr;
|
||||
j.m_data.m_value = value;
|
||||
j.set_parents();
|
||||
j.assert_invariant();
|
||||
}
|
||||
@@ -6698,9 +6709,10 @@ struct external_constructor<value_t::array>
|
||||
template<typename BasicJsonType>
|
||||
static void construct(BasicJsonType& j, typename BasicJsonType::array_t&& arr)
|
||||
{
|
||||
const typename BasicJsonType::json_value value(std::move(arr));
|
||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||
j.m_data.m_type = value_t::array;
|
||||
j.m_data.m_value = std::move(arr);
|
||||
j.m_data.m_value = value;
|
||||
j.set_parents();
|
||||
j.assert_invariant();
|
||||
}
|
||||
@@ -6716,9 +6728,11 @@ struct external_constructor<value_t::array>
|
||||
using std::begin;
|
||||
using std::end;
|
||||
|
||||
typename BasicJsonType::json_value value;
|
||||
value.array = j.template create<typename BasicJsonType::array_t>(begin(arr), end(arr));
|
||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||
j.m_data.m_type = value_t::array;
|
||||
j.m_data.m_value.array = j.template create<typename BasicJsonType::array_t>(begin(arr), end(arr));
|
||||
j.m_data.m_value = value;
|
||||
j.set_parents();
|
||||
j.assert_invariant();
|
||||
}
|
||||
@@ -6726,15 +6740,17 @@ struct external_constructor<value_t::array>
|
||||
template<typename BasicJsonType>
|
||||
static void construct(BasicJsonType& j, const std::vector<bool>& arr)
|
||||
{
|
||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||
j.m_data.m_type = value_t::array;
|
||||
j.m_data.m_value = value_t::array;
|
||||
j.m_data.m_value.array->reserve(arr.size());
|
||||
typename BasicJsonType::array_t elements;
|
||||
elements.reserve(arr.size());
|
||||
for (const bool x : arr)
|
||||
{
|
||||
j.m_data.m_value.array->push_back(x);
|
||||
j.set_parent(j.m_data.m_value.array->back());
|
||||
elements.push_back(x);
|
||||
}
|
||||
const typename BasicJsonType::json_value value(std::move(elements));
|
||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||
j.m_data.m_type = value_t::array;
|
||||
j.m_data.m_value = value;
|
||||
j.set_parents();
|
||||
j.assert_invariant();
|
||||
}
|
||||
|
||||
@@ -6742,11 +6758,12 @@ struct external_constructor<value_t::array>
|
||||
enable_if_t<std::is_convertible<T, BasicJsonType>::value, int> = 0>
|
||||
static void construct(BasicJsonType& j, const std::valarray<T>& arr)
|
||||
{
|
||||
typename BasicJsonType::array_t elements(arr.size());
|
||||
std::copy(std::begin(arr), std::end(arr), elements.begin());
|
||||
const typename BasicJsonType::json_value value(std::move(elements));
|
||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||
j.m_data.m_type = value_t::array;
|
||||
j.m_data.m_value = value_t::array;
|
||||
j.m_data.m_value.array->resize(arr.size());
|
||||
std::copy(std::begin(arr), std::end(arr), j.m_data.m_value.array->begin());
|
||||
j.m_data.m_value = value;
|
||||
j.set_parents();
|
||||
j.assert_invariant();
|
||||
}
|
||||
@@ -6758,14 +6775,16 @@ struct external_constructor<value_t::array>
|
||||
enable_if_t<is_compatible_range_view<std::remove_cvref_t<CompatibleArrayType>>::value, int> = 0>
|
||||
static void construct(BasicJsonType& j, CompatibleArrayType && arr)
|
||||
{
|
||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||
j.m_data.m_type = value_t::array;
|
||||
j.m_data.m_value = value_t::array;
|
||||
typename BasicJsonType::array_t elements;
|
||||
for (auto&& x : std::forward<CompatibleArrayType>(arr))
|
||||
{
|
||||
j.m_data.m_value.array->push_back(x);
|
||||
j.set_parent(j.m_data.m_value.array->back());
|
||||
elements.push_back(x);
|
||||
}
|
||||
const typename BasicJsonType::json_value value(std::move(elements));
|
||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||
j.m_data.m_type = value_t::array;
|
||||
j.m_data.m_value = value;
|
||||
j.set_parents();
|
||||
j.assert_invariant();
|
||||
}
|
||||
#endif
|
||||
@@ -6777,9 +6796,10 @@ struct external_constructor<value_t::object>
|
||||
template<typename BasicJsonType>
|
||||
static void construct(BasicJsonType& j, const typename BasicJsonType::object_t& obj)
|
||||
{
|
||||
const typename BasicJsonType::json_value value(obj);
|
||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||
j.m_data.m_type = value_t::object;
|
||||
j.m_data.m_value = obj;
|
||||
j.m_data.m_value = value;
|
||||
j.set_parents();
|
||||
j.assert_invariant();
|
||||
}
|
||||
@@ -6787,9 +6807,10 @@ struct external_constructor<value_t::object>
|
||||
template<typename BasicJsonType>
|
||||
static void construct(BasicJsonType& j, typename BasicJsonType::object_t&& obj)
|
||||
{
|
||||
const typename BasicJsonType::json_value value(std::move(obj));
|
||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||
j.m_data.m_type = value_t::object;
|
||||
j.m_data.m_value = std::move(obj);
|
||||
j.m_data.m_value = value;
|
||||
j.set_parents();
|
||||
j.assert_invariant();
|
||||
}
|
||||
@@ -6801,9 +6822,11 @@ struct external_constructor<value_t::object>
|
||||
using std::begin;
|
||||
using std::end;
|
||||
|
||||
typename BasicJsonType::json_value value;
|
||||
value.object = j.template create<typename BasicJsonType::object_t>(begin(obj), end(obj));
|
||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||
j.m_data.m_type = value_t::object;
|
||||
j.m_data.m_value.object = j.template create<typename BasicJsonType::object_t>(begin(obj), end(obj));
|
||||
j.m_data.m_value = value;
|
||||
j.set_parents();
|
||||
j.assert_invariant();
|
||||
}
|
||||
@@ -26645,8 +26668,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
if (is_an_object)
|
||||
{
|
||||
// the initializer list is a list of pairs -> create an object
|
||||
m_data.m_type = value_t::object;
|
||||
m_data.m_value = value_t::object;
|
||||
m_data.m_type = value_t::object;
|
||||
|
||||
for (auto& element_ref : init)
|
||||
{
|
||||
@@ -26668,8 +26691,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
}
|
||||
#endif
|
||||
// the initializer list describes an array -> create an array
|
||||
m_data.m_type = value_t::array;
|
||||
m_data.m_value.array = create<array_t>(init.begin(), init.end());
|
||||
m_data.m_type = value_t::array;
|
||||
}
|
||||
|
||||
set_parents();
|
||||
@@ -26682,8 +26705,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
static basic_json binary(const typename binary_t::container_type& init)
|
||||
{
|
||||
auto res = basic_json();
|
||||
res.m_data.m_type = value_t::binary;
|
||||
res.m_data.m_value = init;
|
||||
res.m_data.m_type = value_t::binary;
|
||||
return res;
|
||||
}
|
||||
|
||||
@@ -26693,8 +26716,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
static basic_json binary(const typename binary_t::container_type& init, typename binary_t::subtype_type subtype)
|
||||
{
|
||||
auto res = basic_json();
|
||||
res.m_data.m_type = value_t::binary;
|
||||
res.m_data.m_value = binary_t(init, subtype);
|
||||
res.m_data.m_type = value_t::binary;
|
||||
return res;
|
||||
}
|
||||
|
||||
@@ -26704,8 +26727,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
static basic_json binary(typename binary_t::container_type&& init)
|
||||
{
|
||||
auto res = basic_json();
|
||||
res.m_data.m_type = value_t::binary;
|
||||
res.m_data.m_value = std::move(init);
|
||||
res.m_data.m_type = value_t::binary;
|
||||
return res;
|
||||
}
|
||||
|
||||
@@ -26715,8 +26738,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
static basic_json binary(typename binary_t::container_type&& init, typename binary_t::subtype_type subtype)
|
||||
{
|
||||
auto res = basic_json();
|
||||
res.m_data.m_type = value_t::binary;
|
||||
res.m_data.m_value = binary_t(std::move(init), subtype);
|
||||
res.m_data.m_type = value_t::binary;
|
||||
return res;
|
||||
}
|
||||
|
||||
@@ -27773,8 +27796,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
// implicitly convert a null value to an empty array
|
||||
if (is_null())
|
||||
{
|
||||
m_data.m_type = value_t::array;
|
||||
m_data.m_value.array = create<array_t>();
|
||||
m_data.m_type = value_t::array;
|
||||
assert_invariant();
|
||||
}
|
||||
|
||||
@@ -27833,8 +27856,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
// implicitly convert a null value to an empty object
|
||||
if (is_null())
|
||||
{
|
||||
m_data.m_type = value_t::object;
|
||||
m_data.m_value.object = create<object_t>();
|
||||
m_data.m_type = value_t::object;
|
||||
assert_invariant();
|
||||
}
|
||||
|
||||
@@ -27886,8 +27909,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
// implicitly convert a null value to an empty object
|
||||
if (is_null())
|
||||
{
|
||||
m_data.m_type = value_t::object;
|
||||
m_data.m_value.object = create<object_t>();
|
||||
m_data.m_type = value_t::object;
|
||||
assert_invariant();
|
||||
}
|
||||
|
||||
@@ -28822,8 +28845,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
// transform a null object into an array
|
||||
if (is_null())
|
||||
{
|
||||
m_data.m_type = value_t::array;
|
||||
m_data.m_value = value_t::array;
|
||||
m_data.m_type = value_t::array;
|
||||
assert_invariant();
|
||||
}
|
||||
|
||||
@@ -28855,8 +28878,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
// transform a null object into an array
|
||||
if (is_null())
|
||||
{
|
||||
m_data.m_type = value_t::array;
|
||||
m_data.m_value = value_t::array;
|
||||
m_data.m_type = value_t::array;
|
||||
assert_invariant();
|
||||
}
|
||||
|
||||
@@ -28887,8 +28910,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
// transform a null object into an object
|
||||
if (is_null())
|
||||
{
|
||||
m_data.m_type = value_t::object;
|
||||
m_data.m_value = value_t::object;
|
||||
m_data.m_type = value_t::object;
|
||||
assert_invariant();
|
||||
}
|
||||
|
||||
@@ -28943,8 +28966,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
// transform a null object into an array
|
||||
if (is_null())
|
||||
{
|
||||
m_data.m_type = value_t::array;
|
||||
m_data.m_value = value_t::array;
|
||||
m_data.m_type = value_t::array;
|
||||
assert_invariant();
|
||||
}
|
||||
|
||||
@@ -28968,8 +28991,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
// transform a null object into an object
|
||||
if (is_null())
|
||||
{
|
||||
m_data.m_type = value_t::object;
|
||||
m_data.m_value = value_t::object;
|
||||
m_data.m_type = value_t::object;
|
||||
assert_invariant();
|
||||
}
|
||||
|
||||
@@ -29150,8 +29173,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
// implicitly convert a null value to an empty object
|
||||
if (is_null())
|
||||
{
|
||||
m_data.m_type = value_t::object;
|
||||
m_data.m_value.object = create<object_t>();
|
||||
m_data.m_type = value_t::object;
|
||||
assert_invariant();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user