Project

General

Profile

« Previous | Next » 

Revision c06ddfee

Added by alanwu (Alan Wu) over 6 years ago

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:

/* 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.

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