From 4bdd207b1a2ed20626c502cfc083d4f2d4969bcb Mon Sep 17 00:00:00 2001 From: Kazuki Yamaguchi Date: Sat, 16 Apr 2016 18:13:10 +0900 Subject: ext/openssl: check that the SSL object is still set after reacquiring GVL Fix the segmentation fault due to use after free of SSL object. OpenSSL::SSL::SSLSocket#stop frees the SSL object immediately after SSL_shutdown(), but since some method, such as SSLSocket#connect releases the GVL while waiting the peer, there is a good chance that the SSL object is freed from another thread and this leads to a segmentation fault. --- ext/openssl/ossl_ssl.c | 12 +++++++++--- test/openssl/test_ssl.rb | 22 ++++++++++++++++++++++ 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/ext/openssl/ossl_ssl.c b/ext/openssl/ossl_ssl.c index 96c7990046..244c5d25c0 100644 --- a/ext/openssl/ossl_ssl.c +++ b/ext/openssl/ossl_ssl.c @@ -1284,7 +1284,9 @@ ossl_start_ssl(VALUE self, int (*func)(), const char *funcname, VALUE opts) ossl_ssl_data_get_struct(self, ssl); GetOpenFile(ossl_ssl_get_io(self), fptr); - for(;;){ + for (;;) { + if (RTYPEDDATA_DATA(self) != ssl) + ossl_raise(eSSLError, "connection is shut down"); ret = func(ssl); cb_state = rb_ivar_get(self, ID_callback_state); @@ -1441,7 +1443,9 @@ ossl_ssl_read_internal(int argc, VALUE *argv, VALUE self, int nonblock) if (ssl) { if(!nonblock && SSL_pending(ssl) <= 0) rb_thread_wait_fd(FPTR_TO_FD(fptr)); - for (;;){ + for (;;) { + if (RTYPEDDATA_DATA(self) != ssl) + ossl_raise(eSSLError, "connection is shut down"); nread = SSL_read(ssl, RSTRING_PTR(str), RSTRING_LENINT(str)); switch(ssl_get_error(ssl, nread)){ case SSL_ERROR_NONE: @@ -1533,7 +1537,9 @@ ossl_ssl_write_internal(VALUE self, VALUE str, VALUE opts) GetOpenFile(ossl_ssl_get_io(self), fptr); if (ssl) { - for (;;){ + for (;;) { + if (RTYPEDDATA_DATA(self) != ssl) + ossl_raise(eSSLError, "connection is shut down"); nwrite = SSL_write(ssl, RSTRING_PTR(str), RSTRING_LENINT(str)); switch(ssl_get_error(ssl, nwrite)){ case SSL_ERROR_NONE: diff --git a/test/openssl/test_ssl.rb b/test/openssl/test_ssl.rb index 6e9078dace..d7b996d662 100644 --- a/test/openssl/test_ssl.rb +++ b/test/openssl/test_ssl.rb @@ -1169,6 +1169,28 @@ end } end + def test_close_and_socket_close_while_connecting + # test it doesn't cause a segmentation fault + ctx = OpenSSL::SSL::SSLContext.new + ctx.ciphers = "aNULL" + + sock1, sock2 = socketpair + ssl1 = OpenSSL::SSL::SSLSocket.new(sock1, ctx) + ssl2 = OpenSSL::SSL::SSLSocket.new(sock2, ctx) + + t = Thread.new { ssl1.connect } + ssl2.accept + + ssl1.close + sock1.close + t.value rescue nil + ensure + ssl1.close if ssl1 + ssl2.close if ssl2 + sock1.close if sock1 + sock2.close if sock2 + end + def test_get_ephemeral_key return unless OpenSSL::SSL::SSLSocket.method_defined?(:tmp_key) pkey = OpenSSL::PKey -- cgit v1.2.3