]> git.ipfire.org Git - thirdparty/apache/httpd.git/commitdiff
mpm_event: Handle children killed pathologically.
authorYann Ylavic <ylavic@apache.org>
Thu, 14 Apr 2022 14:38:03 +0000 (14:38 +0000)
committerYann Ylavic <ylavic@apache.org>
Thu, 14 Apr 2022 14:38:03 +0000 (14:38 +0000)
If children processes get killed (SIGSEGV/SIGABRT/..) early after starting or
frequently enough then we never enter perform_idle_server_maintenance() to
try something.

Below three successive children killed restart them immediately, above three
let's sleep the usual 1s (to avoid fork()s flood) and do the idle maintenance.

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

server/mpm/event/event.c

index 595fac0b0f10934b9aed8caa367a321f7e9c8344..b4361cc8988a2b25832b096b92b953ec5b7aaa5f 100644 (file)
@@ -3382,6 +3382,7 @@ static void server_main_loop(int remaining_children_to_start)
 {
     int num_buckets = retained->mpm->num_buckets;
     int max_daemon_used = 0;
+    int successive_signals = 0;
     int child_slot;
     apr_exit_why_e exitwhy;
     int status, processed_status;
@@ -3460,11 +3461,31 @@ static void server_main_loop(int remaining_children_to_start)
             /* Don't perform idle maintenance when a child dies,
              * only do it when there's a timeout.  Remember only a
              * finite number of children can die, and it's pretty
-             * pathological for a lot to die suddenly.
+             * pathological for a lot to die suddenly.  If that happens
+             * anyway, protect against fork()+kill() flood by not restarting
+             * more than 3 children if no timeout happened in between,
+             * otherwise we keep going with idle maintenance.
              */
-            continue;
+            if (child_slot < 0 || !APR_PROC_CHECK_SIGNALED(exitwhy)) {
+                continue;
+            }
+            if (++successive_signals >= 3) {
+                if (successive_signals % 10 == 3) {
+                    ap_log_error(APLOG_MARK, APLOG_WARNING, 0,
+                                 ap_server_conf, APLOGNO(10392)
+                                 "children are killed successively!");
+                }
+                apr_sleep(apr_time_from_sec(1));
+            }
+            else {
+                ++remaining_children_to_start;
+            }
         }
-        else if (remaining_children_to_start) {
+        else {
+            successive_signals = 0;
+        }
+
+        if (remaining_children_to_start) {
             /* we hit a 1 second timeout in which none of the previous
              * generation of children needed to be reaped... so assume
              * they're all done, and pick up the slack if any is left.