openssl: check SSL_write() length on retries

When an SSL_write() blocks we need to retry it with the
same length as before or stupid OpenSSL freaks out. Remember
it, limit any longer sends and fail shorter ones.

Fixes #18121
Reported-by: adamse on github
Closes #18132
This commit is contained in:
Stefan Eissing 2025-08-01 14:55:52 +02:00 committed by Daniel Stenberg
parent 8df0a5ba32
commit ec4c559104
No known key found for this signature in database
GPG key ID: 5CC908FDB71E12C2
4 changed files with 33 additions and 6 deletions

View file

@ -380,8 +380,14 @@ CURLcode Curl_req_send(struct Curl_easy *data, struct dynbuf *req,
data->req.httpversion_sent = httpversion;
buf = curlx_dyn_ptr(req);
blen = curlx_dyn_len(req);
if(!Curl_creader_total_length(data)) {
/* Request without body. Try to send directly from the buf given. */
/* if the sendbuf is empty and the request without body and
* the length to send fits info a sendbuf chunk, we send it directly.
* If `blen` is larger then `chunk_size`, we can not. Because we
* might have to retry a blocked send later from sendbuf and that
* would result in retry sends with a shrunken length. That is trouble. */
if(Curl_bufq_is_empty(&data->req.sendbuf) &&
!Curl_creader_total_length(data) &&
(blen <= data->req.sendbuf.chunk_size)) {
data->req.eos_read = TRUE;
result = xfer_send(data, buf, blen, blen, &nwritten);
if(result)

View file

@ -5316,6 +5316,17 @@ static CURLcode ossl_send(struct Curl_cfilter *cf,
connssl->io_need = CURL_SSL_IO_NEED_NONE;
memlen = (len > (size_t)INT_MAX) ? INT_MAX : (int)len;
if(octx->blocked_ssl_write_len && (octx->blocked_ssl_write_len != memlen)) {
/* The previous SSL_write() call was blocked, using that length.
* We need to use that again or OpenSSL will freak out. A shorter
* length should not happen and is a bug in libcurl. */
if(octx->blocked_ssl_write_len > memlen) {
DEBUGASSERT(0);
return CURLE_BAD_FUNCTION_ARGUMENT;
}
memlen = octx->blocked_ssl_write_len;
}
octx->blocked_ssl_write_len = 0;
nwritten = SSL_write(octx->ssl, mem, memlen);
if(nwritten > 0)
@ -5326,16 +5337,19 @@ static CURLcode ossl_send(struct Curl_cfilter *cf,
switch(err) {
case SSL_ERROR_WANT_READ:
connssl->io_need = CURL_SSL_IO_NEED_RECV;
octx->blocked_ssl_write_len = memlen;
result = CURLE_AGAIN;
goto out;
case SSL_ERROR_WANT_WRITE:
result = CURLE_AGAIN;
octx->blocked_ssl_write_len = memlen;
goto out;
case SSL_ERROR_SYSCALL:
{
int sockerr = SOCKERRNO;
if(octx->io_result == CURLE_AGAIN) {
octx->blocked_ssl_write_len = memlen;
result = CURLE_AGAIN;
goto out;
}

View file

@ -68,6 +68,8 @@ struct ossl_ctx {
X509* server_cert;
BIO_METHOD *bio_method;
CURLcode io_result; /* result of last BIO cfilter operation */
/* blocked writes need to retry with same length, remember it */
int blocked_ssl_write_len;
#ifndef HAVE_KEYLOG_CALLBACK
/* Set to true once a valid keylog entry has been created to avoid dupes.
This is a bool and not a bitfield because it is passed by address. */