[ 
https://issues.apache.org/jira/browse/THRIFT-6138?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
 ]

Dmytro Shteflyuk resolved THRIFT-6138.
--------------------------------------
    Fix Version/s: 0.25.0
       Resolution: Fixed

> 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
>            Priority: Major
>             Fix For: 0.25.0
>
>          Time Spent: 50m
>  Remaining Estimate: 0h
>
> 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