From e276076a2f3aaae3c462c1a0fc98c285ba8a2a28 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Fri, 27 Feb 2026 10:49:57 +0900 Subject: [PATCH 1/3] The `case` expression is preferable to the pseudo-condition operator --- .github/workflows/mingw.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/mingw.yml b/.github/workflows/mingw.yml index 8afa726334a90c..27e21e36389f77 100644 --- a/.github/workflows/mingw.yml +++ b/.github/workflows/mingw.yml @@ -44,7 +44,7 @@ jobs: ) }} MINGW_PACKAGE_PREFIX: >- mingw-w${{ - endsWith(matrix.msystem, '64') && '64' || '32' + case(endsWith(matrix.msystem, '64'), '64', '32') }}-${{ case( startsWith(matrix.msystem, 'clang'), 'clang', startsWith(matrix.msystem, 'ucrt'), 'ucrt', From cded315c516c48d4b4c10d37ce13ea46858cd6c8 Mon Sep 17 00:00:00 2001 From: Samuel Williams Date: Fri, 27 Feb 2026 15:56:33 +1300 Subject: [PATCH 2/3] [ruby/zlib] Avoid retrying on interrupt if there is no further work to be done. (https://github.com/ruby/zlib/pull/121) https://github.com/ruby/zlib/commit/c975060f02 --- ext/zlib/zlib.c | 16 +++++++---- test/zlib/test_zlib.rb | 61 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+), 5 deletions(-) diff --git a/ext/zlib/zlib.c b/ext/zlib/zlib.c index 7e319cae0de3cf..578ad108515d64 100644 --- a/ext/zlib/zlib.c +++ b/ext/zlib/zlib.c @@ -1094,8 +1094,9 @@ zstream_run_func(struct zstream_run_args *args) break; } - if (err != Z_OK && err != Z_BUF_ERROR) + if (err != Z_OK && err != Z_BUF_ERROR) { break; + } if (z->stream.avail_out > 0) { z->flags |= ZSTREAM_FLAG_IN_STREAM; @@ -1170,12 +1171,17 @@ zstream_run_try(VALUE value_arg) /* retry if no exception is thrown */ if (err == Z_OK && args->interrupt) { args->interrupt = 0; - goto loop; + + /* Retry only if both avail_in > 0 (more input to process) and avail_out > 0 + * (output buffer has space). If avail_out == 0, the buffer is full and should + * be consumed by the caller first. If avail_in == 0, there's nothing more to process. */ + if (z->stream.avail_in > 0 && z->stream.avail_out > 0) { + goto loop; + } } - if (flush != Z_FINISH && err == Z_BUF_ERROR - && z->stream.avail_out > 0) { - z->flags |= ZSTREAM_FLAG_IN_STREAM; + if (flush != Z_FINISH && err == Z_BUF_ERROR && z->stream.avail_out > 0) { + z->flags |= ZSTREAM_FLAG_IN_STREAM; } zstream_reset_input(z); diff --git a/test/zlib/test_zlib.rb b/test/zlib/test_zlib.rb index bedf243b3e5919..5b0bf9c9807ce7 100644 --- a/test/zlib/test_zlib.rb +++ b/test/zlib/test_zlib.rb @@ -1266,6 +1266,36 @@ def test_gzfile_read_size_boundary end } 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 + def test_thread_wakeup_interrupt + pend 'fails' if RUBY_ENGINE == 'truffleruby' + content = SecureRandom.base64(5000) + gzipped = Zlib.gzip(content) + + 1000.times do + thr = Thread.new do + loop do + Zlib::GzipReader.new(StringIO.new(gzipped)).read + end + end + + # Wakeup the thread multiple times to trigger interrupts + 10.times do + thr.wakeup + Thread.pass + end + + # Give thread a moment to process + sleep 0.001 + + # Clean up + thr.kill + thr.join + end + end end class TestZlibGzipWriter < Test::Unit::TestCase @@ -1534,5 +1564,36 @@ def test_gunzip_no_memory_leak 10_000.times {Zlib.gunzip(d)} }; 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 + def test_thread_wakeup_interrupt + pend 'fails' if RUBY_ENGINE == 'truffleruby' + + content = SecureRandom.base64(5000) + gzipped = Zlib.gzip(content) + + 1000.times do + thr = Thread.new do + loop do + Zlib::GzipReader.new(StringIO.new(gzipped)).read + end + end + + # Wakeup the thread multiple times to trigger interrupts + 10.times do + thr.wakeup + Thread.pass + end + + # Give thread a moment to process + sleep 0.001 + + # Clean up + thr.kill + thr.join + end + end end end From 2239d54348d9f1f7768860efc9e76876f85766d8 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Thu, 26 Feb 2026 23:29:53 +0900 Subject: [PATCH 3/3] parse.y: narrow excessed_comma to block_param_def --- parse.y | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/parse.y b/parse.y index 6e0c610a7c4359..3a22c68d5df3eb 100644 --- a/parse.y +++ b/parse.y @@ -5008,12 +5008,6 @@ block_param : f_arg ',' f_opt_arg(primary_value) ',' f_rest_arg opt_args_tail(bl $$ = new_args(p, $1, 0, $3, 0, $4, &@$); /*% ripper: params!($:1, Qnil, $:3, Qnil, *$:4[0..2]) %*/ } - | f_arg excessed_comma - { - $$ = new_args_tail(p, 0, 0, 0, &@2); - $$ = new_args(p, $1, 0, $2, 0, $$, &@$); - /*% ripper: params!($:1, Qnil, $:2, Qnil, Qnil, Qnil, Qnil) %*/ - } | f_arg ',' f_rest_arg ',' f_arg opt_args_tail(block_args_tail) { $$ = new_args(p, $1, 0, $3, $5, $6, &@$); @@ -5075,6 +5069,15 @@ block_param_def : '|' opt_block_param opt_bv_decl '|' $$ = $2; /*% ripper: block_var!($:2, $:3) %*/ } + | '|' f_arg excessed_comma opt_bv_decl '|' + { + p->max_numparam = ORDINAL_PARAM; + p->ctxt.in_argdef = 0; + $$ = new_args_tail(p, 0, 0, 0, &@3); + $$ = new_args(p, $2, 0, $3, 0, $$, &@$); + /*% ripper: params!($:2, Qnil, $:3, Qnil, Qnil, Qnil, Qnil) %*/ + /*% ripper: block_var!($:$, $:4) %*/ + } ; opt_block_param : /* none */