From: "peterzhu2118 (Peter Zhu) via ruby-core" Date: 2026-10-14T11:01:40+00:00 Subject: [ruby-core:127101] [Ruby Bug#22436] Array#zip with a method_missing-based argument leaks a stale block handler (segfault when the next C-called method yields) Issue #22436 has been updated by peterzhu2118 (Peter Zhu). Backport changed from 3.3: UNKNOWN, 3.4: UNKNOWN, 4.0: UNKNOWN to 3.3: WONTFIX, 3.4: REQUIRED, 4.0: REQUIRED Thank you for the bug report, I have a fix here: https://github.com/ruby/ruby/pull/19337 ---------------------------------------- Bug #22436: Array#zip with a method_missing-based argument leaks a stale block handler (segfault when the next C-called method yields) https://bugs.ruby-lang.org/issues/22436#change-119501 * Author: 0599jiangyc@gmail.com (Yuancheng Jiang) * Status: Open * Backport: 3.3: WONTFIX, 3.4: REQUIRED, 4.0: REQUIRED ---------------------------------------- The following code: ```rb o = Object.new def o.method_missing(*) = nil # Array#zip calls o.each via method_missing out = Object.new def out.write(*) = nil def out.puts(*) = yield # receives a block it was never given $stdout = out [1].zip(o) puts 1 # Kernel#puts -> $stdout.puts gets a stale ifunc block ``` Resulted in this output (release build, also ruby 3.2.3): ``` min.rb:6: [BUG] Segmentation fault at 0x0000000000000018 -- Control frame information ----------------------------------------------- c:0005 p:---- s:0021 e:000020 l:n b:---- r:(nil) IFUNC :puts c:0004 p:0002 s:0018 e:000017 l:y b:0001 r:(nil) METHOD min.rb:6 [FINISH] c:0003 p:---- s:0013 e:000012 l:y b:0001 r:(nil) CFUNC :puts c:0002 p:0062 s:0008 E:001df0 l:n b:---- r:(nil) EVAL min.rb:10 [FINISH] -- C level backtrace information ------------------------------------------- ruby(vm_yield_with_cfunc+0x329) ../vm_insnhelper.c:5273 ruby(vm_invoke_ifunc_block+0x1a9) ../vm_insnhelper.c:5537 ruby(vm_sendish+0xea) ../vm_insnhelper.c:6290 ruby(rb_vm_exec) vm.c:2884 ruby(vm_call0_body+0xeb2) ../vm_eval.c:225 ruby(rb_call0) ../vm_eval.c:577 ruby(rb_funcallv_kw+0x8e) ../vm_eval.c:896 ruby(vm_call_cfunc_with_frame_) ../vm_insnhelper.c:3906 ``` With `-DRUBY_DEBUG=1` an even smaller program trips the VM assertion: ```rb o = Object.new def o.method_missing(*) = nil [1].zip(o) puts 1 ``` ``` ../vm_core.h:1849: Assertion Failed: VM_BH_IFUNC_P:imemo_type_p(captured->code.val, imemo_ifunc) ruby 4.1.0dev (2026-10-11T06:59:58Z master 46033eff6c) +PRISM [x86_64-linux] ruby(rb_assert_failure) ruby(vm_block_handler_type+0x15b) ../vm_core.h:1849 ruby(vm_block_handler_verify+0xd) ../vm_core.h:1903 ruby(rb_call0) vm.c:327 ruby(rb_funcallv_kw+0xdd) ../vm_eval.c:896 ruby(vm_call_cfunc_with_frame_+0x27b) ../vm_insnhelper.c:3906 ``` The same thing also silently drops the block. `method_missing(:each, &b)` called from `Array#zip` gets `b == nil`. Analysis: `take_items` (array.c) calls `rb_check_block_call`, and `rb_iterate0` sets `ec->passed_block_handler` to an ifunc block captured in the `zip` frame. When `each` is missing, `check_funcall_missing` -> `check_funcall_exec` -> `call_method_entry` (vm_method.c) takes the passed block handler, calls `rb_vm_call_kw` (so `method_missing` runs with no block), and then *restores* the handler. Nothing consumes it after that. After `zip` returns, `ec->passed_block_handler` still points at the popped frame's captured block. The next C-level `rb_funcall*` (here Kernel#puts -> `$stdout.puts`) picks it up in `vm_passed_block_handler()` and passes it as the callee's block. `call_method_entry`'s save/restore suits `respond_to_missing?`, but on the final `method_missing` call the block should be passed to that method, not restored. To reproduce: ``` ruby ./min.rb ``` Commit: ``` 46033eff6c01f05afa2505452fdbf4bea7ca9c6d (also reproduced on 3296d5c99c and ruby 3.2.3) ``` Build configuration: ``` ../configure --prefix=... --disable-install-doc cflags="-fsanitize=address -fno-omit-frame-pointer -DUSE_MN_THREADS=0" cppflags="-DRUBY_DEBUG=1" optflags="-O1" debugflags="-g" (segfault also reproduces with a non-debug build and with Ubuntu's packaged ruby 3.2.3) ``` Operating System: ``` Ubuntu 24.04.4 LTS (x86_64), Docker image fusion-fuzz-ruby:latest ``` -- https://bugs.ruby-lang.org/ ______________________________________________ ruby-core mailing list -- ruby-core@ml.ruby-lang.org To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org ruby-core info -- https://ml.ruby-lang.org/mailman3/lists/ruby-core.ml.ruby-lang.org/