ml.ruby-lang.org
Sign In Sign Up
Manage this list Sign In Sign Up

Keyboard Shortcuts

Thread View

  • j: Next unread message
  • k: Previous unread message
  • j a: Jump to all threads
  • j l: Jump to MailingList overview

ruby-core

Thread Start a new thread
Download
Threads by month
  • ----- 2026 -----
  • August
  • July
  • June
  • May
  • April
  • March
  • February
  • January
  • ----- 2025 -----
  • December
  • November
  • October
  • September
  • August
  • July
  • June
  • May
  • April
  • March
  • February
  • January
  • ----- 2024 -----
  • December
  • November
  • October
  • September
  • August
  • July
  • June
  • May
  • April
  • March
  • February
  • January
  • ----- 2023 -----
  • December
  • November
  • October
  • September
  • August
  • July
  • June
  • May
  • April
  • March
  • February
  • January
  • ----- 2022 -----
  • December
  • November
ruby-core@ml.ruby-lang.org

June 2026

  • 4 participants
  • 147 discussions
[ruby-core:125796] [Ruby Feature#22121] Introduce Parallel Sweep feature
by luke-gru (Luke Gruber) 07 Jul '26

07 Jul '26
Issue #22121 has been reported by luke-gru (Luke Gruber). ---------------------------------------- Feature #22121: Introduce Parallel Sweep feature https://bugs.ruby-lang.org/issues/22121 * Author: luke-gru (Luke Gruber) * Status: Open ---------------------------------------- ## Abstract Ruby's GC sweep implementation is currently incremental and lazy. This is to reduce pause times when sweeping. However, the sweep implementation doesn't take advantage of parallelism (multi-core). Sweeping in a GC is not an "embarassingly parallel" problem, but extra threads can help. I would like to introduce such a feature to Ruby so that users can take advantage of their multi-core CPUs to reduce GC pause times. ## Design I would like to have an additional "sweep thread" that sweeps alongside the Ruby GC thread (in parallel) and at the same time as Ruby code is running (concurrent). When the sweep thread is working alongside the Ruby GC thread, both threads grab pages from the current heap. If the sweep thread has already swept a page, the Ruby GC thread finishes it by clearing its bitmaps and adding the page to the free pages or empty pages lists. If there are no swept pages from the sweep thread, the Ruby GC thread sweeps a page by itself instead of waiting for swept pages. When an incremental sweep step is over, the sweep thread sweeps 1 incremental step's budget worth of slots while Ruby code is running. This is so that during the next incremental step, the Ruby GC thread just has to finish the pages off instead of sweeping the objects. ## Limitations Certain objects aren't safe to be freed by the sweep thread. T_DATA types from native extensions cannot be swept in general (although most are safe) because the user's sweep function may not be thread-safe. That function may modify global state in such a way that when it is called from both the sweep thread AND the Ruby GC thread at once, it behaves badly. In order to get around this, we introduce a new [TypedData flag](https://github.com/ruby/ruby/pull/17395). T_DATA internal to the VM mostly have this flag set, and native extension authors can set this flag if it is defined to allow their type to be swept concurrently. This feature is only available for the default GC. Ruby's MMTK garbage collector has its own implementation of concurrent sweeping that is not affected by this feature. This feature is only available for pthread platforms, although that restriction could be lifted with a bit of work. This feature is not on by default (see section `Building`). ## Implementation The [PR](https://github.com/ruby/ruby/pull/17411) is currently a draft, but it's in a working state. Please play with it and tell me what you think! ## Building ```bash ./configure --enable-parallel-sweep make -j ./ruby --enable-parallel-sweep -v #=> ruby 4.1.0dev (2026-06-19T15:49:17Z parallel-sweep cd7e59d45b) +PRISM +Parallel-Sweep [arm64-darwin25] ``` ## Benchmarks This image below is from running [ruby-bench](https://github.com/ruby/ruby-bench) `--headline` benchmarks 10 times and taking the median of means of the runs. The ruby-bench command was: ```bash ./run_benchmarks.rb --no-pinning --interleave --chruby="ruby_ctl::ruby_ctl --yjit" --chruby="psweep_lockfree::psweep_lockfree --yjit --enable-parallel-sweep" --headline ``` If you want sweep info per run, you can run it with the GC harness: ```bash ./run_benchmarks.rb --no-pinning --interleave --chruby="ruby_ctl::ruby_ctl --yjit" --chruby="psweep_lockfree::psweep_lockfree --yjit --enable-parallel-sweep" --harness=gc --headline ``` <img style="width: 1219px;" src="clipboard-202606191327-7umxq.png"><br> I encourage others to experiment with this feature, run your own benchmarks and share them here. When running your benchmarks, make sure not to pin the process to a CPU with `taskset`, otherwise the sweep thread will not run in parallel. > [!NOTE] > Sometimes there are regressions run to run. Run the benchmark 5-10 times to get more accurate results. > GC micro-benchmarks may not see any improvements or even slight regressions. The implementation targets a workload where there is sufficient time in between sweep steps for the sweep thread to do its work. > [!WARNING] > Benchmarking on MacOS tends to be less accurate than Linux. However, if that's all you have and want to share your results, please do. ## Future Work In the future, I would like it if we could process the metadata of a page in the sweep thread. For example, if we could clear the bitmaps and unlink an empty page, or even add a page to the free pages list then the Ruby pause time would get even smaller (or none at all). I did have a prototype of this, but it caused issues because it was creating too many empty pages too fast, and that had unintended consequences for the rest of the GC. I also believe parallel marking would have a large benefit. Major GCs take a long time due to marking, and if you have lots of threads of fibers than it takes even longer. Marking is a more naturally parallel problem, so would likely benefit from more than 1 worker thread. ---Files-------------------------------- clipboard-202606191327-7umxq.png (342 KB) -- https://bugs.ruby-lang.org/
2 5
0 0
[ruby-core:125855] [Ruby Feature#22132] Scala-like for comprehensions
by shugo (Shugo Maeda) 02 Jul '26

02 Jul '26
Issue #22132 has been reported by shugo (Shugo Maeda). ---------------------------------------- Feature #22132: Scala-like for comprehensions https://bugs.ruby-lang.org/issues/22132 * Author: shugo (Shugo Maeda) * Status: Open ---------------------------------------- ## Abstract How about adding an expression form of `for` that desugars into nested `flat_map`/`map` and `filter` calls. Here's an example, which computes all pairs of numbers between `0` and `n-1` whose sum is equal to a given value `v`: ```ruby def foo(n, v) for i in 0...n, j in 0...n when i + j == v then [i, j] end end p foo(10, 10) #=> [[1, 9], [2, 8], [3, 7]...] ``` The above code is desugared as follows: ```ruby def foo(n, v) (0...n).flat_map { |i| (0...n).filter { |j| i + j == v }.map { |j| [i, j] } } end p foo(10, 10) ``` ## Background and Motivation Some other languages have syntactic sugar that flattens nested code. For example, Scala has for comprehensions: ```scala def foo(n: Int, v: Int) = for i <- 0 until n j <- 0 until n if i + j == v yield (i, j) ``` Haskell has do notation: ```haskell foo :: Int -> Int -> [(Int, Int)] foo n v = do i <- [0 .. n-1] j <- [0 .. n-1] guard (i + j == v) pure (i, j) ``` Blocks are often nested deeply in Ruby, so such syntactic sugar is useful. ## Use cases For comprehensions can be used to flatten nested blocks. For example, ```ruby (1..).lazy.flat_map { |z| (1..z).lazy.flat_map { |x| (x..z).lazy.filter { |y| x**2 + y**2 == z**2 }.map { |y| [x, y, z] } } }.take(3).force ``` can be flattened as follows: ```ruby for z in (1..).lazy, x in (1..z).lazy, y in (x..z).lazy when x**2 + y**2 == z**2 then [x, y, z] end.take(3).force ``` For comprehensions can be used not only for Enumerable objects, but also for other objects which have `flat_map` and `map` that satisfy [the Monad laws](https://wiki.haskell.org/index.php?title=Monad_laws). ## Why `then` and `when`? Scala's `yield` conflicts with the existing `yield` keyword in Ruby, so I chose `then`. While a bare `for ... then` (no guard) reads a little unnaturally in English, Ruby already gives `then` a value-producing meaning (e.g., `Kernel#then`), so I consider it acceptable. The guard keyword is not `if` but `when`, because `if` after the source would be ambiguous with the modifier `if`. ## Limitations The right operand of `in` is `arg_value`, not `expr_value`, to avoid conflicts, so unparenthesized method calls (command calls) must be parenthesized. ## Backward compatibility * `for x in xs do ... end` (and the newline form) is unchanged: it still iterates via `each` and returns the collection. * All of the new forms (`for ... then`, `for ... ,`, `for ... when`) were `SyntaxError` before, so no existing program changes meaning. ## Implementation PoC: https://github.com/ruby/ruby/pull/17500 It's currently implemented only in `parse.y`, not in Prism yet, so requires `--parser=parse.y`. ## Open questions * Variable scope: the loop variables currently leak, same as `for ... do`. Should a comprehension instead scope them like a block? -- https://bugs.ruby-lang.org/
4 9
0 0
[ruby-core:125830] [Ruby Feature#22128] C API: Expose RB_OBJ_SET_FROZEN_SHAREABLE
by byroot (Jean Boussier) 02 Jul '26

02 Jul '26
Issue #22128 has been reported by byroot (Jean Boussier). ---------------------------------------- Feature #22128: C API: Expose RB_OBJ_SET_FROZEN_SHAREABLE https://bugs.ruby-lang.org/issues/22128 * Author: byroot (Jean Boussier) * Status: Open ---------------------------------------- ### Context I'm trying to experiment with adapting Active Record for a Ractor architecture. Since database connections can't possibly be Ractor shareable, the idea is to warp each connection inside its own ractor, and then send SQL queries and responses through a port. But for this to perform well, I'd like to directly build the query response as a fully shareable object, so that it can be pushed into the port for free, instead of having Ruby need to recursively walk the potentially large response to mark objects as shareable. Here's an example of how it would work in trilogy: https://github.com/byroot/trilogy/commit/5f58200b398995d2fc0d4673a5a6bbccd0… ### Problem Unfortunately, the necessary API isn't currently exposed in the C API: - `RB_OBJ_SET_FROZEN_SHAREABLE` - `RB_OBJ_SET_SHAREABLE` / `rb_obj_set_shareable` I understand that this API could potentially be misused, but given it's a C API, I believe it's acceptable to require care from the caller. -- https://bugs.ruby-lang.org/
3 4
0 0
[ruby-core:125071] [Ruby Feature#21957] Introduce `Enumerable#close` to free internal resources.
by ioquatix (Samuel Williams) 02 Jul '26

02 Jul '26
Issue #21957 has been reported by ioquatix (Samuel Williams). ---------------------------------------- Feature #21957: Introduce `Enumerable#close` to free internal resources. https://bugs.ruby-lang.org/issues/21957 * Author: ioquatix (Samuel Williams) * Status: Open * Assignee: ioquatix (Samuel Williams) ---------------------------------------- In some cases, `Enumerable` has substantial internal state (e.g. Fiber) related to enumeration. There is currently no way to clear up this state besides garbage collection, which means that we can accumulate considerable garbage before cleaning up, even if we know when the enumerable is no longer needed. I'd like to introduce `Enumerable#close` which invalidates the enumerable, freeing internal resources. After which, most usage would result in `Enumerable::ClosedError`. -- https://bugs.ruby-lang.org/
2 3
0 0
[ruby-core:115879] [Ruby master Bug#20081] Transfered Fiber doesn't return to Fiber that started it
by rmosolgo (Robert Mosolgo) 01 Jul '26

01 Jul '26
Issue #20081 has been reported by rmosolgo (Robert Mosolgo). ---------------------------------------- Bug #20081: Transfered Fiber doesn't return to Fiber that started it https://bugs.ruby-lang.org/issues/20081 * Author: rmosolgo (Robert Mosolgo) * Status: Open * Priority: Normal * ruby -v: ruby 3.2.2 (2023-03-30 revision e51014f9c0) [x86_64-darwin22] * Backport: 3.0: UNKNOWN, 3.1: UNKNOWN, 3.2: UNKNOWN ---------------------------------------- Hi! I'm trying to figure out how to make sure that Fibers started with `.transfer` end up _terminated_, not just suspended. (If they're suspended, Rails thinks they're still alive, and they continue to hold onto database connections, see: https://github.com/rmosolgo/graphql-ruby/issues/4739#issuecomment-1866930914.) So, I'm looking for way to make sure that any Fiber I start with `.transfer` will be properly terminated. But what I noticed is that when a transfer-based Fiber terminates, it gives control back to the top-most Fiber, not the Fiber which transfered to it. Is this intended? Here's a script to replicate the issue: ```ruby manager = Fiber.new do parent = Fiber.current worker = Fiber.new do puts "2. Begin Worker" parent.transfer puts "4. End Worker" end puts "1. Transfer 1" worker.transfer puts "3. Transfer 2" worker.transfer puts "5. Finished manager" end manager.transfer puts "6. Finished script" ``` I expect the steps to print in order: ``` 1. Transfer 1 2. Begin Worker 3. Transfer 2 4. End Worker 5. Finished manager 6. Finished script ``` But instead, `5. ...` is skipped: ``` 1. Transfer 1 2. Begin Worker 3. Transfer 2 4. End Worker 6. Finished script ``` I think that's because my `worker` fiber terminates and passes control back to the top-level Fiber. Should it have passed control back to the `manager`? Or is there another way to make sure `worker` is terminated, and `manager` gets control? -- https://bugs.ruby-lang.org/
3 9
0 0
[ruby-core:125150] [Ruby Bug#21970] Corrupted `ciobj->operands` in `compile.c: optimize_checktype`
by byroot (Jean Boussier) 30 Jun '26

30 Jun '26
Issue #21970 has been reported by byroot (Jean Boussier). ---------------------------------------- Bug #21970: Corrupted `ciobj->operands` in `compile.c: optimize_checktype` https://bugs.ruby-lang.org/issues/21970 * Author: byroot (Jean Boussier) * Status: Open * ruby -v: ruby 4.0.2 (2026-03-17 revision d3da9fec82) +PRISM [aarch64-linux] * Backport: 3.2: UNKNOWN, 3.3: UNKNOWN, 3.4: UNKNOWN, 4.0: UNKNOWN ---------------------------------------- A rare crash I observed in production. I unfortunately don't have a reproduction, but perhaps this will ring a bell to someone. ``` #4 0x0000aaaac9afb1b8 [PAC] in sigsegv (sig=11, info=0xaaaaff573e30, ctx=0xaaaaff573eb0) at signal.c:948 #5 <signal handler called> #6 0x0000aaaac9ddcb60 in get_next_insn (iobj=0xffffffff00000001) at compile.c:3078 #7 optimize_checktype (iobj=0xaaab00000640, iseq=0xffff6e41ac80) at compile.c:3291 #8 iseq_peephole_optimize (iseq=iseq@entry=0xffff6e41ac80, list=list@entry=0xaaab00000640, do_tailcallopt=do_tailcallopt@entry=0) at compile.c:3363 #9 0x0000aaaac9dde590 [PAC] in iseq_optimize (anchor=<optimized out>, iseq=<optimized out>) at compile.c:4453 #10 iseq_setup_insn (anchor=<optimized out>, iseq=<optimized out>) at compile.c:1648 #11 iseq_setup_insn (iseq=0xffff6e41ac80, anchor=0xffffdb42d078) at compile.c:1637 #12 0x0000aaaac9e22d34 [PAC] in pm_iseq_compile_node (iseq=0xffff6e41ac80, node=<optimized out>) at /ruby-4.0.2/prism_compile.c:10514 #13 0x0000aaaac9a32f08 [PAC] in pm_iseq_new_with_opt_try (d=d@entry=281474360333120) at iseq.c:1106 #14 0x0000aaaac99c81bc [PAC] in rb_protect (proc=proc@entry=0xaaaac9a32eec <pm_iseq_new_with_opt_try>, data=data@entry=281474360333120, pstate=pstate@entry=0xffffdb42d3e4) at eval.c:1127 #15 0x0000aaaac9a39408 [PAC] in pm_iseq_new_with_opt (node=node@entry=0xffffdb42d510, name=name@entry=281472546599520, path=path@entry=281472531544280, realpath=281472531544280, first_lineno=first_lineno@entry=215, parent=parent@entry=0x0, isolated_depth=isolated_depth@entry=0, type=type@entry=ISEQ_TYPE_METHOD, option=0xffffdb42d328, error_state=error_state@entry=0xffffdb42d3e4) at iseq.c:1159 #16 0x0000aaaac9dcf1fc [PAC] in pm_new_child_iseq (iseq=iseq@entry=0xffff6e41b068, node=node@entry=0xffffdb42d510, name=281472546599520, parent=parent@entry=0x0, type=type@entry=ISEQ_TYPE_METHOD, line_no=215) at /ruby-4.0.2/iseq.h:154 #17 0x0000aaaac9e0688c [PAC] in pm_compile_node (iseq=iseq@entry=0xffff6e41b068, node=0xaaab000f0ac0, ret=ret@entry=0xffffdb42dcd8, popped=popped@entry=true, scope_node=scope_node@entry=0xffffdb42e170) at /ruby-4.0.2/prism_compile.c:9073 #18 0x0000aaaac9e0bd7c [PAC] in pm_compile_node (iseq=iseq@entry=0xffff6e41b068, node=0xaaab000eeed0, ret=ret@entry=0xffffdb42dcd8, popped=popped@entry=false, scope_node=scope_node@entry=0xffffdb42e170) at /ruby-4.0.2/prism_compile.c:10327 #19 0x0000aaaac9e1ee4c [PAC] in pm_compile_scope_node (iseq=iseq@entry=0xffff6e41b068, scope_node=scope_node@entry=0xffffdb42e170, ret=ret@entry=0xffffdb42dcd8, popped=popped@entry=false, node_location=<optimized out>, node_location=<optimized out>) at /ruby-4.0.2/prism_compile.c:7047 ``` ``` (gdb) f 7 #7 optimize_checktype (iobj=0xaaab00000640, iseq=0xffff6e41ac80) at compile.c:3291 3291 ciobj = (INSN *)get_next_insn((INSN*)OPERAND_AT(ciobj, 0)); (gdb) p (INSN*)ciobj->operands[0] $7 = (INSN *) 0xffffffff00000001 (gdb) p *(INSN*)ciobj->operands[0] Cannot access memory at address 0xffffffff00000001 ``` -- https://bugs.ruby-lang.org/
2 4
0 0
[ruby-core:125877] [Ruby Feature#22134] Faster rb_scan_args() for keyword args (optimization)
by luke-gru (Luke Gruber) 29 Jun '26

29 Jun '26
Issue #22134 has been reported by luke-gru (Luke Gruber). ---------------------------------------- Feature #22134: Faster rb_scan_args() for keyword args (optimization) https://bugs.ruby-lang.org/issues/22134 * Author: luke-gru (Luke Gruber) * Status: Open ---------------------------------------- ## Motivation When using the `rb_scan_args()` API, often we want to find a value for a given keyword argument. In order to do this, we call `rb_scan_args()` like so: ```c VALUE str; VALUE kwargs; VALUE example; rb_scan_args(argc, argv, "1:", &str, &kwargs); // duplicates the kwargs hash in argv if (!NIL_P(kwargs)) rb_get_kwargs(kwargs, &id_example, 0, 1, &example); // mutates the duplicated kwargs hash to retrieve `example:` ``` This duplicates the keyword args hash given in `argv`. It would be nice to be able to grab a direct reference to the keyword hash and to have a variant of `rb_get_kwargs()` that didn't mutate the passed in hash. ## Proposal Add a new valid format character for `rb_scan_args()`: ```c VALUE str; VALUE kwargs; VALUE example; rb_scan_args(argc, argv, "1:^", &str, &kwargs); // access kwargs directly from argv if (!NIL_P(kwargs)) rb_get_kwargs_const(kwargs, &id_example, 0, 1, &example); // don't mutate the passed kwargs hash ``` This '^' character would only be valid after a ":'. I have a [pull request](https://github.com/ruby/ruby/pull/17558) available for anyone that is interested. Thank you! -- https://bugs.ruby-lang.org/
2 1
0 0
[ruby-core:125876] [Ruby Bug#18995] IO#set_encoding sometimes set an IO's internal encoding to the default external encoding
by javanthropus (Jeremy Bopp) 29 Jun '26

29 Jun '26
Issue #18995 has been updated by javanthropus (Jeremy Bopp). https://github.com/ruby/ruby/pull/17466 ---------------------------------------- Bug #18995: IO#set_encoding sometimes set an IO's internal encoding to the default external encoding https://bugs.ruby-lang.org/issues/18995#change-117803 * Author: javanthropus (Jeremy Bopp) * Status: Open * ruby -v: ruby 3.1.2p20 (2022-04-12 revision 4491bb740a) [x86_64-linux] * Backport: 2.7: UNKNOWN, 3.0: UNKNOWN, 3.1: UNKNOWN ---------------------------------------- This script demonstrates the behavior: ```ruby def show(io) printf( "external encoding: %-25p internal encoding: %-25p\n", io.external_encoding, io.internal_encoding ) end Encoding.default_external = 'iso-8859-1' Encoding.default_internal = 'iso-8859-2' File.open('/dev/null') do |f| f.set_encoding('utf-8', nil) show(f) # f.internal_encoding is iso-8859-2, as expected f.set_encoding('utf-8', 'invalid') show(f) # f.internal_encoding is now iso-8859-1! Encoding.default_external = 'iso-8859-3' Encoding.default_internal = 'iso-8859-4' show(f) # f.internal_encoding is now iso-8859-3! end ``` In the 1st case, we see that the IO's internal encoding is set to the current setting of Encoding.default_internal. In the 2nd case, the IO's internal encoding is set to Encoding.default_external instead. The 3rd case is more interesting because it shows that the IO's internal encoding is actually following the current setting of Encoding.default_external. It didn't just copy it when #set_encoding was called. It changes whenever Encoding.default_external changes. What should the correct behavior be? -- https://bugs.ruby-lang.org/
1 0
0 0
[ruby-core:125788] [Ruby Bug#22120] Segfault caused by ar_find_entry_hint() not checking for conversion to st_table
by Eregon (Benoit Daloze) 29 Jun '26

29 Jun '26
Issue #22120 has been reported by Eregon (Benoit Daloze). ---------------------------------------- Bug #22120: Segfault caused by ar_find_entry_hint() not checking for conversion to st_table https://bugs.ruby-lang.org/issues/22120 * Author: Eregon (Benoit Daloze) * Status: Open * Assignee: Eregon (Benoit Daloze) * Target version: 4.1 * ruby -v: ruby 4.1.0dev (2026-06-17T10:52:40Z master 0888fa8cb5) +PRISM [arm64-darwin25] * Backport: 3.3: UNKNOWN, 3.4: REQUIRED, 4.0: REQUIRED ---------------------------------------- While perusing code in hash.c (motivated by some crashes reported in https://github.com/DataDog/dd-trace-rb/issues/5718) I found it suspicious that `ar_find_entry_hint()` didn't reread `bound` in the loop and yet called arbitrary code through `#eql?`. Indeed, ar_find_entry_hint() does not check if bound or the storage (AR->ST) changed and would return a bin index which is not correct to access: ```c static unsigned ar_find_entry_hint(VALUE hash, ar_hint_t hint, st_data_t key) { unsigned i, bound = RHASH_AR_TABLE_BOUND(hash); const ar_hint_t *hints = RHASH_AR_TABLE(hash)->ar_hint.ary; /* if table is NULL, then bound also should be 0 */ for (i = 0; i < bound; i++) { if (hints[i] == hint) { ar_table_pair *pair = RHASH_AR_TABLE_REF(hash, i); if (ar_equal(key, pair->key)) { RB_DEBUG_COUNTER_INC(artable_hint_hit); return i; } else { RB_DEBUG_COUNTER_INC(artable_hint_miss); } } } RB_DEBUG_COUNTER_INC(artable_hint_notfound); return RHASH_AR_TABLE_MAX_BOUND; } ``` Fix: https://github.com/ruby/ruby/pull/17383 With the conversion from AR to ST it can actually trigger a segfault: This repro uses a single thread and mutates inside `eql?` for simplicity, but the same crash happens with another Thread mutating while the main Thread is in `eql?`. ```ruby class Key attr_reader :v def initialize(v, h = nil) @v = v @h = h end def hash = 0 def eql?(other) if @h # Trigger AR->ST conversion @h[42] = 42 end other.is_a?(Key) && @v == other.v end def inspect = "K(#{@v})" end h = {} 8.times { |i| h[Key.new(i)] = i } # Not in the hash, so ar_find_entry_hint checks every entry. lookup_key = Key.new(-1, h) p h[lookup_key] ``` ``` $ ./miniruby ar_find_entry_hint_bug.rb ar_find_entry_hint_bug.rb:15: [BUG] Segmentation fault at 0x0000000000000004 ruby 4.1.0dev (2026-06-17T10:52:40Z master 0888fa8cb5) +PRISM [arm64-darwin25] -- 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:0003 p:0021 s:0018 e:000015 l:y b:0001 r:0x0 METHOD ar_find_entry_hint_bug.rb:15 [FINISH] c:0002 p:0046 s:0011 E:0014e0 l:n b:---- r:0x0 EVAL ar_find_entry_hint_bug.rb:27 [FINISH] c:0001 p:0000 s:0003 E:002480 l:y b:---- r:0x0 DUMMY [FINISH] -- Ruby level backtrace information ---------------------------------------- ar_find_entry_hint_bug.rb:27:in '<main>' ar_find_entry_hint_bug.rb:15:in 'eql?' -- Threading information --------------------------------------------------- Total ractor count: 1 Ruby thread count for this ractor: 1 -- Machine register context ------------------------------------------------ x0: 0x0000000000000004 x1: 0x00000000000011bf x2: 0x000000016b9ed680 x3: 0x0000000000000000 x4: 0x0000000000000001 x5: 0x0000000000000001 x6: 0x0000000104d400a0 x7: 0xfffff0003ffff800 x18: 0x0000000000000000 x19: 0x000000016b9ed6d0 x20: 0x0000000104d41200 x21: 0x0000000000000000 x22: 0x00000000000011bf x23: 0x0000000000000000 x24: 0x0000000000000000 x25: 0x0000000104a96000 x26: 0x0000000000000009 x27: 0x0000000afd00aa00 x28: 0x0000000104a96000 lr: 0x00000001046a21dc fp: 0x000000016b9ed6c0 sp: 0x000000016b9ed680 -- C level backtrace information ------------------------------------------- /Users/benoit.daloze/code/ruby/miniruby(rb_vm_bugreport+0xbfc) [0x1046aec04] /Users/benoit.daloze/code/ruby/vm_dump.c:1473 /Users/benoit.daloze/code/ruby/miniruby(rb_vm_bugreport) (null):0 /Users/benoit.daloze/code/ruby/miniruby(rb_bug_for_fatal_signal+0x10c) [0x1044c7cd4] /Users/benoit.daloze/code/ruby/error.c:1140 /Users/benoit.daloze/code/ruby/miniruby(sigsegv+0x94) [0x1045ff93c] /Users/benoit.daloze/code/ruby/signal.c:948 /usr/lib/system/libsystem_platform.dylib(_sigtramp+0x38) [0x1891a57a4] /Users/benoit.daloze/code/ruby/miniruby(lookup_method_table+0x7c) [0x1046a21dc] ./vm_method.c:1363 /Users/benoit.daloze/code/ruby/miniruby(search_method0) ./vm_method.c:1822 /Users/benoit.daloze/code/ruby/miniruby(search_method+0x14) [0x104685960] ./vm_method.c:1845 /Users/benoit.daloze/code/ruby/miniruby(callable_method_entry_or_negative) ./vm_method.c:2049 /Users/benoit.daloze/code/ruby/miniruby(callable_method_entry+0x10) [0x104677fd0] ./vm_method.c:2078 /Users/benoit.daloze/code/ruby/miniruby(rb_callable_method_entry) ./vm_method.c:2085 /Users/benoit.daloze/code/ruby/miniruby(rb_vm_search_method_slowpath) ./vm_insnhelper.c:2039 /Users/benoit.daloze/code/ruby/miniruby(vm_search_method_slowpath0+0x8) [0x10467e6cc] ./vm_insnhelper.c:2214 /Users/benoit.daloze/code/ruby/miniruby(vm_exec_core) ./vm_insnhelper.c:2277 /Users/benoit.daloze/code/ruby/miniruby(vm_exec_loop+0x0) [0x10467a970] /Users/benoit.daloze/code/ruby/vm.c:2796 /Users/benoit.daloze/code/ruby/miniruby(rb_vm_exec) /Users/benoit.daloze/code/ruby/vm.c:2799 /Users/benoit.daloze/code/ruby/miniruby(rb_funcallv_scope+0x238) [0x10468a0c0] ./vm_eval.c:101 /Users/benoit.daloze/code/ruby/miniruby(rb_funcallv+0x8) [0x10468a4cc] ./vm_eval.c:1084 /Users/benoit.daloze/code/ruby/miniruby(rb_funcall) ./vm_eval.c:1141 /Users/benoit.daloze/code/ruby/miniruby(RB_TEST+0x0) [0x10455f248] /Users/benoit.daloze/code/ruby/object.c:161 /Users/benoit.daloze/code/ruby/miniruby(rb_eql) /Users/benoit.daloze/code/ruby/object.c:163 /Users/benoit.daloze/code/ruby/miniruby(rb_any_cmp+0xd0) [0x1044ff0e0] /Users/benoit.daloze/code/ruby/hash.c:138 /Users/benoit.daloze/code/ruby/miniruby(ar_equal+0xc) [0x104508328] /Users/benoit.daloze/code/ruby/hash.c:603 /Users/benoit.daloze/code/ruby/miniruby(ar_find_entry+0x54) [0x104500af4] /Users/benoit.daloze/code/ruby/hash.c:617 /Users/benoit.daloze/code/ruby/miniruby(ar_lookup) /Users/benoit.daloze/code/ruby/hash.c:1025 /Users/benoit.daloze/code/ruby/miniruby(rb_hash_aref) /Users/benoit.daloze/code/ruby/hash.c:2037 /Users/benoit.daloze/code/ruby/miniruby(vm_exec_core+0x4790) [0x104680c18] ./vm_insnhelper.c:6958 /Users/benoit.daloze/code/ruby/miniruby(vm_exec_loop+0x0) [0x10467a970] /Users/benoit.daloze/code/ruby/vm.c:2796 /Users/benoit.daloze/code/ruby/miniruby(rb_vm_exec) /Users/benoit.daloze/code/ruby/vm.c:2799 /Users/benoit.daloze/code/ruby/miniruby(rb_ec_exec_node+0xb4) [0x1044d317c] /Users/benoit.daloze/code/ruby/eval.c:284 /Users/benoit.daloze/code/ruby/miniruby(ruby_run_node+0x4c) [0x1044d3074] /Users/benoit.daloze/code/ruby/eval.c:322 /Users/benoit.daloze/code/ruby/miniruby(rb_main+0x1c) [0x104410980] ./main.c:42 /Users/benoit.daloze/code/ruby/miniruby(main) ./main.c:62 -- Other runtime information ----------------------------------------------- * Loaded script: ar_find_entry_hint_bug.rb * Ruby Box: disabled * Loaded features: 0 enumerator.so 1 monitor.so 2 thread.rb 3 fiber.so 4 rational.so 5 complex.so 6 pathname.so 7 ruby2_keywords.rb 8 set.rb ``` I can also repro on 3.2.11. Reproducing locally seems harder on 3.3, 3.4 and 4.0 but I believe they are affected too since they use very similar code and miss those checks, so we should backport there as well. Backporting to 3.2 may make sense too since this could be seen as a form of DoS. The repro depends on how the `ar_table` and `st_table` layouts overlap and specific values written & read. --- For completeness, I initially tried another repro, which shows unclear semantics but doesn't crash: ```ruby $break = false class Key def hash = 42 def eql?(o) if $break $break = false H.shift H[Key.new] = 42 until H.size == 8 o.equal?($key1) else equal?(o) end end end $key1 = key1 = Key.new key2 = Key.new key3 = Key.new h = { key1 => 1, key2 => 2 } H = h p h $break = true p h[key3] ``` ``` $ ./miniruby bug.rb {#<Key:0x0000000101041200> => 1, #<Key:0x0000000101041240> => 2} 2 ``` So reading a key considered `eql?` to `key1` returns the value for `key2`, that seems quite surprising. The underlying issue there is `ar_find_entry_hint()` returns an index but doesn't read the value, so the value is read after `eql?`, in which/during which the Hash may have changed. -- https://bugs.ruby-lang.org/
3 4
0 0
[ruby-core:125675] [Ruby Bug#22101] ASAN heap-use-after-free in rb_data_free after TypedData dfree frees dynamic rb_data_type_t
by rwstauner (Randy Stauner) 29 Jun '26

29 Jun '26
Issue #22101 has been reported by rwstauner (Randy Stauner). ---------------------------------------- Bug #22101: ASAN heap-use-after-free in rb_data_free after TypedData dfree frees dynamic rb_data_type_t https://bugs.ruby-lang.org/issues/22101 * Author: rwstauner (Randy Stauner) * Status: Open * Backport: 3.3: REQUIRED, 3.4: REQUIRED, 4.0: REQUIRED ---------------------------------------- `rb_data_free` currently calls `dfree` and then evaluates `RTYPEDDATA_EMBEDDABLE_P(obj)`. Since `RTYPEDDATA_EMBEDDABLE_P(obj)` reads `RTYPEDDATA_TYPE(obj)->flags`, this can become a use-after-free if an extension's `dfree` releases a dynamically allocated `rb_data_type_t`. This was observed under ASAN with `glib2` 4.3.6, where `cinfo_free` frees `cinfo->data_type`. I have a fix that simply caches the TypedData type and the embeddable/free decision before invoking `dfree`, matching the existing pattern of caching `dfree` and `RUBY_TYPED_FREE_IMMEDIATELY` before extension cleanup code runs: https://github.com/ruby/ruby/pull/17266 This is a small defensive fix and is suitable for backport because it avoids a shutdown-time ASAN heap-use-after-free without changing TypedData ownership semantics. -- https://bugs.ruby-lang.org/
2 4
0 0
  • ← Newer
  • 1
  • ...
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • ...
  • 15
  • Older →

HyperKitty Powered by HyperKitty version 1.3.12.