Skip to content

Commit 727d2e3

Browse files
author
ivigns
committed
fix userver: fix crash on conversion from deeply nested YamlConfig to json::Value
Tests: unittests commit_hash:20e3d16abd61deaff713fd7f3bafa3f3c8b7012d
1 parent fa404d4 commit 727d2e3

File tree

2 files changed

+45
-2
lines changed

2 files changed

+45
-2
lines changed

universal/include/userver/formats/common/conversion_stack.hpp

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@
1010
#include <utility>
1111

1212
#include <fmt/format.h>
13-
#include <boost/container/small_vector.hpp>
13+
#include <boost/container/deque.hpp>
14+
#include <boost/container/options.hpp>
1415

1516
#include <userver/compiler/demangle.hpp>
1617
#include <userver/formats/common/type.hpp>
@@ -140,7 +141,11 @@ class ConversionStack final {
140141
bool is_parsed{false};
141142
};
142143

143-
boost::container::small_vector<StackFrame, 10> stack_;
144+
// The container must have stable references. For example, YamlConfig::const_iterator stores a pointer
145+
// to the YamlConfig node that is being iterated over. If a stack frame moves, then the next frame's
146+
// `current_parsing_elem` will be invalidated. Updating iterators in such cases would be complex and expensive.
147+
boost::container::deque<StackFrame, void, boost::container::deque_options<boost::container::block_size<16>>::type>
148+
stack_;
144149
};
145150

146151
/// @brief Performs the conversion between different formats. Only supports

universal/src/yaml_config/yaml_config_test.cpp

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,10 @@
66
#include <gtest/gtest.h>
77

88
#include <formats/common/value_test.hpp>
9+
#include <userver/formats/common/utils.hpp>
910
#include <userver/formats/json/serialize.hpp>
1011
#include <userver/formats/json/value.hpp>
12+
#include <userver/formats/json/value_builder.hpp>
1113
#include <userver/formats/yaml/serialize.hpp>
1214
#include <userver/formats/yaml/value.hpp>
1315
#include <userver/formats/yaml/value_builder.hpp>
@@ -869,4 +871,40 @@ bar: baz
869871
::unsetenv("ANOTHER_ENV_VARIABLE");
870872
}
871873

874+
template <typename Value>
875+
std::size_t CountDepth(const Value& value) {
876+
std::size_t depth = 0;
877+
auto value_ref = value;
878+
while (value_ref.IsObject()) {
879+
value_ref = value_ref["obj"];
880+
++depth;
881+
}
882+
return depth;
883+
}
884+
885+
TEST(YamlConfig, DeepYamlToJson) {
886+
constexpr std::size_t kDepth = 30;
887+
std::vector<std::string> path(kDepth, "obj");
888+
path.push_back("key");
889+
890+
formats::yaml::ValueBuilder yaml_builder;
891+
formats::common::SetAtPath(yaml_builder, std::vector(path), formats::yaml::ValueBuilder("value").ExtractValue());
892+
const auto node = yaml_builder.ExtractValue();
893+
894+
formats::json::ValueBuilder expected_json_builder;
895+
formats::common::SetAtPath(
896+
expected_json_builder, std::vector(path), formats::json::ValueBuilder("value").ExtractValue()
897+
);
898+
const auto expected_json = expected_json_builder.ExtractValue();
899+
900+
const yaml_config::YamlConfig yaml{node, {}};
901+
const auto json = yaml.As<formats::json::Value>();
902+
903+
EXPECT_EQ(json, expected_json) << "Actual json value:\n"
904+
<< ToString(json) << "\n actual json depth: " << CountDepth(json)
905+
<< "\n expected json value:\n"
906+
<< ToString(expected_json)
907+
<< "\n expected json depth: " << CountDepth(expected_json);
908+
}
909+
872910
USERVER_NAMESPACE_END

0 commit comments

Comments
 (0)