Merge pull request #234 from ToruNiina/limit-value-recursion

Limit value recursion
This commit is contained in:
Toru Niina
2024-01-04 22:32:01 +09:00
committed by GitHub
5 changed files with 71 additions and 33 deletions

View File

@@ -8,30 +8,30 @@ using namespace detail;
BOOST_AUTO_TEST_CASE(test_oneline_array) BOOST_AUTO_TEST_CASE(test_oneline_array)
{ {
TOML11_TEST_PARSE_EQUAL(parse_array<toml::value>, "[]", array()); TOML11_TEST_PARSE_EQUAL_VAT(parse_array<toml::value>, "[]", array());
{ {
array a(5); array a(5);
a[0] = toml::value(3); a[1] = toml::value(1); a[2] = toml::value(4); a[0] = toml::value(3); a[1] = toml::value(1); a[2] = toml::value(4);
a[3] = toml::value(1); a[4] = toml::value(5); a[3] = toml::value(1); a[4] = toml::value(5);
TOML11_TEST_PARSE_EQUAL(parse_array<toml::value>, "[3,1,4,1,5]", a); TOML11_TEST_PARSE_EQUAL_VAT(parse_array<toml::value>, "[3,1,4,1,5]", a);
} }
{ {
array a(3); array a(3);
a[0] = toml::value("foo"); a[1] = toml::value("bar"); a[0] = toml::value("foo"); a[1] = toml::value("bar");
a[2] = toml::value("baz"); a[2] = toml::value("baz");
TOML11_TEST_PARSE_EQUAL(parse_array<toml::value>, "[\"foo\", \"bar\", \"baz\"]", a); TOML11_TEST_PARSE_EQUAL_VAT(parse_array<toml::value>, "[\"foo\", \"bar\", \"baz\"]", a);
} }
{ {
array a(5); array a(5);
a[0] = toml::value(3); a[1] = toml::value(1); a[2] = toml::value(4); a[0] = toml::value(3); a[1] = toml::value(1); a[2] = toml::value(4);
a[3] = toml::value(1); a[4] = toml::value(5); a[3] = toml::value(1); a[4] = toml::value(5);
TOML11_TEST_PARSE_EQUAL(parse_array<toml::value>, "[3,1,4,1,5,]", a); TOML11_TEST_PARSE_EQUAL_VAT(parse_array<toml::value>, "[3,1,4,1,5,]", a);
} }
{ {
array a(3); array a(3);
a[0] = toml::value("foo"); a[1] = toml::value("bar"); a[0] = toml::value("foo"); a[1] = toml::value("bar");
a[2] = toml::value("baz"); a[2] = toml::value("baz");
TOML11_TEST_PARSE_EQUAL(parse_array<toml::value>, "[\"foo\", \"bar\", \"baz\",]", a); TOML11_TEST_PARSE_EQUAL_VAT(parse_array<toml::value>, "[\"foo\", \"bar\", \"baz\",]", a);
} }
} }
@@ -66,8 +66,8 @@ BOOST_AUTO_TEST_CASE(test_oneline_array_value)
BOOST_AUTO_TEST_CASE(test_multiline_array) BOOST_AUTO_TEST_CASE(test_multiline_array)
{ {
TOML11_TEST_PARSE_EQUAL(parse_array<basic_value< discard_comments>>, "[\n#comment\n]", typename basic_value< discard_comments>::array_type()); TOML11_TEST_PARSE_EQUAL_VAT(parse_array<basic_value< discard_comments>>, "[\n#comment\n]", typename basic_value< discard_comments>::array_type());
TOML11_TEST_PARSE_EQUAL(parse_array<basic_value<preserve_comments>>, "[\n#comment\n]", typename basic_value<preserve_comments>::array_type()); TOML11_TEST_PARSE_EQUAL_VAT(parse_array<basic_value<preserve_comments>>, "[\n#comment\n]", typename basic_value<preserve_comments>::array_type());
{ {
typename basic_value<discard_comments>::array_type a(5); typename basic_value<discard_comments>::array_type a(5);
@@ -76,7 +76,7 @@ BOOST_AUTO_TEST_CASE(test_multiline_array)
a[2] = basic_value<discard_comments>(4); a[2] = basic_value<discard_comments>(4);
a[3] = basic_value<discard_comments>(1); a[3] = basic_value<discard_comments>(1);
a[4] = basic_value<discard_comments>(5); a[4] = basic_value<discard_comments>(5);
TOML11_TEST_PARSE_EQUAL(parse_array<basic_value<discard_comments>>, "[3,\n1,\n4,\n1,\n5]", a); TOML11_TEST_PARSE_EQUAL_VAT(parse_array<basic_value<discard_comments>>, "[3,\n1,\n4,\n1,\n5]", a);
} }
{ {
typename basic_value<preserve_comments>::array_type a(5); typename basic_value<preserve_comments>::array_type a(5);
@@ -85,7 +85,7 @@ BOOST_AUTO_TEST_CASE(test_multiline_array)
a[2] = basic_value<preserve_comments>(4); a[2] = basic_value<preserve_comments>(4);
a[3] = basic_value<preserve_comments>(1); a[3] = basic_value<preserve_comments>(1);
a[4] = basic_value<preserve_comments>(5); a[4] = basic_value<preserve_comments>(5);
TOML11_TEST_PARSE_EQUAL(parse_array<basic_value<preserve_comments>>, "[3,\n1,\n4,\n1,\n5]", a); TOML11_TEST_PARSE_EQUAL_VAT(parse_array<basic_value<preserve_comments>>, "[3,\n1,\n4,\n1,\n5]", a);
} }
{ {
@@ -95,7 +95,7 @@ BOOST_AUTO_TEST_CASE(test_multiline_array)
a[2] = basic_value<discard_comments>(4); a[2] = basic_value<discard_comments>(4);
a[3] = basic_value<discard_comments>(1); a[3] = basic_value<discard_comments>(1);
a[4] = basic_value<discard_comments>(5); a[4] = basic_value<discard_comments>(5);
TOML11_TEST_PARSE_EQUAL(parse_array<basic_value<discard_comments>>, "[3,#comment\n1,#comment\n4,#comment\n1,#comment\n5 #comment\n]", a); TOML11_TEST_PARSE_EQUAL_VAT(parse_array<basic_value<discard_comments>>, "[3,#comment\n1,#comment\n4,#comment\n1,#comment\n5 #comment\n]", a);
} }
{ {
typename basic_value<preserve_comments>::array_type a(5); typename basic_value<preserve_comments>::array_type a(5);
@@ -104,7 +104,7 @@ BOOST_AUTO_TEST_CASE(test_multiline_array)
a[2] = basic_value<preserve_comments>(4, {"comment"}); a[2] = basic_value<preserve_comments>(4, {"comment"});
a[3] = basic_value<preserve_comments>(1, {"comment"}); a[3] = basic_value<preserve_comments>(1, {"comment"});
a[4] = basic_value<preserve_comments>(5, {"comment"}); a[4] = basic_value<preserve_comments>(5, {"comment"});
TOML11_TEST_PARSE_EQUAL(parse_array<basic_value<preserve_comments>>, "[3,#comment\n1,#comment\n4,#comment\n1,#comment\n5 #comment\n]", a); TOML11_TEST_PARSE_EQUAL_VAT(parse_array<basic_value<preserve_comments>>, "[3,#comment\n1,#comment\n4,#comment\n1,#comment\n5 #comment\n]", a);
} }
@@ -113,14 +113,14 @@ BOOST_AUTO_TEST_CASE(test_multiline_array)
a[0] = basic_value<discard_comments>("foo"); a[0] = basic_value<discard_comments>("foo");
a[1] = basic_value<discard_comments>("bar"); a[1] = basic_value<discard_comments>("bar");
a[2] = basic_value<discard_comments>("baz"); a[2] = basic_value<discard_comments>("baz");
TOML11_TEST_PARSE_EQUAL(parse_array<basic_value<discard_comments>>, "[\"foo\",\n\"bar\",\n\"baz\"]", a); TOML11_TEST_PARSE_EQUAL_VAT(parse_array<basic_value<discard_comments>>, "[\"foo\",\n\"bar\",\n\"baz\"]", a);
} }
{ {
typename basic_value<preserve_comments>::array_type a(3); typename basic_value<preserve_comments>::array_type a(3);
a[0] = basic_value<preserve_comments>("foo"); a[0] = basic_value<preserve_comments>("foo");
a[1] = basic_value<preserve_comments>("bar"); a[1] = basic_value<preserve_comments>("bar");
a[2] = basic_value<preserve_comments>("baz"); a[2] = basic_value<preserve_comments>("baz");
TOML11_TEST_PARSE_EQUAL(parse_array<basic_value<preserve_comments>>, "[\"foo\",\n\"bar\",\n\"baz\"]", a); TOML11_TEST_PARSE_EQUAL_VAT(parse_array<basic_value<preserve_comments>>, "[\"foo\",\n\"bar\",\n\"baz\"]", a);
} }
{ {
@@ -128,14 +128,14 @@ BOOST_AUTO_TEST_CASE(test_multiline_array)
a[0] = basic_value<discard_comments>("foo"); a[0] = basic_value<discard_comments>("foo");
a[1] = basic_value<discard_comments>("b#r"); a[1] = basic_value<discard_comments>("b#r");
a[2] = basic_value<discard_comments>("b#z"); a[2] = basic_value<discard_comments>("b#z");
TOML11_TEST_PARSE_EQUAL(parse_array<basic_value<discard_comments>>, "[\"foo\",#comment\n\"b#r\",#comment\n\"b#z\"#comment\n]", a); TOML11_TEST_PARSE_EQUAL_VAT(parse_array<basic_value<discard_comments>>, "[\"foo\",#comment\n\"b#r\",#comment\n\"b#z\"#comment\n]", a);
} }
{ {
typename basic_value<preserve_comments>::array_type a(3); typename basic_value<preserve_comments>::array_type a(3);
a[0] = basic_value<preserve_comments>("foo", {"comment"}); a[0] = basic_value<preserve_comments>("foo", {"comment"});
a[1] = basic_value<preserve_comments>("b#r", {"comment"}); a[1] = basic_value<preserve_comments>("b#r", {"comment"});
a[2] = basic_value<preserve_comments>("b#z", {"comment"}); a[2] = basic_value<preserve_comments>("b#z", {"comment"});
TOML11_TEST_PARSE_EQUAL(parse_array<basic_value<preserve_comments>>, "[\"foo\",#comment\n\"b#r\",#comment\n\"b#z\"#comment\n]", a); TOML11_TEST_PARSE_EQUAL_VAT(parse_array<basic_value<preserve_comments>>, "[\"foo\",#comment\n\"b#r\",#comment\n\"b#z\"#comment\n]", a);
} }
} }

View File

@@ -20,11 +20,27 @@ do { \
} while(false); \ } while(false); \
/**/ /**/
#define TOML11_TEST_PARSE_EQUAL_VAT(psr, tkn, expct) \
do { \
const std::string token(tkn); \
toml::detail::location loc("test", token); \
const auto result = psr(loc, 0); \
BOOST_TEST(result.is_ok()); \
if(result.is_ok()){ \
BOOST_TEST(result.unwrap().first == expct); \
} else { \
std::cerr << "parser " << #psr << " failed with input `"; \
std::cerr << token << "`.\n"; \
std::cerr << "reason: " << result.unwrap_err() << '\n'; \
} \
} while(false); \
/**/
#define TOML11_TEST_PARSE_EQUAL_VALUE(psr, tkn, expct) \ #define TOML11_TEST_PARSE_EQUAL_VALUE(psr, tkn, expct) \
do { \ do { \
const std::string token(tkn); \ const std::string token(tkn); \
toml::detail::location loc("test", token); \ toml::detail::location loc("test", token); \
const auto result = psr(loc); \ const auto result = psr(loc, 0); \
BOOST_TEST(result.is_ok()); \ BOOST_TEST(result.is_ok()); \
if(result.is_ok()){ \ if(result.is_ok()){ \
BOOST_TEST(result.unwrap() == expct); \ BOOST_TEST(result.unwrap() == expct); \
@@ -35,3 +51,4 @@ do { \
} \ } \
} while(false); \ } while(false); \
/**/ /**/

View File

@@ -8,19 +8,19 @@ using namespace detail;
BOOST_AUTO_TEST_CASE(test_inline_table) BOOST_AUTO_TEST_CASE(test_inline_table)
{ {
TOML11_TEST_PARSE_EQUAL(parse_inline_table<toml::value>, "{}", table()); TOML11_TEST_PARSE_EQUAL_VAT(parse_inline_table<toml::value>, "{}", table());
{ {
table t; table t;
t["foo"] = toml::value(42); t["foo"] = toml::value(42);
t["bar"] = toml::value("baz"); t["bar"] = toml::value("baz");
TOML11_TEST_PARSE_EQUAL(parse_inline_table<toml::value>, "{foo = 42, bar = \"baz\"}", t); TOML11_TEST_PARSE_EQUAL_VAT(parse_inline_table<toml::value>, "{foo = 42, bar = \"baz\"}", t);
} }
{ {
table t; table t;
table t_sub; table t_sub;
t_sub["name"] = toml::value("pug"); t_sub["name"] = toml::value("pug");
t["type"] = toml::value(t_sub); t["type"] = toml::value(t_sub);
TOML11_TEST_PARSE_EQUAL(parse_inline_table<toml::value>, "{type.name = \"pug\"}", t); TOML11_TEST_PARSE_EQUAL_VAT(parse_inline_table<toml::value>, "{type.name = \"pug\"}", t);
} }
} }

View File

@@ -53,7 +53,7 @@ literal_internal_impl(::toml::detail::location loc)
// If it is neither a table-key or a array-of-table-key, it may be a value. // If it is neither a table-key or a array-of-table-key, it may be a value.
if(!is_table_key && !is_aots_key) if(!is_table_key && !is_aots_key)
{ {
if(auto data = ::toml::detail::parse_value<value_type>(loc)) if(auto data = ::toml::detail::parse_value<value_type>(loc, 0))
{ {
return data.unwrap(); return data.unwrap();
} }

View File

@@ -8,6 +8,7 @@
#include "combinator.hpp" #include "combinator.hpp"
#include "lexer.hpp" #include "lexer.hpp"
#include "macros.hpp"
#include "region.hpp" #include "region.hpp"
#include "result.hpp" #include "result.hpp"
#include "types.hpp" #include "types.hpp"
@@ -22,6 +23,11 @@
#endif // __cpp_lib_filesystem #endif // __cpp_lib_filesystem
#endif // TOML11_DISABLE_STD_FILESYSTEM #endif // TOML11_DISABLE_STD_FILESYSTEM
// the previous commit works with 500+ recursions. so it may be too small.
// but in most cases, i think we don't need such a deep recursion of
// arrays or inline-tables.
#define TOML11_VALUE_RECURSION_LIMIT 64
namespace toml namespace toml
{ {
namespace detail namespace detail
@@ -1138,15 +1144,23 @@ parse_key(location& loc)
// forward-decl to implement parse_array and parse_table // forward-decl to implement parse_array and parse_table
template<typename Value> template<typename Value>
result<Value, std::string> parse_value(location&); result<Value, std::string> parse_value(location&, const std::size_t n_rec);
template<typename Value> template<typename Value>
result<std::pair<typename Value::array_type, region>, std::string> result<std::pair<typename Value::array_type, region>, std::string>
parse_array(location& loc) parse_array(location& loc, const std::size_t n_rec)
{ {
using value_type = Value; using value_type = Value;
using array_type = typename value_type::array_type; using array_type = typename value_type::array_type;
if(n_rec > TOML11_VALUE_RECURSION_LIMIT)
{
// parse_array does not have any way to handle recursive error currently...
throw syntax_error(std::string("toml::parse_array: recursion limit ("
TOML11_STRINGIZE(TOML11_VALUE_RECURSION_LIMIT) ") exceeded"),
source_location(loc));
}
const auto first = loc.iter(); const auto first = loc.iter();
if(loc.iter() == loc.end()) if(loc.iter() == loc.end())
{ {
@@ -1173,7 +1187,7 @@ parse_array(location& loc)
region(loc, first, loc.iter()))); region(loc, first, loc.iter())));
} }
if(auto val = parse_value<value_type>(loc)) if(auto val = parse_value<value_type>(loc, n_rec+1))
{ {
// After TOML v1.0.0-rc.1, array becomes to be able to have values // After TOML v1.0.0-rc.1, array becomes to be able to have values
// with different types. So here we will omit this by default. // with different types. So here we will omit this by default.
@@ -1247,7 +1261,7 @@ parse_array(location& loc)
template<typename Value> template<typename Value>
result<std::pair<std::pair<std::vector<key>, region>, Value>, std::string> result<std::pair<std::pair<std::vector<key>, region>, Value>, std::string>
parse_key_value_pair(location& loc) parse_key_value_pair(location& loc, const std::size_t n_rec)
{ {
using value_type = Value; using value_type = Value;
@@ -1294,7 +1308,7 @@ parse_key_value_pair(location& loc)
} }
const auto after_kvsp = loc.iter(); // err msg const auto after_kvsp = loc.iter(); // err msg
auto val = parse_value<value_type>(loc); auto val = parse_value<value_type>(loc, n_rec);
if(!val) if(!val)
{ {
std::string msg; std::string msg;
@@ -1341,7 +1355,7 @@ result<std::pair<std::vector<key>, region>, std::string>
parse_array_table_key(location& loc); parse_array_table_key(location& loc);
template<typename Value> template<typename Value>
result<std::pair<typename Value::table_type, region>, std::string> result<std::pair<typename Value::table_type, region>, std::string>
parse_inline_table(location& loc); parse_inline_table(location& loc, const std::size_t n_rec);
// The following toml file is allowed. // The following toml file is allowed.
// ```toml // ```toml
@@ -1379,7 +1393,7 @@ bool is_valid_forward_table_definition(const Value& fwd, const Value& inserting,
inserting_reg = ptr->str(); inserting_reg = ptr->str();
} }
location inserting_def("internal", std::move(inserting_reg)); location inserting_def("internal", std::move(inserting_reg));
if(const auto inlinetable = parse_inline_table<Value>(inserting_def)) if(const auto inlinetable = parse_inline_table<Value>(inserting_def, 0))
{ {
// check if we are overwriting existing table. // check if we are overwriting existing table.
// ```toml // ```toml
@@ -1810,11 +1824,18 @@ insert_nested_key(typename Value::table_type& root, const Value& v,
template<typename Value> template<typename Value>
result<std::pair<typename Value::table_type, region>, std::string> result<std::pair<typename Value::table_type, region>, std::string>
parse_inline_table(location& loc) parse_inline_table(location& loc, const std::size_t n_rec)
{ {
using value_type = Value; using value_type = Value;
using table_type = typename value_type::table_type; using table_type = typename value_type::table_type;
if(n_rec > TOML11_VALUE_RECURSION_LIMIT)
{
throw syntax_error(std::string("toml::parse_inline_table: recursion limit ("
TOML11_STRINGIZE(TOML11_VALUE_RECURSION_LIMIT) ") exceeded"),
source_location(loc));
}
const auto first = loc.iter(); const auto first = loc.iter();
table_type retval; table_type retval;
if(!(loc.iter() != loc.end() && *loc.iter() == '{')) if(!(loc.iter() != loc.end() && *loc.iter() == '{'))
@@ -1835,7 +1856,7 @@ parse_inline_table(location& loc)
// it starts from "{". it should be formatted as inline-table // it starts from "{". it should be formatted as inline-table
while(loc.iter() != loc.end()) while(loc.iter() != loc.end())
{ {
const auto kv_r = parse_key_value_pair<value_type>(loc); const auto kv_r = parse_key_value_pair<value_type>(loc, n_rec+1);
if(!kv_r) if(!kv_r)
{ {
return err(kv_r.unwrap_err()); return err(kv_r.unwrap_err());
@@ -2079,7 +2100,7 @@ parse_value_helper(result<std::pair<T, region>, std::string> rslt)
} }
template<typename Value> template<typename Value>
result<Value, std::string> parse_value(location& loc) result<Value, std::string> parse_value(location& loc, const std::size_t n_rec)
{ {
const auto first = loc.iter(); const auto first = loc.iter();
if(first == loc.end()) if(first == loc.end())
@@ -2104,8 +2125,8 @@ result<Value, std::string> parse_value(location& loc)
case value_t::local_datetime : {return parse_value_helper<Value>(parse_local_datetime(loc) );} case value_t::local_datetime : {return parse_value_helper<Value>(parse_local_datetime(loc) );}
case value_t::local_date : {return parse_value_helper<Value>(parse_local_date(loc) );} case value_t::local_date : {return parse_value_helper<Value>(parse_local_date(loc) );}
case value_t::local_time : {return parse_value_helper<Value>(parse_local_time(loc) );} case value_t::local_time : {return parse_value_helper<Value>(parse_local_time(loc) );}
case value_t::array : {return parse_value_helper<Value>(parse_array<Value>(loc) );} case value_t::array : {return parse_value_helper<Value>(parse_array<Value>(loc, n_rec));}
case value_t::table : {return parse_value_helper<Value>(parse_inline_table<Value>(loc));} case value_t::table : {return parse_value_helper<Value>(parse_inline_table<Value>(loc, n_rec));}
default: default:
{ {
const auto msg = format_underline("toml::parse_value: " const auto msg = format_underline("toml::parse_value: "
@@ -2270,7 +2291,7 @@ parse_ml_table(location& loc)
return ok(tab); return ok(tab);
} }
if(const auto kv = parse_key_value_pair<value_type>(loc)) if(const auto kv = parse_key_value_pair<value_type>(loc, 0))
{ {
const auto& kvpair = kv.unwrap(); const auto& kvpair = kv.unwrap();
const std::vector<key>& keys = kvpair.first.first; const std::vector<key>& keys = kvpair.first.first;