Skip to content

Commit 4ac2a34

Browse files
rwstaunerk0kubun
authored andcommitted
[Bug #22383] Fix inverted STR_SHARED check in rb_str_tmp_frozen_release
45a2c95 mistakenly rewrote FL_TEST_RAW(orig, STR_SHARED) && !FL_TEST_RAW(orig, STR_TMPLOCK|RUBY_FL_FREEZE) as FL_TEST_RAW(orig, STR_SHARED | STR_TMPLOCK) == STR_TMPLOCK requiring orig to not be shared, the opposite of the original condition. orig always shares the buffer with tmp here, so the buffer was never given back and the string stayed shared until its next modification copied it. The code then read aux.shared from strings that are not shared, where the union holds aux.capa. This should be backported to 4.0
1 parent 6841c0a commit 4ac2a34

3 files changed

Lines changed: 29 additions & 1 deletion

File tree

‎ext/-test-/string/capacity.c‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,17 @@ bug_str_capacity(VALUE klass, VALUE str)
1111
return LONG2FIX(rb_str_capacity(str));
1212
}
1313

14+
static VALUE
15+
bug_str_tmp_frozen_acquire_release(VALUE klass, VALUE str)
16+
{
17+
VALUE tmp = rb_str_tmp_frozen_acquire(str);
18+
rb_str_tmp_frozen_release(str, tmp);
19+
return str;
20+
}
21+
1422
void
1523
Init_string_capacity(VALUE klass)
1624
{
1725
rb_define_singleton_method(klass, "capacity", bug_str_capacity, 1);
26+
rb_define_singleton_method(klass, "tmp_frozen_acquire_release", bug_str_tmp_frozen_acquire_release, 1);
1827
}

‎string.c‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1592,7 +1592,7 @@ rb_str_tmp_frozen_release(VALUE orig, VALUE tmp)
15921592
if (STR_EMBED_P(tmp)) {
15931593
RUBY_ASSERT(OBJ_FROZEN_RAW(tmp));
15941594
}
1595-
else if (FL_TEST_RAW(orig, STR_SHARED | STR_TMPLOCK) == STR_TMPLOCK &&
1595+
else if (FL_TEST_RAW(orig, STR_SHARED | STR_TMPLOCK) == STR_SHARED &&
15961596
!OBJ_FROZEN_RAW(orig)) {
15971597
VALUE shared = RSTRING(orig)->as.heap.aux.shared;
15981598

‎test/-ext-/string/test_capacity.rb‎

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,12 +59,31 @@ def test_capacity_fstring
5959
assert_equal(s.length, capa(s))
6060
end
6161

62+
# Temporarily handing a string's buffer over to a frozen shared root
63+
# (rb_str_tmp_frozen_acquire) uses an internal ASCII-8BIT string. Use
64+
# UTF-16LE so that the terminator length of the root differs from the
65+
# terminator length of the string to verify that releasing the root restores
66+
# the capacity in the string's own encoding.
67+
def test_frozen_root_capacity_with_multibyte_terminator
68+
s = multibyte_terminator_string
69+
capacity = capa(s)
70+
assert_operator(capacity, :>=, s.bytesize)
71+
72+
Bug::String.tmp_frozen_acquire_release(s)
73+
74+
assert_equal(capacity, capa(s))
75+
end
76+
6277
private
6378

6479
def capa(str)
6580
Bug::String.capacity(str)
6681
end
6782

83+
def multibyte_terminator_string
84+
("\u{30AF}\u{30FC}\u{30DD}\u{30F3}\u{1F381}" * 100).encode("UTF-16LE")
85+
end
86+
6887
def embed_header_size
6988
GC::INTERNAL_CONSTANTS[:RBASIC_SIZE] + RbConfig::SIZEOF['void*']
7089
end

0 commit comments

Comments
 (0)