From b4a35dfd5394542d467f3fa11f3b258c944bd402 Mon Sep 17 00:00:00 2001 From: ameliabarnabyhub Date: Thu, 13 Aug 2026 21:58:56 +0800 Subject: [PATCH] Throw type_error.321 when serializing discarded values to binary formats The CBOR, MessagePack, UBJSON, BJData, and BSON writers silently skipped the payload of a value_t::discarded value nested in an array or object, while still writing its slot in the element/member count (and, for BSON, its entry header), producing a binary document whose declared size does not match what was actually written. Throw type_error.321 instead, for a discarded value anywhere in the tree, including at the top level. Rewritten from the original PR against the current (non-recursive option aside) binary_writer.hpp, which has changed substantially since this was first proposed; the out_of_range.412 MessagePack size check and unrelated test reformatting from that PR are dropped as out of scope here. Signed-off-by: Niels Lohmann --- .../nlohmann/detail/output/binary_writer.hpp | 30 +++++++++++++++---- single_include/nlohmann/json.hpp | 30 +++++++++++++++---- 2 files changed, 50 insertions(+), 10 deletions(-) diff --git a/include/nlohmann/detail/output/binary_writer.hpp b/include/nlohmann/detail/output/binary_writer.hpp index a4921f217..8a26a451c 100644 --- a/include/nlohmann/detail/output/binary_writer.hpp +++ b/include/nlohmann/detail/output/binary_writer.hpp @@ -127,6 +127,7 @@ class binary_writer @throw type_error.316 if a string value or an object key is not valid UTF-8 @throw type_error.317 if @a j is not an object + @throw type_error.321 if a value nested in @a j is discarded */ void write_bson(const BasicJsonType& j) { @@ -158,6 +159,7 @@ class binary_writer @param[in] j JSON value to serialize @throw type_error.316 if a string value or an object key is not valid UTF-8 + @throw type_error.321 if @a j or a value nested in it is discarded */ void write_cbor(const BasicJsonType& j) { @@ -322,7 +324,7 @@ class binary_writer case value_t::discarded: default: - break; + throw_on_discarded(j, "CBOR"); } } @@ -382,6 +384,7 @@ class binary_writer /*! @param[in] j JSON value to serialize + @throw type_error.321 if @a j or a value nested in it is discarded */ void write_msgpack(const BasicJsonType& j) { @@ -655,7 +658,7 @@ class binary_writer case value_t::discarded: default: - break; + throw_on_discarded(j, "MessagePack"); } } @@ -668,6 +671,7 @@ class binary_writer @param[in] bjdata_version which BJData version to use, default is draft2 @throw type_error.316 if a string value or an object key is not valid UTF-8 + @throw type_error.321 if @a j or a value nested in it is discarded */ void write_ubjson(const BasicJsonType& j, const bool use_count, const bool use_type, const bool add_prefix = true, @@ -901,7 +905,7 @@ class binary_writer case value_t::discarded: default: - break; + throw_on_discarded(j, use_bjdata ? "BJData" : "UBJSON"); } } @@ -921,6 +925,15 @@ class binary_writer } private: + /*! + @brief throws because @a j is discarded and cannot be serialized + @throw type_error.321 always + */ + JSON_HEDLEY_NO_RETURN static void throw_on_discarded(const BasicJsonType& j, const char* format_name) + { + JSON_THROW(type_error::create(321, concat("cannot serialize discarded value to ", format_name), &j)); + } + ////////// // BSON // ////////// @@ -1172,6 +1185,7 @@ class binary_writer into a byte, before anything is written @throw type_error.316 if @a j is a string that is not valid UTF-8, before anything is written + @throw type_error.321 if @a j is discarded */ std::size_t calc_bson_value_size(const BasicJsonType& j) { @@ -1198,10 +1212,12 @@ class binary_writer case value_t::null: return 0ul; + case value_t::discarded: + throw_on_discarded(j, "BSON"); + // LCOV_EXCL_START case value_t::object: case value_t::array: - case value_t::discarded: default: JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) return 0ul; @@ -1238,10 +1254,12 @@ class binary_writer case value_t::null: return write_bson_null(name); + case value_t::discarded: + throw_on_discarded(j, "BSON"); + // LCOV_EXCL_START case value_t::object: case value_t::array: - case value_t::discarded: default: JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) return; @@ -1308,6 +1326,8 @@ class binary_writer byte, before anything is written @throw type_error.316 if a string value or a key is not valid UTF-8, before anything is written + @throw type_error.321 if a value nested in @a document is discarded, + before anything is written */ std::size_t calc_bson_sizes(const BasicJsonType& document, std::vector& nested_sizes) { diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index b5e7417e6..2f11fca4a 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -21529,6 +21529,7 @@ class binary_writer @throw type_error.316 if a string value or an object key is not valid UTF-8 @throw type_error.317 if @a j is not an object + @throw type_error.321 if a value nested in @a j is discarded */ void write_bson(const BasicJsonType& j) { @@ -21560,6 +21561,7 @@ class binary_writer @param[in] j JSON value to serialize @throw type_error.316 if a string value or an object key is not valid UTF-8 + @throw type_error.321 if @a j or a value nested in it is discarded */ void write_cbor(const BasicJsonType& j) { @@ -21724,7 +21726,7 @@ class binary_writer case value_t::discarded: default: - break; + throw_on_discarded(j, "CBOR"); } } @@ -21784,6 +21786,7 @@ class binary_writer /*! @param[in] j JSON value to serialize + @throw type_error.321 if @a j or a value nested in it is discarded */ void write_msgpack(const BasicJsonType& j) { @@ -22057,7 +22060,7 @@ class binary_writer case value_t::discarded: default: - break; + throw_on_discarded(j, "MessagePack"); } } @@ -22070,6 +22073,7 @@ class binary_writer @param[in] bjdata_version which BJData version to use, default is draft2 @throw type_error.316 if a string value or an object key is not valid UTF-8 + @throw type_error.321 if @a j or a value nested in it is discarded */ void write_ubjson(const BasicJsonType& j, const bool use_count, const bool use_type, const bool add_prefix = true, @@ -22303,7 +22307,7 @@ class binary_writer case value_t::discarded: default: - break; + throw_on_discarded(j, use_bjdata ? "BJData" : "UBJSON"); } } @@ -22323,6 +22327,15 @@ class binary_writer } private: + /*! + @brief throws because @a j is discarded and cannot be serialized + @throw type_error.321 always + */ + JSON_HEDLEY_NO_RETURN static void throw_on_discarded(const BasicJsonType& j, const char* format_name) + { + JSON_THROW(type_error::create(321, concat("cannot serialize discarded value to ", format_name), &j)); + } + ////////// // BSON // ////////// @@ -22574,6 +22587,7 @@ class binary_writer into a byte, before anything is written @throw type_error.316 if @a j is a string that is not valid UTF-8, before anything is written + @throw type_error.321 if @a j is discarded */ std::size_t calc_bson_value_size(const BasicJsonType& j) { @@ -22600,10 +22614,12 @@ class binary_writer case value_t::null: return 0ul; + case value_t::discarded: + throw_on_discarded(j, "BSON"); + // LCOV_EXCL_START case value_t::object: case value_t::array: - case value_t::discarded: default: JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) return 0ul; @@ -22640,10 +22656,12 @@ class binary_writer case value_t::null: return write_bson_null(name); + case value_t::discarded: + throw_on_discarded(j, "BSON"); + // LCOV_EXCL_START case value_t::object: case value_t::array: - case value_t::discarded: default: JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) return; @@ -22710,6 +22728,8 @@ class binary_writer byte, before anything is written @throw type_error.316 if a string value or a key is not valid UTF-8, before anything is written + @throw type_error.321 if a value nested in @a document is discarded, + before anything is written */ std::size_t calc_bson_sizes(const BasicJsonType& document, std::vector& nested_sizes) {