From 95c1df59efff66d61f0fd7dea28586319bdc4800 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Sun, 6 Sep 2026 17:35:51 +0200 Subject: [PATCH] Replace std::equal with an explicitly-bounded loop in the move prefix check The three-iterator std::equal(first1, last1, first2) form has no explicit end iterator for the second range, which a static analyzer (Flawfinder, CWE-126) flags as a potential over-read even though the preceding size comparison already guarantees the second range is long enough. Rather than argue the point, make the bound visible in the code itself via an explicit loop -- every access to ptr.reference_tokens is now guarded by the same index the loop condition bounds against from_size. (The C++14 four-iterator std::equal(first1, last1, first2, last2) form was tried first as a more minimal fix, but this codebase targets C++11 and that overload is not safely usable under -std=c++11 with all supported standard library implementations.) Signed-off-by: Niels Lohmann --- include/nlohmann/json.hpp | 18 +++++++++++++++--- single_include/nlohmann/json.hpp | 18 +++++++++++++++--- 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp index 59d7a879a..5cd06f0d7 100644 --- a/include/nlohmann/json.hpp +++ b/include/nlohmann/json.hpp @@ -5028,9 +5028,21 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec // reference tokens (already unescaped by json_pointer's // parser) rather than the raw pointer strings, since a // token may itself contain an escaped '/' or '~' that - // would defeat a naive string-prefix comparison. - if (JSON_HEDLEY_UNLIKELY(from_ptr.reference_tokens.size() < ptr.reference_tokens.size() - && std::equal(from_ptr.reference_tokens.begin(), from_ptr.reference_tokens.end(), ptr.reference_tokens.begin()))) + // would defeat a naive string-prefix comparison. This is + // written as an explicit, manually-bounded loop (rather + // than std::equal(first1, last1, first2), whose second + // range has no explicit end iterator) so every access to + // ptr.reference_tokens is visibly guarded by the same + // index the loop condition already bounds against + // from_size -- from_size < ptr.reference_tokens.size() + // is checked once, up front, before the loop runs at all. + const auto from_size = from_ptr.reference_tokens.size(); + bool from_is_proper_prefix_of_path = from_size < ptr.reference_tokens.size(); + for (std::size_t i = 0; from_is_proper_prefix_of_path && i < from_size; ++i) + { + from_is_proper_prefix_of_path = from_ptr.reference_tokens[i] == ptr.reference_tokens[i]; + } + if (JSON_HEDLEY_UNLIKELY(from_is_proper_prefix_of_path)) { JSON_THROW(out_of_range::create(414, detail::concat("cannot move value: 'from' path '", from_path, "' is a proper prefix of 'path' '", path, "'"), &result)); } diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 687a148a9..8fc107e12 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -26456,9 +26456,21 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec // reference tokens (already unescaped by json_pointer's // parser) rather than the raw pointer strings, since a // token may itself contain an escaped '/' or '~' that - // would defeat a naive string-prefix comparison. - if (JSON_HEDLEY_UNLIKELY(from_ptr.reference_tokens.size() < ptr.reference_tokens.size() - && std::equal(from_ptr.reference_tokens.begin(), from_ptr.reference_tokens.end(), ptr.reference_tokens.begin()))) + // would defeat a naive string-prefix comparison. This is + // written as an explicit, manually-bounded loop (rather + // than std::equal(first1, last1, first2), whose second + // range has no explicit end iterator) so every access to + // ptr.reference_tokens is visibly guarded by the same + // index the loop condition already bounds against + // from_size -- from_size < ptr.reference_tokens.size() + // is checked once, up front, before the loop runs at all. + const auto from_size = from_ptr.reference_tokens.size(); + bool from_is_proper_prefix_of_path = from_size < ptr.reference_tokens.size(); + for (std::size_t i = 0; from_is_proper_prefix_of_path && i < from_size; ++i) + { + from_is_proper_prefix_of_path = from_ptr.reference_tokens[i] == ptr.reference_tokens[i]; + } + if (JSON_HEDLEY_UNLIKELY(from_is_proper_prefix_of_path)) { JSON_THROW(out_of_range::create(414, detail::concat("cannot move value: 'from' path '", from_path, "' is a proper prefix of 'path' '", path, "'"), &result)); }