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)

Reply via email to