]> git.pld-linux.org Git - packages/apache.git/blob - apache-shutdown-sockets.patch
6e5c5f80401c8612cedbbe8dfa3d7f9a20595a6f
[packages/apache.git] / apache-shutdown-sockets.patch
1 --- httpd-2.2.14-p/server/mpm/prefork/prefork.c 2009-02-01 07:54:55.000000000 +1100
2 +++ httpd-2.2.14/server/mpm/prefork/prefork.c   2009-11-02 12:09:50.511530535 +1100
3 @@ -48,6 +48,7 @@
4  #include "ap_listen.h"
5  #include "ap_mmn.h"
6  #include "apr_poll.h"
7 +#include "apr_md5.h"
8  
9  #ifdef HAVE_BSTRING_H
10  #include <bstring.h>            /* for IRIX, FD_SET calls bzero() */
11 @@ -336,6 +337,28 @@
12      die_now = 1;
13  }
14  
15 +static int volatile client_socket = -1;
16 +
17 +#ifndef NO_USE_SIGACTION
18 +static void shutdown_socket(int sig, siginfo_t *info, void *context)
19 +#else
20 +static void shutdown_socket(int sig)
21 +#endif
22 +{
23 +#ifndef NO_USE_SIGACTION
24 +    if (info->si_pid == getppid()) {
25 +#endif
26 +        if (client_socket != -1) {
27 +            shutdown(client_socket, SHUT_RDWR);
28 +        }
29 +#ifndef NO_USE_SIGACTION
30 +    }
31 +    else {
32 +        clean_child_exit(0);
33 +    }
34 +#endif
35 +}
36 +
37  /* volatile just in case */
38  static int volatile shutdown_pending;
39  static int volatile restart_pending;
40 @@ -659,8 +682,12 @@
41  
42          current_conn = ap_run_create_connection(ptrans, ap_server_conf, csd, my_child_num, sbh, bucket_alloc);
43          if (current_conn) {
44 +            apr_os_sock_get((apr_os_sock_t *)&client_socket, csd);
45 +
46              ap_process_connection(current_conn, csd);
47              ap_lingering_close(current_conn);
48 +
49 +            client_socket = -1;
50          }
51  
52          /* Check the pod and the generation number after processing a
53 @@ -733,6 +760,10 @@
54      }
55  
56      if (!pid) {
57 +#ifndef NO_USE_SIGACTION
58 +        struct sigaction act;
59 +#endif
60 +
61  #ifdef HAVE_BINDPROCESSOR
62          /* by default AIX binds to a single processor
63           * this bit unbinds children which will then bind to another cpu
64 @@ -755,6 +786,19 @@
65           * The pod is used for signalling the graceful restart.
66           */
67          apr_signal(AP_SIG_GRACEFUL, stop_listening);
68 +
69 +        /* If the parent sends SIGINT to the child, we shutdown the
70 +         * client socket, as we suspect that we are under a DoS attack.
71 +         */ 
72 +#ifndef NO_USE_SIGACTION
73 +        memset(&act, 0, sizeof(act));
74 +        act.sa_flags = SA_SIGINFO;
75 +        act.sa_sigaction = shutdown_socket;
76 +        sigaction(SIGINT, &act, NULL);
77 +#else
78 +        apr_signal(SIGINT, shutdown_socket);
79 +#endif
80 +
81          child_main(slot);
82      }
83  
84 @@ -803,6 +847,8 @@
85      int free_slots[MAX_SPAWN_RATE];
86      int last_non_dead;
87      int total_non_dead;
88 +    int status;
89 +    static apr_time_t maxed_out = 0;
90  
91      /* initialize the free_list */
92      free_length = 0;
93 @@ -813,8 +859,6 @@
94      total_non_dead = 0;
95  
96      for (i = 0; i < ap_daemons_limit; ++i) {
97 -        int status;
98 -
99          if (i >= ap_max_daemons_limit && free_length == idle_spawn_rate)
100              break;
101          ws = &ap_scoreboard_image->servers[i][0];
102 @@ -856,12 +900,17 @@
103           */
104          ap_mpm_pod_signal(pod);
105          idle_spawn_rate = 1;
106 +        maxed_out = 0;
107      }
108      else if (idle_count < ap_daemons_min_free) {
109          /* terminate the free list */
110          if (free_length == 0) {
111              /* only report this condition once */
112              static int reported = 0;
113 +            static unsigned char sb_digest[APR_MD5_DIGESTSIZE];
114 +            apr_time_t now = apr_time_now();
115 +            apr_md5_ctx_t ctx;
116 +            pid_t pid;
117  
118              if (!reported) {
119                  ap_log_error(APLOG_MARK, APLOG_ERR, 0, ap_server_conf,
120 @@ -870,6 +919,120 @@
121                  reported = 1;
122              }
123              idle_spawn_rate = 1;
124 +
125 +            /* If after one maintenace interval we still see the same
126 +             * situation on the scoreboard, shutdown all client sockets
127 +             * in read state and at least 10% of all client sockets.
128 +             * Crude, but seems to clear things out.
129 +             */
130 +            if (maxed_out) {
131 +                apr_time_t diff = now - maxed_out;
132 +
133 +                if (diff >= SCOREBOARD_MAINTENANCE_INTERVAL) {
134 +                    unsigned char cur_digest[APR_MD5_DIGESTSIZE];
135 +
136 +                    /* Current digest of the scoreboard.
137 +                     */
138 +                    apr_md5_init(&ctx);
139 +                    for (i = 0; i < ap_daemons_limit; ++i) {
140 +                        status = ap_scoreboard_image->servers[i][0].status;
141 +                        apr_md5_update(&ctx, &status, sizeof(status));
142 +
143 +                        pid = ap_scoreboard_image->parent[i].pid;
144 +                        apr_md5_update(&ctx, &pid, sizeof(pid));
145 +                    }
146 +                    apr_md5_final(cur_digest, &ctx);
147 +
148 +                    /* If we haven't had a change for one maintenance
149 +                     * interval, we need to make room.
150 +                     */
151 +                    if (memcmp(sb_digest, cur_digest, APR_MD5_DIGESTSIZE)) {
152 +                        maxed_out = 0;
153 +                    }
154 +                    else {
155 +                        int rdrs = 0, cull = ap_daemons_limit / 10;
156 +
157 +                        /* Disconnect all readers (includes keep alive).
158 +                         */
159 +                        for (i = 0; i < ap_daemons_limit; ++i) {
160 +                            pid = ap_scoreboard_image->parent[i].pid;
161 +                            status = ap_scoreboard_image->servers[i][0].status;
162 +
163 +                            if (status == SERVER_BUSY_READ ||
164 +                                status == SERVER_BUSY_KEEPALIVE) {
165 +
166 +                                ap_mpm_safe_kill(pid, SIGINT);
167 +                                rdrs++;
168 +                            }
169 +                        }
170 +
171 +                        /* Make up to 10% of all sockets, if required.
172 +                         */
173 +                        for (i = 0; i < ap_daemons_limit && cull > rdrs; ++i) {
174 +                            status = ap_scoreboard_image->servers[i][0].status;
175 +
176 +                            if (status != SERVER_BUSY_READ &&
177 +                                status != SERVER_BUSY_KEEPALIVE) {
178 +
179 +                                pid = ap_scoreboard_image->parent[i].pid;
180 +                                ap_mpm_safe_kill(pid, SIGINT);
181 +                                cull--;
182 +                            }
183 +                        }
184 +                    }
185 +                }
186 +            }
187 +            else {
188 +                int rdrs = 0;
189 +
190 +                /* Create digest of the scorboard, see if things
191 +                 * change next time around.
192 +                 */
193 +                apr_md5_init(&ctx);
194 +                for (i = 0; i < ap_daemons_limit; ++i) {
195 +                    status = ap_scoreboard_image->servers[i][0].status;
196 +
197 +                    /* These are the conditions we are concerned with.
198 +                     */ 
199 +                    switch (status) { 
200 +                    case SERVER_BUSY_READ:
201 +                    case SERVER_BUSY_KEEPALIVE:
202 +                        rdrs++;
203 +                    case SERVER_BUSY_WRITE:
204 +                    case SERVER_DEAD:
205 +                    case SERVER_GRACEFUL:
206 +                        break;
207 +                    default:
208 +                        return;
209 +                    }
210 +
211 +                    apr_md5_update(&ctx, &status, sizeof(status));
212 +
213 +                    pid = ap_scoreboard_image->parent[i].pid;
214 +                    apr_md5_update(&ctx, &pid, sizeof(pid));
215 +                }
216 +                apr_md5_final(sb_digest, &ctx);
217 +
218 +                /* Over 95% in read state (includes keep alive), clear now.
219 +                 */
220 +                if (ap_daemons_limit - rdrs < ap_daemons_limit / 20) {
221 +                    /* Disconnect all readers (includes keep alive).
222 +                     */
223 +                    for (i = 0; i < ap_daemons_limit; ++i) {
224 +                        pid = ap_scoreboard_image->parent[i].pid;
225 +                        status = ap_scoreboard_image->servers[i][0].status;
226 +
227 +                        if (status == SERVER_BUSY_READ ||
228 +                            status == SERVER_BUSY_KEEPALIVE) {
229 +                            ap_mpm_safe_kill(pid, SIGINT);
230 +                            rdrs++;
231 +                        }
232 +                    }
233 +                }
234 +                else {
235 +                    maxed_out = now;
236 +                }
237 +            }
238          }
239          else {
240              if (idle_spawn_rate >= 8) {
241 @@ -902,10 +1065,13 @@
242              else if (idle_spawn_rate < MAX_SPAWN_RATE) {
243                  idle_spawn_rate *= 2;
244              }
245 +
246 +            maxed_out = 0;
247          }
248      }
249      else {
250          idle_spawn_rate = 1;
251 +        maxed_out = 0;
252      }
253  }
254  
255 --- httpd-2.2.14-p/server/mpm/worker/worker.c   2009-11-02 09:40:23.129750043 +1100
256 +++ httpd-2.2.14/server/mpm/worker/worker.c     2009-11-02 12:37:53.987529627 +1100
257 @@ -33,6 +33,7 @@
258  #define APR_WANT_STRFUNC
259  #include "apr_want.h"
260  #include "apr_atomic.h"
261 +#include "apr_md5.h"
262  
263  #if APR_HAVE_UNISTD_H
264  #include <unistd.h>
265 @@ -422,6 +423,101 @@
266      clean_child_exit(0);
267  }
268  
269 +#if !defined(NO_USE_SIGACTION) && defined(HAVE_PTHREAD_KILL)
270 +static void shutdown_sockets(int sig, siginfo_t *info, void *context)
271 +{
272 +    int csd, i, j, slot = 0, status, total_rdrs = 0, rdrs = 0,
273 +        cull = ap_daemons_limit * ap_threads_per_child / 10;
274 +
275 +    /* not from parent, ignore */
276 +    if (info->si_pid != getppid()) {
277 +        return;
278 +    }
279 +
280 +    suspend_workers = 1;
281 +    apr_atomic_set32(&suspended_workers, 0);
282 +
283 +    /* suspend worker threads */
284 +    for (i = 0; i < ap_threads_per_child; i++) {
285 +        if (worker_os_threads[i]) {
286 +            pthread_kill(*worker_os_threads[i], WORKER_SIGNAL);
287 +        }
288 +    }
289 +
290 +    /* wait for threads to suspend, but press ahead after a while anyway */
291 +    for (i = 0;
292 +         apr_atomic_read32(&suspended_workers) < ap_threads_per_child && i < 25;
293 +         i++) {
294 +        apr_sleep(apr_time_from_sec(1) / 5);
295 +    }
296 +
297 +    /* Determine total number of readers (includes keep alive), our
298 +     * slot and the number of our own readers.
299 +     */
300 +    for (i = 0; i < ap_daemons_limit; ++i) {
301 +        if (ap_scoreboard_image->parent[i].pid == ap_my_pid) {
302 +            slot = i;
303 +        }
304 +
305 +        for (j = 0; j < ap_threads_per_child; j++) {
306 +            status = ap_scoreboard_image->servers[i][j].status;
307 +
308 +            if (status == SERVER_BUSY_READ ||
309 +                status == SERVER_BUSY_KEEPALIVE) {
310 +
311 +                total_rdrs++;
312 +
313 +                if (slot == i) {
314 +                    rdrs++;
315 +                }
316 +            }
317 +        }
318 +    }
319 +
320 +    /* Disconnect all readers (includes keep alive).
321 +     */
322 +    for (j = 0; j < ap_threads_per_child; j++) {
323 +        status = ap_scoreboard_image->servers[slot][j].status;
324 +
325 +        if (worker_sockets[j] &&
326 +            (status == SERVER_BUSY_READ ||
327 +             status == SERVER_BUSY_KEEPALIVE)) {
328 +
329 +            apr_os_sock_get((apr_os_sock_t *)&csd, worker_sockets[j]);
330 +            shutdown(csd, SHUT_RDWR);
331 +        }
332 +    }
333 +
334 +    /* Make up to 10% of all sockets, if required.
335 +     */
336 +    if (total_rdrs < cull) {
337 +        cull = ((ap_threads_per_child - rdrs) * (cull - total_rdrs)) / cull;
338 +
339 +        for (j = 0; j < ap_threads_per_child && cull > 0; j++) {
340 +            status = ap_scoreboard_image->servers[slot][j].status;
341 +
342 +            if (worker_sockets[j] &&
343 +                status != SERVER_BUSY_READ &&
344 +                status != SERVER_BUSY_KEEPALIVE) {
345 +
346 +                apr_os_sock_get((apr_os_sock_t *)&csd, worker_sockets[j]);
347 +                shutdown(csd, SHUT_RDWR);
348 +                cull--;
349 +            }
350 +        }
351 +    }
352 +
353 +    suspend_workers = 0;
354 +
355 +    /* resume worker threads */
356 +    for (i = 0; i < ap_threads_per_child; i++) {
357 +        if (worker_os_threads[i]) {
358 +            pthread_kill(*worker_os_threads[i], WORKER_SIGNAL);
359 +        }
360 +    }
361 +}
362 +#endif
363 +
364  /*****************************************************************
365   * Connection structures and accounting...
366   */
367 @@ -1319,12 +1415,28 @@
368          join_workers(ts->listener, threads);
369      }
370      else { /* !one_process */
371 +#if !defined(NO_USE_SIGACTION) && defined(HAVE_PTHREAD_KILL)
372 +        struct sigaction act;
373 +#endif
374 +
375          /* remove SIGTERM from the set of blocked signals...  if one of
376           * the other threads in the process needs to take us down
377           * (e.g., for MaxRequestsPerChild) it will send us SIGTERM
378           */
379          unblock_signal(SIGTERM);
380          apr_signal(SIGTERM, dummy_signal_handler);
381 +
382 +        /* If the parent sends SIGINT to the child, we shutdown the
383 +         * client socket, as we suspect that we are under a DoS attack.
384 +         */
385 +#if !defined(NO_USE_SIGACTION) && defined(HAVE_PTHREAD_KILL)
386 +        unblock_signal(SIGINT);
387 +        memset(&act, 0, sizeof(act));
388 +        act.sa_flags = SA_SIGINFO;
389 +        act.sa_sigaction = shutdown_sockets;
390 +        sigaction(SIGINT, &act, NULL);
391 +#endif
392 +
393          /* Watch for any messages from the parent over the POD */
394          while (1) {
395              rv = ap_mpm_pod_check(pod);
396 @@ -1476,6 +1588,8 @@
397      int last_non_dead;
398      int total_non_dead;
399      int active_thread_count = 0;
400 +    int status = SERVER_DEAD;
401 +    static apr_time_t maxed_out = 0;
402  
403      /* initialize the free_list */
404      free_length = 0;
405 @@ -1487,7 +1601,6 @@
406      for (i = 0; i < ap_daemons_limit; ++i) {
407          /* Initialization to satisfy the compiler. It doesn't know
408           * that ap_threads_per_child is always > 0 */
409 -        int status = SERVER_DEAD;
410          int any_dying_threads = 0;
411          int any_dead_threads = 0;
412          int all_dead_threads = 1;
413 @@ -1581,12 +1694,17 @@
414          /* Kill off one child */
415          ap_mpm_pod_signal(pod, TRUE);
416          idle_spawn_rate = 1;
417 +        maxed_out = 0;
418      }
419      else if (idle_thread_count < min_spare_threads) {
420          /* terminate the free list */
421          if (free_length == 0) {
422              /* only report this condition once */
423              static int reported = 0;
424 +            static unsigned char sb_digest[APR_MD5_DIGESTSIZE];
425 +            apr_time_t now = apr_time_now();
426 +            apr_md5_ctx_t ctx;
427 +            pid_t pid;
428  
429              if (!reported) {
430                  ap_log_error(APLOG_MARK, APLOG_ERR, 0,
431 @@ -1596,6 +1714,95 @@
432                  reported = 1;
433              }
434              idle_spawn_rate = 1;
435 +
436 +            /* If after one maintenace interval we still see the same
437 +             * situation on the scoreboard, shutdown all client sockets
438 +             * in read state and at least 10% of all client sockets.
439 +             * Crude, but seems to clear things out.
440 +             */
441 +            if (maxed_out) {
442 +                apr_time_t diff = now - maxed_out;
443 +
444 +                if (diff >= SCOREBOARD_MAINTENANCE_INTERVAL) {
445 +                    unsigned char cur_digest[APR_MD5_DIGESTSIZE];
446 +
447 +                    /* Current digest of the scoreboard.
448 +                     */
449 +                    apr_md5_init(&ctx);
450 +                    for (i = 0; i < ap_daemons_limit; ++i) {
451 +                        for (j = 0; j < ap_threads_per_child; j++) {
452 +                            status = ap_scoreboard_image->servers[i][j].status;
453 +                            apr_md5_update(&ctx, &status, sizeof(status));
454 +                        }
455 +
456 +                        pid = ap_scoreboard_image->parent[i].pid;
457 +                        apr_md5_update(&ctx, &pid, sizeof(pid));
458 +                    }
459 +                    apr_md5_final(cur_digest, &ctx);
460 +
461 +                    /* If we haven't had a change for one maintenance
462 +                     * interval, we need to make room.
463 +                     */
464 +                    if (memcmp(sb_digest, cur_digest, APR_MD5_DIGESTSIZE)) {
465 +                        maxed_out = 0;
466 +                    }
467 +                    else {
468 +                        /* Signal child processes to shutdown client sockets.
469 +                         */
470 +                        for (i = 0; i < ap_daemons_limit; ++i) {
471 +                            pid = ap_scoreboard_image->parent[i].pid;
472 +                            ap_mpm_safe_kill(pid, SIGINT);
473 +                        }
474 +                    }
475 +                }
476 +            }
477 +            else {
478 +                int rdrs = 0;
479 +
480 +                /* Create digest of the scoreboard, see if things
481 +                 * change next time around.
482 +                 */
483 +                apr_md5_init(&ctx);
484 +                for (i = 0; i < ap_daemons_limit; ++i) {
485 +                    for (j = 0; j < ap_threads_per_child; j++) {
486 +                        status = ap_scoreboard_image->servers[i][j].status;
487 +
488 +                        /* These are conditions we are concerned with.
489 +                         */
490 +                        switch (status) {
491 +                        case SERVER_BUSY_READ:
492 +                        case SERVER_BUSY_KEEPALIVE:
493 +                            rdrs++;
494 +                        case SERVER_BUSY_WRITE:
495 +                        case SERVER_DEAD:
496 +                        case SERVER_GRACEFUL:
497 +                            break;
498 +                        default:
499 +                            return;
500 +                        }
501 +
502 +                        apr_md5_update(&ctx, &status, sizeof(status));
503 +                    }
504 +
505 +                    pid = ap_scoreboard_image->parent[i].pid;
506 +                    apr_md5_update(&ctx, &pid, sizeof(pid));
507 +                }
508 +                apr_md5_final(sb_digest, &ctx);
509 +
510 +                /* Over 95% in read state (includes keep alive), clear now.
511 +                 */
512 +                if (ap_daemons_limit - rdrs < ap_daemons_limit / 20) {
513 +                    /* Signal child processes to shutdown client sockets.
514 +                     */
515 +                    for (i = 0; i < ap_daemons_limit; ++i) {
516 +                        pid = ap_scoreboard_image->parent[i].pid;
517 +                        ap_mpm_safe_kill(pid, SIGINT);
518 +                    }
519 +                }
520 +                else {
521 +                    maxed_out = now;
522 +                }
523 +            }
524          }
525          else {
526              if (free_length > idle_spawn_rate) {
527 @@ -1623,10 +1830,13 @@
528              else if (idle_spawn_rate < MAX_SPAWN_RATE) {
529                  idle_spawn_rate *= 2;
530              }
531 +
532 +            maxed_out = 0;
533          }
534      }
535      else {
536        idle_spawn_rate = 1;
537 +      maxed_out = 0;
538      }
539  }
540  
This page took 0.084881 seconds and 2 git commands to generate.