On 4/11/06, Chris Darroch <[EMAIL PROTECTED]> wrote:
>
>    In my case, the problem relates to how long the child_init phase
> takes to execute.  I can "tune" this by raising DBDMin (and DBDKeep)
> so that mod_dbd attempts to open increasingly large numbers of
> DB connections during child_init.  With DBDMin set to 0 or 1,
> all is well; no funny behaviour.  Up at DBDMin and DBDKeep at 3,
> that's when (for me) things go pear-shaped.

I have been working with a user on one of these fork bomb scenarios
and assumed it was the child_init hook.  But after giving them a test
fix that relies on a child setting scoreboard fields in child_main
before child-init hooks run, and also adds some debugging traces
related to calling child-init hooks, it is clear that their stall
occurs BEFORE the child-init hook.  Which leaves a stretch of fairly
simple code.

(Best theory is bad stuff happening in an atfork handler registred by
a third-party module or some library it uses.  But that's besides the
point.)

Greg Ames and I chatted about this today and (98% Greg) came up with
the attached patch, which does not rely on the child storing anything
in the scoreboard in order to avoid the fork bomb.

I've played with it a bit on 2.0 but not trunk, which this patch is
for.  But the code's pretty much the same ;)
Index: server/mpm/worker/worker.c
===================================================================
--- server/mpm/worker/worker.c  (revision 395711)
+++ server/mpm/worker/worker.c  (working copy)
@@ -1380,6 +1380,7 @@
     int last_non_dead;
     int total_non_dead;
     int active_thread_count = 0;
+    int dont_fork = 0;
 
     /* initialize the free_list */
     free_length = 0;
@@ -1422,10 +1423,18 @@
              */
             if (ps->pid != 0) { /* XXX just set all_dead_threads in outer for
                                    loop if no pid?  not much else matters */
-                if (status <= SERVER_READY && status != SERVER_DEAD &&
+                if (status <= SERVER_READY &&
                         !ps->quiescing &&
                         ps->generation == ap_my_generation) {
-                    ++idle_thread_count;
+                    if (status == SERVER_DEAD) {
+                        /* child process still initializing; don't fork faster
+                         * than children can initialize
+                         */
+                        dont_fork = 1;
+                    }
+                    else {
+                        ++idle_thread_count;
+                    }
                 }
                 if (status >= SERVER_READY && status < SERVER_GRACEFUL) {
                     ++active_thread_count;
@@ -1489,6 +1498,14 @@
         ap_mpm_pod_signal(pod, TRUE);
         idle_spawn_rate = 1;
     }
+    else if (dont_fork) {
+        /* child still initializing; don't create more right now,
+         * don't mess with idle spawn rate; if child processes are
+         * slow to initialize, we simply skip doing any forks during
+         * some maintenance cycles; it doesn't prevent spawn rate from
+         * increasing however
+         */
+     }
     else if (idle_thread_count < min_spare_threads) {
         /* terminate the free list */
         if (free_length == 0) { /* scoreboard is full, can't fork */

Reply via email to