From a2622cdbd595f5454ebfde80fcad78387a41a3b6 Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Sat, 28 Dec 2024 12:19:19 +0100 Subject: [PATCH] mbedtls: fix handling of blocked sends MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit mbedtls is picky when a mbedtls_ssl_write) was previously blocked. It requires to be called with the same amount of bytes again, or it will lose bytes, e.g. reporting all was sent but they were not. Remember the blocked length and use that when set. Reported-by: Tamás Bálint Misius Fixes #15801 Closes #15846 --- docs/KNOWN_BUGS | 5 +++++ lib/vtls/mbedtls.c | 21 +++++++++++++++++++++ tests/http/test_08_caddy.py | 2 ++ 3 files changed, 28 insertions(+) diff --git a/docs/KNOWN_BUGS b/docs/KNOWN_BUGS index 606fa66f07..bdb029bf04 100644 --- a/docs/KNOWN_BUGS +++ b/docs/KNOWN_BUGS @@ -21,6 +21,7 @@ problems may have been fixed or changed somewhat since this was written. 2.7 Client cert (MTLS) issues with Schannel 2.11 Schannel TLS 1.2 handshake bug in old Windows versions 2.13 CURLOPT_CERTINFO results in CURLE_OUT_OF_MEMORY with Schannel + 2.14 mbedTLS and CURLE_AGAIN handling 3. Email protocols 3.1 IMAP SEARCH ALL truncated response @@ -159,6 +160,10 @@ problems may have been fixed or changed somewhat since this was written. https://github.com/curl/curl/issues/8741 +2.14 mbedTLS and CURLE_AGAIN handling + + https://github.com/curl/curl/issues/15801 + 3. Email protocols 3.1 IMAP SEARCH ALL truncated response diff --git a/lib/vtls/mbedtls.c b/lib/vtls/mbedtls.c index e5d162df83..f2c7b6c238 100644 --- a/lib/vtls/mbedtls.c +++ b/lib/vtls/mbedtls.c @@ -101,8 +101,10 @@ struct mbed_ssl_backend_data { const char *protocols[3]; #endif int *ciphersuites; + size_t send_blocked_len; BIT(initialized); /* mbedtls_ssl_context is initialized */ BIT(sent_shutdown); + BIT(send_blocked); }; /* apply threading? */ @@ -1196,6 +1198,17 @@ static ssize_t mbed_send(struct Curl_cfilter *cf, struct Curl_easy *data, (void)data; DEBUGASSERT(backend); + /* mbedtls is picky when a mbedtls_ssl_write) was previously blocked. + * It requires to be called with the same amount of bytes again, or it + * will lose bytes, e.g. reporting all was sent but they were not. + * Remember the blocked length and use that when set. */ + if(backend->send_blocked) { + DEBUGASSERT(backend->send_blocked_len <= len); + CURL_TRC_CF(data, cf, "mbedtls_ssl_write(len=%zu) -> previously blocked " + "on %zu bytes", len, backend->send_blocked_len); + len = backend->send_blocked_len; + } + ret = mbedtls_ssl_write(&backend->ssl, (unsigned char *)mem, len); if(ret < 0) { @@ -1207,6 +1220,14 @@ static ssize_t mbed_send(struct Curl_cfilter *cf, struct Curl_easy *data, #endif ) ? CURLE_AGAIN : CURLE_SEND_ERROR; ret = -1; + if((*curlcode == CURLE_AGAIN) && !backend->send_blocked) { + backend->send_blocked = TRUE; + backend->send_blocked_len = len; + } + } + else { + CURL_TRC_CF(data, cf, "mbedtls_ssl_write(len=%zu) -> %d", len, ret); + backend->send_blocked = FALSE; } return ret; diff --git a/tests/http/test_08_caddy.py b/tests/http/test_08_caddy.py index 2f7f649c08..418eaae70f 100644 --- a/tests/http/test_08_caddy.py +++ b/tests/http/test_08_caddy.py @@ -212,6 +212,8 @@ class TestCaddy: @pytest.mark.parametrize("proto", ['http/1.1', 'h2', 'h3']) def test_08_08_earlydata(self, env: Env, httpd, caddy, proto): + if proto == 'h3' and not env.have_h3(): + pytest.skip("h3 not supported") count = 2 docname = 'data10k.data' url = f'https://{env.domain1}:{caddy.port}/{docname}'