From ccfca5a3f990edc45dc4bc2a4cce4e1dc8e5d4a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Jaramago=20Fern=C3=A1ndez?= Date: Fri, 12 Jul 2024 13:43:24 +0200 Subject: [PATCH] Fix busy-wait on client SSL connection Errors and EOF returned by 'recv' are now properly handled, isolated from the return value ('r') from the later and conditional 'SSL_read'. Previously, a busy loop was possible in the following scenario: 1. A client connection was closed, leading to a return of 0 by 'recv', and 'errno' to be set with 'EAGAIN'. 2. Error handling is performed taking into account a never performed call to 'SSL_read' (as if this '0' was a result for this call). 3. The call to 'SSL_get_error' returns with 'SSL_ERROR_SYSCALL', as a result of using this invalid param of '0' (which for 'SSL_read' would mean non-retryable error), and the non-reset 'errno' would still be 'EAGAIN'. 4. This would create the illusion of an interrupted read. So a new read is tried again, while in reality the socket is closed. This busy loop will continue until 'POLLHUP' is received when we attempt to write back to the client and a 'RST' is received as a response. --- lib/mysql_data_stream.cpp | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/lib/mysql_data_stream.cpp b/lib/mysql_data_stream.cpp index 3ff6e5da4..97aba3a65 100644 --- a/lib/mysql_data_stream.cpp +++ b/lib/mysql_data_stream.cpp @@ -611,18 +611,15 @@ int MySQL_Data_Stream::read_from_net() { return 0; // no enough space for reads } char buf[MY_SSL_BUFFER]; - //ssize_t n = read(fd, buf, sizeof(buf)); - int n = recv(fd, buf, sizeof(buf), 0); - //proxy_info("SSL recv of %d bytes\n", n); - proxy_debug(PROXY_DEBUG_NET, 7, "Session=%p: recv() read %d bytes. num_write: %lu , num_read: %lu\n", sess, n, rbio_ssl->num_write , rbio_ssl->num_read); - if (n > 0 || rbio_ssl->num_write > rbio_ssl->num_read) { - //on_read_cb(buf, (size_t)n); + int ssl_recv_bytes = recv(fd, buf, sizeof(buf), 0); + proxy_debug(PROXY_DEBUG_NET, 7, "Session=%p: recv() read %d bytes. num_write: %lu , num_read: %lu\n", sess, ssl_recv_bytes, rbio_ssl->num_write , rbio_ssl->num_read); + if (ssl_recv_bytes > 0 || rbio_ssl->num_write > rbio_ssl->num_read) { char buf2[MY_SSL_BUFFER]; int n2; enum sslstatus status; char *src = buf; - int len = n; + int len = ssl_recv_bytes; while (len > 0) { n2 = BIO_write(rbio_ssl, src, len); proxy_debug(PROXY_DEBUG_NET, 5, "Session=%p: write %d bytes into BIO %p, len=%d\n", sess, n2, rbio_ssl, len); @@ -681,9 +678,13 @@ int MySQL_Data_Stream::read_from_net() { return -1; } } else { - r = n; - //r += SSL_read (ssl, queue_w_ptr(queueIN), s); - //proxy_info("Read %d bytes from SSL\n", r); + // Shutdown if we either received the EOF, or operation failed with non-retryable error. + if (ssl_recv_bytes==0 || (ssl_recv_bytes==-1 && errno != EINTR && errno != EAGAIN)) { + proxy_debug(PROXY_DEBUG_NET, 5, "Received EOF, shutting down soft socket -- Session=%p, Datastream=%p\n", sess, this); + shut_soft(); + return -1; + } + r = ssl_recv_bytes; } } //__exit_read_from_next: