]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
openssl: fix handling of buffered data
authorStefan Eissing <stefan@eissing.org>
Thu, 12 Jun 2025 06:45:20 +0000 (08:45 +0200)
committerViktor Szakats <commit@vsz.me>
Thu, 12 Jun 2025 22:32:06 +0000 (00:32 +0200)
`SSL_pending()` only checks if the *current* TLS packet has more data.
There might be more data in SSL's buffers.

`SSL_has_pending()` only checks if there is data in buffers, but does
*not* check if there is a complete TLS packet that can be decoded.

If we only check the first, we will poll on socket events without having
processed all data and may stall. If we only check the second, we would
busy loop without SSL_read() ever giving something.

Add the flag `connssl->input_pending` that is set on incoming data in
the BIO receive. Clear the flag when encountering a CURLE_AGAIN on
the filters receive (via SSL_read()) or see an EOF.

Ref: #17596
Closes #17601

lib/pingpong.c
lib/vtls/openssl.c
lib/vtls/vtls_int.h

index c7531039844f4284774e0c54634ed4a2064de81c..a661a0285b524e026708e3e6f45501e810f3fbc7 100644 (file)
@@ -116,11 +116,15 @@ CURLcode Curl_pp_statemach(struct Curl_easy *data,
   else if(!pp->sendleft && Curl_conn_data_pending(data, FIRSTSOCKET))
     /* We are receiving and there is data ready in the SSL library */
     rc = 1;
-  else
+  else {
+    DEBUGF(infof(data, "pp_statematch, select, timeout=%" FMT_TIMEDIFF_T
+           ", sendleft=%zu",
+           timeout_ms, pp->sendleft));
     rc = Curl_socket_check(pp->sendleft ? CURL_SOCKET_BAD : sock, /* reading */
                            CURL_SOCKET_BAD,
                            pp->sendleft ? sock : CURL_SOCKET_BAD, /* writing */
                            interval_ms);
+  }
 
   if(block) {
     /* if we did not wait, we do not have to spend time on this now */
index 9a805589a2c1f749415051e9a8597eec1f57daea..69cde17c2c5ff9c8d85c62ee74ab6de7bbcb05dd 100644 (file)
@@ -769,8 +769,11 @@ static int ossl_bio_cf_in_read(BIO *bio, char *buf, int blen)
     if(CURLE_AGAIN == result)
       BIO_set_retry_read(bio);
   }
-  else if(nread == 0) {
-    connssl->peer_closed = TRUE;
+  else {
+    /* feeding data to OpenSSL means SSL_read() might succeed */
+    connssl->input_pending = TRUE;
+    if(nread == 0)
+      connssl->peer_closed = TRUE;
   }
 
   /* Before returning server replies to the SSL instance, we need
@@ -5216,13 +5219,8 @@ static bool ossl_data_pending(struct Curl_cfilter *cf,
                               const struct Curl_easy *data)
 {
   struct ssl_connect_data *connssl = cf->ctx;
-  struct ossl_ctx *octx = (struct ossl_ctx *)connssl->backend;
-
   (void)data;
-  DEBUGASSERT(connssl && octx);
-  if(octx->ssl && SSL_pending(octx->ssl))
-    return TRUE;
-  return FALSE;
+  return connssl->input_pending;
 }
 
 static ssize_t ossl_send(struct Curl_cfilter *cf,
@@ -5415,6 +5413,15 @@ static ssize_t ossl_recv(struct Curl_cfilter *cf,
   }
 
 out:
+  if(!nread || ((nread < 0) && (*curlcode == CURLE_AGAIN))) {
+    /* This happens when:
+     * - we read an EOF
+     * - OpenSSLs buffers are empty, there is no more data
+     * - OpenSSL read is blocked on writing something first
+     * - an incomplete TLS packet is buffered that cannot be read
+     *   until more data arrives */
+    connssl->input_pending = FALSE;
+  }
   return nread;
 }
 
index c01a44864ebde105c621380bf6b305dbf1199ed3..6cd2ae4d26c0706fa2b71df15c1d2dd8c87ed4c5 100644 (file)
@@ -127,6 +127,7 @@ struct ssl_connect_data {
   BIT(use_alpn);                    /* if ALPN shall be used in handshake */
   BIT(peer_closed);                 /* peer has closed connection */
   BIT(prefs_checked);               /* SSL preferences have been checked */
+  BIT(input_pending);               /* data for SSL_read() may be available */
 };