]> git.ipfire.org Git - thirdparty/apache/httpd.git/commitdiff
mod_proxy: Fix potential tunneling infinite loop and spurious timeout.
authorYann Ylavic <ylavic@apache.org>
Mon, 30 Aug 2021 18:04:20 +0000 (18:04 +0000)
committerYann Ylavic <ylavic@apache.org>
Mon, 30 Aug 2021 18:04:20 +0000 (18:04 +0000)
           PRs 65521 and 65519.

* modules/proxy/proxy_util.c(ap_proxy_tunnel_run):
  Avoid an infinite loop by shutting down the connection for write when poll()
  returns POLLHUP and read is already down.  PR 65521.

* modules/proxy/proxy_util.c(ap_proxy_tunnel_run):
  When write completion is finished don't check for ap_filter_input_pending()
  before proxy_tunnel_forward() to flush input data, this is a nonblocking read
  already which will do the same thing implicitely. ap_filter_input_pending()
  is broken in 2.4.x without the whole pending data mechanism (not backported
  yet), so let's align here.  PR 65519.

git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1892740 13f79535-47bb-0310-9956-ffa450edef68

changes-entries/ap_proxy_tunnel_run.txt [new file with mode: 0644]
modules/proxy/proxy_util.c

diff --git a/changes-entries/ap_proxy_tunnel_run.txt b/changes-entries/ap_proxy_tunnel_run.txt
new file mode 100644 (file)
index 0000000..c31cfcd
--- /dev/null
@@ -0,0 +1,4 @@
+  *) mod_proxy: Fix a potential infinite loop when tunneling Upgrade(d)
+     protocols from mod_proxy_http, and a timeout triggering falsely when
+     using mod_proxy_wstunnel, mod_proxy_connect or mod_proxy_http with
+     upgrade= setting.  PRs 65521 and 65519.  [Yann Ylavic]
\ No newline at end of file
index 90e8e190fa776fcadc802ccebcf825c759a03208..86acaba2112bbcb041f51a5f4449d2df11ece892 100644 (file)
@@ -4890,12 +4890,16 @@ PROXY_DECLARE(int) ap_proxy_tunnel_run(proxy_tunnel_rec *tunnel)
                 return HTTP_INTERNAL_SERVER_ERROR;
             }
 
-            /* Write if we asked for POLLOUT, and got it or POLLERR
-             * alone (i.e. not with POLLIN|HUP). We want the output filters
-             * to know about the socket error if any, by failing the write.
+            /* We want to write if we asked for POLLOUT and got:
+             * - POLLOUT: the socket is ready for write;
+             * - !POLLIN: the socket is in error state (POLLERR) so we let
+             *   the user know by failing the write and log, OR the socket
+             *   is shutdown for read already (POLLHUP) so we have to
+             *   shutdown for write.
              */
             if ((tc->pfd->reqevents & APR_POLLOUT)
                     && ((pfd->rtnevents & APR_POLLOUT)
+                        || !(tc->pfd->reqevents & APR_POLLIN)
                         || !(pfd->rtnevents & (APR_POLLIN | APR_POLLHUP)))) {
                 struct proxy_tunnel_conn *out = tc, *in = tc->other;
 
@@ -4944,12 +4948,25 @@ PROXY_DECLARE(int) ap_proxy_tunnel_run(proxy_tunnel_rec *tunnel)
                             return rc;
                         }
                     }
+
+                    /* Flush any pending input data now, we don't know when
+                     * the next POLLIN will trigger and retaining data might
+                     * deadlock the underlying protocol. We don't check for
+                     * pending data first with ap_filter_input_pending() since
+                     * the read from proxy_tunnel_forward() is nonblocking
+                     * anyway and returning OK if there's no data.
+                     */
+                    rc = proxy_tunnel_forward(tunnel, in);
+                    if (rc != OK) {
+                        return rc;
+                    }
                 }
             }
 
-            /* Read if we asked for POLLIN|HUP, and got it or POLLERR
-             * alone (i.e. not with POLLOUT). We want the input filters
-             * to know about the socket error if any, by failing the read.
+            /* We want to read if we asked for POLLIN|HUP and got:
+             * - POLLIN|HUP: the socket is ready for read or EOF (POLLHUP);
+             * - !POLLOUT: the socket is in error state (POLLERR) so we let
+             *   the user know by failing the read and log.
              */
             if ((tc->pfd->reqevents & APR_POLLIN)
                     && ((pfd->rtnevents & (APR_POLLIN | APR_POLLHUP))