Skip to content

Commit

Permalink
json_pointer::array_index: Use unsigned values for the array index wh…
Browse files Browse the repository at this point in the history
…en parsing

The current code uses std::stoi to convert the input string to an int
array_index. This limits the maximum addressable array size to ~2GB on
most platforms.

But all callers immediately convert the result of array_index to
BasicJsonType::size_type.

So let's parse it as unsigned long long, which allows us to have as
big arrays as available memory. And also makes the call sites nicer to
read.
  • Loading branch information
t-b committed Jun 20, 2020
1 parent f0e7316 commit 54a3988
Show file tree
Hide file tree
Showing 4 changed files with 36 additions and 32 deletions.
22 changes: 11 additions & 11 deletions include/nlohmann/detail/json_pointer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -329,8 +329,10 @@ class json_pointer
@throw parse_error.109 if an array index begins not with a digit
@throw out_of_range.404 if string @a s could not be converted to an integer
*/
static int array_index(const std::string& s)
static typename BasicJsonType::size_type array_index(const std::string& s)
{
using size_type = typename BasicJsonType::size_type;

// error condition (cf. RFC 6901, Sect. 4)
if (JSON_HEDLEY_UNLIKELY(s.size() > 1 and s[0] == '0'))
{
Expand All @@ -346,10 +348,10 @@ class json_pointer
}

std::size_t processed_chars = 0;
int res = 0;
size_type res = 0;
JSON_TRY
{
res = std::stoi(s, &processed_chars);
res = std::stoull(s, &processed_chars);
}
JSON_CATCH(std::out_of_range&)
{
Expand Down Expand Up @@ -421,7 +423,7 @@ class json_pointer
case detail::value_t::array:
{
// create an entry in the array
result = &result->operator[](static_cast<size_type>(array_index(reference_token)));
result = &result->operator[](array_index(reference_token));
break;
}

Expand Down Expand Up @@ -499,8 +501,7 @@ class json_pointer
else
{
// convert array index to number; unchecked access
ptr = &ptr->operator[](
static_cast<size_type>(array_index(reference_token)));
ptr = &ptr->operator[](array_index(reference_token));
}
break;
}
Expand Down Expand Up @@ -544,7 +545,7 @@ class json_pointer
}

// note: at performs range check
ptr = &ptr->at(static_cast<size_type>(array_index(reference_token)));
ptr = &ptr->at(array_index(reference_token));
break;
}

Expand Down Expand Up @@ -594,8 +595,7 @@ class json_pointer
}

// use unchecked array access
ptr = &ptr->operator[](
static_cast<size_type>(array_index(reference_token)));
ptr = &ptr->operator[](array_index(reference_token));
break;
}

Expand Down Expand Up @@ -638,7 +638,7 @@ class json_pointer
}

// note: at performs range check
ptr = &ptr->at(static_cast<size_type>(array_index(reference_token)));
ptr = &ptr->at(array_index(reference_token));
break;
}

Expand Down Expand Up @@ -702,7 +702,7 @@ class json_pointer
}
}

const auto idx = static_cast<size_type>(array_index(reference_token));
const auto idx = array_index(reference_token);
if (idx >= ptr->size())
{
// index out of range
Expand Down
4 changes: 2 additions & 2 deletions include/nlohmann/json.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -8179,7 +8179,7 @@ class basic_json
else
{
const auto idx = json_pointer::array_index(last_path);
if (JSON_HEDLEY_UNLIKELY(static_cast<size_type>(idx) > parent.size()))
if (JSON_HEDLEY_UNLIKELY(idx > parent.size()))
{
// avoid undefined behavior
JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range"));
Expand Down Expand Up @@ -8222,7 +8222,7 @@ class basic_json
else if (parent.is_array())
{
// note erase performs range check
parent.erase(static_cast<size_type>(json_pointer::array_index(last_path)));
parent.erase(json_pointer::array_index(last_path));
}
};

Expand Down
26 changes: 13 additions & 13 deletions single_include/nlohmann/json.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11361,8 +11361,10 @@ class json_pointer
@throw parse_error.109 if an array index begins not with a digit
@throw out_of_range.404 if string @a s could not be converted to an integer
*/
static int array_index(const std::string& s)
static typename BasicJsonType::size_type array_index(const std::string& s)
{
using size_type = typename BasicJsonType::size_type;

// error condition (cf. RFC 6901, Sect. 4)
if (JSON_HEDLEY_UNLIKELY(s.size() > 1 and s[0] == '0'))
{
Expand All @@ -11378,10 +11380,10 @@ class json_pointer
}

std::size_t processed_chars = 0;
int res = 0;
size_type res = 0;
JSON_TRY
{
res = std::stoi(s, &processed_chars);
res = std::stoull(s, &processed_chars);
}
JSON_CATCH(std::out_of_range&)
{
Expand Down Expand Up @@ -11453,7 +11455,7 @@ class json_pointer
case detail::value_t::array:
{
// create an entry in the array
result = &result->operator[](static_cast<size_type>(array_index(reference_token)));
result = &result->operator[](array_index(reference_token));
break;
}

Expand Down Expand Up @@ -11531,8 +11533,7 @@ class json_pointer
else
{
// convert array index to number; unchecked access
ptr = &ptr->operator[](
static_cast<size_type>(array_index(reference_token)));
ptr = &ptr->operator[](array_index(reference_token));
}
break;
}
Expand Down Expand Up @@ -11576,7 +11577,7 @@ class json_pointer
}

// note: at performs range check
ptr = &ptr->at(static_cast<size_type>(array_index(reference_token)));
ptr = &ptr->at(array_index(reference_token));
break;
}

Expand Down Expand Up @@ -11626,8 +11627,7 @@ class json_pointer
}

// use unchecked array access
ptr = &ptr->operator[](
static_cast<size_type>(array_index(reference_token)));
ptr = &ptr->operator[](array_index(reference_token));
break;
}

Expand Down Expand Up @@ -11670,7 +11670,7 @@ class json_pointer
}

// note: at performs range check
ptr = &ptr->at(static_cast<size_type>(array_index(reference_token)));
ptr = &ptr->at(array_index(reference_token));
break;
}

Expand Down Expand Up @@ -11734,7 +11734,7 @@ class json_pointer
}
}

const auto idx = static_cast<size_type>(array_index(reference_token));
const auto idx = array_index(reference_token);
if (idx >= ptr->size())
{
// index out of range
Expand Down Expand Up @@ -23971,7 +23971,7 @@ class basic_json
else
{
const auto idx = json_pointer::array_index(last_path);
if (JSON_HEDLEY_UNLIKELY(static_cast<size_type>(idx) > parent.size()))
if (JSON_HEDLEY_UNLIKELY(idx > parent.size()))
{
// avoid undefined behavior
JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range"));
Expand Down Expand Up @@ -24014,7 +24014,7 @@ class basic_json
else if (parent.is_array())
{
// note erase performs range check
parent.erase(static_cast<size_type>(json_pointer::array_index(last_path)));
parent.erase(json_pointer::array_index(last_path));
}
};

Expand Down
16 changes: 10 additions & 6 deletions test/src/unit-json_pointer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -348,12 +348,16 @@ TEST_CASE("JSON pointers")
CHECK_THROWS_WITH(j_const["/1+1"_json_pointer] == 1,
"[json.exception.out_of_range.404] unresolved reference token '1+1'");

CHECK_THROWS_AS(j["/111111111111111111111111"_json_pointer] = 1, json::out_of_range&);
CHECK_THROWS_WITH(j["/111111111111111111111111"_json_pointer] = 1,
"[json.exception.out_of_range.404] unresolved reference token '111111111111111111111111'");
CHECK_THROWS_AS(j_const["/111111111111111111111111"_json_pointer] == 1, json::out_of_range&);
CHECK_THROWS_WITH(j_const["/111111111111111111111111"_json_pointer] == 1,
"[json.exception.out_of_range.404] unresolved reference token '111111111111111111111111'");
{
auto too_large_index = std::to_string((std::numeric_limits<unsigned long long>::max)()) + "1";
json::json_pointer jp(std::string("/") + too_large_index);
std::string throw_msg = std::string("[json.exception.out_of_range.404] unresolved reference token '") + too_large_index + "'";

CHECK_THROWS_AS(j[jp] = 1, json::out_of_range&);
CHECK_THROWS_WITH(j[jp] == 1, throw_msg.c_str());
CHECK_THROWS_AS(j_const[jp] == 1, json::out_of_range&);
CHECK_THROWS_WITH(j_const[jp] == 1, throw_msg.c_str());
}

CHECK_THROWS_AS(j.at("/one"_json_pointer) = 1, json::parse_error&);
CHECK_THROWS_WITH(j.at("/one"_json_pointer) = 1,
Expand Down

0 comments on commit 54a3988

Please sign in to comment.