Skip to content
  • Alan Wu's avatar
    c06ddfee
    str_duplicate: Don't share with a frozen shared string · c06ddfee
    Alan Wu authored
    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
    c06ddfee
    str_duplicate: Don't share with a frozen shared string
    Alan Wu authored
    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
Loading