You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@httpd.apache.org by ji...@apache.org on 2026/10/08 11:42:22 UTC

svn commit: r1938912 - httpd/httpd/trunk/modules/proxy

Author: jim
Date: Thu Oct  8 11:42:22 2026
New Revision: 1938912

Log:
mod_proxy_beacon: bound listener memory and work per tick under a UDP flood

PR 70255.  Based in part on a patch from Tristan Madani.

Modified:
   httpd/httpd/trunk/modules/proxy/mod_proxy_beacon.c

Modified: httpd/httpd/trunk/modules/proxy/mod_proxy_beacon.c
==============================================================================
--- httpd/httpd/trunk/modules/proxy/mod_proxy_beacon.c	Thu Oct  8 09:38:32 2026	(r1938911)
+++ httpd/httpd/trunk/modules/proxy/mod_proxy_beacon.c	Thu Oct  8 11:42:22 2026	(r1938912)
@@ -171,6 +171,10 @@ typedef struct {
  * reached, unknown urls are dropped (rate-limited log) rather than tracked/added. */
 #define BEACON_MAX_MEMBERS      256
 
+/* Cap on datagrams handled per watchdog tick, so a flood can't hold the
+ * watchdog thread in the receive loop; the rest wait for the next tick. */
+#define BEACON_MAX_DRAIN        1024
+
 /* Process-wide watchdog handle, like mod_proxy_hcheck's static watchdog. */
 static ap_watchdog_t *beacon_watchdog;
 
@@ -1072,18 +1076,25 @@ static void beacon_cb_running(beacon_ctx
     }
     else if (ctx->role == BEACON_ROLE_LISTEN) {
         apr_time_t now;
+        apr_pool_t *ptemp;
+        int n;
+
+        apr_pool_create(&ptemp, pool);
+        apr_pool_tag(ptemp, "beacon_recv");
 
-        /* Drain the socket every tick (re-enable latency matters). */
-        for (;;) {
+        /* Drain the socket every tick (re-enable latency matters), up to
+         * BEACON_MAX_DRAIN datagrams. */
+        for (n = 0; n < BEACON_MAX_DRAIN; n++) {
             char buf[BEACON_MAX_MSG_LEN + 1];
             apr_sockaddr_t from;
             apr_size_t sz = BEACON_MAX_MSG_LEN;
             apr_time_t msg_now;
-            const char *msg_str, *safe;
+            const char *msg_str;
             char url[512];
             apr_int64_t msg_ts = 0;
 
-            from.pool = pool;
+            apr_pool_clear(ptemp);
+            from.pool = ptemp;
             rv = apr_socket_recvfrom(&from, ctx->sock, 0, buf, &sz);
             if (APR_STATUS_IS_EAGAIN(rv)) {
                 break; /* socket drained */
@@ -1107,14 +1118,6 @@ static void beacon_cb_running(beacon_ctx
             buf[sz] = '\0';
             msg_str = buf;
 
-            /* Escape the untrusted payload before it can reach the log: on the
-             * log-only path (no ProxyBeaconBalancer) messages are logged without
-             * MAC verification, so anyone who can reach the listen port could
-             * embed newline/control/ANSI sequences to forge or corrupt error-log
-             * lines.  (On the balancer path, forged messages are dropped at
-             * verification below and `safe` is only logged after it passes.) */
-            safe = ap_escape_logitem(pool, msg_str);
-
             /* Phase 4: verify the MAC and freshness BEFORE logging or acting on
              * the payload, so forged/tampered content is dropped (throttled) and
              * never reaches the INFO log.  A secret is required (enforced at
@@ -1130,8 +1133,16 @@ static void beacon_cb_running(beacon_ctx
                 }
             }
 
-            ap_log_error(APLOG_MARK, APLOG_INFO, 0, s,
-                         APLOGNO(10584) "mod_proxy_beacon: received: %s", safe);
+            /* Escape the payload before it reaches the log: on the log-only
+             * path (no ProxyBeaconBalancer) it is unverified, so anyone who can
+             * reach the listen port could otherwise embed newline/control/ANSI
+             * sequences to forge or corrupt error-log lines.  Done only after
+             * verification so a rejected datagram allocates nothing. */
+            if (APLOGinfo(s)) {
+                ap_log_error(APLOG_MARK, APLOG_INFO, 0, s,
+                             APLOGNO(10584) "mod_proxy_beacon: received: %s",
+                             ap_escape_logitem(ptemp, msg_str));
+            }
 
             if (!ctx->balancer_name) {
                 continue;
@@ -1140,9 +1151,10 @@ static void beacon_cb_running(beacon_ctx
             /* Phase 2/3: carries a routable url= -> add the backend (or
              * refresh last-seen / re-enable if previously evicted). */
             if (beacon_parse_url(msg_str, url, sizeof(url))) {
-                beacon_handle_announce(ctx, pool, url, msg_now, msg_ts);
+                beacon_handle_announce(ctx, ptemp, url, msg_now, msg_ts);
             }
         }
+        apr_pool_destroy(ptemp);
 
         /* Phase 3: evict backends that stopped announcing.  Throttle the scan
          * to ~1s (the recv drain above already runs every tick). */