From: "jeremyevans0 (Jeremy Evans) via ruby-core" Date: 2025-05-03T18:23:06+00:00 Subject: [ruby-core:121816] [Ruby Bug#21305] heap-use-after-free of set#merge via mutating hash method Issue #21305 has been updated by jeremyevans0 (Jeremy Evans). Potential fix in https://github.com/ruby/ruby/pull/13253 I'm not sure ASAN is supported on OpenBSD, so I'm not sure I can test with ASAN. If you could test to confirm the fix, I would appreciate it. ---------------------------------------- Bug #21305: heap-use-after-free of set#merge via mutating hash method https://bugs.ruby-lang.org/issues/21305#change-112877 * Author: cyruscyliu (Qiang Liu) * Status: Open * Assignee: jeremyevans0 (Jeremy Evans) * ruby -v: 3.5.0 * Backport: 3.2: UNKNOWN, 3.3: UNKNOWN, 3.4: UNKNOWN ---------------------------------------- Hi, we found a heap-use-after-free of set#merge via mutating hash method. Here is the PoC. ``` class C def hash $a.clear return 0 end end $a = (1..100).to_a $a.insert(0, C.new) $b = Set.new([]) $b.merge($a) ``` Calling Set#merge on a set with an object whose hash method mutates the array causes memory corruption. In this case, C#hash clears $a while Set#merge is iterating over it, breaking internal state and potentially leading to a crash. To reproduce, compile the recent Ruby with ASAN, and run the PoC. ``` $ git log | head -n3 commit 36c64b3be83f17992137d63ffd0b94f90e24424a Author: John Hawthorn Date: Fri Apr 11 16:02:23 2025 -070 `RubyGems' were not loaded. `error_highlight' was not loaded. `did_you_mean' was not loaded. `syntax_suggest' was not loaded. ================================================================= ==106456==ERROR: AddressSanitizer: heap-use-after-free on address 0x51900000dc88 at pc 0x5c83c02ed8b8 bp 0x7fff8201f1b0 sp 0x7fff8201f1a8 READ of size 8 at 0x51900000dc88 thread T0 #0 0x5c83c02ed8b7 in set_merge_enum_into /media/test/ruby/build/../set.c:1126:13 #1 0x5c83c02eb51f in set_i_merge /media/test/ruby/build/../set.c:1156:9 #2 0x5c83c041eb3b in vm_call_cfunc_with_frame_ /media/test/ruby/build/../vm_insnhelper.c:3797:11 #3 0x5c83c04074e3 in vm_call_method_each_type /media/test/ruby/build/../vm_insnhelper.c:4775:16 #4 0x5c83c0406fd3 in vm_call_method /media/test/ruby/build/../vm_insnhelper.c #5 0x5c83c03cd1d8 in vm_sendish /media/test/ruby/build/../vm_insnhelper.c:5972:15 #6 0x5c83c03cd1d8 in vm_exec_core /media/test/ruby/build/../insns.def:899:11 #7 0x5c83c03c5a47 in rb_vm_exec /media/test/ruby/build/../vm.c #8 0x5c83c0097ce0 in rb_ec_exec_node /media/test/ruby/build/../eval.c:281:9 #9 0x5c83c0097ce0 in ruby_run_node /media/test/ruby/build/../eval.c:319:30 #10 0x5c83c00933a0 in rb_main /media/test/ruby/build/../main.c:42:12 #11 0x5c83c00933a0 in main /media/test/ruby/build/../main.c:62:12 #12 0x72c910229d8f in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16 #13 0x72c910229e3f in __libc_start_main csu/../csu/libc-start.c:392:3 #14 0x5c83bffbbd54 in _start (/media/test/ruby/build/ruby+0x148d54) (BuildId: 58c97094e0527fad484552e230da980d80ffa516) 0x51900000dc88 is located 8 bytes inside of 1024-byte region [0x51900000dc80,0x51900000e080) freed by thread T0 here: #0 0x5c83c005637c in realloc (/media/test/ruby/build/ruby+0x1e337c) (BuildId: 58c97094e0527fad484552e230da980d80ffa516) #1 0x5c83c00f8acb in rb_gc_impl_realloc /media/test/ruby/build/../gc/default/default.c:8330:5 #2 0x5c83c00d5fd9 in ruby_sized_xrealloc2_body /media/test/ruby/build/../gc.c:4772:12 #3 0x5c83c00d5fd9 in ruby_sized_xrealloc2 /media/test/ruby/build/../gc.c:4765:34 #4 0x5c83c00d5fd9 in ruby_xrealloc2 /media/test/ruby/build/../gc.c:4778:12 #5 0x5c83c0485d64 in ary_heap_realloc /media/test/ruby/build/../array.c:370:5 #6 0x5c83c0485d64 in ary_resize_capa /media/test/ruby/build/../array.c:412:24 #7 0x5c83c048d663 in rb_ary_clear /media/test/ruby/build/../array.c:4750:13 #8 0x5c83c041eb3b in vm_call_cfunc_with_frame_ /media/test/ruby/build/../vm_insnhelper.c:3797:11 #9 0x5c83c04074e3 in vm_call_method_each_type /media/test/ruby/build/../vm_insnhelper.c:4775:16 #10 0x5c83c0406fd3 in vm_call_method /media/test/ruby/build/../vm_insnhelper.c #11 0x5c83c03cd1d8 in vm_sendish /media/test/ruby/build/../vm_insnhelper.c:5972:15 #12 0x5c83c03cd1d8 in vm_exec_core /media/test/ruby/build/../insns.def:899:11 #13 0x5c83c03c5a47 in rb_vm_exec /media/test/ruby/build/../vm.c #14 0x5c83c04308ed in vm_call0_body /media/test/ruby/build/../vm_eval.c:225:20 #15 0x5c83c03eb3bb in vm_call0_cc /media/test/ruby/build/../vm_eval.c:101:12 #16 0x5c83c03eb3bb in rb_funcallv_scope /media/test/ruby/build/../vm_eval.c:1047:16 #17 0x5c83c03f3471 in vm_catch_protect /media/test/ruby/build/../vm_eval.c:2612:15 #18 0x5c83c03548b5 in exec_recursive /media/test/ruby/build/../thread.c:5300:22 #19 0x5c83c0100fd3 in obj_any_hash /media/test/ruby/build/../hash.c:238:16 #20 0x5c83c0100cf2 in any_hash /media/test/ruby/build/../hash.c:207:16 #21 0x5c83c02e3c42 in set_do_hash /media/test/ruby/build/../st.c:2349:33 #22 0x5c83c02e3c42 in rb_set_insert /media/test/ruby/build/../st.c:2892:18 #23 0x5c83c02ed75b in set_table_insert_wb /media/test/ruby/build/../set.c:378:15 #24 0x5c83c02ed75b in set_merge_enum_into /media/test/ruby/build/../set.c:1126:13 #25 0x5c83c02eb51f in set_i_merge /media/test/ruby/build/../set.c:1156:9 #26 0x5c83c041eb3b in vm_call_cfunc_with_frame_ /media/test/ruby/build/../vm_insnhelper.c:3797:11 #27 0x5c83c04074e3 in vm_call_method_each_type /media/test/ruby/build/../vm_insnhelper.c:4775:16 #28 0x5c83c0406fd3 in vm_call_method /media/test/ruby/build/../vm_insnhelper.c #29 0x5c83c03cd1d8 in vm_sendish /media/test/ruby/build/../vm_insnhelper.c:5972:15 #30 0x5c83c03cd1d8 in vm_exec_core /media/test/ruby/build/../insns.def:899:11 #31 0x5c83c03c5a47 in rb_vm_exec /media/test/ruby/build/../vm.c #32 0x5c83c0097ce0 in rb_ec_exec_node /media/test/ruby/build/../eval.c:281:9 #33 0x5c83c0097ce0 in ruby_run_node /media/test/ruby/build/../eval.c:319:30 #34 0x5c83c00933a0 in rb_main /media/test/ruby/build/../main.c:42:12 #35 0x5c83c00933a0 in main /media/test/ruby/build/../main.c:62:12 #36 0x72c910229d8f in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16 previously allocated by thread T0 here: #0 0x5c83c005637c in realloc (/media/test/ruby/build/ruby+0x1e337c) (BuildId: 58c97094e0527fad484552e230da980d80ffa516) #1 0x5c83c00f8acb in rb_gc_impl_realloc /media/test/ruby/build/../gc/default/default.c:8330:5 #2 0x5c83c00d5fd9 in ruby_sized_xrealloc2_body /media/test/ruby/build/../gc.c:4772:12 #3 0x5c83c00d5fd9 in ruby_sized_xrealloc2 /media/test/ruby/build/../gc.c:4765:34 #4 0x5c83c00d5fd9 in ruby_xrealloc2 /media/test/ruby/build/../gc.c:4778:12 #5 0x5c83c0485d64 in ary_heap_realloc /media/test/ruby/build/../array.c:370:5 #6 0x5c83c0485d64 in ary_resize_capa /media/test/ruby/build/../array.c:412:24 #7 0x5c83c048551b in ary_double_capa /media/test/ruby/build/../array.c:461:5 #8 0x5c83c048551b in ary_ensure_room_for_push /media/test/ruby/build/../array.c:620:9 #9 0x5c83c04850c4 in rb_ary_push /media/test/ruby/build/../array.c:1386:24 #10 0x5c83c0684eb9 in collect_all /media/test/ruby/build/../enum.c:636:5 #11 0x5c83c03c1803 in vm_yield_with_cfunc /media/test/ruby/build/../vm_insnhelper.c:5146:11 #12 0x5c83c04381f8 in invoke_block_from_c_bh /media/test/ruby/build/../vm.c:1667:16 #13 0x5c83c03ed02a in vm_yield_with_cref /media/test/ruby/build/../vm.c:1699:12 #14 0x5c83c03ed02a in vm_yield /media/test/ruby/build/../vm.c:1707:12 #15 0x5c83c03ed02a in rb_yield_0 /media/test/ruby/build/../vm_eval.c:1344:12 #16 0x5c83c03ed02a in rb_yield /media/test/ruby/build/../vm_eval.c #17 0x5c83c021cd87 in range_each_fixnum_loop /media/test/ruby/build/../range.c:1059:9 #18 0x5c83c021cd87 in range_each /media/test/ruby/build/../range.c:1096:16 #19 0x5c83c0430bb1 in vm_call0_cfunc_with_frame /media/test/ruby/build/../vm_eval.c:164:15 #20 0x5c83c0430bb1 in vm_call0_cfunc /media/test/ruby/build/../vm_eval.c:178:12 #21 0x5c83c0430bb1 in vm_call0_body /media/test/ruby/build/../vm_eval.c:229:15 #22 0x5c83c0434443 in vm_call0_cc /media/test/ruby/build/../vm_eval.c:101:12 #23 0x5c83c0434443 in rb_call0 /media/test/ruby/build/../vm_eval.c:554:12 #24 0x5c83c03ee955 in rb_call /media/test/ruby/build/../vm_eval.c:873:12 #25 0x5c83c03ee955 in iterate_method /media/test/ruby/build/../vm_eval.c:1528:12 #26 0x5c83c03eef55 in rb_iterate0 /media/test/ruby/build/../vm_eval.c:1470:18 #27 0x5c83c03ee7c9 in rb_iterate_internal /media/test/ruby/build/../vm_eval.c:1502:12 #28 0x5c83c03ee7c9 in rb_block_call_kw /media/test/ruby/build/../vm_eval.c:1551:12 #29 0x5c83c067fb7b in enum_to_a /media/test/ruby/build/../enum.c:735:5 #30 0x5c83c0430bb1 in vm_call0_cfunc_with_frame /media/test/ruby/build/../vm_eval.c:164:15 #31 0x5c83c0430bb1 in vm_call0_cfunc /media/test/ruby/build/../vm_eval.c:178:12 #32 0x5c83c0430bb1 in vm_call0_body /media/test/ruby/build/../vm_eval.c:229:15 #33 0x5c83c03e9076 in vm_call0_cc /media/test/ruby/build/../vm_eval.c:101:12 #34 0x5c83c03e9076 in rb_vm_call0 /media/test/ruby/build/../vm_eval.c:61:12 #35 0x5c83c03e9076 in rb_vm_call_kw /media/test/ruby/build/../vm_eval.c:326:12 #36 0x5c83c03e9076 in vm_call_super /media/test/ruby/build/../vm_eval.c:350:12 #37 0x5c83c03e9076 in rb_call_super_kw /media/test/ruby/build/../vm_eval.c:358:12 #38 0x5c83c041eb3b in vm_call_cfunc_with_frame_ /media/test/ruby/build/../vm_insnhelper.c:3797:11 #39 0x5c83c04074e3 in vm_call_method_each_type /media/test/ruby/build/../vm_insnhelper.c:4775:16 #40 0x5c83c0406fd3 in vm_call_method /media/test/ruby/build/../vm_insnhelper.c #41 0x5c83c03cd1d8 in vm_sendish /media/test/ruby/build/../vm_insnhelper.c:5972:15 #42 0x5c83c03cd1d8 in vm_exec_core /media/test/ruby/build/../insns.def:899:11 #43 0x5c83c03c5a47 in rb_vm_exec /media/test/ruby/build/../vm.c #44 0x5c83c0097ce0 in rb_ec_exec_node /media/test/ruby/build/../eval.c:281:9 #45 0x5c83c0097ce0 in ruby_run_node /media/test/ruby/build/../eval.c:319:30 #46 0x5c83c00933a0 in rb_main /media/test/ruby/build/../main.c:42:12 #47 0x5c83c00933a0 in main /media/test/ruby/build/../main.c:62:12 #48 0x72c910229d8f in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16 SUMMARY: AddressSanitizer: heap-use-after-free /media/test/ruby/build/../set.c:1126:13 in set_merge_enum_into Shadow bytes around the buggy address: 0x51900000da00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x51900000da80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x51900000db00: 00 00 00 00 00 00 00 00 00 fa fa fa fa fa fa fa 0x51900000db80: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x51900000dc00: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa =>0x51900000dc80: fd[fd]fd fd fd fd fd fd fd fd fd fd fd fd fd fd 0x51900000dd00: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd 0x51900000dd80: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd 0x51900000de00: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd 0x51900000de80: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd 0x51900000df00: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd Shadow byte legend (one shadow byte represents 8 application bytes): Addressable: 00 Partially addressable: 01 02 03 04 05 06 07 Heap left redzone: fa Freed heap region: fd Stack left redzone: f1 Stack mid redzone: f2 Stack right redzone: f3 Stack after return: f5 Stack use after scope: f8 Global redzone: f9 Global init order: f6 Poisoned by user: f7 Container overflow: fc Array cookie: ac Intra object redzone: bb ASan internal: fe Left alloca redzone: ca Right alloca redzone: cb ==106456==ABORTING ../triaged/set_merge.rb:11: [BUG] ASAN error ruby 3.5.0dev (2025-05-02T21:28:25Z master 36c64b3be8) +PRISM [x86_64-linux] -- Control frame information ----------------------------------------------- c:0003 p:---- s:0011 e:000010 CFUNC :merge c:0002 p:0060 s:0006 e:000005 EVAL ../triaged/set_merge.rb:11 [FINISH] c:0001 p:0000 s:0003 E:000540 DUMMY [FINISH] -- Ruby level backtrace information ---------------------------------------- ../triaged/set_merge.rb:11:in '
' ../triaged/set_merge.rb:11:in 'merge' -- Threading information --------------------------------------------------- Total ractor count: 1 Ruby thread count for this ractor: 1 -- C level backtrace information ------------------------------------------- ./ruby(___interceptor_backtrace) [0x5c83c0000006] /media/test/ruby/build/ruby(rb_print_backtrace+0x14) [0x5c83c074e337] /media/test/ruby/build/../vm_dump.c:839 /media/test/ruby/build/ruby(rb_vm_bugreport) /media/test/ruby/build/../vm_dump.c:1171 /media/test/ruby/build/ruby(rb_bug_without_die_internal+0x23c) [0x5c83c06a276c] /media/test/ruby/build/../error.c:1097 /media/test/ruby/build/ruby(rb_bug_without_die+0x127) [0x5c83c06a2487] /media/test/ruby/build/../error.c:1106 ./ruby(0x5c83c0079bc6) [0x5c83c0079bc6] ./ruby(0x5c83c005ac9f) [0x5c83c005ac9f] ./ruby(0x5c83c005dce5) [0x5c83c005dce5] ./ruby(__asan_report_load8) [0x5c83c005e988] /media/test/ruby/build/ruby(set_merge_enum_into+0x428) [0x5c83c02ed8b8] /media/test/ruby/build/../set.c:1126 /media/test/ruby/build/ruby(set_i_merge+0xa0) [0x5c83c02eb520] /media/test/ruby/build/../set.c:1156 /media/test/ruby/build/ruby(vm_cfp_consistent_p+0x0) [0x5c83c041eb3c] ../vm_insnhelper.c:3797 /media/test/ruby/build/ruby(vm_call_cfunc_with_frame_) ../vm_insnhelper.c:3799 /media/test/ruby/build/ruby(vm_call_method_each_type+0x264) [0x5c83c04074e4] ../vm_insnhelper.c:4775 ./ruby(vm_call_method+0x2d4) [0x5c83c0406fd4] /media/test/ruby/build/ruby(vm_sendish+0x1c8) [0x5c83c03cd1d9] ../vm_insnhelper.c:5972 /media/test/ruby/build/ruby(vm_exec_core) ../insns.def:899 ./ruby(vm_exec_loop+0x0) [0x5c83c03c5a48] /media/test/ruby/build/ruby(rb_vm_exec) /media/test/ruby/build/../vm.c:2621 /media/test/ruby/build/ruby(rb_ec_exec_node+0x53) [0x5c83c0097ce1] /media/test/ruby/build/../eval.c:281 /media/test/ruby/build/ruby(ruby_run_node) /media/test/ruby/build/../eval.c:319 /media/test/ruby/build/ruby(rb_main+0x29) [0x5c83c00933a1] /media/test/ruby/build/../main.c:42 /media/test/ruby/build/ruby(main) /media/test/ruby/build/../main.c:62 /lib/x86_64-linux-gnu/libc.so.6(__libc_start_call_main+0x80) [0x72c910229d90] ../sysdeps/nptl/libc_start_call_main.h:58 /lib/x86_64-linux-gnu/libc.so.6(call_init+0x0) [0x72c910229e40] ../csu/libc-start.c:392 /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main_impl) ../csu/libc-start.c:379 /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main) (null):0 ./ruby(_start) [0x5c83bffbbd55] -- Other runtime information ----------------------------------------------- * Loaded script: ../triaged/set_merge.rb * Loaded features: 0 enumerator.so 1 thread.rb 2 fiber.so 3 rational.so 4 complex.so 5 ruby2_keywords.rb 6 set.rb ``` -- 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/