aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKazuki Yamaguchi <k@rhe.jp>2016-04-16 18:13:10 +0900
committerKazuki Yamaguchi <k@rhe.jp>2016-04-16 20:06:40 +0900
commit4bdd207b1a2ed20626c502cfc083d4f2d4969bcb (patch)
tree766fc0bd4173d27cd42cbc436f3970c5232c23f7
parent1826991f26249735e29b06778723f4808f0ee1dc (diff)
downloadruby-4bdd207b1a2ed20626c502cfc083d4f2d4969bcb.tar.gz
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.
-rw-r--r--ext/openssl/ossl_ssl.c12
-rw-r--r--test/openssl/test_ssl.rb22
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