]> git.kaiwu.me - nginx.git/commitdiff
SSL: avoid writing to a connection with a failed read
authorSergey Kandaurov <pluknet@nginx.com>
Mon, 14 Sep 2026 13:34:42 +0000 (13:34 +0000)
committerSergey Kandaurov <pluknet@nginx.com>
Thu, 24 Sep 2026 16:16:39 +0000 (20:16 +0400)
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>

src/event/ngx_event_openssl.c

index c9f94c982bc16ae11cafb6803afccabe40568a87..c827b996f5970599c5cda7d8838f02bdfd29565c 100644 (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,