Repository navigation
Keep converted object keys alive while writing UBJSON and BJData - #5791
Merged
Merged
Conversation
Since #5746, write_ubjson and write_ubjson_iterative pass each object key to sanitize_utf8_for_write and keep the returned reference. When object_t::key_type is not string_t but converts to it, the argument is a temporary that is destroyed at the end of the statement, and the function returns a reference to it in every case but a sanitized copy, so the key bytes are read from a dead object (AddressSanitizer: stack-use-after-scope). Default json and ordered_json are unaffected. Bind the key to a named object_key_string_t first: a reference when key_type is string_t, so no copy is added there, and a converted copy otherwise. A deleted overload of sanitize_utf8_for_write for anything other than string_t turns a recurrence into a compile error. Signed-off-by: Niels Lohmann <mail@nlohmann.me>
converting_key::data() is only called when JSON_DIAGNOSTICS is enabled. Signed-off-by: Niels Lohmann <mail@nlohmann.me>
gregmarr
approved these changes
Oct 9, 2026
Suppress hicpp/modernize-use-equals-delete on the deleted sanitize_utf8_for_write overload: it guards a private helper and must stay private. Replace the C-style array in the new test with std::array. Signed-off-by: Niels Lohmann <mail@nlohmann.me>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Since #5746,
write_ubjson(recursive) andwrite_ubjson_iterative(used pastdetail::recursion_depth_limit(), #5781) write each object key withWhen
object_t::key_typeis notstring_tbut converts to it implicitly,el.firstbecomes a temporarystring_tthat is destroyed at the end of this statement.sanitize_utf8_for_writereturns a reference to its argument in every case except a sanitizedreplace/ignorecopy, sokeythen refers to a dead object. AddressSanitizer reportsstack-use-after-scopeinwrite_ubjsonforto_ubjsonandto_bjdatawith such a key type.ObjectTypes whosekey_typediffers fromstring_tare affected. Defaultjsonandordered_jsonbind the key directly and never see a temporary.el.first.size()/el.first.c_str(). It was found while reviewing Avoid allocating temporary basic_json for cbor and msgpack object keys #5328.Fix
binary_writer::object_key_string_t:const string_t&whenkey_typeisstring_t,string_totherwise. Both UBJSON sites bind the key to it before sanitizing, so the default types get no extra copy.sanitize_utf8_for_write(const T&, ...)for anyTother thanstring_tmakes passing a converting key a compile error instead of a dangling reference. The doc comment explains this.Tests
New test case in
tests/src/unit-binary_utf8_error_handler.cppusing a local key type that converts tostd::string. Keys are longer than the small-string buffer.error_handler, for UBJSON and BJData draft2/draft3, with alluse_count/use_typecombinations.strictthrows,replace/ignorematchdump()+parse(),keeppasses the bytes through.Verified locally with
-fsanitize=address,undefined:stack-use-after-scopeinwrite_ubjson.write_ubjson_iterative.include/) and C++17 withJSON_DIAGNOSTICS=1(single_include/).sanitize_utf8_for_write(el.first, ...)fails to compile for the custom key type with "call to deleted member function", whilejson/ordered_jsonstill compile.Public API
No breaking changes. The alias and the deleted overload are private members of
detail::binary_writer. All public entry points compile for exactly the samebasic_jsonspecializations as before; the only behavior change is that the affected custom key types no longer read freed memory.🤖 Generated with Claude Code