[
https://issues.apache.org/jira/browse/THRIFT-6144?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
]
Dmytro Shteflyuk resolved THRIFT-6144.
--------------------------------------
Fix Version/s: 0.25.0
Resolution: Fixed
> Ruby BaseTransport read_all should report EOF when reads make no progress
> -------------------------------------------------------------------------
>
> Key: THRIFT-6144
> URL: https://issues.apache.org/jira/browse/THRIFT-6144
> Project: Thrift
> Issue Type: Bug
> Components: Ruby - Library
> Reporter: Dmytro Shteflyuk
> Assignee: Dmytro Shteflyuk
> Priority: Major
> Fix For: 0.25.0
>
> Time Spent: 20m
> Remaining Estimate: 0h
>
> h3. Problem
> {{BaseTransport#read_all}} repeatedly calls {{read}} until it has received
> the requested number of bytes. It currently assumes that every call returns a
> non-empty string.
> That assumption does not hold for every Ruby transport. {{IOStreamTransport}}
> delegates to Ruby {{IO#read}}, which returns {{nil}} at end of file, while
> layered transports can return an empty string when no bytes remain. An empty
> result makes {{read_all}} repeat forever without making progress. A {{nil}}
> result instead escapes as a raw {{NoMethodError}}.
> h3. Client impact
> A client reading a truncated or exhausted input through an affected transport
> can hang indefinitely when the transport returns an empty string. If the
> transport returns {{nil}}, the client receives an implementation-level Ruby
> exception rather than a typed Thrift transport exception.
> Normal partial reads are also affected when the input ends after some, but
> not all, of the requested bytes have been received.
> h3. Reproduction
> Testing on master commit {{c2def39207a73394420088da9b4b105571dd9036}}
> produces:
> {code:bash}
> bundle exec ruby -Ilib -e '
> require "timeout"
> require "thrift"
> [["empty string", ""], ["nil", nil]].each do |label, result|
> transport = Thrift::BaseTransport.new
> transport.define_singleton_method(:read) { |_| result }
> begin
> Timeout.timeout(0.1) { transport.read_all(1) }
> rescue => error
> puts "#{label}: #{error.class}: #{error.message}"
> end
> end
> '
> {code}
> {code}
> empty string: Timeout::Error: execution expired
> nil: NoMethodError: undefined method 'encoding' for nil
> {code}
> h3. Expected behavior
> {{read_all(size)}} should continue across partial reads while each read
> returns at least one byte. If a positive-size read returns {{nil}} or an
> empty string before the requested size is satisfied, it should stop
> immediately and raise {{TransportException::END_OF_FILE}}.
> A zero-size read should continue to return an empty binary string without
> invoking the underlying transport.
--
This message was sent by Atlassian Jira
(v8.20.10#820010)