From 715474c24cd9517d30430dc84c53549f7ebf6870 Mon Sep 17 00:00:00 2001 From: Yusuke Endoh Date: Sun, 25 Oct 2026 05:24:08 +0900 Subject: [PATCH] Close the encoding converter when a GzipFile is freed The converter opened for external/internal encoding options was only closed on rewind, so it leaked for every GzipReader and GzipWriter. gz = Zlib.gzip("abc") opts = {external_encoding: "Shift_JIS", internal_encoding: "UTF-8"} 300_000.times { Zlib::GzipReader.new(StringIO.new(gz), **opts).close } # RSS: 1.3 GB (35 MB without the options) Co-Authored-By: Claude Opus 5.5 --- ext/zlib/zlib.c | 3 +++ test/zlib/test_zlib.rb | 10 ++++++++++ 2 files changed, 13 insertions(+) diff --git a/ext/zlib/zlib.c b/ext/zlib/zlib.c index f13ef4d..560cc13 100644 --- a/ext/zlib/zlib.c +++ b/ext/zlib/zlib.c @@ -2519,6 +2519,9 @@ gzfile_free(void *p) } zstream_finalize(z); } + if (gz->ec) { + rb_econv_close(gz->ec); + } xfree(gz); } diff --git a/test/zlib/test_zlib.rb b/test/zlib/test_zlib.rb index e1ecbf8..4feddab 100644 --- a/test/zlib/test_zlib.rb +++ b/test/zlib/test_zlib.rb @@ -1732,6 +1732,16 @@ def test_gunzip_no_memory_leak }; end + def test_gzip_reader_encoding_conversion_no_memory_leak + assert_no_memory_leak(%W[-r#{LOADED_ZLIB} -rstringio], "#{<<~"{#"}", "#{<<~'};'}", rss: true) + d = Zlib.gzip("data") + opts = {external_encoding: "Shift_JIS", internal_encoding: "UTF-8"} + 10_000.times {Zlib::GzipReader.new(StringIO.new(d), **opts).close} + {# + 20_000.times {Zlib::GzipReader.new(StringIO.new(d), **opts).close} + }; + end + # Test for signal interrupt bug: Z_BUF_ERROR with avail_out > 0 # This reproduces the issue where thread wakeup during GzipReader operations # can cause Z_BUF_ERROR to be raised incorrectly