SSL: avoid writing to a connection with a failed read

It is possible to receive application data together with socket close.
In this case, SSL_read() may return SSL_ERROR_SYSCALL with ECONNRESET,
specifically after OpenSSL sent TLSv1.3 tickets on a closed connection.

Before OpenSSL 4.0, a subsequent SSL_write() returned SSL_ERROR_SYSCALL
with EPIPE in this case.  The behaviour was changed to remember the
error state and handle such a call as a protocol error, see for details
https://github.com/openssl/openssl/issues/30631.  This makes SSL_write()
return SSL_ERROR_SSL with the empty error queue, which results in
"[crit] ... SSL_write() failed" errors, as caught by ssl_stapling.t.

The fix is to check the result of the last read stored in c->ssl->last,
to avoid calling SSL_write() after a failed read.  For consistency, the
check is added to ngx_ssl_sendfile(): SSL_sendfile() resets the error
state the same way SSL_write() does, though it does not seem to be
reachable in http.

Based on a patch by Maxim Dounin.

Signed-off-by: Sergey Kandaurov <pluknet@nginx.com>
Origin: <https://freenginx.org/hg/nginx/rev/ff5becaf5444>
This commit is contained in:
Sergey Kandaurov 2026-09-14 13:34:42 +00:00
parent 381c876ec6
commit 1967e2bdc4

View file

@ -3242,6 +3242,12 @@ ngx_ssl_write(ngx_connection_t *c, u_char *data, size_t size)
int n, sslerr;
ngx_err_t err;
if (c->ssl->last == NGX_ERROR) {
c->write->ready = 0;
c->write->error = 1;
return NGX_ERROR;
}
#ifdef SSL_READ_EARLY_DATA_SUCCESS
if (c->ssl->in_early) {
return ngx_ssl_write_early(c, data, size);
@ -3473,6 +3479,12 @@ ngx_ssl_sendfile(ngx_connection_t *c, ngx_buf_t *file, size_t size)
ssize_t n;
ngx_err_t err;
if (c->ssl->last == NGX_ERROR) {
c->write->ready = 0;
c->write->error = 1;
return NGX_ERROR;
}
ngx_ssl_clear_error(c->log);
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,