From 118ee2a734152c35ebb6e963d4052d7152ab3ba0 Mon Sep 17 00:00:00 2001 From: rhe Date: Sat, 21 May 2016 07:25:00 +0000 Subject: openssl: fix possible SEGV on race between SSLSocket#stop and #connect * ext/openssl/ossl_ssl.c (ossl_ssl_stop): Don't free the SSL struct here. Since some methods such as SSLSocket#connect releases GVL, there is a chance of use after free if we free the SSL from another thread. SSLSocket#stop was documented as "prepares it for another connection" so this is a slightly incompatible change. However when this sentence was added (r30090, Add toplevel documentation for OpenSSL, 2010-12-06), it didn't actually. The current behavior is from r40304 (Correct shutdown behavior w.r.t GC., 2013-04-15). [ruby-core:74978] [Bug #12292] * ext/openssl/lib/openssl/ssl.rb (sysclose): Update doc. * test/openssl/test_ssl.rb: Test this. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@55100 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- ChangeLog | 16 ++++++++++++++++ ext/openssl/lib/openssl/ssl.rb | 5 ++++- ext/openssl/ossl_ssl.c | 14 ++++---------- test/openssl/test_ssl.rb | 22 ++++++++++++++++++++++ 4 files changed, 46 insertions(+), 11 deletions(-) diff --git a/ChangeLog b/ChangeLog index 8e69ed79b3..c6f79c2073 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,19 @@ +Sat May 21 16:16:03 2016 Kazuki Yamaguchi + + * ext/openssl/ossl_ssl.c (ossl_ssl_stop): Don't free the SSL struct + here. Since some methods such as SSLSocket#connect releases GVL, + there is a chance of use after free if we free the SSL from another + thread. SSLSocket#stop was documented as "prepares it for another + connection" so this is a slightly incompatible change. However when + this sentence was added (r30090, Add toplevel documentation for + OpenSSL, 2010-12-06), it didn't actually. The current behavior is + from r40304 (Correct shutdown behavior w.r.t GC., 2013-04-15). + [ruby-core:74978] [Bug #12292] + + * ext/openssl/lib/openssl/ssl.rb (sysclose): Update doc. + + * test/openssl/test_ssl.rb: Test this. + Sat May 21 14:41:14 2016 Kazuki Yamaguchi * ext/openssl/ossl.c: [DOC] Fix SSL client example. The variable name diff --git a/ext/openssl/lib/openssl/ssl.rb b/ext/openssl/lib/openssl/ssl.rb index 57519f2c21..9893757011 100644 --- a/ext/openssl/lib/openssl/ssl.rb +++ b/ext/openssl/lib/openssl/ssl.rb @@ -290,7 +290,10 @@ module OpenSSL # call-seq: # ssl.sysclose => nil # - # Shuts down the SSL connection and prepares it for another connection. + # Sends "close notify" to the peer and tries to shut down the SSL + # connection gracefully. + # + # If sync_close is set to +true+, the underlying IO is also closed. def sysclose return if closed? stop diff --git a/ext/openssl/ossl_ssl.c b/ext/openssl/ossl_ssl.c index 87df7f9f78..d5ea130489 100644 --- a/ext/openssl/ossl_ssl.c +++ b/ext/openssl/ossl_ssl.c @@ -1601,23 +1601,17 @@ ossl_ssl_write_nonblock(int argc, VALUE *argv, VALUE self) * call-seq: * ssl.stop => nil * - * Stops the SSL connection and prepares it for another connection. + * Sends "close notify" to the peer and tries to shut down the SSL connection + * gracefully. */ static VALUE ossl_ssl_stop(VALUE self) { SSL *ssl; - /* ossl_ssl_data_get_struct() is not usable here because it may return - * from this function; */ - - GetSSL(self, ssl); + ossl_ssl_data_get_struct(self, ssl); - if (ssl) { - ossl_ssl_shutdown(ssl); - SSL_free(ssl); - } - DATA_PTR(self) = NULL; + ossl_ssl_shutdown(ssl); return Qnil; } 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