[ https://issues.apache.org/jira/browse/THRIFT-4326?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=16160213#comment-16160213 ]
ASF GitHub Bot commented on THRIFT-4326: ---------------------------------------- GitHub user benweint opened a pull request: https://github.com/apache/thrift/pull/1352 THRIFT-4326 Allow safer reuse of BufferedTransport instances in Ruby Addresses [THRIFT-4326](https://issues.apache.org/jira/browse/THRIFT-4326). ## The problem In the case where a single `Thrift::BufferedTransport` instance is re-used across multiple service calls, certain kinds of malformed responses from one service call can 'leak' into subsequent calls and cause them to fail with a `Thrift::ProtocolException`. The most easily reproducible example is when a service returns a well-formed Thrift response for the first service call, but with N extra bytes of garbage tacked onto the end. In such a case, the initial service call will be handled just fine (at least when using the compact protocol), however, the next N service calls that go through the same `Thrift::BufferedTransport` instance will fail with a `Thrift::ProtocolException`. This happens because the `BufferedTransport` doesn't re-set the `@rbuf` instance variable until the read buffer is fully exhausted, so each of the N subsequent service calls will attempt to read one byte identifying the protocol from the remaining buffer, and will get some bogus value from the garbage bytes at the end of the response from the initial service call. This can also happen if reading from `@rbuf` is interrupted part-way through, while `@index` is still pointing to the middle of the read buffer (e.g. due to a Ruby timeout exception). ## Proposed solution Re-setting the `@rbuf` instance variable to an empty byte buffer upon every call to `#flush` addresses this problem, and is conceptually similar to what happens in `HttpClientTransport#flush` (where `@inbuf` and `@outbuf` are both always reset). You can merge this pull request into a Git repository by running: $ git pull https://github.com/benweint/thrift THRIFT-4326 Alternatively you can review and apply these changes as the patch at: https://github.com/apache/thrift/pull/1352.patch To close this pull request, make a commit to your master/trunk branch with (at least) the following in the commit message: This closes #1352 ---- commit 79936408af223c5df3fe8a52ec65873ccc32a255 Author: Ben Weintraub <b...@newrelic.com> Date: 2017-09-10T04:17:42Z THRIFT-4326 Ensure rbuf in BufferedTransport reset upon flush to allow reuse ---- > Ruby BufferedTransport not safe for reuse after reading corrupted input > ----------------------------------------------------------------------- > > Key: THRIFT-4326 > URL: https://issues.apache.org/jira/browse/THRIFT-4326 > Project: Thrift > Issue Type: Bug > Components: Ruby - Library > Affects Versions: 0.10.0 > Environment: Originally observed with Thrift 0.9.3 on Linux with Ruby > 2.3.4, but have also reproduced on Mac OS X with Thrift 0.10.0. > Reporter: Ben Weintraub > > We've experimented with the Ruby {{BufferedTransport}} class as a wrapper > around the {{HttpClientTransport}} class, and found that we were getting > clusters sporadic {{Thrift::ProtocolException}} errors in Ruby client > processes after network issues caused corruption of some Thrift response > bodies. > Using a bare {{HttpClientTransport}} makes these issues disappear. > For a given service, we retain a long-lived protocol instance > ({{CompactProtocol}} in our case), which in turn holds a reference to a > long-lived {{BufferedTransport}} instance. > The problem seems to stem from the case where the Thrift client is > interrupted (e.g. by a Ruby timeout exception) before consuming to the end of > the {{@rbuf}} instance variable in {{BufferedTransport}}, leaving {{@index}} > pointing to the middle of the read buffer, and meaning that when the > transport is re-used upon the next service call, the {{BufferedTransport}} > continues reading where it left off in the old buffer, rather than calling > through to the wrapped {{HttpClientTransport}} to read the new response > obtained from the last call to {{#flush}}. > Now I know {{Timeout}} is fundamentally unsafe in Ruby and can lead to all > kinds of issues like this, but I've also found that this same issue can be > triggered by another fairly plausible scenario: if the Thrift service returns > a well-formed Thrift response but with N extra bytes of garbage tacked onto > the end, then the next N following service calls through the same > {{BufferedTransport}} instance will fail with a > {{Thrift::ProtocolException}}, as the {{BufferedTransport}} will continue > attempting to read the left-over bytes in {{@rbuf}}. > The naive solution seems like it would be to just reset {{@rbuf}} from > {{#flush}}. -- This message was sent by Atlassian JIRA (v6.4.14#64029)