OpenSSL: multi interface handshake could hang
authorDaniel Stenberg <daniel@haxx.se>
Fri, 14 May 2010 20:34:10 +0000 (22:34 +0200)
committerDaniel Stenberg <daniel@haxx.se>
Fri, 14 May 2010 20:35:08 +0000 (22:35 +0200)
John-Mark Bell filed bug #3000052 that identified a problem (with
an associated patch) with the OpenSSL handshake state machine
when the multi interface is used:

Performing an https request using a curl multi handle and using
select or epoll to wait for events results in a hang. It appears
that the cause is the fix for bug #2958179, which makes
ossl_connect_common unconditionally return from the step 2 loop
when fetching from a multi handle.

When ossl_connect_step2 has completed, it updates
connssl->connecting_state to ssl_connect_3. ossl_connect_common
will then return to the caller, as a multi handle is in
use. Eventually, the client code will call curl_multi_fdset to
obtain an updated fdset to select or epoll on. For https
requests, curl_multi_fdset will cause https_getsock to be called.
https_getsock will only return a socket handle if the
connecting_state is ssl_connect_2_reading or
ssl_connect_2_writing.  Therefore, the client will never obtain a
valid fdset, and thus not drive the multi handle, resulting in a
hang.

(http://curl.haxx.se/bug/view.cgi?id=3000052)

CHANGES
RELEASE-NOTES
lib/ssluse.c

diff --git a/CHANGES b/CHANGES
index ecb36c4..d895ebe 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -7,6 +7,27 @@
                                   Changelog
 
 Daniel Stenberg (14 May 2010)
+- John-Mark Bell filed bug #3000052 that identified a problem (with an
+  associated patch) with the OpenSSL handshake state machine when the multi
+  interface is used:
+
+  Performing an https request using a curl multi handle and using select or
+  epoll to wait for events results in a hang. It appears that the cause is the
+  fix for bug #2958179, which makes ossl_connect_common unconditionally return
+  from the step 2 loop when fetching from a multi handle.
+
+  When ossl_connect_step2 has completed, it updates connssl->connecting_state
+  to ssl_connect_3. ossl_connect_common will then return to the caller, as a
+  multi handle is in use. Eventually, the client code will call
+  curl_multi_fdset to obtain an updated fdset to select or epoll on. For https
+  requests, curl_multi_fdset will cause https_getsock to be called.
+  https_getsock will only return a socket handle if the connecting_state is
+  ssl_connect_2_reading or ssl_connect_2_writing.  Therefore, the client will
+  never obtain a valid fdset, and thus not drive the multi handle, resulting
+  in a hang.
+
+  (http://curl.haxx.se/bug/view.cgi?id=3000052)
+
 - Sebastian V reported bug #3000056 identifying a problem with redirect
   following. It showed that when curl followed redirects it didn't properly
   ignore the response body of the 30X response if that response was using
index 83b682e..37348f9 100644 (file)
@@ -29,6 +29,7 @@ This release includes the following bugfixes:
  o multi interface missed storing connection time
  o broken CRL support in libcurl-NSS
  o ignore response-body on redirect even if compressed
+ o OpenSSL handshake state-machine for multi interface
 
 This release includes the following known bugs:
 
@@ -39,6 +40,6 @@ advice from friends like these:
 
  Rainer Canavan, Paul Howarth, Jerome Vouillon, Ruslan Gazizov, Yang Tse,
  Kamil Dudka, Alex Bligh, Ben Greear, Hoi-Ho Chan, Howard Chu, Dirk Manske,
- Pavel Raiskup
+ Pavel Raiskup, John-Mark Bell
 
         Thanks! (and sorry if I forgot to mention someone)
index ce62605..01eba90 100644 (file)
@@ -2425,8 +2425,18 @@ ossl_connect_common(struct connectdata *conn,
       /* socket is readable or writable */
     }
 
+    /* Run transaction, and return to the caller if it failed or if
+     * this connection is part of a multi handle and this loop would
+     * execute again. This permits the owner of a multi handle to
+     * abort a connection attempt before step2 has completed while
+     * ensuring that a client using select() or epoll() will always
+     * have a valid fdset to wait on.
+     */
     retcode = ossl_connect_step2(conn, sockindex);
-    if(retcode || (data->state.used_interface == Curl_if_multi))
+    if(retcode || (data->state.used_interface == Curl_if_multi &&
+                   (ssl_connect_2 == connssl->connecting_state ||
+                    ssl_connect_2_reading == connssl->connecting_state ||
+                    ssl_connect_2_writing == connssl->connecting_state)))
       return retcode;
 
   } /* repeat step2 until all transactions are done. */