Project

General

Profile

Actions

Bug #21306

closed

heap-use-after-free in set initialization via clearing the array while it’s being iterated

Added by cyruscyliu (Qiang Liu) 2 days ago. Updated 2 days ago.

Status:
Closed
Target version:
-
[ruby-core:121809]

Description

Hi, we found a heap-use-after-free in set initialization
via clearing the array while it’s being iterated. Here is the PoC.

$a = (1..100).to_a

s = Set.new($a) { |x|
    $a.clear
}

Initializing a Set with an array that has a block modifying the array causes
memory corruption. In this case, the block clears $a while Set is being
initialized, which can lead to inconsistent internal state and potential
crashes.

To reproduce, compile the recent Ruby with ASAN, and run the PoC.

$ git log | head -n3
commit 36c64b3be83f17992137d63ffd0b94f90e24424a
Author: John Hawthorn <john@hawthorn.email>
Date:   Fri Apr 11 16:02:23 2025 -070

./ruby set_initialize.rb
`RubyGems' were not loaded.
`error_highlight' was not loaded.
`did_you_mean' was not loaded.
`syntax_suggest' was not loaded.
=================================================================
==107246==ERROR: AddressSanitizer: heap-use-after-free on address 0x51900000dc88 at pc 0x5de0c6510d40 bp 0x7ffc82400690 sp 0x7ffc82400688
READ of size 8 at 0x51900000dc88 thread T0
    #0 0x5de0c6510d3f in set_i_initialize /media/test/ruby/build/../set.c:502:17
    #1 0x5de0c6658bb1 in vm_call0_cfunc_with_frame /media/test/ruby/build/../vm_eval.c:164:15
    #2 0x5de0c6658bb1 in vm_call0_cfunc /media/test/ruby/build/../vm_eval.c:178:12
    #3 0x5de0c6658bb1 in vm_call0_body /media/test/ruby/build/../vm_eval.c:229:15
    #4 0x5de0c665c443 in vm_call0_cc /media/test/ruby/build/../vm_eval.c:101:12
    #5 0x5de0c665c443 in rb_call0 /media/test/ruby/build/../vm_eval.c:554:12
    #6 0x5de0c6613825 in rb_call /media/test/ruby/build/../vm_eval.c:873:12
    #7 0x5de0c6613825 in rb_funcallv_kw /media/test/ruby/build/../vm_eval.c:1070:12
    #8 0x5de0c63edc54 in rb_class_new_instance_pass_kw /media/test/ruby/build/../object.c:2203:5
    #9 0x5de0c6646b3b in vm_call_cfunc_with_frame_ /media/test/ruby/build/../vm_insnhelper.c:3797:11
    #10 0x5de0c662f4e3 in vm_call_method_each_type /media/test/ruby/build/../vm_insnhelper.c:4775:16
    #11 0x5de0c662efd3 in vm_call_method /media/test/ruby/build/../vm_insnhelper.c
    #12 0x5de0c65f7f98 in vm_sendish /media/test/ruby/build/../vm_insnhelper.c:5972:15
    #13 0x5de0c65f7f98 in vm_exec_core /media/test/ruby/build/../insns.def:851:11
    #14 0x5de0c65eda47 in rb_vm_exec /media/test/ruby/build/../vm.c
    #15 0x5de0c62bfce0 in rb_ec_exec_node /media/test/ruby/build/../eval.c:281:9
    #16 0x5de0c62bfce0 in ruby_run_node /media/test/ruby/build/../eval.c:319:30
    #17 0x5de0c62bb3a0 in rb_main /media/test/ruby/build/../main.c:42:12
    #18 0x5de0c62bb3a0 in main /media/test/ruby/build/../main.c:62:12
    #19 0x77f098629d8f in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16
    #20 0x77f098629e3f in __libc_start_main csu/../csu/libc-start.c:392:3
    #21 0x5de0c61e3d54 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 0x5de0c627e37c in realloc (/media/test/ruby/build/ruby+0x1e337c) (BuildId: 58c97094e0527fad484552e230da980d80ffa516)
    #1 0x5de0c6320acb in rb_gc_impl_realloc /media/test/ruby/build/../gc/default/default.c:8330:5
    #2 0x5de0c62fdfd9 in ruby_sized_xrealloc2_body /media/test/ruby/build/../gc.c:4772:12
    #3 0x5de0c62fdfd9 in ruby_sized_xrealloc2 /media/test/ruby/build/../gc.c:4765:34
    #4 0x5de0c62fdfd9 in ruby_xrealloc2 /media/test/ruby/build/../gc.c:4778:12
    #5 0x5de0c66add64 in ary_heap_realloc /media/test/ruby/build/../array.c:370:5
    #6 0x5de0c66add64 in ary_resize_capa /media/test/ruby/build/../array.c:412:24
    #7 0x5de0c66b5663 in rb_ary_clear /media/test/ruby/build/../array.c:4750:13
    #8 0x5de0c6646b3b in vm_call_cfunc_with_frame_ /media/test/ruby/build/../vm_insnhelper.c:3797:11
    #9 0x5de0c662f4e3 in vm_call_method_each_type /media/test/ruby/build/../vm_insnhelper.c:4775:16
    #10 0x5de0c662efd3 in vm_call_method /media/test/ruby/build/../vm_insnhelper.c
    #11 0x5de0c65f51d8 in vm_sendish /media/test/ruby/build/../vm_insnhelper.c:5972:15
    #12 0x5de0c65f51d8 in vm_exec_core /media/test/ruby/build/../insns.def:899:11
    #13 0x5de0c65eda47 in rb_vm_exec /media/test/ruby/build/../vm.c
    #14 0x5de0c66607b6 in invoke_iseq_block_from_c /media/test/ruby/build/../vm.c:1648:12
    #15 0x5de0c66607b6 in invoke_block_from_c_bh /media/test/ruby/build/../vm.c:1662:20
    #16 0x5de0c661502a in vm_yield_with_cref /media/test/ruby/build/../vm.c:1699:12
    #17 0x5de0c661502a in vm_yield /media/test/ruby/build/../vm.c:1707:12
    #18 0x5de0c661502a in rb_yield_0 /media/test/ruby/build/../vm_eval.c:1344:12
    #19 0x5de0c661502a in rb_yield /media/test/ruby/build/../vm_eval.c
    #20 0x5de0c6510aa4 in set_i_initialize /media/test/ruby/build/../set.c:502:17
    #21 0x5de0c6658bb1 in vm_call0_cfunc_with_frame /media/test/ruby/build/../vm_eval.c:164:15
    #22 0x5de0c6658bb1 in vm_call0_cfunc /media/test/ruby/build/../vm_eval.c:178:12
    #23 0x5de0c6658bb1 in vm_call0_body /media/test/ruby/build/../vm_eval.c:229:15
    #24 0x5de0c665c443 in vm_call0_cc /media/test/ruby/build/../vm_eval.c:101:12
    #25 0x5de0c665c443 in rb_call0 /media/test/ruby/build/../vm_eval.c:554:12
    #26 0x5de0c6613825 in rb_call /media/test/ruby/build/../vm_eval.c:873:12
    #27 0x5de0c6613825 in rb_funcallv_kw /media/test/ruby/build/../vm_eval.c:1070:12
    #28 0x5de0c63edc54 in rb_class_new_instance_pass_kw /media/test/ruby/build/../object.c:2203:5
    #29 0x5de0c6646b3b in vm_call_cfunc_with_frame_ /media/test/ruby/build/../vm_insnhelper.c:3797:11
    #30 0x5de0c662f4e3 in vm_call_method_each_type /media/test/ruby/build/../vm_insnhelper.c:4775:16
    #31 0x5de0c662efd3 in vm_call_method /media/test/ruby/build/../vm_insnhelper.c
    #32 0x5de0c65f7f98 in vm_sendish /media/test/ruby/build/../vm_insnhelper.c:5972:15
    #33 0x5de0c65f7f98 in vm_exec_core /media/test/ruby/build/../insns.def:851:11
    #34 0x5de0c65eda47 in rb_vm_exec /media/test/ruby/build/../vm.c
    #35 0x5de0c62bfce0 in rb_ec_exec_node /media/test/ruby/build/../eval.c:281:9
    #36 0x5de0c62bfce0 in ruby_run_node /media/test/ruby/build/../eval.c:319:30
    #37 0x5de0c62bb3a0 in rb_main /media/test/ruby/build/../main.c:42:12
    #38 0x5de0c62bb3a0 in main /media/test/ruby/build/../main.c:62:12
    #39 0x77f098629d8f in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16

previously allocated by thread T0 here:
    #0 0x5de0c627e37c in realloc (/media/test/ruby/build/ruby+0x1e337c) (BuildId: 58c97094e0527fad484552e230da980d80ffa516)
    #1 0x5de0c6320acb in rb_gc_impl_realloc /media/test/ruby/build/../gc/default/default.c:8330:5
    #2 0x5de0c62fdfd9 in ruby_sized_xrealloc2_body /media/test/ruby/build/../gc.c:4772:12
    #3 0x5de0c62fdfd9 in ruby_sized_xrealloc2 /media/test/ruby/build/../gc.c:4765:34
    #4 0x5de0c62fdfd9 in ruby_xrealloc2 /media/test/ruby/build/../gc.c:4778:12
    #5 0x5de0c66add64 in ary_heap_realloc /media/test/ruby/build/../array.c:370:5
    #6 0x5de0c66add64 in ary_resize_capa /media/test/ruby/build/../array.c:412:24
    #7 0x5de0c66ad51b in ary_double_capa /media/test/ruby/build/../array.c:461:5
    #8 0x5de0c66ad51b in ary_ensure_room_for_push /media/test/ruby/build/../array.c:620:9
    #9 0x5de0c66ad0c4 in rb_ary_push /media/test/ruby/build/../array.c:1386:24
    #10 0x5de0c68aceb9 in collect_all /media/test/ruby/build/../enum.c:636:5
    #11 0x5de0c65e9803 in vm_yield_with_cfunc /media/test/ruby/build/../vm_insnhelper.c:5146:11
    #12 0x5de0c66601f8 in invoke_block_from_c_bh /media/test/ruby/build/../vm.c:1667:16
    #13 0x5de0c661502a in vm_yield_with_cref /media/test/ruby/build/../vm.c:1699:12
    #14 0x5de0c661502a in vm_yield /media/test/ruby/build/../vm.c:1707:12
    #15 0x5de0c661502a in rb_yield_0 /media/test/ruby/build/../vm_eval.c:1344:12
    #16 0x5de0c661502a in rb_yield /media/test/ruby/build/../vm_eval.c
    #17 0x5de0c6444d87 in range_each_fixnum_loop /media/test/ruby/build/../range.c:1059:9
    #18 0x5de0c6444d87 in range_each /media/test/ruby/build/../range.c:1096:16
    #19 0x5de0c6658bb1 in vm_call0_cfunc_with_frame /media/test/ruby/build/../vm_eval.c:164:15
    #20 0x5de0c6658bb1 in vm_call0_cfunc /media/test/ruby/build/../vm_eval.c:178:12
    #21 0x5de0c6658bb1 in vm_call0_body /media/test/ruby/build/../vm_eval.c:229:15
    #22 0x5de0c665c443 in vm_call0_cc /media/test/ruby/build/../vm_eval.c:101:12
    #23 0x5de0c665c443 in rb_call0 /media/test/ruby/build/../vm_eval.c:554:12
    #24 0x5de0c6616955 in rb_call /media/test/ruby/build/../vm_eval.c:873:12
    #25 0x5de0c6616955 in iterate_method /media/test/ruby/build/../vm_eval.c:1528:12
    #26 0x5de0c6616f55 in rb_iterate0 /media/test/ruby/build/../vm_eval.c:1470:18
    #27 0x5de0c66167c9 in rb_iterate_internal /media/test/ruby/build/../vm_eval.c:1502:12
    #28 0x5de0c66167c9 in rb_block_call_kw /media/test/ruby/build/../vm_eval.c:1551:12
    #29 0x5de0c68a7b7b in enum_to_a /media/test/ruby/build/../enum.c:735:5
    #30 0x5de0c6658bb1 in vm_call0_cfunc_with_frame /media/test/ruby/build/../vm_eval.c:164:15
    #31 0x5de0c6658bb1 in vm_call0_cfunc /media/test/ruby/build/../vm_eval.c:178:12
    #32 0x5de0c6658bb1 in vm_call0_body /media/test/ruby/build/../vm_eval.c:229:15
    #33 0x5de0c6611076 in vm_call0_cc /media/test/ruby/build/../vm_eval.c:101:12
    #34 0x5de0c6611076 in rb_vm_call0 /media/test/ruby/build/../vm_eval.c:61:12
    #35 0x5de0c6611076 in rb_vm_call_kw /media/test/ruby/build/../vm_eval.c:326:12
    #36 0x5de0c6611076 in vm_call_super /media/test/ruby/build/../vm_eval.c:350:12
    #37 0x5de0c6611076 in rb_call_super_kw /media/test/ruby/build/../vm_eval.c:358:12
    #38 0x5de0c6646b3b in vm_call_cfunc_with_frame_ /media/test/ruby/build/../vm_insnhelper.c:3797:11
    #39 0x5de0c662f4e3 in vm_call_method_each_type /media/test/ruby/build/../vm_insnhelper.c:4775:16
    #40 0x5de0c662efd3 in vm_call_method /media/test/ruby/build/../vm_insnhelper.c
    #41 0x5de0c65f51d8 in vm_sendish /media/test/ruby/build/../vm_insnhelper.c:5972:15
    #42 0x5de0c65f51d8 in vm_exec_core /media/test/ruby/build/../insns.def:899:11
    #43 0x5de0c65eda47 in rb_vm_exec /media/test/ruby/build/../vm.c
    #44 0x5de0c62bfce0 in rb_ec_exec_node /media/test/ruby/build/../eval.c:281:9
    #45 0x5de0c62bfce0 in ruby_run_node /media/test/ruby/build/../eval.c:319:30
    #46 0x5de0c62bb3a0 in rb_main /media/test/ruby/build/../main.c:42:12
    #47 0x5de0c62bb3a0 in main /media/test/ruby/build/../main.c:62:12
    #48 0x77f098629d8f 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:502:17 in set_i_initialize
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
==107246==ABORTING
../triaged/set_initialize.rb:3: [BUG] ASAN error
ruby 3.5.0dev (2025-05-02T21:28:25Z master 36c64b3be8) +PRISM [x86_64-linux]

-- Control frame information -----------------------------------------------
c:0004 p:---- s:0015 e:000014 CFUNC  :initialize
c:0003 p:---- s:0012 e:000011 CFUNC  :new
c:0002 p:0013 s:0007 E:000c48 EVAL   ../triaged/set_initialize.rb:3 [FINISH]
c:0001 p:0000 s:0003 E:000540 DUMMY  [FINISH]

-- Ruby level backtrace information ----------------------------------------
../triaged/set_initialize.rb:3:in '<main>'
../triaged/set_initialize.rb:3:in 'new'
../triaged/set_initialize.rb:3:in 'initialize'

-- Threading information ---------------------------------------------------
Total ractor count: 1
Ruby thread count for this ractor: 1

-- C level backtrace information -------------------------------------------
./ruby(___interceptor_backtrace) [0x5de0c6228006]
/media/test/ruby/build/ruby(rb_print_backtrace+0x14) [0x5de0c6976337] /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) [0x5de0c68ca76c] /media/test/ruby/build/../error.c:1097
/media/test/ruby/build/ruby(rb_bug_without_die+0x127) [0x5de0c68ca487] /media/test/ruby/build/../error.c:1106
./ruby(0x5de0c62a1bc6) [0x5de0c62a1bc6]
./ruby(0x5de0c6282c9f) [0x5de0c6282c9f]
./ruby(0x5de0c6285ce5) [0x5de0c6285ce5]
./ruby(__asan_report_load8) [0x5de0c6286988]
/media/test/ruby/build/ruby(set_i_initialize+0x4c0) [0x5de0c6510d40] /media/test/ruby/build/../set.c:502
/media/test/ruby/build/ruby(vm_call0_cfunc_with_frame+0x280) [0x5de0c6658bb2] ../vm_eval.c:164
/media/test/ruby/build/ruby(vm_call0_cfunc) ../vm_eval.c:178
/media/test/ruby/build/ruby(vm_call0_body) ../vm_eval.c:229
/media/test/ruby/build/ruby(vm_call0_cc+0x141) [0x5de0c665c444] ../vm_eval.c:101
/media/test/ruby/build/ruby(rb_call0) ../vm_eval.c:554
/media/test/ruby/build/ruby(rb_funcallv_kw+0x86) [0x5de0c6613826] ../vm_eval.c:873
/media/test/ruby/build/ruby(rb_class_new_instance_pass_kw+0x35) [0x5de0c63edc55] /media/test/ruby/build/../object.c:2203
/media/test/ruby/build/ruby(vm_cfp_consistent_p+0x0) [0x5de0c6646b3c] ../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) [0x5de0c662f4e4] ../vm_insnhelper.c:4775
./ruby(vm_call_method+0x2d4) [0x5de0c662efd4]
/media/test/ruby/build/ruby(vm_sendish+0x10e) [0x5de0c65f7f99] ../vm_insnhelper.c:5972
/media/test/ruby/build/ruby(vm_exec_core) ../insns.def:851
./ruby(vm_exec_loop+0x0) [0x5de0c65eda48]
/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) [0x5de0c62bfce1] /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) [0x5de0c62bb3a1] /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) [0x77f098629d90] ../sysdeps/nptl/libc_start_call_main.h:58
/lib/x86_64-linux-gnu/libc.so.6(call_init+0x0) [0x77f098629e40] ../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) [0x5de0c61e3d55]

-- Other runtime information -----------------------------------------------

* Loaded script: ../triaged/set_initialize.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

Updated by mame (Yusuke Endoh) 2 days ago

  • Assignee set to jeremyevans0 (Jeremy Evans)

Updated by jeremyevans0 (Jeremy Evans) 2 days ago

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.

Actions #3

Updated by jeremyevans (Jeremy Evans) 2 days ago

  • Status changed from Open to Closed

Applied in changeset git|21035c826db5933cf836a4a12fb74b696a76b255.


Handle mutating of array passed to Set.new during iteration

This avoids a heap-use-after-free.

Fixes [Bug #21306]

Actions

Also available in: Atom PDF

Like0
Like0Like0Like0