From d05ac29f9be757259833933b6f99e47f6059d351 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Thu, 10 Sep 2026 18:39:24 +0200 Subject: [PATCH] Make parse_cbor_internal's top a copy so it survives pop_back() top aliased container_stack.back(), and was still read (top.is_object) right after container_stack.pop_back() destroyed the element it aliased. Nothing currently reorders those two lines, but the comment claiming the reference's lifetime was already fine only accounted for reallocation from a push, not this. A trivially-copyable container_frame makes top a copy instead, so reads of it stay valid regardless of what happens to the stack; the one place that mutates the live entry now does so through container_stack.back() directly rather than through top. Signed-off-by: Niels Lohmann --- include/nlohmann/detail/input/binary_reader.hpp | 12 ++++++------ single_include/nlohmann/json.hpp | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/include/nlohmann/detail/input/binary_reader.hpp b/include/nlohmann/detail/input/binary_reader.hpp index 29e7f0873..74e15e1c7 100644 --- a/include/nlohmann/detail/input/binary_reader.hpp +++ b/include/nlohmann/detail/input/binary_reader.hpp @@ -1424,9 +1424,10 @@ class binary_reader { if (!container_stack.empty()) { - // the reference is not held across parse_cbor_value() below, - // which can push onto the stack and reallocate it - container_frame& top = container_stack.back(); + // a copy, not a reference: it must stay valid across the + // pop_back() below, which destroys the container_stack element + // it would otherwise alias + container_frame top = container_stack.back(); bool at_end = false; if (top.remaining != npos) @@ -1437,7 +1438,7 @@ class binary_reader if (!at_end) { // claim the element about to be read - --top.remaining; + --container_stack.back().remaining; if (top.is_object) { get(); @@ -1456,9 +1457,8 @@ class binary_reader if (at_end) { - const bool is_object = top.is_object; container_stack.pop_back(); - if (JSON_HEDLEY_UNLIKELY(is_object ? !sax->end_object() : !sax->end_array())) + if (JSON_HEDLEY_UNLIKELY(top.is_object ? !sax->end_object() : !sax->end_array())) { return false; } diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index efa147211..ef091bd88 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -12248,9 +12248,10 @@ class binary_reader { if (!container_stack.empty()) { - // the reference is not held across parse_cbor_value() below, - // which can push onto the stack and reallocate it - container_frame& top = container_stack.back(); + // a copy, not a reference: it must stay valid across the + // pop_back() below, which destroys the container_stack element + // it would otherwise alias + container_frame top = container_stack.back(); bool at_end = false; if (top.remaining != npos) @@ -12261,7 +12262,7 @@ class binary_reader if (!at_end) { // claim the element about to be read - --top.remaining; + --container_stack.back().remaining; if (top.is_object) { get(); @@ -12280,9 +12281,8 @@ class binary_reader if (at_end) { - const bool is_object = top.is_object; container_stack.pop_back(); - if (JSON_HEDLEY_UNLIKELY(is_object ? !sax->end_object() : !sax->end_array())) + if (JSON_HEDLEY_UNLIKELY(top.is_object ? !sax->end_object() : !sax->end_array())) { return false; }