Raise error for JSON overflow encoding in Ruby (#5752)

* add check for overflow

* de-nestify

* break long lines
diff --git a/ruby/Rakefile b/ruby/Rakefile
index 79b7df3..140f5e5 100644
--- a/ruby/Rakefile
+++ b/ruby/Rakefile
@@ -118,11 +118,11 @@
 end
 
 file "tests/basic_test.rb" => "tests/basic_test.proto" do |file_task|
-  sh "../src/protoc --ruby_out=. tests/basic_test.proto"
+  sh "../src/protoc -I../src -I. --ruby_out=. tests/basic_test.proto"
 end
 
 file "tests/basic_test_proto2.rb" => "tests/basic_test_proto2.proto" do |file_task|
-  sh "../src/protoc --ruby_out=. tests/basic_test_proto2.proto"
+  sh "../src/protoc -I../src -I. --ruby_out=. tests/basic_test_proto2.proto"
 end
 
 task :genproto => genproto_output
diff --git a/ruby/ext/google/protobuf_c/encode_decode.c b/ruby/ext/google/protobuf_c/encode_decode.c
index c5078bc..5ead9b8 100644
--- a/ruby/ext/google/protobuf_c/encode_decode.c
+++ b/ruby/ext/google/protobuf_c/encode_decode.c
@@ -1061,6 +1061,11 @@
                            upb_sink *sink,
                            bool emit_defaults,
                            bool is_json) {
+  if (depth > ENCODE_MAX_NESTING) {
+    rb_raise(rb_eRuntimeError,
+             "Maximum recursion depth exceeded during encoding.");
+  }
+
   upb_selector_t sel = 0;
   if (upb_fielddef_isprimitive(f)) {
     sel = getsel(f, upb_handlers_getprimitivehandlertype(f));
diff --git a/ruby/tests/basic_test.proto b/ruby/tests/basic_test.proto
index 4010fe3..684f3e6 100644
--- a/ruby/tests/basic_test.proto
+++ b/ruby/tests/basic_test.proto
@@ -2,6 +2,8 @@
 
 package basic_test;
 
+import "google/protobuf/struct.proto";
+
 message Foo {
   Bar bar = 1;
   repeated Baz baz = 2;
@@ -106,4 +108,14 @@
 }
 
 message Inner {
-}
\ No newline at end of file
+}
+
+
+message MyRepeatedStruct {
+  repeated MyStruct structs = 1;
+}
+
+message MyStruct {
+  string string = 1;
+  google.protobuf.Struct struct = 2;
+}
diff --git a/ruby/tests/basic_test_proto2.proto b/ruby/tests/basic_test_proto2.proto
index a503eae..2c4b300 100644
--- a/ruby/tests/basic_test_proto2.proto
+++ b/ruby/tests/basic_test_proto2.proto
@@ -2,6 +2,8 @@
 
 package basic_test_proto2;
 
+import "google/protobuf/struct.proto";
+
 message Foo {
   optional Bar bar = 1;
   repeated Baz baz = 2;
@@ -115,3 +117,12 @@
     TestEnum d = 4;
   }
 }
+
+message MyRepeatedStruct {
+  repeated MyStruct structs = 1;
+}
+
+message MyStruct {
+  optional string string = 1;
+  optional google.protobuf.Struct struct = 2;
+}
diff --git a/ruby/tests/common_tests.rb b/ruby/tests/common_tests.rb
index 3876300..9464deb 100644
--- a/ruby/tests/common_tests.rb
+++ b/ruby/tests/common_tests.rb
@@ -1137,6 +1137,67 @@
     assert JSON.parse(actual, :symbolize_names => true) == expected
   end
 
+  def value_from_ruby(value)
+    ret = Google::Protobuf::Value.new
+    case value
+    when String
+      ret.string_value = value
+    when Google::Protobuf::Struct
+      ret.struct_value = value
+    when Hash
+      ret.struct_value = struct_from_ruby(value)
+    when Google::Protobuf::ListValue
+      ret.list_value = value
+    when Array
+      ret.list_value = list_from_ruby(value)
+    else
+      @log.error "Unknown type: #{value.class}"
+      raise Google::Protobuf::Error, "Unknown type: #{value.class}"
+    end
+    ret
+  end
+
+  def list_from_ruby(arr)
+    ret = Google::Protobuf::ListValue.new
+    arr.each do |v|
+      ret.values << value_from_ruby(v)
+    end
+    ret
+  end
+
+  def struct_from_ruby(hash)
+    ret = Google::Protobuf::Struct.new
+    hash.each do |k, v|
+      ret.fields[k] ||= value_from_ruby(v)
+    end
+    ret
+  end
+
+  def test_deep_json
+    # will not overflow
+    json = '{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":'\
+           '{"a":{"a":{"a":{"a":{}}}}}}}}}}}}}}}}'
+
+    struct = struct_from_ruby(JSON.parse(json))
+    assert_equal json, struct.to_json
+
+    encoded = proto_module::MyRepeatedStruct.encode(
+      proto_module::MyRepeatedStruct.new(structs: [proto_module::MyStruct.new(struct: struct)]))
+    assert_equal json, proto_module::MyRepeatedStruct.decode(encoded).structs[0].struct.to_json
+
+    # will overflow
+    json = '{"a":{"a":{"a":[{"a":{"a":[{"a":[{"a":{"a":[{"a":[{"a":'\
+           '{"a":[{"a":[{"a":{"a":{"a":[{"a":"a"}]}}}]}]}}]}]}}]}]}}]}}}'
+
+    struct = struct_from_ruby(JSON.parse(json))
+    assert_equal json, struct.to_json
+
+    assert_raise(RuntimeError, "Maximum recursion depth exceeded during encoding") do
+      proto_module::MyRepeatedStruct.encode(
+        proto_module::MyRepeatedStruct.new(structs: [proto_module::MyStruct.new(struct: struct)]))
+    end
+  end
+
   def test_comparison_with_arbitrary_object
     assert proto_module::TestMessage.new != nil
   end