Skip to content

Commit 0a513a3

Browse files
authored
Merge pull request nlohmann#1436 from nickaein/iterate-on-destruction
Prevent stackoverflow caused by recursive deconstruction
2 parents 67259d6 + 7e2445a commit 0a513a3

File tree

4 files changed

+148
-0
lines changed

4 files changed

+148
-0
lines changed

include/nlohmann/json.hpp

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -998,6 +998,55 @@ class basic_json
998998

999999
void destroy(value_t t) noexcept
10001000
{
1001+
// flatten the current json_value to a heap-allocated stack
1002+
std::vector<basic_json> stack;
1003+
1004+
// move the top-level items to stack
1005+
if (t == value_t::array)
1006+
{
1007+
stack.reserve(array->size());
1008+
std::move(array->begin(), array->end(), std::back_inserter(stack));
1009+
}
1010+
else if (t == value_t::object)
1011+
{
1012+
stack.reserve(object->size());
1013+
1014+
for (auto&& it : *object)
1015+
{
1016+
stack.push_back(std::move(it.second));
1017+
}
1018+
}
1019+
1020+
while (!stack.empty())
1021+
{
1022+
// move the last item to local variable to be processed
1023+
basic_json current_item(std::move(stack.back()));
1024+
stack.pop_back();
1025+
1026+
// if current_item is array/object, move
1027+
// its children to the stack to be processed later
1028+
if (current_item.is_array())
1029+
{
1030+
stack.reserve(stack.size() + current_item.m_value.array->size());
1031+
1032+
std::move(current_item.m_value.array->begin(), current_item.m_value.array->end(),
1033+
std::back_inserter(stack));
1034+
1035+
current_item.m_value.array->clear();
1036+
}
1037+
else if (current_item.is_object())
1038+
{
1039+
stack.reserve(stack.size() + current_item.m_value.object->size());
1040+
1041+
for (auto&& it : *current_item.m_value.object)
1042+
{
1043+
stack.push_back(std::move(it.second));
1044+
}
1045+
}
1046+
1047+
// current_item is destroyed here
1048+
}
1049+
10011050
switch (t)
10021051
{
10031052
case value_t::object:

single_include/nlohmann/json.hpp

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15541,6 +15541,55 @@ class basic_json
1554115541

1554215542
void destroy(value_t t) noexcept
1554315543
{
15544+
// flatten the current json_value to a heap-allocated stack
15545+
std::vector<basic_json> stack;
15546+
15547+
// move the top-level items to stack
15548+
if (t == value_t::array)
15549+
{
15550+
stack.reserve(array->size());
15551+
std::move(array->begin(), array->end(), std::back_inserter(stack));
15552+
}
15553+
else if (t == value_t::object)
15554+
{
15555+
stack.reserve(object->size());
15556+
15557+
for (auto&& it : *object)
15558+
{
15559+
stack.push_back(std::move(it.second));
15560+
}
15561+
}
15562+
15563+
while (!stack.empty())
15564+
{
15565+
// move the last item to local variable to be processed
15566+
basic_json current_item(std::move(stack.back()));
15567+
stack.pop_back();
15568+
15569+
// if current_item is array/object, move
15570+
// its children to the stack to be processed later
15571+
if (current_item.is_array())
15572+
{
15573+
stack.reserve(stack.size() + current_item.m_value.array->size());
15574+
15575+
std::move(current_item.m_value.array->begin(), current_item.m_value.array->end(),
15576+
std::back_inserter(stack));
15577+
15578+
current_item.m_value.array->clear();
15579+
}
15580+
else if (current_item.is_object())
15581+
{
15582+
stack.reserve(stack.size() + current_item.m_value.object->size());
15583+
15584+
for (auto&& it : *current_item.m_value.object)
15585+
{
15586+
stack.push_back(std::move(it.second));
15587+
}
15588+
}
15589+
15590+
// current_item is destroyed here
15591+
}
15592+
1554415593
switch (t)
1554515594
{
1554615595
case value_t::object:

test/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ set(files
127127
src/unit-iterators2.cpp
128128
src/unit-json_patch.cpp
129129
src/unit-json_pointer.cpp
130+
src/unit-large_json.cpp
130131
src/unit-merge_patch.cpp
131132
src/unit-meta.cpp
132133
src/unit-modifiers.cpp

test/src/unit-large_json.cpp

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/*
2+
__ _____ _____ _____
3+
__| | __| | | | JSON for Modern C++ (test suite)
4+
| | |__ | | | | | | version 3.7.1
5+
|_____|_____|_____|_|___| https://github.com/nlohmann/json
6+
7+
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
8+
SPDX-License-Identifier: MIT
9+
Copyright (c) 2013-2019 Niels Lohmann <http://nlohmann.me>.
10+
11+
Permission is hereby granted, free of charge, to any person obtaining a copy
12+
of this software and associated documentation files (the "Software"), to deal
13+
in the Software without restriction, including without limitation the rights
14+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15+
copies of the Software, and to permit persons to whom the Software is
16+
furnished to do so, subject to the following conditions:
17+
18+
The above copyright notice and this permission notice shall be included in all
19+
copies or substantial portions of the Software.
20+
21+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27+
SOFTWARE.
28+
*/
29+
30+
#include "doctest_compatibility.h"
31+
32+
#include <nlohmann/json.hpp>
33+
using nlohmann::json;
34+
35+
#include <algorithm>
36+
37+
TEST_CASE("tests on very large JSONs")
38+
{
39+
SECTION("issue #1419 - Segmentation fault (stack overflow) due to unbounded recursion")
40+
{
41+
const auto depth = 5000000;
42+
43+
std::string s(2 * depth, '[');
44+
std::fill(s.begin() + depth, s.end(), ']');
45+
46+
CHECK_NOTHROW(nlohmann::json::parse(s));
47+
}
48+
}
49+

0 commit comments

Comments
 (0)