[#122643] [Ruby Bug#21498] Windows - Ruby Overrides C Library APIs thus breaking them — "cfis (Charlie Savage) via ruby-core" <ruby-core@...>

Issue #21498 has been reported by cfis (Charlie Savage).

9 messages 2025/07/02

[#122658] [Ruby Feature#21501] Include native filenames in backtraces as sources for native methods — "ivoanjo (Ivo Anjo) via ruby-core" <ruby-core@...>

Issue #21501 has been reported by ivoanjo (Ivo Anjo).

10 messages 2025/07/05

[#122665] [Ruby Bug#21503] \p{Word} does not match on \p{Join_Control} while docs say it does — "procmarco (Marco Concetto Rudilosso) via ruby-core" <ruby-core@...>

SXNzdWUgIzIxNTAzIGhhcyBiZWVuIHJlcG9ydGVkIGJ5IHByb2NtYXJjbyAoTWFyY28gQ29uY2V0

8 messages 2025/07/07

[#122734] [Ruby Bug#21511] Use-after-free of the execution context after the fiber object carrying it is freed in GC — "tuonigou (tianyang sun) via ruby-core" <ruby-core@...>

Issue #21511 has been reported by tuonigou (tianyang sun).

10 messages 2025/07/14

[#122797] [Ruby Feature#21515] Add `&return` as sugar for `x=my_calculation; return x if x` — "nhorton (Noah Horton) via ruby-core" <ruby-core@...>

Issue #21515 has been reported by nhorton (Noah Horton).

13 messages 2025/07/16

[#122842] [Ruby Feature#21518] Statistical helpers to `Enumerable` — "Amitleshed (Amit Leshed) via ruby-core" <ruby-core@...>

SXNzdWUgIzIxNTE4IGhhcyBiZWVuIHJlcG9ydGVkIGJ5IEFtaXRsZXNoZWQgKEFtaXQgTGVzaGVk

12 messages 2025/07/23

[#122847] [Ruby Feature#21520] Feature Proposal: Enumerator::Lazy#peek — "nuzair46 (Nuzair Rasheed) via ruby-core" <ruby-core@...>

SXNzdWUgIzIxNTIwIGhhcyBiZWVuIHJlcG9ydGVkIGJ5IG51emFpcjQ2IChOdXphaXIgUmFzaGVl

12 messages 2025/07/24

[ruby-core:122739] [Ruby Bug#21511] Use-after-free of the execution context after the fiber object carrying it is freed in GC

From: "tuonigou (tianyang sun) via ruby-core" <ruby-core@...>
Date: 2025-07-14 09:29:38 UTC
List: ruby-core #122739
Issue #21511 has been updated by tuonigou (tianyang sun).


using ruby 3.5.0dev (2025-07-14T05:11:58Z master 8f54b5bb93) +PRISM [x86_64-linux]

``` shell
# this is currently in the forked proc in GC (the `fork { GC.start }` part)
# thread 3.3 is the created thread by Thread.new 
(gdb) i threads
  Id   Target Id                                           Frame 
  1.1  Thread 0x7ffff7de4580 (LWP 2916144) "ruby"          vfork ()
    at ../sysdeps/unix/sysv/linux/x86_64/vfork.S:41
  1.2  Thread 0x7fffdddff640 (LWP 2916147) "ruby"          0x00007ffff7b25e2e in epoll_wait (
    epfd=4, events=0x555555b219fc <timer_th+28>, maxevents=16, timeout=-1)
    at ../sysdeps/unix/sysv/linux/epoll_wait.c:30
  1.3  Thread 0x7fffdc0bd640 (LWP 2916150) "runner.rb:546" 0x00007ffff7b18bcf in __GI___poll (
    fds=0x7fffdc0bb1f0, nfds=1, timeout=-1) at ../sysdeps/unix/sysv/linux/poll.c:29
  3.1  Thread 0x7ffff7de4580 (LWP 2916151) "miniruby"      __futex_abstimed_wait_common64 (
    private=0, cancel=true, abstime=0x0, op=393, expected=0, futex_word=0x555555b34350)
    at ./nptl/futex-internal.c:57
  3.3  Thread 0x7fffdc1be640 (LWP 2916153) "zzz_t1.rb:2"   arch_fork (ctid=0x7fffdc1be910)
    at ../sysdeps/unix/sysv/linux/arch-fork.h:52
* 4.1  Thread 0x7fffdc1be640 (LWP 2916154) "zzz_t1.rb:2"   cont_free (ptr=0x555555ba84f0)
    at ../cont.c:1094

# showing the ec's address in those two threads which will be used after freeing in thread 4.1's GC
(gdb) t 3.1
[Switching to thread 3.1 (Thread 0x7ffff7de4580 (LWP 2916151))]
#0  __futex_abstimed_wait_common64 (private=0, cancel=true, abstime=0x0, op=393, expected=0, 
    futex_word=0x555555b34350) at ./nptl/futex-internal.c:57
57	./nptl/futex-internal.c: No such file or directory.
(gdb) p ruby_current_ec
$6 = (struct rb_execution_context_struct *) 0x555555b3c230
(gdb) t 3.3
[Switching to thread 3.3 (Thread 0x7fffdc1be640 (LWP 2916153))]
#0  arch_fork (ctid=0x7fffdc1be910) at ../sysdeps/unix/sysv/linux/arch-fork.h:52
52	../sysdeps/unix/sysv/linux/arch-fork.h: No such file or directory.
(gdb) p ruby_current_ec
$7 = (struct rb_execution_context_struct *) 0x555555ba8540


# the fiber_free in GC frees fiber that contains above ec's 
Thread 4.1 "zzz_t1.rb:2" hit Breakpoint 3, fiber_free (ptr=0x555555b3c1e0) at ../cont.c:1170
1170	    rb_fiber_t *fiber = ptr;
(gdb) p fiber
$1 = (rb_fiber_t *) 0x555555b3c1e0
ruby_xfree (x=0x555555b3c1e0) at ../gc.c:5301
5301	    ruby_sized_xfree(x, 0);
(gdb) p x
$2 = (void *) 0x555555b3c1e0

Thread 4.1 "zzz_t1.rb:2" hit Breakpoint 3, fiber_free (ptr=0x555555ba84f0) at ../cont.c:1170
1170	    rb_fiber_t *fiber = ptr;
(gdb) p fiber
$4 = (rb_fiber_t *) 0x555555ba84f0
1094	    ruby_xfree(ptr);
(gdb) p ptr
$5 = (void *) 0x555555ba84f0

# after 4.1 exits
[Inferior 4 (process 2916154) exited normally]

# they were used 

(gdb) awatch *0x555555b3c230
Hardware access (read/write) watchpoint 4: *0x555555b3c230
(gdb) awatch *0x555555ba8540
Hardware access (read/write) watchpoint 5: *0x555555ba8540

Thread 3.3 "zzz_t1.rb:2" hit Hardware access (read/write) watchpoint 5: *0x555555ba8540

Old value = -141955056
New value = 0
rb_ec_set_vm_stack (ec=0x555555ba8540, stack=0x0, size=0) at ../vm.c:3648
3648	    ec->vm_stack_size = size;
(gdb) p ec
$8 = (rb_execution_context_t *) 0x555555ba8540

Thread 3.1 "miniruby" hit Hardware access (read/write) watchpoint 4: *0x555555b3c230

Value = -137850864
0x00005555558a0bd1 in ruby_vm_destruct (vm=0x555555b35310) at ../vm.c:3144
3144	        VALUE *stack = th->ec->vm_stack;
(gdb) p th->ec
$9 = (rb_execution_context_t *) 0x555555b3c230

Thread 3.1 "miniruby" hit Hardware access (read/write) watchpoint 4: *0x555555b3c230

Old value = -137850864
New value = 0
rb_ec_set_vm_stack (ec=0x555555b3c230, stack=0x0, size=0) at ../vm.c:3648
3648	    ec->vm_stack_size = size;
(gdb) p ec
$10 = (rb_execution_context_t *) 0x555555b3c230
```

sizeof rb_fiber_t = 0x250, so 
sizeof(struct rb_execution_context_struct) = 0x170
[0x555555b3c230-0x555555B3C3A0] is within [0x555555b3c1e0-0x555555B3C430] the range of rb_fiber_t which is freed
[0x555555ba8540-0x555555BA86B0] is within [0x555555ba84f0-0x555555BA8740] the range of rb_fiber_t which is freed

----------------------------------------
Bug #21511: Use-after-free of the execution context after the fiber object carrying it is freed in GC
https://bugs.ruby-lang.org/issues/21511#change-114020

* Author: tuonigou (tianyang sun)
* Status: Open
* ruby -v: ruby 3.4.1 (2025-06-13 revision de8de51182) +PRISM [x86_64-linux]
* Backport: 3.2: UNKNOWN, 3.3: UNKNOWN, 3.4: UNKNOWN
----------------------------------------
In bootstraptest/test_thread.rb, 

``` ruby
assert_equal 'ok', %{
  File.write("zzz_t1.rb", <<-END)
      begin
        Thread.new { fork { GC.start } }.join
        pid, status = Process.wait2
        $result = status.success? ? :ok : :ng
      rescue NotImplementedError
        $result = :ok
      end
    END
  require "./zzz_t1.rb"
  $result
}
```
``` shell
# in build/
make btest BTESTS="file_containing_above.rb" 
# or
ruby --disable=gems  "../bootstraptest/runner.rb" --ruby="./miniruby -I../lib -I. -I.ext/common  -r./x86_64-linux-fake --disable-gems" file_containing_above.rb
```

Suppose **thread 1** called the `Thread.new` and created **thread 2**

The forked process by thread 2 that initiates GC with `GC.start` would sweep the fiber object embedded in `RTypedData` in the `gc_sweep_rest()` stage of sweep in `fiber_free()`. That fiber object contains the execution context of thread 1, `rb_execution_context_t saved_ec` field of `cont`. 

Since the fiber object is freed, the allocated area pointed by it should be invalid, including the embedded struct for ec, but after thread 2 joins, thread 1 still uses the ec in rb_current_thread(), causing a use after free.





-- 
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/


In This Thread