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 */