Skip to content

Commit 1bdae34

Browse files
committed
merge revision(s) 0564973: [Backport #21357]
[Bug #21357] Fix crash in Hash#merge with block Prior to ruby@49b306e 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.
1 parent 247b452 commit 1bdae34

File tree

3 files changed

+8
-3
lines changed

3 files changed

+8
-3
lines changed

hash.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1723,14 +1723,14 @@ tbl_update(VALUE hash, VALUE key, tbl_update_func func, st_data_t optional_arg)
17231723
.func = func,
17241724
.hash = hash,
17251725
.key = key,
1726-
.value = (VALUE)optional_arg,
1726+
.value = 0
17271727
};
17281728

17291729
int ret = rb_hash_stlike_update(hash, key, tbl_update_modify, (st_data_t)&arg);
17301730

17311731
/* write barrier */
17321732
RB_OBJ_WRITTEN(hash, Qundef, arg.key);
1733-
RB_OBJ_WRITTEN(hash, Qundef, arg.value);
1733+
if (arg.value) RB_OBJ_WRITTEN(hash, Qundef, arg.value);
17341734

17351735
return ret;
17361736
}

test/ruby/test_hash.rb

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2352,6 +2352,11 @@ def test_bug_12706
23522352
end
23532353
end
23542354

2355+
def test_bug_21357
2356+
h = {x: []}.merge(x: nil) { |_k, v1, _v2| v1 }
2357+
assert_equal({x: []}, h)
2358+
end
2359+
23552360
def test_any_hash_fixable
23562361
20.times do
23572362
assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}")

version.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
# define RUBY_VERSION_MINOR RUBY_API_VERSION_MINOR
1212
#define RUBY_VERSION_TEENY 4
1313
#define RUBY_RELEASE_DATE RUBY_RELEASE_YEAR_STR"-"RUBY_RELEASE_MONTH_STR"-"RUBY_RELEASE_DAY_STR
14-
#define RUBY_PATCHLEVEL 35
14+
#define RUBY_PATCHLEVEL 36
1515

1616
#include "ruby/version.h"
1717
#include "ruby/internal/abi.h"

0 commit comments

Comments
 (0)