Skip to content

Commit f8937a6

Browse files
authored
Merge pull request #821 from rhenium/ky/ssl-read-write-check-cb-state
ssl: handle callback exceptions in SSLSocket#sysread and #syswrite
2 parents 81409ee + aac9ce1 commit f8937a6

File tree

2 files changed

+67
-8
lines changed

2 files changed

+67
-8
lines changed

ext/openssl/ossl_ssl.c

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1916,7 +1916,7 @@ ossl_ssl_read_internal(int argc, VALUE *argv, VALUE self, int nonblock)
19161916
{
19171917
SSL *ssl;
19181918
int ilen;
1919-
VALUE len, str;
1919+
VALUE len, str, cb_state;
19201920
VALUE opts = Qnil;
19211921

19221922
if (nonblock) {
@@ -1949,6 +1949,14 @@ ossl_ssl_read_internal(int argc, VALUE *argv, VALUE self, int nonblock)
19491949
rb_str_locktmp(str);
19501950
for (;;) {
19511951
int nread = SSL_read(ssl, RSTRING_PTR(str), ilen);
1952+
1953+
cb_state = rb_attr_get(self, ID_callback_state);
1954+
if (!NIL_P(cb_state)) {
1955+
rb_ivar_set(self, ID_callback_state, Qnil);
1956+
ossl_clear_error();
1957+
rb_jump_tag(NUM2INT(cb_state));
1958+
}
1959+
19521960
switch (ssl_get_error(ssl, nread)) {
19531961
case SSL_ERROR_NONE:
19541962
rb_str_unlocktmp(str);
@@ -2038,7 +2046,7 @@ ossl_ssl_write_internal(VALUE self, VALUE str, VALUE opts)
20382046
SSL *ssl;
20392047
rb_io_t *fptr;
20402048
int num, nonblock = opts != Qfalse;
2041-
VALUE tmp;
2049+
VALUE tmp, cb_state;
20422050

20432051
GetSSL(self, ssl);
20442052
if (!ssl_started(ssl))
@@ -2055,6 +2063,14 @@ ossl_ssl_write_internal(VALUE self, VALUE str, VALUE opts)
20552063

20562064
for (;;) {
20572065
int nwritten = SSL_write(ssl, RSTRING_PTR(tmp), num);
2066+
2067+
cb_state = rb_attr_get(self, ID_callback_state);
2068+
if (!NIL_P(cb_state)) {
2069+
rb_ivar_set(self, ID_callback_state, Qnil);
2070+
ossl_clear_error();
2071+
rb_jump_tag(NUM2INT(cb_state));
2072+
}
2073+
20582074
switch (ssl_get_error(ssl, nwritten)) {
20592075
case SSL_ERROR_NONE:
20602076
return INT2NUM(nwritten);

test/openssl/test_ssl_session.rb

Lines changed: 49 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -219,11 +219,11 @@ def test_server_session_cache
219219
# deadlock.
220220
TEST_SESSION_REMOVE_CB = ENV["OSSL_TEST_ALL"] == "1"
221221

222-
def test_ctx_client_session_cb
223-
ctx_proc = proc { |ctx| ctx.ssl_version = :TLSv1_2 }
224-
start_server(ctx_proc: ctx_proc) do |port|
222+
def test_ctx_client_session_cb_tls12
223+
start_server do |port|
225224
called = {}
226225
ctx = OpenSSL::SSL::SSLContext.new
226+
ctx.min_version = ctx.max_version = :TLS1_2
227227
ctx.session_cache_mode = OpenSSL::SSL::SSLContext::SESSION_CACHE_CLIENT
228228
ctx.session_new_cb = lambda { |ary|
229229
sock, sess = ary
@@ -233,23 +233,66 @@ def test_ctx_client_session_cb
233233
ctx.session_remove_cb = lambda { |ary|
234234
ctx, sess = ary
235235
called[:remove] = [ctx, sess]
236-
# any resulting value is OK (ignored)
237236
}
238237
end
239238

240239
server_connect_with_session(port, ctx, nil) { |ssl|
241240
assert_equal(1, ctx.session_cache_stats[:cache_num])
242241
assert_equal(1, ctx.session_cache_stats[:connect_good])
243242
assert_equal([ssl, ssl.session], called[:new])
244-
assert(ctx.session_remove(ssl.session))
245-
assert(!ctx.session_remove(ssl.session))
243+
assert_equal(true, ctx.session_remove(ssl.session))
244+
assert_equal(false, ctx.session_remove(ssl.session))
246245
if TEST_SESSION_REMOVE_CB
247246
assert_equal([ctx, ssl.session], called[:remove])
248247
end
249248
}
250249
end
251250
end
252251

252+
def test_ctx_client_session_cb_tls13
253+
omit "TLS 1.3 not supported" unless tls13_supported?
254+
omit "LibreSSL does not call session_new_cb in TLS 1.3" if libressl?
255+
256+
start_server do |port|
257+
called = {}
258+
ctx = OpenSSL::SSL::SSLContext.new
259+
ctx.min_version = :TLS1_3
260+
ctx.session_cache_mode = OpenSSL::SSL::SSLContext::SESSION_CACHE_CLIENT
261+
ctx.session_new_cb = lambda { |ary|
262+
sock, sess = ary
263+
called[:new] = [sock, sess]
264+
}
265+
266+
server_connect_with_session(port, ctx, nil) { |ssl|
267+
ssl.puts("abc"); assert_equal("abc\n", ssl.gets)
268+
269+
assert_operator(1, :<=, ctx.session_cache_stats[:cache_num])
270+
assert_operator(1, :<=, ctx.session_cache_stats[:connect_good])
271+
assert_equal([ssl, ssl.session], called[:new])
272+
}
273+
end
274+
end
275+
276+
def test_ctx_client_session_cb_tls13_exception
277+
omit "TLS 1.3 not supported" unless tls13_supported?
278+
omit "LibreSSL does not call session_new_cb in TLS 1.3" if libressl?
279+
280+
start_server do |port|
281+
ctx = OpenSSL::SSL::SSLContext.new
282+
ctx.min_version = :TLS1_3
283+
ctx.session_cache_mode = OpenSSL::SSL::SSLContext::SESSION_CACHE_CLIENT
284+
ctx.session_new_cb = lambda { |ary|
285+
raise "in session_new_cb"
286+
}
287+
288+
server_connect_with_session(port, ctx, nil) { |ssl|
289+
assert_raise_with_message(RuntimeError, /in session_new_cb/) {
290+
ssl.puts("abc"); assert_equal("abc\n", ssl.gets)
291+
}
292+
}
293+
end
294+
end
295+
253296
def test_ctx_server_session_cb
254297
connections = nil
255298
called = {}

0 commit comments

Comments
 (0)