https://bugs.ruby-lang.org/https://bugs.ruby-lang.org/favicon.ico?17113305112013-09-08T02:57:15ZRuby Issue Tracking SystemRuby master - Bug #8875: Select is not usable with SSLSockethttps://bugs.ruby-lang.org/issues/8875?journal_id=416672013-09-08T02:57:15Zheadius (Charles Nutter)headius@headius.com
<ul></ul><p>I have been experimenting with some fixes for this.</p>
<p>In JRuby, my first fix was to make IO.select aware of SSLSocket's native buffers by adding a method to query if SSLSocket had buffered data itself. This adds the socket to the list of pending read streams and does not attempt to do a blocking select on it. This fixes the simple issue of sysread/sysread_nonblock reading more data than requested and potentially draining the stream and allows select to work correctly.</p>
<p>However, the buffering issue is harder to fix. I believe buffering.rb needs to go away entirely, or at least needs to not buffer data on its own.</p>
<p>A partial patch for buffering.rb (does not fix all uses of the Ruby-land buffer) allows my original case to run to completion, since the only buffers are the ones in SSLSocket proper and my IO.select patch can see those.</p>
<p>Here's the patch as it stands right now: <a href="https://gist.github.com/headius/6477733" class="external">https://gist.github.com/headius/6477733</a></p> Ruby master - Bug #8875: Select is not usable with SSLSockethttps://bugs.ruby-lang.org/issues/8875?journal_id=416692013-09-08T03:02:07Zheadius (Charles Nutter)headius@headius.com
<ul></ul><p>See also recent comments on <a href="http://jira.codehaus.org/browse/JRUBY-6874" class="external">http://jira.codehaus.org/browse/JRUBY-6874</a></p> Ruby master - Bug #8875: Select is not usable with SSLSockethttps://bugs.ruby-lang.org/issues/8875?journal_id=416712013-09-08T05:23:19Znormalperson (Eric Wong)normalperson@yhbt.net
<ul></ul><p>"headius (Charles Nutter)" <a href="mailto:headius@headius.com" class="email">headius@headius.com</a> wrote:</p>
<blockquote>
<p>However, the buffering issue is harder to fix. I believe buffering.rb<br>
needs to go away entirely, or at least needs to not buffer data on its<br>
own.</p>
</blockquote>
<p>I think so, too. I believe any sort of explicit buffering by Ruby on<br>
socket/pipe wrappers is wrong and only leads to surprising behavior and<br>
bugs.</p>
<p>Anyways, I'm not sure if read/readpartial/write for IO.select should<br>
ever be recommended. select() manpage in Linux recommends using<br>
O_NONBLOCK for all I/O operations because of spurious wakeups, too.<br>
So perhaps we should only be doing the _nonblock variants anyways.</p>
<p>(Also, consider the multi-threaded case where several threads are all<br>
doing select + (recvmsg|accept) on the same shared socket)</p> Ruby master - Bug #8875: Select is not usable with SSLSockethttps://bugs.ruby-lang.org/issues/8875?journal_id=416722013-09-08T07:03:47Zakr (Akira Tanaka)akr@fsij.org
<ul></ul><p>2013/9/8 headius (Charles Nutter) <a href="mailto:headius@headius.com" class="email">headius@headius.com</a>:</p>
<blockquote>
<p>In JRuby, my first fix was to make IO.select aware of SSLSocket's native buffers by adding a method to query if SSLSocket had buffered data itself. This adds the socket to the list of pending read streams and does not attempt to do a blocking select on it. This fixes the simple issue of sysread/sysread_nonblock reading more data than requested and potentially draining the stream and allows select to work correctly.</p>
</blockquote>
<p>I think the right fix is that application uses read_nonblock before select.<br>
See the document of IO#read_nonblock and OpenSSL::Buffering#read_nonblock.<br>
I wrote this problem in <a href="/issues/5138">[ruby-core:38681]</a>.</p>
<blockquote>
<p>However, the buffering issue is harder to fix. I believe buffering.rb needs to go away entirely, or at least needs to not buffer data on its own.</p>
</blockquote>
<h2>OpenSSL::Buffering#gets may be difficult to implement efficiently<br>
without buffering.</h2>
<p>Tanaka Akira</p> Ruby master - Bug #8875: Select is not usable with SSLSockethttps://bugs.ruby-lang.org/issues/8875?journal_id=416832013-09-08T23:14:23Zheadius (Charles Nutter)headius@headius.com
<ul></ul><p>akr (Akira Tanaka) wrote:</p>
<blockquote>
<p>2013/9/8 headius (Charles Nutter) <a href="mailto:headius@headius.com" class="email">headius@headius.com</a>:</p>
<blockquote>
<p>In JRuby, my first fix was to make IO.select aware of SSLSocket's native buffers by adding a method to query if SSLSocket had buffered data itself. This adds the socket to the list of pending read streams and does not attempt to do a blocking select on it. This fixes the simple issue of sysread/sysread_nonblock reading more data than requested and potentially draining the stream and allows select to work correctly.</p>
</blockquote>
<p>I think the right fix is that application uses read_nonblock before select.<br>
See the document of IO#read_nonblock and OpenSSL::Buffering#read_nonblock.<br>
I wrote this problem in <a href="/issues/5138">[ruby-core:38681]</a>.</p>
</blockquote>
<p>I would agree, except that users are shown, through examples online and in source, that SSLSocket is "IO-like" and can be used anywhere an IO can be used. IO can do buffered IO and still be selectable. SSLSocket cannot.</p>
<p>This is a shame because without the buffering in buffering.rb, it <em>would</em> be feasible to make select work with SSLSocket, as I have done in JRuby.</p>
<blockquote>
<blockquote>
<p>However, the buffering issue is harder to fix. I believe buffering.rb needs to go away entirely, or at least needs to not buffer data on its own.</p>
</blockquote>
<p>OpenSSL::Buffering#gets may be difficult to implement efficiently<br>
without buffering.</p>
</blockquote>
<p>Difficult but not impossible :-) If the buffer in buffering.rb was known to SSLSocket proper and SSLSocket included it when reporting to IO.select that it has data buffered, all would be fine. This could either mean moving the implementations of gets and friends into C code to work directly against the SSLSocket buffers, or moving @rbuffer into a native location so it can be efficiently queried for data.</p>
<p>I may attempt to implement the latter proposal for JRuby, and then we could translate it for MRI. The bottom line is that there are too many buffers and not enough visibility into them. So we need to fix one thing or the other.</p>
<ul>
<li>Charlie</li>
</ul> Ruby master - Bug #8875: Select is not usable with SSLSockethttps://bugs.ruby-lang.org/issues/8875?journal_id=416862013-09-09T02:40:45Zdrbrain (Eric Hodel)drbrain@segment7.net
<ul><li><strong>Status</strong> changed from <i>Open</i> to <i>Assigned</i></li><li><strong>Assignee</strong> set to <i>MartinBosslet (Martin Bosslet)</i></li></ul> Ruby master - Bug #8875: Select is not usable with SSLSockethttps://bugs.ruby-lang.org/issues/8875?journal_id=417042013-09-10T10:53:23Zakr (Akira Tanaka)akr@fsij.org
<ul></ul><p>2013/9/8 headius (Charles Nutter) <a href="mailto:headius@headius.com" class="email">headius@headius.com</a>:</p>
<blockquote>
<p>I would agree, except that users are shown, through examples online and in source, that SSLSocket is "IO-like" and can be used anywhere an IO can be used. IO can do buffered IO and still be selectable. SSLSocket cannot.</p>
<p>This is a shame because without the buffering in buffering.rb, it <em>would</em> be feasible to make select work with SSLSocket, as I have done in JRuby.</p>
</blockquote>
<p>What's happen when the remote side of SSL protocol sends a partial record?<br>
In that case, select notify readability (of encrypted data) but no decrypted<br>
data readable.</p>
<p>What's also happen when the remote side request renegotiation and local side<br>
write system call blocks?<br>
It is difficult to say it is not happen.</p>
<h2>I think checking bufer emptyness of SSL is not enough to avoid blocking at<br>
blocking read invocation after select.</h2>
<p>Tanaka Akira</p> Ruby master - Bug #8875: Select is not usable with SSLSockethttps://bugs.ruby-lang.org/issues/8875?journal_id=420302013-09-27T19:41:55Zheadius (Charles Nutter)headius@headius.com
<ul></ul><p>akr (Akira Tanaka) wrote:</p>
<blockquote>
<p>2013/9/8 headius (Charles Nutter) <a href="mailto:headius@headius.com" class="email">headius@headius.com</a>:</p>
<blockquote>
<p>I would agree, except that users are shown, through examples online and in source, that SSLSocket is "IO-like" and can be used anywhere an IO can be used. IO can do buffered IO and still be selectable. SSLSocket cannot.</p>
<p>This is a shame because without the buffering in buffering.rb, it <em>would</em> be feasible to make select work with SSLSocket, as I have done in JRuby.</p>
</blockquote>
<p>What's happen when the remote side of SSL protocol sends a partial record?<br>
In that case, select notify readability (of encrypted data) but no decrypted<br>
data readable.</p>
</blockquote>
<p>Doing a select followed by a blocking read of more data than is actually available would block on any socket. The amount of data available in this case is therefore 0 even if the socket is readable.</p>
<p>You should use read_nonblock in combination with select, so only what is available without blocking gets read off the wire. In this case, it would be an empty result (empty string or nil) or raise EAGAIN. The read logic, however, would drain the socket of its partial record into the encrypted buffer. Could we not just associate the buffer check the decrypted buffer, and always attempt to drain the encrypted buffer on any read?</p>
<ol>
<li>Code selects on socket. Decrypted buffer is empty and no data is on the wire yet, so it blocks.</li>
<li>Partial record is written and select returns.</li>
<li>read_nonblock triggers read from the socket into encrypted buffer.</li>
<li>Not enough encrypted data is available to decrypt, so the data remains in the encrypted buffer.</li>
<li>read_nonblock raises EAGAIN</li>
<li>Code selects on socket. Decrypted buffer is still empty and no data is on the wire yet, so it blocks.</li>
<li>The rest of the partial record arrives on the wire. Select returns.</li>
<li>read_nonblock drains the rest of the data from the wire into encrypted buffer and drains encrypted buffer into decrypted buffer.</li>
</ol>
<p>At this point, if the read_nonblock requested all of the decrypted data, that buffer would be drained and select would block again. If read_nonblock requested less than the available data, some would remain in the buffer and select would not block.</p>
<blockquote>
<p>What's also happen when the remote side request renegotiation and local side<br>
write system call blocks?<br>
It is difficult to say it is not happen.</p>
</blockquote>
<p>It may be that you would need to select for both read and write to ensure this case succeeds. I think it would be a rare occurrence, though, since it will almost always be possible to write into the socket's internal buffer the small amount of data needed for renegotiation.</p> Ruby master - Bug #8875: Select is not usable with SSLSockethttps://bugs.ruby-lang.org/issues/8875?journal_id=423112013-10-07T19:43:36Zakr (Akira Tanaka)akr@fsij.org
<ul></ul><p>headius (Charles Nutter) wrote:</p>
<blockquote>
<p>You should use read_nonblock in combination with select, so only what is available without blocking gets read off the wire. In this case, it would be an empty result (empty string or nil) or raise EAGAIN. The read logic, however, would drain the socket of its partial record into the encrypted buffer. Could we not just associate the buffer check the decrypted buffer, and always attempt to drain the encrypted buffer on any read?</p>
</blockquote>
<p>Yes. We should use read_nonblock.</p>
<blockquote>
<p>It may be that you would need to select for both read and write to ensure this case succeeds. I think it would be a rare occurrence, though, since it will almost always be possible to write into the socket's internal buffer the small amount of data needed for renegotiation.</p>
</blockquote>
<p>I think you are optimistic.</p> Ruby master - Bug #8875: Select is not usable with SSLSockethttps://bugs.ruby-lang.org/issues/8875?journal_id=444172014-01-18T14:20:10Zakr (Akira Tanaka)akr@fsij.org
<ul><li><strong>Status</strong> changed from <i>Assigned</i> to <i>Rejected</i></li></ul><p>Applications should use read_nonblock and read_nonblock doesn't block.</p>
<p>So this is a problem of applications which use IO.select and blocking methods of SSLSocket.</p>