Skip to content

Commit 744ccb8

Browse files
committed
ssl: fix SSLSocket#sysread leaking tmplock String on timeout
Commit 3bbf517 made blocking methods on SSLSocket follow the IO#timeout= value. The commit changed io_wait_readable() to potentially raise an exception without unlocking the String. The String is currently locked for the entire duration of a #sysread method call. This does not seem to be necessary, as SSL_read() does not require that the same buffer is specified when retrying. Locking the String during each SSL_read() call should be sufficient.
1 parent f4e7c4b commit 744ccb8

File tree

2 files changed

+11
-9
lines changed

2 files changed

+11
-9
lines changed

ext/openssl/ossl_ssl.c

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1959,9 +1959,12 @@ ossl_ssl_read_internal(int argc, VALUE *argv, VALUE self, int nonblock)
19591959

19601960
VALUE io = rb_attr_get(self, id_i_io);
19611961

1962-
rb_str_locktmp(str);
19631962
for (;;) {
1963+
if (rb_str_capacity(str) < (size_t)ilen)
1964+
rb_raise(eSSLError, "read buffer was modified");
1965+
rb_str_locktmp(str);
19641966
int nread = SSL_read(ssl, RSTRING_PTR(str), ilen);
1967+
rb_str_unlocktmp(str);
19651968

19661969
cb_state = rb_attr_get(self, ID_callback_state);
19671970
if (!NIL_P(cb_state)) {
@@ -1972,32 +1975,27 @@ ossl_ssl_read_internal(int argc, VALUE *argv, VALUE self, int nonblock)
19721975

19731976
switch (ssl_get_error(ssl, nread)) {
19741977
case SSL_ERROR_NONE:
1975-
rb_str_unlocktmp(str);
19761978
rb_str_set_len(str, nread);
19771979
return str;
19781980
case SSL_ERROR_ZERO_RETURN:
1979-
rb_str_unlocktmp(str);
19801981
if (no_exception_p(opts)) { return Qnil; }
19811982
rb_eof_error();
19821983
case SSL_ERROR_WANT_WRITE:
19831984
if (nonblock) {
1984-
rb_str_unlocktmp(str);
19851985
if (no_exception_p(opts)) { return sym_wait_writable; }
19861986
write_would_block(nonblock);
19871987
}
19881988
io_wait_writable(io);
19891989
continue;
19901990
case SSL_ERROR_WANT_READ:
19911991
if (nonblock) {
1992-
rb_str_unlocktmp(str);
19931992
if (no_exception_p(opts)) { return sym_wait_readable; }
19941993
read_would_block(nonblock);
19951994
}
19961995
io_wait_readable(io);
19971996
continue;
19981997
case SSL_ERROR_SYSCALL:
19991998
if (!ERR_peek_error()) {
2000-
rb_str_unlocktmp(str);
20011999
if (errno)
20022000
rb_sys_fail(0);
20032001
else {
@@ -2014,7 +2012,6 @@ ossl_ssl_read_internal(int argc, VALUE *argv, VALUE self, int nonblock)
20142012
}
20152013
/* fall through */
20162014
default:
2017-
rb_str_unlocktmp(str);
20182015
ossl_raise(eSSLError, "SSL_read");
20192016
}
20202017
}

test/openssl/test_ssl.rb

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -256,11 +256,16 @@ def test_read_with_timeout
256256
ssl.syswrite(str)
257257
assert_equal(str, ssl.sysread(str.bytesize))
258258

259-
ssl.timeout = 1
260-
assert_raise(IO::TimeoutError) {ssl.read(1)}
259+
ssl.timeout = 0.1
260+
assert_raise(IO::TimeoutError) { ssl.sysread(1) }
261261

262262
ssl.syswrite(str)
263263
assert_equal(str, ssl.sysread(str.bytesize))
264+
265+
buf = "orig".b
266+
assert_raise(IO::TimeoutError) { ssl.sysread(1, buf) }
267+
assert_equal("orig", buf)
268+
assert_nothing_raised { buf.clear }
264269
end
265270
end
266271
end

0 commit comments

Comments
 (0)