Project

General

Profile

Actions

Bug #21357

closed

Crash in Hash#merge! with ruby-dev in rubocop-rspec test suite

Added by Earlopain (Earlopain _) 4 days ago. Updated 1 day ago.

Status:
Closed
Assignee:
-
Target version:
-
ruby -v:
ruby 3.5.0dev (2025-05-20T09:22:41Z master a82e7132df) +PRISM [x86_64-linux]
[ruby-core:122218]

Description

I've encountered crahses on ruby-head in recent days, related to hash methods like merge and merge!. I can now reproduce it locally while running the rubocop-rspec test suite:

I attached the crash report. I want to reduce this down but have no idea how. The crash is very consistent for me though, it doesn't take very long for it to happen (only a few seconds usually).

There were related(?) changes to hash like https://bugs.ruby-lang.org/issues/21333, maybe related.


Files

test.log (224 KB) test.log Earlopain (Earlopain _), 05/21/2025 12:48 PM
Actions #1

Updated by Earlopain (Earlopain _) 4 days ago

  • Description updated (diff)

Updated by Earlopain (Earlopain _) 4 days ago

I reduced it down significantly:

require "yaml"

YML = <<~YML
  foo:
    - bar: abc
      baz: def
      bat: ghi
YML
x = 1500 # lower numbers don't consistently crash

(0..x).each_with_object({}) do |_i, hash|
  hash.merge!(YAML.safe_load(YML)) do |_key, first, second|
    first.concat(second)
  end
end

This doesn't have much in common with the real code anymore, but the crash is the same.

Actions #4

Updated by dodecadaniel (Daniel Colson) 3 days ago

  • Status changed from Open to Closed

Applied in changeset git|056497319658cbefe22351c6ec5c9fa6e4df72bd.


[Bug #21357] Fix crash in Hash#merge with block

Prior to https://github.com/ruby/ruby/commit/49b306ecb9e2e9e06e0b1590bacc5f4b38169c3c
the optional_arg passed from rb_hash_update_block_i to tbl_update
was a hash value (i.e. a VALUE). After that commit it changed to an
update_call_args.

If the block sets or changes the value, tbl_update_modify will set the
arg.value back to an actual value and we won't crash. But in the case
where the block returns the original value we end up calling
RB_OBJ_WRITTEN with the update_call_args which is not expected and
may crash.

arg.value appears to only be used to pass to RB_OBJ_WRITTEN (others
who need the update_call_args get it from arg.arg), so I don't think
it needs to be set to anything upfront. And tbl_update_modify will set
the arg.value in the cases we need the write barrier.

Updated by Earlopain (Earlopain _) 3 days ago

Thanks! Works nicely.

Actions #6

Updated by byroot (Jean Boussier) 3 days ago

  • Backport changed from 3.2: UNKNOWN, 3.3: UNKNOWN, 3.4: UNKNOWN to 3.2: UNKNOWN, 3.3: UNKNOWN, 3.4: REQUIRED

Updated by nagachika (Tomoyuki Chikanaga) 1 day ago

  • Backport changed from 3.2: UNKNOWN, 3.3: UNKNOWN, 3.4: REQUIRED to 3.2: UNKNOWN, 3.3: REQUIRED, 3.4: REQUIRED

I'm going to backport the fix for #21333, and fill Backport field REQUIRED for 3.3.

Updated by nagachika (Tomoyuki Chikanaga) 1 day ago

  • Backport changed from 3.2: UNKNOWN, 3.3: REQUIRED, 3.4: REQUIRED to 3.2: UNKNOWN, 3.3: DONE, 3.4: REQUIRED
Actions

Also available in: Atom PDF

Like0
Like0Like0Like0Like0Like0Like0Like0Like0