Dmytro Shteflyuk created THRIFT-6148:
----------------------------------------
Summary: Ruby HeaderTransport should enforce limits against
complete frames
Key: THRIFT-6148
URL: https://issues.apache.org/jira/browse/THRIFT-6148
Project: Thrift
Issue Type: Bug
Components: Ruby - Library
Reporter: Dmytro Shteflyuk
Assignee: Dmytro Shteflyuk
h3. Problem
Ruby {{HeaderTransport#flush}} checks {{max_frame_size}} against the serialized
payload before constructing the Header frame. The completed frame also contains
the fixed Header envelope, transformed payload, protocol and transform
identifiers, key/value metadata, and alignment padding.
The read path applies the same configured limit to the frame size declared on
the wire. That declared size includes the complete Header frame but excludes
its four-byte length prefix. A writer can therefore emit a frame that a reader
configured with the same limit rejects.
This is not double counting: each Header field and payload byte belongs to the
declared frame exactly once, while the outer four-byte length prefix is
excluded on both write and read.
h3. Client impact
Applications using the same {{max_frame_size}} policy on both peers cannot rely
on locally written Header frames being accepted by the receiving peer. The
mismatch is more likely when messages include metadata or transforms because
those bytes are added after the current writer-side check.
The writer reports successful flush even though a reader using the identical
limit rejects the resulting frame.
h3. Reproduction
{code:ruby}
require "thrift"
limit = 20
buffer = Thrift::MemoryBufferTransport.new
writer = Thrift::HeaderTransport.new(buffer)
writer.set_max_frame_size(limit)
writer.write("x" * 10)
writer.flush
frame = buffer.read(buffer.available)
puts "declared=#{frame.unpack1("N")} emitted=#{frame.bytesize}"
reader = Thrift::HeaderTransport.new(
Thrift::MemoryBufferTransport.new(frame)
)
reader.set_max_frame_size(limit)
begin
reader.read(10)
rescue => error
puts "#{error.class}: #{error.message}"
end
{code}
Testing on master commit {{4d0faf31a8c06e84fe48f35556b3ccdce569dbd9}} produces:
{code}
declared=24 emitted=28
Thrift::TransportException: Frame size 24 exceeds maximum 20
{code}
The declared size is 24 bytes and excludes the four-byte length prefix. The
complete emitted buffer is therefore 28 bytes.
h3. Expected behavior
The writer should apply {{max_frame_size}} to the completed declared Header
frame after transforms, metadata, and padding have been constructed. A frame
exactly at the configured limit should be emitted, while a larger frame should
raise {{TransportException}} before writing any bytes.
Framed Binary/Compact compatibility modes should continue comparing their
payload size because their four-byte length prefix is excluded from the
declared frame. Unframed Binary/Compact modes should continue comparing the
complete protocol message because they have no frame prefix.
If local validation rejects a Header frame before any underlying write, pending
one-shot headers should remain available for a subsequent write.
--
This message was sent by Atlassian Jira
(v8.20.10#820010)