Skip to content

Commit a0bf65f

Browse files
authored
Merge pull request #832 from rhenium/ky/ssl-read-unlock-str-timeout
ssl: fix SSLSocket#sysread leaking locktmp String on timeout
2 parents e5153db + 8f791d7 commit a0bf65f

File tree

2 files changed

+16
-11
lines changed

2 files changed

+16
-11
lines changed

ext/openssl/ossl_ssl.c

+9-9
Original file line numberDiff line numberDiff line change
@@ -1959,9 +1959,10 @@ 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+
rb_str_locktmp(str);
19641964
int nread = SSL_read(ssl, RSTRING_PTR(str), ilen);
1965+
rb_str_unlocktmp(str);
19651966

19661967
cb_state = rb_attr_get(self, ID_callback_state);
19671968
if (!NIL_P(cb_state)) {
@@ -1972,32 +1973,27 @@ ossl_ssl_read_internal(int argc, VALUE *argv, VALUE self, int nonblock)
19721973

19731974
switch (ssl_get_error(ssl, nread)) {
19741975
case SSL_ERROR_NONE:
1975-
rb_str_unlocktmp(str);
19761976
rb_str_set_len(str, nread);
19771977
return str;
19781978
case SSL_ERROR_ZERO_RETURN:
1979-
rb_str_unlocktmp(str);
19801979
if (no_exception_p(opts)) { return Qnil; }
19811980
rb_eof_error();
19821981
case SSL_ERROR_WANT_WRITE:
19831982
if (nonblock) {
1984-
rb_str_unlocktmp(str);
19851983
if (no_exception_p(opts)) { return sym_wait_writable; }
19861984
write_would_block(nonblock);
19871985
}
19881986
io_wait_writable(io);
1989-
continue;
1987+
break;
19901988
case SSL_ERROR_WANT_READ:
19911989
if (nonblock) {
1992-
rb_str_unlocktmp(str);
19931990
if (no_exception_p(opts)) { return sym_wait_readable; }
19941991
read_would_block(nonblock);
19951992
}
19961993
io_wait_readable(io);
1997-
continue;
1994+
break;
19981995
case SSL_ERROR_SYSCALL:
19991996
if (!ERR_peek_error()) {
2000-
rb_str_unlocktmp(str);
20011997
if (errno)
20021998
rb_sys_fail(0);
20031999
else {
@@ -2014,9 +2010,13 @@ ossl_ssl_read_internal(int argc, VALUE *argv, VALUE self, int nonblock)
20142010
}
20152011
/* fall through */
20162012
default:
2017-
rb_str_unlocktmp(str);
20182013
ossl_raise(eSSLError, "SSL_read");
20192014
}
2015+
2016+
// Ensure the buffer is not modified during io_wait_*able()
2017+
rb_str_modify(str);
2018+
if (rb_str_capacity(str) < (size_t)ilen)
2019+
rb_raise(eSSLError, "read buffer was modified");
20202020
}
20212021
}
20222022

test/openssl/test_ssl.rb

+7-2
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)