diff --git a/test/ruby/test_transcode.rb b/test/ruby/test_transcode.rb index d0710358ebefdc..7b0bde91ff2262 100644 --- a/test/ruby/test_transcode.rb +++ b/test/ruby/test_transcode.rb @@ -2232,6 +2232,16 @@ def test_fallback_proc assert_equal("U+3042", "\u{3042}".encode("US-ASCII", fallback: fallback)) end + def test_fallback_grow_insert_buffer + # A later fallback insertion larger than the first one's buffer grows it + # in rb_econv_insert_output; the sized realloc there passed a wrong old + # size (caught by RUBY_DEBUG builds). + n = 0 + r = "\u{3042}\u{3044}\u{3046}".encode("US-ASCII", + fallback: proc {|x| n += 1; "Y" * (5000 * n)}) + assert_equal(30000, r.bytesize) + end + def test_fallback_method def (fallback = "U+%.4X").escape(x) self % x.unpack("U") diff --git a/transcode.c b/transcode.c index e4400f6622d86a..de363c9dcd5789 100644 --- a/transcode.c +++ b/transcode.c @@ -1712,7 +1712,7 @@ rb_econv_insert_output(rb_econv_t *ec, size_t s = (*data_end_p - *buf_start_p) + need; if (s < need) goto fail; - buf = ruby_xrealloc_sized(*buf_start_p, s, buf_end_p - buf_start_p); + buf = ruby_xrealloc_sized(*buf_start_p, s, *buf_end_p - *buf_start_p); *data_start_p = buf; *data_end_p = buf + (*data_end_p - *buf_start_p); *buf_start_p = buf; @@ -2439,6 +2439,7 @@ transcode_loop(const unsigned char **in_pos, unsigned char **out_pos, if (!UNDEF_P(rep) && !NIL_P(rep)) { ret = rb_econv_insert_output(ec, (const unsigned char *)RSTRING_PTR(rep), RSTRING_LEN(rep), rb_enc_name(rb_enc_get(rep))); + RB_GC_GUARD(rep); // insert_output may GC while reading rep's bytes if ((int)ret == -1) { rb_econv_close(ec); rb_raise(rb_eArgError, "too big fallback string");