]> git.ipfire.org Git - thirdparty/apache/httpd.git/commitdiff
Make sure the pollers collect
authorJim Jagielski <jim@apache.org>
Tue, 2 Jun 2026 14:39:34 +0000 (14:39 +0000)
committerJim Jagielski <jim@apache.org>
Tue, 2 Jun 2026 14:39:34 +0000 (14:39 +0000)
git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1934888 13f79535-47bb-0310-9956-ffa450edef68

server/mpm/motorz/motorz.c

index 8b5b4fb62b8630be8a233879a487b36fefcd409f..6080edc2daa89532b2e5782813b50cdcbe0789a9 100644 (file)
@@ -1297,6 +1297,37 @@ static void clean_child_exit(int code)
     apr_signal(SIGHUP, SIG_IGN);
     apr_signal(SIGTERM, SIG_IGN);
 
+    /* Join the poller threads before tearing down the worker pool. A poller
+     * thread, mid motorz_pollset_cb, may be about to hand a ready connection
+     * to the worker pool via apr_thread_pool_push(mz->workers, ...); if we
+     * destroy mz->workers (or pchild, on which the poller threads were
+     * created) out from under it, that push dereferences freed memory and
+     * crashes in APR's add_task. child_main's normal exit already joins the
+     * pollers before getting here, but the abrupt paths (just_die, and the
+     * mid-startup error exits below where some pollers may already be
+     * running) do not -- so quiesce and join them here too. die_now stops the
+     * poll loops; the join is skipped for any poller running on the *current*
+     * thread (defensive: clean_child_exit is normally reached on the main
+     * thread, never a poller thread).
+     */
+    die_now = 1;
+    if (mz->pollers) {
+        apr_os_thread_t self = apr_os_thread_current();
+        int i;
+        for (i = 0; i < mz->num_pollers; i++) {
+            motorz_poller_t *poller = mz->pollers[i];
+            if (poller && poller->thread) {
+                apr_os_thread_t *pos = NULL;
+                if (apr_os_thread_get(&pos, poller->thread) != APR_SUCCESS
+                        || pos == NULL
+                        || !apr_os_thread_equal(*pos, self)) {
+                    apr_status_t pstatus;
+                    apr_thread_join(&pstatus, poller->thread);
+                }
+            }
+        }
+    }
+
     /* Drain the worker thread pool before tearing down pools. Without this,
      * worker threads executing motorz_io_process or motorz_conn_done (which
      * call ap_log_error and apr_pool_clear) may still be running when pchild
@@ -1458,7 +1489,15 @@ static const char *motorz_get_name(void)
 
 static void just_die(int sig)
 {
-    clean_child_exit(0);
+    /* Async-signal context: do the minimum. Setting die_now stops the poller
+     * loops and breaks motorz_supervise on the main thread, which then joins
+     * the pollers and calls clean_child_exit -- the one teardown path that
+     * quiesces the poller threads before destroying mz->workers and pchild.
+     * Calling clean_child_exit() directly from here (the old behaviour) tore
+     * those down while a poller could still be pushing work to the pool, a
+     * use-after-free that crashed in APR's add_task.
+     */
+    die_now = 1;
 }
 
 static void stop_listening(int sig)