From faefcb11050ea61ee011e3b73c929ff406d30b68 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Wed, 15 Apr 2026 18:51:20 +0900 Subject: [PATCH] [Bug #22461] Fix dangling pointers after associating encoding `rb_enc_associate` may re-allocate the string buffer because of the terminator length change. --- sprintf.c | 21 ++++++++++++++++----- test/ruby/test_sprintf.rb | 12 ++++++++++++ 2 files changed, 28 insertions(+), 5 deletions(-) diff --git a/sprintf.c b/sprintf.c index 42c73bd1f81..d48310abfec 100644 --- a/sprintf.c +++ b/sprintf.c @@ -234,6 +234,18 @@ rb_f_sprintf(int argc, const VALUE *argv) return rb_str_format(argc - 1, argv + 1, GETNTHARG(0)); } +static char * +reassoc_enc(VALUE result, rb_encoding *enc) +{ + return RSTRING_PTR(rb_enc_associate(result, enc)); +} + +static char * +reassoc_enc_index(VALUE result, int encidx) +{ + return RSTRING_PTR(rb_enc_associate_index(result, encidx)); +} + VALUE rb_str_format_ary(int argc, const VALUE *argv, VALUE fmt, VALUE ary) { @@ -295,8 +307,7 @@ rb_str_format_ary(int argc, const VALUE *argv, VALUE fmt, VALUE ary) blen = 0; bsiz = 120; result = rb_str_buf_new(bsiz); - rb_enc_associate(result, enc); - buf = RSTRING_PTR(result); + buf = reassoc_enc(result, enc); memset(buf, 0, bsiz); ENC_CODERANGE_SET(result, coderange); @@ -487,7 +498,7 @@ rb_str_format_ary(int argc, const VALUE *argv, VALUE fmt, VALUE ary) } if (encidx >= 0 && encidx != rb_enc_to_index(enc)) { /* special case */ - rb_enc_associate_index(result, encidx); + buf = reassoc_enc_index(result, encidx); enc = rb_enc_from_index(encidx); coderange = ENC_CODERANGE_VALID; } @@ -550,13 +561,13 @@ rb_str_format_ary(int argc, const VALUE *argv, VALUE fmt, VALUE ary) if (flags&FMINUS) { FILL_(' ', width); } - rb_enc_associate(result, enc); + buf = reassoc_enc(result, enc); break; } } PUSH(RSTRING_PTR(str), len); RB_GC_GUARD(str); - rb_enc_associate(result, enc); + buf = reassoc_enc(result, enc); } break; diff --git a/test/ruby/test_sprintf.rb b/test/ruby/test_sprintf.rb index a267249ae0c..2f563576f9d 100644 --- a/test/ruby/test_sprintf.rb +++ b/test/ruby/test_sprintf.rb @@ -574,4 +574,16 @@ class TestSprintf < Test::Unit::TestCase assert_equal i, str.bytesize end end + + def test_dangling_pointer_after_encoding_set + str = "\u0141" * 61 + wide_encodings = [ + Encoding::UTF_16BE, Encoding::UTF_16LE, + Encoding::UTF_32BE, Encoding::UTF_32LE, + ] + all_assertions_foreach(nil, *wide_encodings) do |enc| + s = str.encode(enc) + assert_equal(s, ("%s" % s), enc.name) + end + end end