]> git.pld-linux.org Git - packages/apache.git/blame - apache-shutdown-sockets.patch
- added patch 26 from:
[packages/apache.git] / apache-shutdown-sockets.patch
CommitLineData
7aa65e52
AM
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.13914 seconds and 4 git commands to generate.