Dmytro Shteflyuk created THRIFT-6138:
----------------------------------------
Summary: Ruby MemoryBufferTransport should privately own initial
buffers
Key: THRIFT-6138
URL: https://issues.apache.org/jira/browse/THRIFT-6138
Project: Thrift
Issue Type: Bug
Components: Ruby - Library
Reporter: Dmytro Shteflyuk
Assignee: Dmytro Shteflyuk
h3. Problem
{{MemoryBufferTransport}} stores a caller-supplied String as its internal
buffer. The transport and caller therefore continue to share the same mutable
object.
A caller can change unread transport data by modifying the original String,
while writes through the transport modify the caller's String. An
already-binary frozen String is accepted during construction but later raises
{{FrozenError}} when the transport writes to or resets its buffer. Construction
can also change a mutable caller String's encoding to {{ASCII-8BIT}}.
h3. Client impact
Ruby clients that construct a memory transport from a String must currently
duplicate that String themselves to avoid shared mutation. Reusing or freezing
the input can otherwise change the bytes observed by the transport or cause an
exception during a later operation. The behavior is the same with and without
the native extension.
Changing the transport to own its initial buffer deliberately removes the
historical shared-buffer behavior. Callers that intentionally used the original
String as a live view of the transport would no longer observe transport writes
through that object.
h3. Reproduction
{code:ruby}
require "thrift"
source = +"abc"
transport = Thrift::MemoryBufferTransport.new(source)
source.replace("xyz")
puts "caller mutation: #{transport.read(3).inspect}"
source = +"abc"
transport = Thrift::MemoryBufferTransport.new(source)
transport.write("d")
puts "transport write: #{source.inspect}"
frozen = "abc".b.freeze
transport = Thrift::MemoryBufferTransport.new(frozen)
begin
transport.write("d")
rescue => e
puts "frozen write: #{e.class}: #{e.message}"
end
utf8 = +"é"
Thrift::MemoryBufferTransport.new(utf8)
puts "caller encoding: #{utf8.encoding}"
{code}
Testing on master commit {{e4473c9e296b79003ca04128612e7b6a846a6552}} produces:
{noformat}
caller mutation: "xyz"
transport write: "abcd"
frozen write: FrozenError: can't modify frozen String: "abc"
caller encoding: ASCII-8BIT
{noformat}
h3. Expected behavior
{{MemoryBufferTransport}} should create a private mutable binary buffer from
constructor input. Mutating either the caller's String or the transport should
not affect the other object. Frozen input should remain usable for subsequent
writes and resets, and construction should not change the caller's encoding.
--
This message was sent by Atlassian Jira
(v8.20.10#820010)