Skip to content

contains(json_pointer) throws on out-of-range numeric array tokens, contradicting its documented "does not throw exceptions" #5395

Closed

Description

@nlohmann

Description

The contains(const json_pointer&) overload is documented as never throwing. docs/mkdocs/docs/api/basic_json/contains.md states under Exceptions for all three overloads:

  1. The function does not throw exceptions.
  2. The function does not throw exceptions.
  3. The function does not throw exceptions.

However, when the pointer contains an array-index token that consists only of digits but is numerically too large for size_type (or exceeds ULLONG_MAX, making strtoull set ERANGE), contains() throws.

contains() was deliberately written to return false for malformed reference tokens ("00", "one", "+1", …) instead of throwing. The check at include/nlohmann/detail/json_pointer.hpp:751 validates only the characters of the token, not its magnitude, and then calls array_index, which throws out_of_range.404 / out_of_range.410 (throw sites at json_pointer.hpp:276 and :283).

This means a documented-noexcept-in-spirit API throws on attacker-controllable pointer strings. In a noexcept context this leads to std::terminate.

Reproduction steps

Build and run the minimal example below against an array (or any value) with a JSON pointer whose token is all digits and ≥ the maximum representable index.

Expected vs. actual results

  • Expected: contains() returns false (the reference token cannot resolve), consistent with the documentation and with its handling of other malformed tokens.
  • Actual: it throws out_of_range.410 (index exceeds size_type) or out_of_range.404 (unresolved reference token for values above ULLONG_MAX).

Minimal code example

#include <nlohmann/json.hpp>
using json = nlohmann::json;

int main()
{
    json arr = json::array({1, 2, 3});
    // token is all digits but larger than size_type max:
    arr.contains(json::json_pointer("/18446744073709551615")); // throws out_of_range.410
    // token larger than ULLONG_MAX (ERANGE):
    arr.contains(json::json_pointer("/99999999999999999999")); // throws out_of_range.404
}

Error messages

terminate called after throwing an instance of 'nlohmann::json_abi_v3_12_0::detail::out_of_range'
  what():  [json.exception.out_of_range.410] array index 18446744073709551615 exceeds size_type

(For the ULLONG_MAX+ case: [json.exception.out_of_range.404] unresolved reference token '99999999999999999999'.)

Suggested fix

In contains(), treat a numeric token that does not fit size_type the same way malformed tokens are already treated — return false rather than delegating to array_index. The existing unit tests cover this range check only for operator[]/at (unit-json_pointer.cpp:316), never for contains.

Compiler and operating system

g++ 13.3.0 (Ubuntu 24.04, x86-64)

Library version

develop @ 01853ed6bcf9ebe88ec2e248ea757b86417b1487

Validation

  • The bug also occurs if the latest version from the develop branch is used.
  • I can successfully compile and run the unit tests.

Metadata

Metadata

Assignees

Labels

kind: bugsolution: proposed fixa fix for the issue has been proposed and waits for confirmation

Projects

No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions