diff -Nru apache2-2.4.52/debian/changelog apache2-2.4.52/debian/changelog --- apache2-2.4.52/debian/changelog 2022-06-14 12:30:21.000000000 +0000 +++ apache2-2.4.52/debian/changelog 2022-09-30 04:09:50.000000000 +0000 @@ -1,3 +1,15 @@ +apache2 (2.4.52-1ubuntu4.2) jammy; urgency=medium + + * d/p/fix-a-possible-listener-deadlock.patch, + d/p/handle-children-killed-pathologically.patch: Fix situation + where Apache fails to start its child processes after a certain + number of requests, causing requests for new pages to hang. + (LP: #1988224) + * d/perl-framework/t/ssl/ocsp.t: Update test framework + - Cherry pick from Debian 2.4.53-1 + + -- Bryce Harrington Thu, 29 Sep 2022 21:09:50 -0700 + apache2 (2.4.52-1ubuntu4.1) jammy-security; urgency=medium * SECURITY UPDATE: HTTP Request Smuggling diff -Nru apache2-2.4.52/debian/patches/fix-a-possible-listener-deadlock.patch apache2-2.4.52/debian/patches/fix-a-possible-listener-deadlock.patch --- apache2-2.4.52/debian/patches/fix-a-possible-listener-deadlock.patch 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.52/debian/patches/fix-a-possible-listener-deadlock.patch 2022-09-30 04:09:50.000000000 +0000 @@ -0,0 +1,114 @@ +Origin: upstream, https://svn.apache.org/viewvc?view=revision&revision=1897149 +Bug-Ubuntu: https://bugs.launchpad.net/ubuntu/+source/apache2/+bug/1988224 +Reviewed-By: Bryce Harrington +Last-Update: 2022-09-28 + +Merge r1896505 from trunk: + +mpm_event: Fix a possible listener deadlock. PR 65769. + +When the listener starts accepting more connections than the number of workers +already started (due to scheduling), the listening sockets gets disabled (per +AH03269) but nothing was re-enabling them before the end of the connections, +despite the creation of more idle/available workers in the meantime. +In the wost case there is no idle worker when the listener accepts the first +connection thus nothing to wake up the listener blocked in poll() with no +socket, hence a deadlock. + +Fix this by waking up the listener when a worker becomes idle and this unblocks +connections_above_limit(). This is also worthwhile when all the workers are +started (fully initialized runtime) since the number of idle workers is a +condition for connections_above_limit() anyway so the sooner the listeners are +re-enabled the better (the other condition is the number of connections which +is unblocked appropriately by decrement_connection_count() already). + +Also when a child exists with ps->quiescing == 1 and it's caught by +server_main_loop() before perform_idle_server_maintenance(), active_daemons was +not decrement as needed (including accross restarts), leading to an invalid +active_daemons accounting. + +* server/mpm/event/event.c(should_enable_listensocks): + New helper that returns whether listenning sockets can be poll()ed again. + +* server/mpm/event/event.c(decrement_connection_count, listener_thread): + Use should_enable_listensocks() where previously open-coded. + +* server/mpm/event/event.c(worker_thread): + Wake up the listener when is_idle => 1 and should_enable_listensocks(). + Have a single point of exit when workers_may_exit to make sure that the + wake always occurs (even when exiting). + +* server/mpm/event/event.c(server_main_loop): + Decrement active_daemons not only when !ps->quiescing but also when + ps->quiescing == 1, i.e. all the cases not handled by + perform_idle_server_maintenance() already. + + +Submitted by: ylavic +Reviewed by: rpluem, ylavic, gbechis + +--- a/server/mpm/event/event.c ++++ b/server/mpm/event/event.c +@@ -528,6 +528,11 @@ + return 1; + } + ++static APR_INLINE int should_enable_listensocks(void) ++{ ++ return !dying && listeners_disabled() && !connections_above_limit(NULL); ++} ++ + static void close_socket_nonblocking_(apr_socket_t *csd, + const char *from, int line) + { +@@ -774,7 +779,7 @@ + is_last_connection = !apr_atomic_dec32(&connection_count); + if (listener_is_wakeable + && ((is_last_connection && listener_may_exit) +- || (listeners_disabled() && !connections_above_limit(NULL)))) { ++ || should_enable_listensocks())) { + apr_pollset_wakeup(event_pollset); + } + if (dying) { +@@ -2002,9 +2007,7 @@ + } + } + +- if (listeners_disabled() +- && !workers_were_busy +- && !connections_above_limit(NULL)) { ++ if (!workers_were_busy && should_enable_listensocks()) { + enable_listensocks(); + } + } /* listener main loop */ +@@ -2066,7 +2069,7 @@ + ap_update_child_status_from_indexes(process_slot, thread_slot, + SERVER_STARTING, NULL); + +- while (!workers_may_exit) { ++ for (;;) { + apr_socket_t *csd = NULL; + event_conn_state_t *cs; + timer_event_t *te = NULL; +@@ -2081,6 +2084,12 @@ + signal_threads(ST_GRACEFUL); + break; + } ++ /* A new idler may have changed connections_above_limit(), ++ * let the listener know and decide. ++ */ ++ if (listener_is_wakeable && should_enable_listensocks()) { ++ apr_pollset_wakeup(event_pollset); ++ } + is_idle = 1; + } + +@@ -3045,7 +3054,7 @@ + + event_note_child_killed(child_slot, 0, 0); + ps = &ap_scoreboard_image->parent[child_slot]; +- if (!ps->quiescing) ++ if (ps->quiescing != 2) + retained->active_daemons--; + ps->quiescing = 0; + /* NOTE: We don't dec in the (child_slot < 0) case! */ diff -Nru apache2-2.4.52/debian/patches/handle-children-killed-pathologically.patch apache2-2.4.52/debian/patches/handle-children-killed-pathologically.patch --- apache2-2.4.52/debian/patches/handle-children-killed-pathologically.patch 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.52/debian/patches/handle-children-killed-pathologically.patch 2022-09-30 04:02:31.000000000 +0000 @@ -0,0 +1,99 @@ +Origin: upstream, https://svn.apache.org/viewvc?view=revision&revision=1901234 +Bug-Ubuntu: https://bugs.launchpad.net/ubuntu/+source/apache2/+bug/1988224 +Reviewed-By: Bryce Harrington +Last-Update: 2022-09-28 + +Merge /httpd/httpd/trunk:r1899858,1899865,1899884,1900991 + + *) mpm_event, mpm_worker: Handle children killed pathologically. + *) MPM event: Restart chilren processes killed before idle maintenance. + PR 65769. [Yann Ylavic, Ruediger Pluem] + +--- a/server/mpm/event/event.c ++++ b/server/mpm/event/event.c +@@ -3001,6 +3001,7 @@ + static void server_main_loop(int remaining_children_to_start) + { + int num_buckets = retained->mpm->num_buckets; ++ int successive_kills = 0; + int child_slot; + apr_exit_why_e exitwhy; + int status, processed_status; +@@ -3090,11 +3091,30 @@ + /* 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 a child is ++ * killed by a signal (faulting) we want to restart it ASAP ++ * though, up to 3 successive faults or we stop this until ++ * a timeout happens again (to avoid the flood of fork()ed ++ * processes that keep being killed early). + */ +- continue; ++ if (child_slot < 0 || !APR_PROC_CHECK_SIGNALED(exitwhy)) { ++ continue; ++ } ++ if (++successive_kills >= 3) { ++ if (successive_kills % 10 == 3) { ++ ap_log_error(APLOG_MARK, APLOG_WARNING, 0, ++ ap_server_conf, APLOGNO(10392) ++ "children are killed successively!"); ++ } ++ continue; ++ } ++ ++remaining_children_to_start; + } +- else if (remaining_children_to_start) { ++ else { ++ successive_kills = 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. +--- a/server/mpm/worker/worker.c ++++ b/server/mpm/worker/worker.c +@@ -1575,6 +1575,7 @@ + static void server_main_loop(int remaining_children_to_start) + { + int num_buckets = retained->mpm->num_buckets; ++ int successive_kills = 0; + ap_generation_t old_gen; + int child_slot; + apr_exit_why_e exitwhy; +@@ -1669,11 +1670,30 @@ + /* 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 a child is ++ * killed by a signal (faulting) we want to restart it ASAP ++ * though, up to 3 successive faults or we stop this until ++ * a timeout happens again (to avoid the flood of fork()ed ++ * processes that keep being killed early). + */ +- continue; ++ if (child_slot < 0 || !APR_PROC_CHECK_SIGNALED(exitwhy)) { ++ continue; ++ } ++ if (++successive_kills >= 3) { ++ if (successive_kills % 10 == 3) { ++ ap_log_error(APLOG_MARK, APLOG_WARNING, 0, ++ ap_server_conf, APLOGNO(10392) ++ "children are killed successively!"); ++ } ++ continue; ++ } ++ ++remaining_children_to_start; + } +- else if (remaining_children_to_start) { ++ else { ++ successive_kills = 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. diff -Nru apache2-2.4.52/debian/patches/series apache2-2.4.52/debian/patches/series --- apache2-2.4.52/debian/patches/series 2022-06-14 12:30:11.000000000 +0000 +++ apache2-2.4.52/debian/patches/series 2022-09-30 03:47:44.000000000 +0000 @@ -23,3 +23,5 @@ CVE-2022-30522.patch CVE-2022-30556.patch CVE-2022-31813.patch +fix-a-possible-listener-deadlock.patch +handle-children-killed-pathologically.patch diff -Nru apache2-2.4.52/debian/perl-framework/t/ssl/ocsp.t apache2-2.4.52/debian/perl-framework/t/ssl/ocsp.t --- apache2-2.4.52/debian/perl-framework/t/ssl/ocsp.t 2022-03-23 02:00:46.000000000 +0000 +++ apache2-2.4.52/debian/perl-framework/t/ssl/ocsp.t 2022-09-30 04:09:50.000000000 +0000 @@ -32,13 +32,21 @@ $r = GET $url, cert => undef; my $message = $r->content() || ''; my $warning = $r->header('Client-Warning') || ''; + print "warning: $warning\n"; + print "message: $message"; + print "response:\n"; print $r->as_string; $r->code == 500 && $warning =~ 'Internal response' && - $message =~ /alert handshake failure|read failed/; + $message =~ /alert handshake failure|read failed|closed connection without sending any data/; }; sok { $r = GET $url, cert => 'client_ok'; + my $warning = $r->header('Client-Warning') || ''; + my $message = $r->content() || ''; + print "warning: $warning\n"; + print "message: $message"; + print "response:\n"; print $r->as_string; $r->code == 200; }; @@ -47,7 +55,10 @@ $r = GET $url, cert => 'client_revoked'; my $message = $r->content() || ''; my $warning = $r->header('Client-Warning') || ''; + print "warning: $warning\n"; + print "message: $message"; + print "response:\n"; print $r->as_string; $r->code == 500 && $warning =~ 'Internal response' && - $message =~ /alert certificate revoked|read failed/; + $message =~ /alert certificate revoked|read failed|closed connection without sending any data/; };