[Bug #21357] Fix crash in Hash#merge with block
Prior to 49b306ecb9
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.
This commit is contained in:
parent
7154b4208b
commit
0564973196
Notes:
git
2025-05-22 03:26:07 +00:00
4
hash.c
4
hash.c
@ -1723,14 +1723,14 @@ tbl_update(VALUE hash, VALUE key, tbl_update_func func, st_data_t optional_arg)
|
|||||||
.func = func,
|
.func = func,
|
||||||
.hash = hash,
|
.hash = hash,
|
||||||
.key = key,
|
.key = key,
|
||||||
.value = (VALUE)optional_arg,
|
.value = 0
|
||||||
};
|
};
|
||||||
|
|
||||||
int ret = rb_hash_stlike_update(hash, key, tbl_update_modify, (st_data_t)&arg);
|
int ret = rb_hash_stlike_update(hash, key, tbl_update_modify, (st_data_t)&arg);
|
||||||
|
|
||||||
/* write barrier */
|
/* write barrier */
|
||||||
RB_OBJ_WRITTEN(hash, Qundef, arg.key);
|
RB_OBJ_WRITTEN(hash, Qundef, arg.key);
|
||||||
RB_OBJ_WRITTEN(hash, Qundef, arg.value);
|
if (arg.value) RB_OBJ_WRITTEN(hash, Qundef, arg.value);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -2355,6 +2355,11 @@ class TestHashOnly < Test::Unit::TestCase
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_bug_21357
|
||||||
|
h = {x: []}.merge(x: nil) { |_k, v1, _v2| v1 }
|
||||||
|
assert_equal({x: []}, h)
|
||||||
|
end
|
||||||
|
|
||||||
def test_any_hash_fixable
|
def test_any_hash_fixable
|
||||||
20.times do
|
20.times do
|
||||||
assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}")
|
assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}")
|
||||||
|
Loading…
x
Reference in New Issue
Block a user