ruby-core@ruby-lang.org archive (unofficial mirror)
 help / color / mirror / Atom feed
* [ruby-core:121806] [Ruby Bug#21303] heap-buffer-overflow of Array#difference via mutating hash method
@ 2025-05-03 12:55 cyruscyliu (Qiang Liu) via ruby-core
  2025-05-03 16:48 ` [ruby-core:121811] " mame (Yusuke Endoh) via ruby-core
  0 siblings, 1 reply; 2+ messages in thread
From: cyruscyliu (Qiang Liu) via ruby-core @ 2025-05-03 12:55 UTC (permalink / raw)
  To: ruby-core; +Cc: cyruscyliu (Qiang Liu)

Issue #21303 has been reported by cyruscyliu (Qiang Liu).

----------------------------------------
Bug #21303: heap-buffer-overflow of Array#difference via mutating hash method
https://bugs.ruby-lang.org/issues/21303

* Author: cyruscyliu (Qiang Liu)
* Status: Open
* ruby -v: ruby 3.0.2p107 (2021-07-07 revision 0db68f0233) [x86_64-linux-gnu]
* Backport: 3.2: UNKNOWN, 3.3: UNKNOWN, 3.4: UNKNOWN
----------------------------------------
Hi, we found a heap-buffer-overflow of Array#difference via mutating hash
method. Here is the PoC.

```
class C
    def hash
        $a.clear
        return 0
    end

    def eql?(other)
        return true
    end
end

$b = (1..20).to_a
$a = (1..10000).to_a
$a.push(C.new)
$a.difference($b, $b)
```

Specifically, calling Array#difference on an array with an object whose hash
method mutates the array causes memory corruption. Here, C#hash clears $a while
difference is iterating over it, breaking internal invariants and leading to a
crash or invalid memory access.

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

```
$ ruby -v
ruby 3.0.2p107 (2021-07-07 revision 0db68f0233) [x86_64-linux-gnu]
$ ruby array_difference.rb
`RubyGems' were not loaded.
`error_highlight' was not loaded.
`did_you_mean' was not loaded.
`syntax_suggest' was not loaded.
=================================================================
==88761==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x51100001d500 at pc 0x598237a7f324 bp 0x7ffc9d9f0150 sp 0x7ffc9d9f0148
READ of size 8 at 0x51100001d500 thread T0
    #0 0x598237a7f323 in RARRAY_AREF /media/test/ruby/build/../internal/array.h:147:11
    #1 0x598237a7f323 in rb_ary_difference_multi /media/test/ruby/build/../array.c:5616:52
    #2 0x598237a0ab3b in vm_call_cfunc_with_frame_ /media/test/ruby/build/../vm_insnhelper.c:3797:11
    #3 0x5982379f34e3 in vm_call_method_each_type /media/test/ruby/build/../vm_insnhelper.c:4775:16
    #4 0x5982379f2fd3 in vm_call_method /media/test/ruby/build/../vm_insnhelper.c
    #5 0x5982379b91d8 in vm_sendish /media/test/ruby/build/../vm_insnhelper.c:5972:15
    #6 0x5982379b91d8 in vm_exec_core /media/test/ruby/build/../insns.def:899:11
    #7 0x5982379b1a47 in rb_vm_exec /media/test/ruby/build/../vm.c
    #8 0x598237683ce0 in rb_ec_exec_node /media/test/ruby/build/../eval.c:281:9
    #9 0x598237683ce0 in ruby_run_node /media/test/ruby/build/../eval.c:319:30
    #10 0x59823767f3a0 in rb_main /media/test/ruby/build/../main.c:42:12
    #11 0x59823767f3a0 in main /media/test/ruby/build/../main.c:62:12
    #12 0x7f619c429d8f in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16
    #13 0x7f619c429e3f in __libc_start_main csu/../csu/libc-start.c:392:3
    #14 0x5982375a7d54 in _start (/media/test/ruby/build/ruby+0x148d54) (BuildId: 58c97094e0527fad484552e230da980d80ffa516)

Address 0x51100001d500 is a wild pointer inside of access range of size 0x000000000008.
SUMMARY: AddressSanitizer: heap-buffer-overflow /media/test/ruby/build/../internal/array.h:147:11 in RARRAY_AREF
Shadow bytes around the buggy address:
  0x51100001d280: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x51100001d300: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x51100001d380: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x51100001d400: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x51100001d480: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
=>0x51100001d500:[fa]fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x51100001d580: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x51100001d600: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x51100001d680: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x51100001d700: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x51100001d780: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
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
==88761==ABORTING
../triaged/array_difference.rb:16: [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:0012 e:000011 CFUNC  :difference
c:0002 p:0048 s:0006 e:000005 EVAL   ../triaged/array_difference.rb:16 [FINISH]
c:0001 p:0000 s:0003 E:000540 DUMMY  [FINISH]

-- Ruby level backtrace information ----------------------------------------
../triaged/array_difference.rb:16:in '<main>'
../triaged/array_difference.rb:16:in 'difference'

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

-- C level backtrace information -------------------------------------------
./ruby(___interceptor_backtrace) [0x5982375ec006]
/media/test/ruby/build/ruby(rb_print_backtrace+0x14) [0x598237d3a337] /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) [0x598237c8e76c] /media/test/ruby/build/../error.c:1097
/media/test/ruby/build/ruby(rb_bug_without_die+0x127) [0x598237c8e487] /media/test/ruby/build/../error.c:1106
./ruby(0x598237665bc6) [0x598237665bc6]
./ruby(0x598237646c9f) [0x598237646c9f]
./ruby(0x598237649ce5) [0x598237649ce5]
./ruby(__asan_report_load8) [0x59823764a988]
/media/test/ruby/build/ruby(rb_ary_difference_multi+0xb64) [0x598237a7f324] ../internal/array.h:147
/media/test/ruby/build/ruby(vm_cfp_consistent_p+0x0) [0x598237a0ab3c] ../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) [0x5982379f34e4] ../vm_insnhelper.c:4775
./ruby(vm_call_method+0x2d4) [0x5982379f2fd4]
/media/test/ruby/build/ruby(vm_sendish+0x1c8) [0x5982379b91d9] ../vm_insnhelper.c:5972
/media/test/ruby/build/ruby(vm_exec_core) ../insns.def:899
./ruby(vm_exec_loop+0x0) [0x5982379b1a48]
/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) [0x598237683ce1] /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) [0x59823767f3a1] /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) [0x7f619c429d90] ../sysdeps/nptl/libc_start_call_main.h:58
/lib/x86_64-linux-gnu/libc.so.6(call_init+0x0) [0x7f619c429e40] ../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) [0x5982375a7d55]

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

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

^ permalink raw reply	[flat|nested] 2+ messages in thread

* [ruby-core:121811] [Ruby Bug#21303] heap-buffer-overflow of Array#difference via mutating hash method
  2025-05-03 12:55 [ruby-core:121806] [Ruby Bug#21303] heap-buffer-overflow of Array#difference via mutating hash method cyruscyliu (Qiang Liu) via ruby-core
@ 2025-05-03 16:48 ` mame (Yusuke Endoh) via ruby-core
  0 siblings, 0 replies; 2+ messages in thread
From: mame (Yusuke Endoh) via ruby-core @ 2025-05-03 16:48 UTC (permalink / raw)
  To: ruby-core; +Cc: mame (Yusuke Endoh)

Issue #21303 has been updated by mame (Yusuke Endoh).


Thanks! I created a PR: https://github.com/ruby/ruby/pull/13249

----------------------------------------
Bug #21303: heap-buffer-overflow of Array#difference via mutating hash method
https://bugs.ruby-lang.org/issues/21303#change-112872

* Author: cyruscyliu (Qiang Liu)
* Status: Open
* ruby -v: 3.5.0
* Backport: 3.2: UNKNOWN, 3.3: UNKNOWN, 3.4: UNKNOWN
----------------------------------------
Hi, we found a heap-buffer-overflow of Array#difference via mutating hash
method. Here is the PoC.

```
class C
    def hash
        $a.clear
        return 0
    end

    def eql?(other)
        return true
    end
end

$b = (1..20).to_a
$a = (1..10000).to_a
$a.push(C.new)
$a.difference($b, $b)
```

Specifically, calling Array#difference on an array with an object whose hash
method mutates the array causes memory corruption. Here, C#hash clears $a while
difference is iterating over it, breaking internal invariants and leading to a
crash or invalid memory access.

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

```
$ ruby -v
ruby 3.0.2p107 (2021-07-07 revision 0db68f0233) [x86_64-linux-gnu]
$ ruby array_difference.rb
`RubyGems' were not loaded.
`error_highlight' was not loaded.
`did_you_mean' was not loaded.
`syntax_suggest' was not loaded.
=================================================================
==88761==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x51100001d500 at pc 0x598237a7f324 bp 0x7ffc9d9f0150 sp 0x7ffc9d9f0148
READ of size 8 at 0x51100001d500 thread T0
    #0 0x598237a7f323 in RARRAY_AREF /media/test/ruby/build/../internal/array.h:147:11
    #1 0x598237a7f323 in rb_ary_difference_multi /media/test/ruby/build/../array.c:5616:52
    #2 0x598237a0ab3b in vm_call_cfunc_with_frame_ /media/test/ruby/build/../vm_insnhelper.c:3797:11
    #3 0x5982379f34e3 in vm_call_method_each_type /media/test/ruby/build/../vm_insnhelper.c:4775:16
    #4 0x5982379f2fd3 in vm_call_method /media/test/ruby/build/../vm_insnhelper.c
    #5 0x5982379b91d8 in vm_sendish /media/test/ruby/build/../vm_insnhelper.c:5972:15
    #6 0x5982379b91d8 in vm_exec_core /media/test/ruby/build/../insns.def:899:11
    #7 0x5982379b1a47 in rb_vm_exec /media/test/ruby/build/../vm.c
    #8 0x598237683ce0 in rb_ec_exec_node /media/test/ruby/build/../eval.c:281:9
    #9 0x598237683ce0 in ruby_run_node /media/test/ruby/build/../eval.c:319:30
    #10 0x59823767f3a0 in rb_main /media/test/ruby/build/../main.c:42:12
    #11 0x59823767f3a0 in main /media/test/ruby/build/../main.c:62:12
    #12 0x7f619c429d8f in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16
    #13 0x7f619c429e3f in __libc_start_main csu/../csu/libc-start.c:392:3
    #14 0x5982375a7d54 in _start (/media/test/ruby/build/ruby+0x148d54) (BuildId: 58c97094e0527fad484552e230da980d80ffa516)

Address 0x51100001d500 is a wild pointer inside of access range of size 0x000000000008.
SUMMARY: AddressSanitizer: heap-buffer-overflow /media/test/ruby/build/../internal/array.h:147:11 in RARRAY_AREF
Shadow bytes around the buggy address:
  0x51100001d280: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x51100001d300: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x51100001d380: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x51100001d400: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x51100001d480: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
=>0x51100001d500:[fa]fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x51100001d580: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x51100001d600: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x51100001d680: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x51100001d700: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x51100001d780: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
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
==88761==ABORTING
../triaged/array_difference.rb:16: [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:0012 e:000011 CFUNC  :difference
c:0002 p:0048 s:0006 e:000005 EVAL   ../triaged/array_difference.rb:16 [FINISH]
c:0001 p:0000 s:0003 E:000540 DUMMY  [FINISH]

-- Ruby level backtrace information ----------------------------------------
../triaged/array_difference.rb:16:in '<main>'
../triaged/array_difference.rb:16:in 'difference'

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

-- C level backtrace information -------------------------------------------
./ruby(___interceptor_backtrace) [0x5982375ec006]
/media/test/ruby/build/ruby(rb_print_backtrace+0x14) [0x598237d3a337] /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) [0x598237c8e76c] /media/test/ruby/build/../error.c:1097
/media/test/ruby/build/ruby(rb_bug_without_die+0x127) [0x598237c8e487] /media/test/ruby/build/../error.c:1106
./ruby(0x598237665bc6) [0x598237665bc6]
./ruby(0x598237646c9f) [0x598237646c9f]
./ruby(0x598237649ce5) [0x598237649ce5]
./ruby(__asan_report_load8) [0x59823764a988]
/media/test/ruby/build/ruby(rb_ary_difference_multi+0xb64) [0x598237a7f324] ../internal/array.h:147
/media/test/ruby/build/ruby(vm_cfp_consistent_p+0x0) [0x598237a0ab3c] ../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) [0x5982379f34e4] ../vm_insnhelper.c:4775
./ruby(vm_call_method+0x2d4) [0x5982379f2fd4]
/media/test/ruby/build/ruby(vm_sendish+0x1c8) [0x5982379b91d9] ../vm_insnhelper.c:5972
/media/test/ruby/build/ruby(vm_exec_core) ../insns.def:899
./ruby(vm_exec_loop+0x0) [0x5982379b1a48]
/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) [0x598237683ce1] /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) [0x59823767f3a1] /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) [0x7f619c429d90] ../sysdeps/nptl/libc_start_call_main.h:58
/lib/x86_64-linux-gnu/libc.so.6(call_init+0x0) [0x7f619c429e40] ../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) [0x5982375a7d55]

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

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

^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2025-05-03 16:49 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2025-05-03 12:55 [ruby-core:121806] [Ruby Bug#21303] heap-buffer-overflow of Array#difference via mutating hash method cyruscyliu (Qiang Liu) via ruby-core
2025-05-03 16:48 ` [ruby-core:121811] " mame (Yusuke Endoh) via ruby-core

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).