mirror of
https://github.com/ruby/ruby.git
synced 2026-10-29 00:46:26 +08:00
capa does not include the terminator, so when a buffer moves
between strings with different terminator lengths the capacity has to
be converted or we will try to free the buffer with the wrong size:
[BUG] buffer 0x00005030039ff768 freed with old_size=18, but was allocated with size=17
[BUG] buffer 0x00005631adc48f48 freed with old_size=8268, but was allocated with size=8269
Three related fixes:
* `rb_str_tmp_frozen_release`: convert tmp's capacity to orig's
terminator length when handing the buffer back. This is the inverse
of a calculation added to `rb_str_tmp_frozen_no_embed_acquire` in
91619f0230.
* `heap_str_make_shared`: since bdf3032e35
the root created by `rb_str_tmp_frozen_acquire` does not copy the
encoding of the string it takes the buffer from, so the two can have
different terminator lengths and the root's capacity has to be
adjusted as well, otherwise the root frees fewer bytes than were
allocated once its only child stops sharing the buffer. termlen is
passed in because the caller assigns the encoding of the root after
this function returns.
* `rb_str_tmp_frozen_no_embed_acquire`: unset `STR_BORROWED` on the
root. `STR_SET_SHARED` sets `STR_BORROWED` on roots with
`klass == 0` to mark the ones that may have other children, but this
root only ever has orig as its child, so unset it like
`heap_str_make_shared` does. Any string that starts sharing the
root later will set the flag again. Without this
`rb_str_tmp_frozen_release` never gives the buffer back, so strings
written by `io_fwrite` stay shared and are copied on their next
modification.
The release and make_shared adjustments go together
as they were previously canceling each other out.