Skip to content
  • nagachika's avatar
    f5930c87
    merge revision(s)... · f5930c87
    nagachika authored
    merge revision(s) 3f956201,c06ddfee,3b3b4a44: [Backport #15792]
    
    	Get rid of indirect sharing
    
    	* string.c (str_duplicate): share the root shared string if the
    	  original string is already sharing, so that all shared strings
    	  refer the root shared string directly.  indirect sharing can
    	  cause a dangling pointer.
    
    	[Bug #15792]
    
    	str_duplicate: Don't share with a frozen shared string
    
    	This is a follow up for 3f956201.
    	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
    
    	Update dependencies
    
    
    git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/branches/ruby_2_6@67731 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
    f5930c87
    merge revision(s)...
    nagachika authored
    merge revision(s) 3f956201,c06ddfee,3b3b4a44: [Backport #15792]
    
    	Get rid of indirect sharing
    
    	* string.c (str_duplicate): share the root shared string if the
    	  original string is already sharing, so that all shared strings
    	  refer the root shared string directly.  indirect sharing can
    	  cause a dangling pointer.
    
    	[Bug #15792]
    
    	str_duplicate: Don't share with a frozen shared string
    
    	This is a follow up for 3f956201.
    	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
    
    	Update dependencies
    
    
    git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/branches/ruby_2_6@67731 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Loading