Bug #22412
closedAssertion Failed: vm_ep_in_heap_p_:env->ep == ep
Description
The following code:
(also lambda {|a, b| }.curry.clone[1], or define_method(:m, &proc {|a, b| }.curry); m(1) which dups the proc internally)
Resulted in this output (RUBY_DEBUG build):
../vm.c:281: Assertion Failed: vm_ep_in_heap_p_:env->ep == ep
ruby 4.1.0dev (2026-10-07) +PRISM [x86_64-linux]
-- Ruby level backtrace information ----------------------------------------
min.rb:2:in '<main>'
min.rb:2:in 'proc'
-- C level backtrace information -------------------------------------------
(vm_ep_in_heap_p_+0x1a2) vm.c:281
(VM_ENV_ESCAPED_P+0xae) vm.c:295
(vm_make_env_each+0x133) vm.c:1120
(vm_make_env_object+0x8) vm.c:1217
(rb_vm_make_proc_lambda) vm.c:1720
(vm_call0_body+0x100a) ../vm_eval.c:164
(rb_call0+0x4c5) ../vm_eval.c:101
(iterate_method+0x144) ../vm_eval.c:896
(rb_iterate0+0x298) ../vm_eval.c:1506
(rb_block_call_kw+0x42) ../vm_eval.c:1580
(make_curry_proc) proc.c:4525
(curry+0x118) proc.c:4545
(vm_yield_with_cfunc+0x172) ../vm_insnhelper.c:5275
rb_func_proc_dup (proc.c) gives the copy its own ep inside the new cfunc_proc_t, but copies
ep[VM_ENV_DATA_INDEX_ENV] from the source. For a curried proc that slot already holds an env object
(created when curry called Kernel#proc/lambda on the ifunc block), whose env->ep points into the
source proc's storage. The duplicate's ep and its env therefore disagree. Only the debug assertion fires
in our testing; we did not observe a crash or ASan report on a non-debug build, even after dropping the
source proc and running GC.start/GC.compact. (Related area: Bug #20950, whose backport note mentions a
vm_ep_in_heap_p_ VM_ASSERT failure; possibly also related to Bug #22113.)
To reproduce:
Commit:
3296d5c99ce005e4698fb27a405e01d766ad2647 (2026-09-15); also reproduced on master 9ce0df671980d669cbab8afc48124c7453897533 (2026-10-07)
Build configuration:
../configure --disable-install-doc CC=clang-18 cflags="-fsanitize=address -fno-omit-frame-pointer -DUSE_MN_THREADS=0" cppflags="-DRUBY_DEBUG=1" optflags="-O1" debugflags="-g"
Operating System:
This bug was found by fusion-fuzz
Updated by peterzhu2118 (Peter Zhu) 29 days ago
- Backport changed from 3.3: UNKNOWN, 3.4: UNKNOWN, 4.0: UNKNOWN to 3.3: DONTNEED, 3.4: DONTNEED, 4.0: DONTNEED
Thank you for the bug report. I have a fix here: https://github.com/ruby/ruby/pull/19251
Updated by peterzhu2118 (Peter Zhu) 28 days ago
- Status changed from Open to Closed
Applied in changeset git|bd24fa62a592ec4b8c2eecbebf5e6d347ade9052.
Copy ep when duplicating ifunc procs
[Bug #22412]
When duplicating ifunc procs that have heap allocated env, we should share
it instead of making a copy.
In debug mode, the following script triggers an assertion failure:
It crashes with:
vm.c:281: Assertion Failed: vm_ep_in_heap_p_:env->ep == ep
ruby 4.1.0dev (2026-10-07T07:57:31Z master 9ce0df6719) +PRISM [arm64-darwin27]
-- Crash Report log information --------------------------------------------
See Crash Report log file in one of the following locations:
* ~/Library/Logs/DiagnosticReports
* /Library/Logs/DiagnosticReports
for more details.
Don't forget to include the above Crash Report log file in bug reports.
-- Control frame information -----------------------------------------------
c:0004 p:---- s:0014 e:000013 l:y b:0001 r:0x0 CFUNC :proc
c:0003 p:---- s:0011 e:000010 l:n b:---- r:0x0 IFUNC
c:0002 p:0015 s:0008 E:001e90 l:n b:---- r:0x0 EVAL test.rb:2 [FINISH]
c:0001 p:0000 s:0003 E:001ff8 l:y b:---- r:0x0 DUMMY [FINISH]
-- Ruby level backtrace information ----------------------------------------
test.rb:2:in '<main>'
test.rb:2:in 'proc'
-- Threading information ---------------------------------------------------
Total ractor count: 1
Ruby thread count for this ractor: 1
-- C level backtrace information -------------------------------------------
miniruby(rb_assert_failure_detail+0xf0) [0x105456fe4] error.c:1268
miniruby(rb_assert_failure_detail) (null):0
miniruby(rb_assert_failure_detail+0x0) [0x105456ef4] error.c:1244
miniruby(rb_assert_failure) (null):0
miniruby(vm_ep_in_heap_p_.cold.1+0x28) [0x105463d24] vm.c:281
miniruby(vm_ep_in_heap_p_.cold.2) (null):0
miniruby(rb_vm_cref_dup+0x0) [0x10519a4e4] vm.c:281
miniruby(vm_ep_in_heap_p_) (null):0
miniruby(VM_ENV_ESCAPED_P+0x68) [0x1051ba188] vm.c:295
miniruby(vm_make_env_each+0xa4) [0x1051d56c8] vm.c:1120
miniruby(vm_make_env_object+0x8) [0x1051b9f18] vm.c:1217
miniruby(rb_vm_make_proc_lambda) vm.c:1720
miniruby(vm_call0_body+0x778) [0x1051d39dc] vm_eval.c:164
miniruby(rb_call0+0x2ac) [0x1051d47e0] vm_eval.c:101
miniruby(rb_iterate0+0x1d0) [0x1051b5e04] vm_eval.c:1506
miniruby(rb_block_call_kw+0x28) [0x1051b59a4] vm_eval.c:1580
miniruby(rb_block_call) vm_eval.c:1566
miniruby(RB_SPECIAL_CONST_P+0x0) [0x105096de0] proc.c:4236
miniruby(rbimpl_RB_TYPE_P_fastpath) include/ruby/internal/value_type.h:349
miniruby(rbimpl_obj_typeddata_p) include/ruby/internal/core/rtypeddata.h:661
miniruby(rb_proc_new) include/ruby/internal/core/rtypeddata.h:735
miniruby(make_curry_proc) proc.c:4524
miniruby(vm_yield_with_cfunc+0xc0) [0x10519de40] vm_insnhelper.c:5275
miniruby(vm_invoke_ifunc_block+0xe0) [0x1051cd8bc] vm_insnhelper.c:5539
miniruby(vm_exec_core+0x42ec) [0x1051a5d48] vm_insnhelper.c:6285
miniruby(vm_exec_loop+0x0) [0x10519f240] vm.c:2909
miniruby(rb_vm_exec) vm.c:2912
miniruby(rb_ec_exec_node+0xa0) [0x104fac450] eval.c:300
miniruby(ruby_run_node+0x64) [0x104fac354] eval.c:338
miniruby(rb_main+0x1c) [0x104ee09b0] main.c:42
miniruby(main) main.c:62