This is an automated email from the ASF dual-hosted git repository.

wwbmmm pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/brpc.git


The following commit(s) were added to refs/heads/master by this push:
     new 49c3e4df Add pb2json depth limit (#3099)
49c3e4df is described below

commit 49c3e4df460ace322b4139e3980e205d282b32fa
Author: Weibing Wang <[email protected]>
AuthorDate: Wed Sep 24 09:40:58 2025 +0800

    Add pb2json depth limit (#3099)
    
    * Add pb2json depth limit
    
    * fix multi json test case failed
    
    * define DEEP_RECURSION_TEST_DEPTH constant
    
    * Remove duplicate root_msg and root_val param
---
 src/butil/third_party/rapidjson/reader.h |  10 ++-
 src/json2pb/json_to_pb.cpp               |  48 ++++++----
 src/json2pb/pb_to_json.cpp               |  86 +++++++++++++++---
 test/brpc_protobuf_json_unittest.cpp     | 147 +++++++++++++++++++++++++++++++
 test/echo.proto                          |   5 ++
 5 files changed, 264 insertions(+), 32 deletions(-)

diff --git a/src/butil/third_party/rapidjson/reader.h 
b/src/butil/third_party/rapidjson/reader.h
index fd6398b5..26e53c2a 100644
--- a/src/butil/third_party/rapidjson/reader.h
+++ b/src/butil/third_party/rapidjson/reader.h
@@ -1536,9 +1536,15 @@ private:
             state = d;
 
             // Do not further consume streams if a root JSON has been parsed.
-            if ((parseFlags & kParseStopWhenDoneFlag) && state == 
IterativeParsingFinishState)
+            if ((parseFlags & kParseStopWhenDoneFlag) && state == 
IterativeParsingFinishState) {
+                // wwb: Update parseResult_.Offset() when 
kParseStopWhenDoneFlag
+                // is set which means the user needs to know where to resume
+                // parsing in next calls to JsonToProtoMessage()
+                if (is.Peek() != '\0') {
+                    SetParseError(kParseErrorNone, is.Tell());
+                }
                 break;
-
+            }
             SkipWhitespace(is);
         }
 
diff --git a/src/json2pb/json_to_pb.cpp b/src/json2pb/json_to_pb.cpp
index 82327cd6..e758bdb3 100644
--- a/src/json2pb/json_to_pb.cpp
+++ b/src/json2pb/json_to_pb.cpp
@@ -26,6 +26,7 @@
 #include <google/protobuf/descriptor.h>
 #include <google/protobuf/io/coded_stream.h>
 #include <google/protobuf/io/zero_copy_stream_impl_lite.h>
+#include <gflags/gflags.h>
 #include "butil/strings/string_number_conversions.h"
 #include "butil/third_party/rapidjson/error/error.h"
 #include "butil/third_party/rapidjson/rapidjson.h"
@@ -59,6 +60,12 @@
 
 namespace json2pb {
 
+// Use iterative parsing to avoid stack overflow.
+const int RAPIDJSON_PARSE_FLAG_DEFAULT = 
BUTIL_RAPIDJSON_NAMESPACE::kParseIterativeFlag;
+const int RAPIDJSON_PARSE_FLAG_STOP_WHEN_DONE = 
BUTIL_RAPIDJSON_NAMESPACE::kParseStopWhenDoneFlag | 
RAPIDJSON_PARSE_FLAG_DEFAULT;
+
+DEFINE_int32(json2pb_max_recursion_depth, 100, "Maximum recursion depth of 
JSON parser");
+
 Json2PbOptions::Json2PbOptions()
 #ifdef BAIDU_INTERNAL
     : base64_to_bytes(false)
@@ -284,8 +291,7 @@ bool JsonValueToProtoMessage(const 
BUTIL_RAPIDJSON_NAMESPACE::Value& json_value,
                              google::protobuf::Message* message,
                              const Json2PbOptions& options,
                              std::string* err,
-                             bool root_val = false);
-
+                             int depth);
 //Json value to protobuf convert rules for type:
 //Json value type                 Protobuf type                convert rules
 //int                             int uint int64 uint64        valid convert 
is available
@@ -314,7 +320,8 @@ static bool JsonValueToProtoField(const 
BUTIL_RAPIDJSON_NAMESPACE::Value& value,
                                   const google::protobuf::FieldDescriptor* 
field,
                                   google::protobuf::Message* message,
                                   const Json2PbOptions& options,
-                                  std::string* err) {
+                                  std::string* err,
+                                  int depth) {
     if (value.IsNull()) {
         if (field->is_required()) {
             J2PERROR(err, "Missing required field: %s", 
field->full_name().c_str());
@@ -477,13 +484,13 @@ static bool JsonValueToProtoField(const 
BUTIL_RAPIDJSON_NAMESPACE::Value& value,
                 const BUTIL_RAPIDJSON_NAMESPACE::Value& item = value[index];
                 if (TYPE_MATCH == J2PCHECKTYPE(item, message, Object)) { 
                     if (!JsonValueToProtoMessage(
-                            item, reflection->AddMessage(message, field), 
options, err)) {
+                            item, reflection->AddMessage(message, field), 
options, err, depth + 1)) {
                         return false;
                     }
                 } 
             }
         } else if (!JsonValueToProtoMessage(
-            value, reflection->MutableMessage(message, field), options, err)) {
+            value, reflection->MutableMessage(message, field), options, err, 
depth + 1)) {
             return false;
         }
         break;
@@ -495,7 +502,8 @@ bool JsonMapToProtoMap(const 
BUTIL_RAPIDJSON_NAMESPACE::Value& value,
                        const google::protobuf::FieldDescriptor* map_desc,
                        google::protobuf::Message* message,
                        const Json2PbOptions& options,
-                       std::string* err) {
+                       std::string* err,
+                       int depth) {
     if (!value.IsObject()) {
         J2PERROR(err, "Non-object value for map field: %s",
                  map_desc->full_name().c_str());
@@ -515,7 +523,7 @@ bool JsonMapToProtoMap(const 
BUTIL_RAPIDJSON_NAMESPACE::Value& value,
         entry_reflection->SetString(
             entry, key_desc, std::string(it->name.GetString(),
                                          it->name.GetStringLength()));
-        if (!JsonValueToProtoField(it->value, value_desc, entry, options, 
err)) {
+        if (!JsonValueToProtoField(it->value, value_desc, entry, options, err, 
depth + 1)) {
             return false;
         }
     }
@@ -526,10 +534,14 @@ bool JsonValueToProtoMessage(const 
BUTIL_RAPIDJSON_NAMESPACE::Value& json_value,
                              google::protobuf::Message* message,
                              const Json2PbOptions& options,
                              std::string* err,
-                             bool root_val) {
+                             int depth) {
+    if (depth > FLAGS_json2pb_max_recursion_depth) {
+        J2PERROR_WITH_PB(message, err, "Exceeded maximum recursion depth");
+        return false;
+    }
     const google::protobuf::Descriptor* descriptor = message->GetDescriptor();
     if (!json_value.IsObject() &&
-        !(json_value.IsArray() && options.array_to_single_repeated && 
root_val)) {
+        !(json_value.IsArray() && options.array_to_single_repeated && depth == 
0)) {
         J2PERROR_WITH_PB(message, err, "The input is not a json object");
         return false;
     }
@@ -560,7 +572,7 @@ bool JsonValueToProtoMessage(const 
BUTIL_RAPIDJSON_NAMESPACE::Value& json_value,
 
     if (json_value.IsArray()) {
         if (fields.size() == 1 && fields.front()->is_repeated()) {
-            return JsonValueToProtoField(json_value, fields.front(), message, 
options, err);
+            return JsonValueToProtoField(json_value, fields.front(), message, 
options, err, depth);
         }
 
         J2PERROR_WITH_PB(message, err, "the input json can't be array here");
@@ -602,11 +614,11 @@ bool JsonValueToProtoMessage(const 
BUTIL_RAPIDJSON_NAMESPACE::Value& json_value,
 
         if (IsProtobufMap(field) && value_ptr->IsObject()) {
             // Try to parse json like {"key":value, ...} into protobuf map
-            if (!JsonMapToProtoMap(*value_ptr, field, message, options, err)) {
+            if (!JsonMapToProtoMap(*value_ptr, field, message, options, err, 
depth)) {
                 return false;
             }
         } else {
-            if (!JsonValueToProtoField(*value_ptr, field, message, options, 
err)) {
+            if (!JsonValueToProtoField(*value_ptr, field, message, options, 
err, depth)) {
                 return false;
             }
         }
@@ -624,12 +636,12 @@ inline bool JsonToProtoMessageInline(const std::string& 
json_string,
     }
     BUTIL_RAPIDJSON_NAMESPACE::Document d;
     if (options.allow_remaining_bytes_after_parsing) {
-        
d.Parse<BUTIL_RAPIDJSON_NAMESPACE::kParseStopWhenDoneFlag>(json_string.c_str());
+        d.Parse<RAPIDJSON_PARSE_FLAG_STOP_WHEN_DONE>(json_string.c_str());
         if (parsed_offset != nullptr) {
             *parsed_offset = d.GetErrorOffset();
         }
     } else {
-        d.Parse<0>(json_string.c_str());
+        d.Parse<RAPIDJSON_PARSE_FLAG_DEFAULT>(json_string.c_str());
     }
     if (d.HasParseError()) {
         if (options.allow_remaining_bytes_after_parsing) {
@@ -642,7 +654,7 @@ inline bool JsonToProtoMessageInline(const std::string& 
json_string,
         J2PERROR_WITH_PB(message, error, "Invalid json: %s", 
BUTIL_RAPIDJSON_NAMESPACE::GetParseError_En(d.GetParseError()));
         return false;
     }
-    return JsonValueToProtoMessage(d, message, options, error, true);
+    return JsonValueToProtoMessage(d, message, options, error, 0);
 }
 
 bool JsonToProtoMessage(const std::string& json_string,
@@ -672,12 +684,12 @@ bool JsonToProtoMessage(ZeroCopyStreamReader* reader,
     }
     BUTIL_RAPIDJSON_NAMESPACE::Document d;
     if (options.allow_remaining_bytes_after_parsing) {
-        d.ParseStream<BUTIL_RAPIDJSON_NAMESPACE::kParseStopWhenDoneFlag, 
BUTIL_RAPIDJSON_NAMESPACE::UTF8<>>(*reader);
+        d.ParseStream<RAPIDJSON_PARSE_FLAG_STOP_WHEN_DONE, 
BUTIL_RAPIDJSON_NAMESPACE::UTF8<>>(*reader);
         if (parsed_offset != nullptr) {
             *parsed_offset = d.GetErrorOffset();
         }
     } else {
-        d.ParseStream<0, BUTIL_RAPIDJSON_NAMESPACE::UTF8<>>(*reader);
+        d.ParseStream<RAPIDJSON_PARSE_FLAG_DEFAULT, 
BUTIL_RAPIDJSON_NAMESPACE::UTF8<>>(*reader);
     }
     if (d.HasParseError()) {
         if (options.allow_remaining_bytes_after_parsing) {
@@ -690,7 +702,7 @@ bool JsonToProtoMessage(ZeroCopyStreamReader* reader,
         J2PERROR_WITH_PB(message, error, "Invalid json: %s", 
BUTIL_RAPIDJSON_NAMESPACE::GetParseError_En(d.GetParseError()));
         return false;
     }
-    return JsonValueToProtoMessage(d, message, options, error, true);
+    return JsonValueToProtoMessage(d, message, options, error, 0);
 }
 
 bool JsonToProtoMessage(const std::string& json_string, 
diff --git a/src/json2pb/pb_to_json.cpp b/src/json2pb/pb_to_json.cpp
index 9671979c..f2322267 100644
--- a/src/json2pb/pb_to_json.cpp
+++ b/src/json2pb/pb_to_json.cpp
@@ -23,6 +23,7 @@
 #include <time.h>
 #include <google/protobuf/descriptor.h>
 #include <google/protobuf/io/zero_copy_stream_impl_lite.h>
+#include <gflags/gflags.h>
 #include "json2pb/zero_copy_stream_writer.h"
 #include "json2pb/encode_decode.h"
 #include "json2pb/protobuf_map.h"
@@ -33,6 +34,57 @@
 #include "butil/base64.h"
 
 namespace json2pb {
+
+DECLARE_int32(json2pb_max_recursion_depth);
+
+// Helper function to check if the maximum depth of a message is exceeded.
+bool ExceedMaxDepth(const google::protobuf::Message& message, int 
current_depth) {
+    if (current_depth > FLAGS_json2pb_max_recursion_depth) {
+        return true;
+    }
+
+    const google::protobuf::Descriptor* descriptor = message.GetDescriptor();
+    const google::protobuf::Reflection* reflection = message.GetReflection();
+
+    std::vector<const google::protobuf::FieldDescriptor*> fields;
+    // Collect declared fields.
+    for (int i = 0; i < descriptor->field_count(); ++i) {
+        fields.push_back(descriptor->field(i));
+    }
+    // Collect extension fields (if any).
+    {
+        std::vector<const google::protobuf::FieldDescriptor*> ext_fields;
+        descriptor->file()->pool()->FindAllExtensions(descriptor, &ext_fields);
+        fields.insert(fields.end(), ext_fields.begin(), ext_fields.end());
+    }
+
+    for (const auto* field : fields) {
+        if (field->cpp_type() != 
google::protobuf::FieldDescriptor::CPPTYPE_MESSAGE) {
+            continue;
+        }
+
+        if (field->is_repeated()) {
+            const int count = reflection->FieldSize(message, field);
+            for (int j = 0; j < count; ++j) {
+                const google::protobuf::Message& sub_message =
+                    reflection->GetRepeatedMessage(message, field, j);
+                if (ExceedMaxDepth(sub_message, current_depth + 1)) {
+                    return true;
+                }
+            }
+        } else {
+            if (reflection->HasField(message, field)) {
+                const google::protobuf::Message& sub_message =
+                    reflection->GetMessage(message, field);
+                if (ExceedMaxDepth(sub_message, current_depth + 1)) {
+                    return true;
+                }
+            }
+        }
+    }
+    return false;
+}
+
 Pb2JsonOptions::Pb2JsonOptions()
     : enum_option(OUTPUT_ENUM_BY_NAME)
     , pretty_json(false)
@@ -52,7 +104,7 @@ public:
     explicit PbToJsonConverter(const Pb2JsonOptions& opt) : _option(opt) {}
 
     template <typename Handler>
-    bool Convert(const google::protobuf::Message& message, Handler& handler, 
bool root_msg = false);
+    bool Convert(const google::protobuf::Message& message, Handler& handler, 
int depth = 0);
 
     const std::string& ErrorText() const { return _error; }
 
@@ -60,14 +112,18 @@ private:
     template <typename Handler>
     bool _PbFieldToJson(const google::protobuf::Message& message,
                         const google::protobuf::FieldDescriptor* field,
-                        Handler& handler);
+                        Handler& handler, int depth);
 
     std::string _error;
     Pb2JsonOptions _option;
 };
 
 template <typename Handler>
-bool PbToJsonConverter::Convert(const google::protobuf::Message& message, 
Handler& handler, bool root_msg) {
+bool PbToJsonConverter::Convert(const google::protobuf::Message& message, 
Handler& handler, int depth) {
+    if (depth > FLAGS_json2pb_max_recursion_depth) {
+        _error = "Exceeded maximum recursion depth";
+        return false;
+    }
     const google::protobuf::Reflection* reflection = message.GetReflection();
     const google::protobuf::Descriptor* descriptor = message.GetDescriptor();
 
@@ -101,9 +157,9 @@ bool PbToJsonConverter::Convert(const 
google::protobuf::Message& message, Handle
         }
     }
 
-    if (root_msg && _option.single_repeated_to_array) {
+    if (depth == 0 && _option.single_repeated_to_array) {
         if (map_fields.empty() && fields.size() == 1 && 
fields.front()->is_repeated()) {
-            return _PbFieldToJson(message, fields.front(), handler);
+            return _PbFieldToJson(message, fields.front(), handler, depth);
         }
     }
 
@@ -134,7 +190,7 @@ bool PbToJsonConverter::Convert(const 
google::protobuf::Message& message, Handle
         bool decoded = decode_name(orig_name, field_name_str); 
         const std::string& name = decoded ? field_name_str : orig_name;
         handler.Key(name.data(), name.size(), false);
-        if (!_PbFieldToJson(message, field, handler)) {
+        if (!_PbFieldToJson(message, field, handler, depth)) {
             return false;
         }
     }
@@ -164,7 +220,7 @@ bool PbToJsonConverter::Convert(const 
google::protobuf::Message& message, Handle
             handler.Key(entry_name.data(), entry_name.size(), false);
 
             // Fill in entries into this json object
-            if (!_PbFieldToJson(entry, value_desc, handler)) {
+            if (!_PbFieldToJson(entry, value_desc, handler, depth)) {
                 return false;
             }
         }
@@ -180,7 +236,7 @@ template <typename Handler>
 bool PbToJsonConverter::_PbFieldToJson(
     const google::protobuf::Message& message,
     const google::protobuf::FieldDescriptor* field,
-    Handler& handler) {
+    Handler& handler, int depth) {
     const google::protobuf::Reflection* reflection = message.GetReflection();
     switch (field->cpp_type()) {
 #define CASE_FIELD_TYPE(cpptype, method, valuetype, handle)             \
@@ -280,14 +336,14 @@ bool PbToJsonConverter::_PbFieldToJson(
             handler.StartArray();
             for (int index = 0; index < field_size; ++index) {
                 if (!Convert(reflection->GetRepeatedMessage(
-                        message, field, index), handler)) {
+                        message, field, index), handler, depth + 1)) {
                     return false;
                 }
             }
             handler.EndArray(field_size);
             
         } else {
-            if (!Convert(reflection->GetMessage(message, field), handler)) {
+            if (!Convert(reflection->GetMessage(message, field), handler, 
depth + 1)) {
                 return false;
             }
         }
@@ -305,10 +361,10 @@ bool ProtoMessageToJsonStream(const 
google::protobuf::Message& message,
     bool succ = false;
     if (options.pretty_json) {
         BUTIL_RAPIDJSON_NAMESPACE::PrettyWriter<OutputStream> writer(os);
-        succ = converter.Convert(message, writer, true);
+        succ = converter.Convert(message, writer);
     } else {
         BUTIL_RAPIDJSON_NAMESPACE::OptimizedWriter<OutputStream> writer(os);
-        succ = converter.Convert(message, writer, true);
+        succ = converter.Convert(message, writer);
     }
     if (!succ && error) {
         error->clear();
@@ -352,6 +408,12 @@ bool ProtoMessageToJson(const google::protobuf::Message& 
message,
 bool ProtoMessageToProtoJson(const google::protobuf::Message& message,
                              google::protobuf::io::ZeroCopyOutputStream* json,
                              const Pb2ProtoJsonOptions& options, std::string* 
error) {
+    if (ExceedMaxDepth(message, 0)) {
+        if (error) {
+            *error = "Exceeded maximum recursion depth";
+        }
+        return false;
+    }
     butil::IOBuf buf;
     butil::IOBufAsZeroCopyOutputStream output_stream(&buf);
     if (!message.SerializeToZeroCopyStream(&output_stream)) {
diff --git a/test/brpc_protobuf_json_unittest.cpp 
b/test/brpc_protobuf_json_unittest.cpp
index e8435d2c..b9289b20 100644
--- a/test/brpc_protobuf_json_unittest.cpp
+++ b/test/brpc_protobuf_json_unittest.cpp
@@ -26,6 +26,7 @@
 #include "butil/strings/string_util.h"
 #include "butil/third_party/rapidjson/rapidjson.h"
 #include "butil/time.h"
+#include "butil/memory/scope_guard.h"
 #include "gperftools_helper.h"
 #include "json2pb/pb_to_json.h"
 #include "json2pb/json_to_pb.h"
@@ -36,6 +37,7 @@
 #include "addressbook.pb.h"
 #include "addressbook_encode_decode.pb.h"
 #include "addressbook_map.pb.h"
+#include "echo.pb.h"
 
 namespace {  // just for coding-style check
 
@@ -497,6 +499,151 @@ TEST_F(ProtobufJsonTest, json_to_pb_expected_failed_case) 
{
     ASSERT_STREQ("Invalid value `23' for optional field `Content.uid' which 
SHOULD be string, Missing required field: Ext.databyte", error.data());
 }
 
+const int DEEP_RECURSION_TEST_DEPTH = 140000;
+
+TEST_F(ProtobufJsonTest, json_to_pb_unbounded_recursion) {
+    test::RecursiveMessage msg;
+
+    // Generate a deeply nested JSON string to trigger unbounded recursion.
+    const int recursion_depth = DEEP_RECURSION_TEST_DEPTH;
+    std::string nested_json = "";
+    for (int i = 0; i < recursion_depth; ++i) {
+        nested_json += "{\"child\":";
+    }
+    nested_json += "{\"data\":\"leaf\"}";
+    for (int i = 0; i < recursion_depth; ++i) {
+        nested_json += "}";
+    }
+
+    {
+        std::string error;
+        bool ret = json2pb::JsonToProtoMessage(nested_json, &msg, &error);
+        ASSERT_FALSE(ret);
+        ASSERT_EQ("Exceeded maximum recursion depth [RecursiveMessage]", 
error);
+    }
+    {
+        json2pb::ProtoJson2PbOptions options;
+        std::string error;
+        bool ret = json2pb::ProtoJsonToProtoMessage(nested_json, &msg, 
options, &error);
+        ASSERT_FALSE(ret);
+        ASSERT_EQ("INVALID_ARGUMENT:Message too deep. Max recursion depth 
reached for key 'child'", error);
+    }
+}
+
+TEST_F(ProtobufJsonTest, pb_to_json_unbounded_recursion) {
+    test::RecursiveMessage msg;
+
+    // Create a deeply nested protobuf message.
+    const int recursion_depth = DEEP_RECURSION_TEST_DEPTH;
+    test::RecursiveMessage* current = &msg;
+    std::vector<test::RecursiveMessage*> nodes;
+    nodes.reserve(recursion_depth);
+    for (int i = 0; i < recursion_depth; ++i) {
+        nodes.push_back(current);
+        current = current->mutable_child();
+    }
+    current->set_data("leaf");
+
+    BRPC_SCOPE_EXIT {
+        // Release msg memory from end to start to avoid stack overflow.
+        for (size_t i = nodes.size() - 1; i > 0; --i) {
+            delete nodes[i]->release_child();
+        }
+    };
+
+    {
+        std::string json_output;
+        std::string error;
+        bool ret = json2pb::ProtoMessageToJson(msg, &json_output, &error);
+        ASSERT_FALSE(ret);
+        ASSERT_EQ("Exceeded maximum recursion depth", error);
+    }
+    {
+        std::string json_output;
+        std::string error;
+        json2pb::Pb2ProtoJsonOptions options;
+        bool ret = json2pb::ProtoMessageToProtoJson(msg, &json_output, 
options, &error);
+        ASSERT_FALSE(ret);
+        ASSERT_EQ("Exceeded maximum recursion depth", error);
+    }
+}
+
+TEST_F(ProtobufJsonTest, pb_parse_unbounded_recursion) {
+    auto generate_binary = [](int recursion_depth) {
+        // Innermost message: { data: "leaf" }
+        // data field: tag = (2<<3)|2 = 0x12, len=4, bytes "leaf"
+        const char kLeafRaw[] = "\x12\x04" "leaf";
+        const std::string leaf_msg(kLeafRaw, sizeof(kLeafRaw) - 1);
+
+        // Precompute sizes:
+        // S[0] = leaf size
+        // S[i] = 1 (tag 0x0A) + varint_len(S[i-1]) + S[i-1]
+        auto varint_len = [](size_t v) {
+            int n = 1;
+            while (v >= 128) { v >>= 7; ++n; }
+            return n;
+        };
+
+        std::vector<size_t> sizes;
+        sizes.reserve(recursion_depth + 1);
+        sizes.push_back(leaf_msg.size());
+        for (int i = 1; i <= recursion_depth; ++i) {
+            size_t inner = sizes[i-1];
+            sizes.push_back(1 + varint_len(inner) + inner);
+        }
+        const size_t final_size = sizes.back();
+
+        std::string out;
+        out.resize(final_size);
+        size_t off = 0;
+
+        // Emit outermost -> innermost wrappers: tag(0x0A) + varint(len(inner))
+        for (int depth = recursion_depth; depth >= 1; --depth) {
+            out[off++] = static_cast<char>(0x0A);  // tag for child
+            size_t len = sizes[depth - 1];
+            while (true) {
+                uint8_t byte = static_cast<uint8_t>(len & 0x7F);
+                len >>= 7;
+                if (len) byte |= 0x80;
+                out[off++] = static_cast<char>(byte);
+                if (!len) break;
+            }
+        }
+
+        // Copy leaf payload
+        memcpy(&out[off], leaf_msg.data(), leaf_msg.size());
+        off += leaf_msg.size();
+        return out;
+    };
+
+    // Test protobuf max depth limit (100).
+    {
+        test::RecursiveMessage msg;
+        std::string binary_data = generate_binary(100);
+        bool ret = msg.ParseFromString(binary_data);
+        ASSERT_TRUE(ret);
+        ASSERT_TRUE(msg.IsInitialized());
+
+        std::string error;
+        std::string json_output;
+        ret = json2pb::ProtoMessageToJson(msg, &json_output, &error);
+        ASSERT_TRUE(ret);
+        ASSERT_EQ("", error);
+    }
+    {
+        test::RecursiveMessage msg;
+        std::string binary_data = generate_binary(101);
+        bool ret = msg.ParseFromString(binary_data);
+        ASSERT_FALSE(ret);
+    }
+    {
+        test::RecursiveMessage msg;
+        std::string binary_data = generate_binary(DEEP_RECURSION_TEST_DEPTH);
+        bool ret = msg.ParseFromString(binary_data);
+        ASSERT_FALSE(ret);
+    }
+}
+
 TEST_F(ProtobufJsonTest, json_to_pb_perf_case) {
     
     std::string info3 = "{\"content\":[{\"distance\":1.0,\
diff --git a/test/echo.proto b/test/echo.proto
index d7573fc6..970ef1db 100644
--- a/test/echo.proto
+++ b/test/echo.proto
@@ -110,3 +110,8 @@ message Message1 {
 message Message2 {
     required State1 stat = 1;
 };
+
+message RecursiveMessage {
+  optional RecursiveMessage child = 1;
+  optional string data = 2;
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to