summaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
authorAlan Wu <XrXr@users.noreply.github.com>2019-05-08 09:44:41 -0400
committerNobuyoshi Nakada <nobu@ruby-lang.org>2019-05-09 10:04:19 +0900
commitc06ddfee878524168e4af07443217ed2f8d0954b (patch)
tree46ebccbc5e21a929eb5fd644543975cce4cf115e /test
parentd802698d3e27a3cf091a56556df3562cc6ff996c (diff)
str_duplicate: Don't share with a frozen shared string
This is a follow up for 3f9562015e651735bfc2fdd14e8f6963b673e22a. Before this commit, it was possible to create a shared string which shares with another shared string by passing a frozen shared string to `str_duplicate`. Such string looks like: ``` -------- ----------------- | root | ------ owns -----> | root's buffer | -------- ----------------- ^ ^ ^ ----------- | | | shared1 | ------ references ----- | ----------- | ^ | ----------- | | shared2 | ------ references --------- ----------- ``` This is bad news because `rb_fstring(shared2)` can make `shared1` independent, which severs the reference from `shared1` to `root`: ```c /* from fstr_update_callback() */ str = str_new_frozen(rb_cString, shared2); /* can return shared1 */ if (STR_SHARED_P(str)) { /* shared1 is also a shared string */ str_make_independent(str); /* no frozen check */ } ``` If `shared1` was the only reference to `root`, then `root` can be reclaimed by the GC, leaving `shared2` in a corrupted state: ``` ----------- -------------------- | shared1 | -------- owns --------> | shared1's buffer | ----------- -------------------- ^ | ----------- ------------------------- | shared2 | ------ references ----> | root's buffer (freed) | ----------- ------------------------- ``` Here is a reproduction script for the situation this commit fixes. ```ruby a = ('a' * 24).strip.freeze.strip -a p a 4.times { GC.start } p a ``` - string.c (str_duplicate): always share with the root string when the original is a shared string. - test_rb_str_dup.rb: specifically test `rb_str_dup` to make sure it does not try to share with a shared string. [Bug #15792] Closes: https://github.com/ruby/ruby/pull/2159
Diffstat (limited to 'test')
-rw-r--r--test/-ext-/string/test_rb_str_dup.rb16
1 files changed, 16 insertions, 0 deletions
diff --git a/test/-ext-/string/test_rb_str_dup.rb b/test/-ext-/string/test_rb_str_dup.rb
new file mode 100644
index 0000000000..49b6af9598
--- /dev/null
+++ b/test/-ext-/string/test_rb_str_dup.rb
@@ -0,0 +1,16 @@
+require 'test/unit'
+require '-test-/string'
+
+class Test_RbStrDup < Test::Unit::TestCase
+ def test_nested_shared_non_frozen
+ str = Bug::String.rb_str_dup(Bug::String.rb_str_dup("a" * 50))
+ assert_send([Bug::String, :shared_string?, str])
+ assert_not_send([Bug::String, :sharing_with_shared?, str], '[Bug #15792]')
+ end
+
+ def test_nested_shared_frozen
+ str = Bug::String.rb_str_dup(Bug::String.rb_str_dup("a" * 50).freeze)
+ assert_send([Bug::String, :shared_string?, str])
+ assert_not_send([Bug::String, :sharing_with_shared?, str], '[Bug #15792]')
+ end
+end