diff -Nru apache2-2.4.29/debian/changelog apache2-2.4.29/debian/changelog --- apache2-2.4.29/debian/changelog 2020-03-13 12:26:16.000000000 +0000 +++ apache2-2.4.29/debian/changelog 2020-08-12 21:33:25.000000000 +0000 @@ -1,3 +1,30 @@ +apache2 (2.4.29-1ubuntu4.14) bionic-security; urgency=medium + + * SECURITY UPDATE: mod_rewrite redirect issue + - debian/patches/CVE-2020-1927-1.patch: factor out default regex flags + in include/ap_regex.h, server/core.c, server/util_pcre.c. + - debian/patches/CVE-2020-1927-2.patch: add AP_REG_NO_DEFAULT to allow + opt-out of pcre defaults in include/ap_regex.h, + modules/filters/mod_substitute.c, server/util_pcre.c, + server/util_regex.c. + - CVE-2020-1927 + * SECURITY UPDATE: mod_proxy_ftp uninitialized memory issue + - debian/patches/CVE-2020-1934.patch: trap bad FTP responses in + modules/proxy/mod_proxy_ftp.c. + - CVE-2020-1934 + * SECURITY UPDATE: DoS via invalid Cache-Digest header + - debian/patches/CVE-2020-9490.patch: remove support for abandoned + http-wg draft in modules/http2/h2_push.c, modules/http2/h2_push.h. + - CVE-2020-9490 + * SECURITY UPDATE: concurrent use of memory pools in HTTP/2 module + - debian/patches/CVE-2020-11993-pre1.patch: fixed rare cases where a h2 + worker could deadlock the main connection in modules/http2/*. + - debian/patches/CVE-2020-11993.patch: fix logging and rename + terminology in modules/http2/*. + - CVE-2020-11993 + + -- Marc Deslauriers Wed, 12 Aug 2020 17:33:25 -0400 + apache2 (2.4.29-1ubuntu4.13) bionic-security; urgency=medium * Add additional missing commits to TLSv1.3 support. (LP: #1867223) diff -Nru apache2-2.4.29/debian/patches/CVE-2020-11993.patch apache2-2.4.29/debian/patches/CVE-2020-11993.patch --- apache2-2.4.29/debian/patches/CVE-2020-11993.patch 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.29/debian/patches/CVE-2020-11993.patch 2020-08-12 21:33:13.000000000 +0000 @@ -0,0 +1,1905 @@ +Backport of: + +From 63a0a87efa0925514d15c211b508f6594669888c Mon Sep 17 00:00:00 2001 +From: Graham Leggett +Date: Wed, 8 Jul 2020 11:53:48 +0000 +Subject: [PATCH] *) mod_http2: connection terminology renamed to + master/secondary. trunk patch: http://svn.apache.org/r1878926 + http://svn.apache.org/r1879156 2.4.x patch: + https://svn.apache.org/repos/asf/httpd/httpd/patches/2.4.x/h2-master-secondary.patch + +1: icing, ylavic, minfrin ylavic: nitpicking, mixed + "H2_secondary_IN" and "H2_secondary_OUT" case to register the + filters, but not for adding them. IIRC filters names are case + insentive so shouldn't matter, just popped at my eyes.. icing: updated + patch and added r1879156 to fix the eye bleed. jailletc36: CHANGES could + also be looked at if it makes sense to update the terminology + also here + +git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x@1879642 13f79535-47bb-0310-9956-ffa450edef68 +--- + CHANGES | 3 + + STATUS | 11 -- + modules/http2/h2_conn.c | 52 +++---- + modules/http2/h2_conn.h | 8 +- + modules/http2/h2_filter.c | 4 +- + modules/http2/h2_h2.c | 10 +- + modules/http2/h2_mplx.c | 283 +++++++++++++++++++------------------ + modules/http2/h2_mplx.h | 160 ++++++--------------- + modules/http2/h2_request.c | 7 +- + modules/http2/h2_session.c | 30 ++-- + modules/http2/h2_session.h | 2 +- + modules/http2/h2_stream.c | 2 +- + modules/http2/h2_task.c | 68 ++++----- + modules/http2/h2_task.h | 2 +- + modules/http2/h2_workers.c | 6 +- + modules/http2/mod_http2.c | 4 +- + 16 files changed, 288 insertions(+), 364 deletions(-) + +#diff --git a/CHANGES b/CHANGES +#index 50432a7ca09..31ad6741715 100644 +#--- a/CHANGES +#+++ b/CHANGES +#@@ -1,6 +1,9 @@ +# -*- coding: utf-8 -*- +# Changes with Apache 2.4.44 +# +#+ *) mod_http2: The module now handles master/secondary connections and has marked +#+ methods according to use. [Stefan Eissing] +#+ +# *) core: Drop an invalid Last-Modified header value coming +# from a FCGI/CGI script instead of replacing it with Unix epoch. +# [Luca Toscano] +#diff --git a/STATUS b/STATUS +#index c4d0dde0534..72220708a70 100644 +#--- a/STATUS +#+++ b/STATUS +#@@ -135,17 +135,6 @@ RELEASE SHOWSTOPPERS: +# PATCHES ACCEPTED TO BACKPORT FROM TRUNK: +# [ start all new proposals below, under PATCHES PROPOSED. ] +# +#- *) mod_http2: connection terminology renamed to master/secondary. +#- trunk patch: http://svn.apache.org/r1878926 +#- http://svn.apache.org/r1879156 +#- 2.4.x patch: https://svn.apache.org/repos/asf/httpd/httpd/patches/2.4.x/h2-master-secondary.patch +#- +1: icing, ylavic, minfrin +#- ylavic: nitpicking, mixed "H2_secondary_IN" and "H2_secondary_OUT" case to +#- register the filters, but not for adding them. IIRC filters names +#- are case insentive so shouldn't matter, just popped at my eyes.. +#- icing: updated patch and added r1879156 to fix the eye bleed. +#- jailletc36: CHANGES could also be looked at if it makes sense to update the terminology +#- also here +# +# PATCHES PROPOSED TO BACKPORT FROM TRUNK: +# [ New proposals should be added at the end of the list ] +--- a/modules/http2/h2_conn.c ++++ b/modules/http2/h2_conn.c +@@ -138,7 +138,7 @@ apr_status_t h2_conn_child_init(apr_pool + ap_register_input_filter("H2_IN", h2_filter_core_input, + NULL, AP_FTYPE_CONNECTION); + +- status = h2_mplx_child_init(pool, s); ++ status = h2_mplx_m_child_init(pool, s); + + if (status == APR_SUCCESS) { + status = apr_socket_create(&dummy_socket, APR_INET, SOCK_STREAM, +@@ -260,7 +260,7 @@ apr_status_t h2_conn_pre_close(struct h2 + return DONE; + } + +-conn_rec *h2_slave_create(conn_rec *master, int slave_id, apr_pool_t *parent) ++conn_rec *h2_secondary_create(conn_rec *master, int sec_id, apr_pool_t *parent) + { + apr_allocator_t *allocator; + apr_status_t status; +@@ -271,7 +271,7 @@ conn_rec *h2_slave_create(conn_rec *mast + + ap_assert(master); + ap_log_cerror(APLOG_MARK, APLOG_TRACE3, 0, master, +- "h2_stream(%ld-%d): create slave", master->id, slave_id); ++ "h2_stream(%ld-%d): create secondary", master->id, sec_id); + + /* We create a pool with its own allocator to be used for + * processing a request. This is the only way to have the processing +@@ -284,18 +284,18 @@ conn_rec *h2_slave_create(conn_rec *mast + status = apr_pool_create_ex(&pool, parent, NULL, allocator); + if (status != APR_SUCCESS) { + ap_log_cerror(APLOG_MARK, APLOG_ERR, status, master, +- APLOGNO(10004) "h2_session(%ld-%d): create slave pool", +- master->id, slave_id); ++ APLOGNO(10004) "h2_session(%ld-%d): create secondary pool", ++ master->id, sec_id); + return NULL; + } + apr_allocator_owner_set(allocator, pool); +- apr_pool_tag(pool, "h2_slave_conn"); ++ apr_pool_tag(pool, "h2_secondary_conn"); + + c = (conn_rec *) apr_palloc(pool, sizeof(conn_rec)); + if (c == NULL) { + ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_ENOMEM, master, +- APLOGNO(02913) "h2_session(%ld-%d): create slave", +- master->id, slave_id); ++ APLOGNO(02913) "h2_session(%ld-%d): create secondary", ++ master->id, sec_id); + apr_pool_destroy(pool); + return NULL; + } +@@ -322,19 +322,19 @@ conn_rec *h2_slave_create(conn_rec *mast + c->clogging_input_filters = 1; + c->log = NULL; + c->log_id = apr_psprintf(pool, "%ld-%d", +- master->id, slave_id); ++ master->id, sec_id); + c->aborted = 0; +- /* We cannot install the master connection socket on the slaves, as ++ /* We cannot install the master connection socket on the secondary, as + * modules mess with timeouts/blocking of the socket, with + * unwanted side effects to the master connection processing. +- * Fortunately, since we never use the slave socket, we can just install ++ * Fortunately, since we never use the secondary socket, we can just install + * a single, process-wide dummy and everyone is happy. + */ + ap_set_module_config(c->conn_config, &core_module, dummy_socket); + /* TODO: these should be unique to this thread */ + c->sbh = master->sbh; +- /* TODO: not all mpm modules have learned about slave connections yet. +- * copy their config from master to slave. ++ /* TODO: not all mpm modules have learned about secondary connections yet. ++ * copy their config from master to secondary. + */ + if ((mpm = h2_conn_mpm_module()) != NULL) { + cfg = ap_get_module_config(master->conn_config, mpm); +@@ -342,38 +342,38 @@ conn_rec *h2_slave_create(conn_rec *mast + } + + ap_log_cerror(APLOG_MARK, APLOG_TRACE3, 0, c, +- "h2_slave(%s): created", c->log_id); ++ "h2_secondary(%s): created", c->log_id); + return c; + } + +-void h2_slave_destroy(conn_rec *slave) ++void h2_secondary_destroy(conn_rec *secondary) + { +- ap_log_cerror(APLOG_MARK, APLOG_TRACE3, 0, slave, +- "h2_slave(%s): destroy", slave->log_id); +- slave->sbh = NULL; +- apr_pool_destroy(slave->pool); ++ ap_log_cerror(APLOG_MARK, APLOG_TRACE3, 0, secondary, ++ "h2_secondary(%s): destroy", secondary->log_id); ++ secondary->sbh = NULL; ++ apr_pool_destroy(secondary->pool); + } + +-apr_status_t h2_slave_run_pre_connection(conn_rec *slave, apr_socket_t *csd) ++apr_status_t h2_secondary_run_pre_connection(conn_rec *secondary, apr_socket_t *csd) + { +- if (slave->keepalives == 0) { ++ if (secondary->keepalives == 0) { + /* Simulate that we had already a request on this connection. Some + * hooks trigger special behaviour when keepalives is 0. + * (Not necessarily in pre_connection, but later. Set it here, so it + * is in place.) */ +- slave->keepalives = 1; ++ secondary->keepalives = 1; + /* We signal that this connection will be closed after the request. + * Which is true in that sense that we throw away all traffic data +- * on this slave connection after each requests. Although we might ++ * on this secondary connection after each requests. Although we might + * reuse internal structures like memory pools. + * The wanted effect of this is that httpd does not try to clean up + * any dangling data on this connection when a request is done. Which + * is unneccessary on a h2 stream. + */ +- slave->keepalive = AP_CONN_CLOSE; +- return ap_run_pre_connection(slave, csd); ++ secondary->keepalive = AP_CONN_CLOSE; ++ return ap_run_pre_connection(secondary, csd); + } +- ap_assert(slave->output_filters); ++ ap_assert(secondary->output_filters); + return APR_SUCCESS; + } + +--- a/modules/http2/h2_conn.h ++++ b/modules/http2/h2_conn.h +@@ -68,10 +68,10 @@ h2_mpm_type_t h2_conn_mpm_type(void); + const char *h2_conn_mpm_name(void); + int h2_mpm_supported(void); + +-conn_rec *h2_slave_create(conn_rec *master, int slave_id, apr_pool_t *parent); +-void h2_slave_destroy(conn_rec *slave); ++conn_rec *h2_secondary_create(conn_rec *master, int sec_id, apr_pool_t *parent); ++void h2_secondary_destroy(conn_rec *secondary); + +-apr_status_t h2_slave_run_pre_connection(conn_rec *slave, apr_socket_t *csd); +-void h2_slave_run_connection(conn_rec *slave); ++apr_status_t h2_secondary_run_pre_connection(conn_rec *secondary, apr_socket_t *csd); ++void h2_secondary_run_connection(conn_rec *secondary); + + #endif /* defined(__mod_h2__h2_conn__) */ +--- a/modules/http2/h2_filter.c ++++ b/modules/http2/h2_filter.c +@@ -370,7 +370,7 @@ static void add_streams(apr_bucket_briga + x.s = s; + x.idx = 0; + bbout(bb, " \"streams\": {"); +- h2_mplx_stream_do(s->mplx, add_stream, &x); ++ h2_mplx_m_stream_do(s->mplx, add_stream, &x); + bbout(bb, "\n }%s\n", last? "" : ","); + } + +@@ -433,7 +433,7 @@ static void add_stats(apr_bucket_brigade + static apr_status_t h2_status_insert(h2_task *task, apr_bucket *b) + { + h2_mplx *m = task->mplx; +- h2_stream *stream = h2_mplx_stream_get(m, task->stream_id); ++ h2_stream *stream = h2_mplx_t_stream_get(m, task); + h2_session *s; + conn_rec *c; + +--- a/modules/http2/h2_h2.c ++++ b/modules/http2/h2_h2.c +@@ -668,7 +668,7 @@ static int h2_h2_pre_close_conn(conn_rec + { + h2_ctx *ctx; + +- /* slave connection? */ ++ /* secondary connection? */ + if (c->master) { + return DECLINED; + } +@@ -712,7 +712,7 @@ static void check_push(request_rec *r, c + + static int h2_h2_post_read_req(request_rec *r) + { +- /* slave connection? */ ++ /* secondary connection? */ + if (r->connection->master) { + struct h2_task *task = h2_ctx_get_task(r->connection); + /* This hook will get called twice on internal redirects. Take care +@@ -731,7 +731,7 @@ static int h2_h2_post_read_req(request_r + ap_add_output_filter("H2_RESPONSE", task, r, r->connection); + + for (f = r->input_filters; f; f = f->next) { +- if (!strcmp("H2_SLAVE_IN", f->frec->name)) { ++ if (!strcmp("H2_SECONDARY_IN", f->frec->name)) { + f->r = r; + break; + } +@@ -745,7 +745,7 @@ static int h2_h2_post_read_req(request_r + + static int h2_h2_late_fixups(request_rec *r) + { +- /* slave connection? */ ++ /* secondary connection? */ + if (r->connection->master) { + struct h2_task *task = h2_ctx_get_task(r->connection); + if (task) { +@@ -753,7 +753,7 @@ static int h2_h2_late_fixups(request_rec + task->output.copy_files = h2_config_rgeti(r, H2_CONF_COPY_FILES); + if (task->output.copy_files) { + ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, task->c, +- "h2_slave_out(%s): copy_files on", task->id); ++ "h2_secondary_out(%s): copy_files on", task->id); + h2_beam_on_file_beam(task->output.beam, h2_beam_no_files, NULL); + } + check_push(r, "late_fixup"); +--- a/modules/http2/h2_mplx.c ++++ b/modules/http2/h2_mplx.c +@@ -56,10 +56,18 @@ typedef struct { + apr_size_t count; + } stream_iter_ctx; + +-static apr_status_t mplx_be_happy(h2_mplx *m); +-static apr_status_t mplx_be_annoyed(h2_mplx *m); ++/** ++ * Naming convention for static functions: ++ * - m_*: function only called from the master connection ++ * - s_*: function only called from a secondary connection ++ * - t_*: function only called from a h2_task holder ++ * - mst_*: function called from everyone ++ */ + +-apr_status_t h2_mplx_child_init(apr_pool_t *pool, server_rec *s) ++static apr_status_t s_mplx_be_happy(h2_mplx *m, h2_task *task); ++static apr_status_t m_be_annoyed(h2_mplx *m); ++ ++apr_status_t h2_mplx_m_child_init(apr_pool_t *pool, server_rec *s) + { + return APR_SUCCESS; + } +@@ -81,26 +89,25 @@ apr_status_t h2_mplx_child_init(apr_pool + #define H2_MPLX_LEAVE_MAYBE(m, dolock) \ + if (dolock) apr_thread_mutex_unlock(m->lock) + +-static void check_data_for(h2_mplx *m, h2_stream *stream, int mplx_is_locked); ++static void mst_check_data_for(h2_mplx *m, h2_stream *stream, int mplx_is_locked); + +-static void stream_output_consumed(void *ctx, +- h2_bucket_beam *beam, apr_off_t length) ++static void mst_stream_output_consumed(void *ctx, h2_bucket_beam *beam, apr_off_t length) + { + } + +-static void stream_input_ev(void *ctx, h2_bucket_beam *beam) ++static void mst_stream_input_ev(void *ctx, h2_bucket_beam *beam) + { + h2_stream *stream = ctx; + h2_mplx *m = stream->session->mplx; + apr_atomic_set32(&m->event_pending, 1); + } + +-static void stream_input_consumed(void *ctx, h2_bucket_beam *beam, apr_off_t length) ++static void m_stream_input_consumed(void *ctx, h2_bucket_beam *beam, apr_off_t length) + { + h2_stream_in_consumed(ctx, length); + } + +-static void stream_joined(h2_mplx *m, h2_stream *stream) ++static void ms_stream_joined(h2_mplx *m, h2_stream *stream) + { + ap_assert(!h2_task_has_started(stream->task) || stream->task->worker_done); + +@@ -109,7 +116,7 @@ static void stream_joined(h2_mplx *m, h2 + h2_ihash_add(m->spurge, stream); + } + +-static void stream_cleanup(h2_mplx *m, h2_stream *stream) ++static void m_stream_cleanup(h2_mplx *m, h2_stream *stream) + { + ap_assert(stream->state == H2_SS_CLEANUP); + +@@ -128,7 +135,7 @@ static void stream_cleanup(h2_mplx *m, h + h2_iq_remove(m->q, stream->id); + + if (!h2_task_has_started(stream->task) || stream->task->done_done) { +- stream_joined(m, stream); ++ ms_stream_joined(m, stream); + } + else { + h2_ififo_remove(m->readyq, stream->id); +@@ -150,8 +157,8 @@ static void stream_cleanup(h2_mplx *m, h + * their HTTP/1 cousins, the separate allocator seems to work better + * than protecting a shared h2_session one with an own lock. + */ +-h2_mplx *h2_mplx_create(conn_rec *c, server_rec *s, apr_pool_t *parent, +- h2_workers *workers) ++h2_mplx *h2_mplx_m_create(conn_rec *c, server_rec *s, apr_pool_t *parent, ++ h2_workers *workers) + { + apr_status_t status = APR_SUCCESS; + apr_allocator_t *allocator; +@@ -165,7 +172,7 @@ h2_mplx *h2_mplx_create(conn_rec *c, ser + m->s = s; + + /* We create a pool with its own allocator to be used for +- * processing slave connections. This is the only way to have the ++ * processing secondary connections. This is the only way to have the + * processing independant of its parent pool in the sense that it + * can work in another thread. Also, the new allocator needs its own + * mutex to synchronize sub-pools. +@@ -217,12 +224,12 @@ h2_mplx *h2_mplx_create(conn_rec *c, ser + m->last_mood_change = apr_time_now(); + m->mood_update_interval = apr_time_from_msec(100); + +- m->spare_slaves = apr_array_make(m->pool, 10, sizeof(conn_rec*)); ++ m->spare_secondary = apr_array_make(m->pool, 10, sizeof(conn_rec*)); + } + return m; + } + +-int h2_mplx_shutdown(h2_mplx *m) ++int h2_mplx_m_shutdown(h2_mplx *m) + { + int max_stream_started = 0; + +@@ -236,7 +243,7 @@ int h2_mplx_shutdown(h2_mplx *m) + return max_stream_started; + } + +-static int input_consumed_signal(h2_mplx *m, h2_stream *stream) ++static int m_input_consumed_signal(h2_mplx *m, h2_stream *stream) + { + if (stream->input) { + return h2_beam_report_consumption(stream->input); +@@ -244,12 +251,12 @@ static int input_consumed_signal(h2_mplx + return 0; + } + +-static int report_consumption_iter(void *ctx, void *val) ++static int m_report_consumption_iter(void *ctx, void *val) + { + h2_stream *stream = val; + h2_mplx *m = ctx; + +- input_consumed_signal(m, stream); ++ m_input_consumed_signal(m, stream); + if (stream->state == H2_SS_CLOSED_L + && (!stream->task || stream->task->worker_done)) { + ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, m->c, +@@ -260,7 +267,7 @@ static int report_consumption_iter(void + return 1; + } + +-static int output_consumed_signal(h2_mplx *m, h2_task *task) ++static int s_output_consumed_signal(h2_mplx *m, h2_task *task) + { + if (task->output.beam) { + return h2_beam_report_consumption(task->output.beam); +@@ -268,7 +275,7 @@ static int output_consumed_signal(h2_mpl + return 0; + } + +-static int stream_destroy_iter(void *ctx, void *val) ++static int m_stream_destroy_iter(void *ctx, void *val) + { + h2_mplx *m = ctx; + h2_stream *stream = val; +@@ -278,7 +285,7 @@ static int stream_destroy_iter(void *ctx + + if (stream->input) { + /* Process outstanding events before destruction */ +- input_consumed_signal(m, stream); ++ m_input_consumed_signal(m, stream); + h2_beam_log(stream->input, m->c, APLOG_TRACE2, "stream_destroy"); + h2_beam_destroy(stream->input); + stream->input = NULL; +@@ -286,12 +293,12 @@ static int stream_destroy_iter(void *ctx + + if (stream->task) { + h2_task *task = stream->task; +- conn_rec *slave; +- int reuse_slave = 0; ++ conn_rec *secondary; ++ int reuse_secondary = 0; + + stream->task = NULL; +- slave = task->c; +- if (slave) { ++ secondary = task->c; ++ if (secondary) { + /* On non-serialized requests, the IO logging has not accounted for any + * meta data send over the network: response headers and h2 frame headers. we + * counted this on the stream and need to add this now. +@@ -300,26 +307,25 @@ static int stream_destroy_iter(void *ctx + if (task->request && !task->request->serialize && h2_task_logio_add_bytes_out) { + apr_off_t unaccounted = stream->out_frame_octets - stream->out_data_octets; + if (unaccounted > 0) { +- h2_task_logio_add_bytes_out(slave, unaccounted); ++ h2_task_logio_add_bytes_out(secondary, unaccounted); + } + } + +- if (m->s->keep_alive_max == 0 || slave->keepalives < m->s->keep_alive_max) { +- reuse_slave = ((m->spare_slaves->nelts < (m->limit_active * 3 / 2)) +- && !task->rst_error); ++ if (m->s->keep_alive_max == 0 || secondary->keepalives < m->s->keep_alive_max) { ++ reuse_secondary = ((m->spare_secondary->nelts < (m->limit_active * 3 / 2)) ++ && !task->rst_error); + } + +- task->c = NULL; +- if (reuse_slave) { ++ if (reuse_secondary) { + h2_beam_log(task->output.beam, m->c, APLOG_DEBUG, +- APLOGNO(03385) "h2_task_destroy, reuse slave"); ++ APLOGNO(03385) "h2_task_destroy, reuse secondary"); + h2_task_destroy(task); +- APR_ARRAY_PUSH(m->spare_slaves, conn_rec*) = slave; ++ APR_ARRAY_PUSH(m->spare_secondary, conn_rec*) = secondary; + } + else { + h2_beam_log(task->output.beam, m->c, APLOG_TRACE1, +- "h2_task_destroy, destroy slave"); +- h2_slave_destroy(slave); ++ "h2_task_destroy, destroy secondary"); ++ h2_secondary_destroy(secondary); + } + } + } +@@ -327,11 +333,11 @@ static int stream_destroy_iter(void *ctx + return 0; + } + +-static void purge_streams(h2_mplx *m, int lock) ++static void m_purge_streams(h2_mplx *m, int lock) + { + if (!h2_ihash_empty(m->spurge)) { + H2_MPLX_ENTER_MAYBE(m, lock); +- while (!h2_ihash_iter(m->spurge, stream_destroy_iter, m)) { ++ while (!h2_ihash_iter(m->spurge, m_stream_destroy_iter, m)) { + /* repeat until empty */ + } + H2_MPLX_LEAVE_MAYBE(m, lock); +@@ -343,13 +349,13 @@ typedef struct { + void *ctx; + } stream_iter_ctx_t; + +-static int stream_iter_wrap(void *ctx, void *stream) ++static int m_stream_iter_wrap(void *ctx, void *stream) + { + stream_iter_ctx_t *x = ctx; + return x->cb(stream, x->ctx); + } + +-apr_status_t h2_mplx_stream_do(h2_mplx *m, h2_mplx_stream_cb *cb, void *ctx) ++apr_status_t h2_mplx_m_stream_do(h2_mplx *m, h2_mplx_stream_cb *cb, void *ctx) + { + stream_iter_ctx_t x; + +@@ -357,13 +363,13 @@ apr_status_t h2_mplx_stream_do(h2_mplx * + + x.cb = cb; + x.ctx = ctx; +- h2_ihash_iter(m->streams, stream_iter_wrap, &x); ++ h2_ihash_iter(m->streams, m_stream_iter_wrap, &x); + + H2_MPLX_LEAVE(m); + return APR_SUCCESS; + } + +-static int report_stream_iter(void *ctx, void *val) { ++static int m_report_stream_iter(void *ctx, void *val) { + h2_mplx *m = ctx; + h2_stream *stream = val; + h2_task *task = stream->task; +@@ -388,7 +394,7 @@ static int report_stream_iter(void *ctx, + return 1; + } + +-static int unexpected_stream_iter(void *ctx, void *val) { ++static int m_unexpected_stream_iter(void *ctx, void *val) { + h2_mplx *m = ctx; + h2_stream *stream = val; + ap_log_cerror(APLOG_MARK, APLOG_WARNING, 0, m->c, /* NO APLOGNO */ +@@ -397,7 +403,7 @@ static int unexpected_stream_iter(void * + return 1; + } + +-static int stream_cancel_iter(void *ctx, void *val) { ++static int m_stream_cancel_iter(void *ctx, void *val) { + h2_mplx *m = ctx; + h2_stream *stream = val; + +@@ -411,11 +417,11 @@ static int stream_cancel_iter(void *ctx, + h2_stream_rst(stream, H2_ERR_NO_ERROR); + /* All connection data has been sent, simulate cleanup */ + h2_stream_dispatch(stream, H2_SEV_EOS_SENT); +- stream_cleanup(m, stream); ++ m_stream_cleanup(m, stream); + return 0; + } + +-void h2_mplx_release_and_join(h2_mplx *m, apr_thread_cond_t *wait) ++void h2_mplx_m_release_and_join(h2_mplx *m, apr_thread_cond_t *wait) + { + apr_status_t status; + int i, wait_secs = 60, old_aborted; +@@ -429,7 +435,7 @@ void h2_mplx_release_and_join(h2_mplx *m + + H2_MPLX_ENTER_ALWAYS(m); + +- /* While really terminating any slave connections, treat the master ++ /* While really terminating any secondary connections, treat the master + * connection as aborted. It's not as if we could send any more data + * at this point. */ + old_aborted = m->c->aborted; +@@ -441,7 +447,7 @@ void h2_mplx_release_and_join(h2_mplx *m + "h2_mplx(%ld): release, %d/%d/%d streams (total/hold/purge), %d active tasks", + m->id, (int)h2_ihash_count(m->streams), + (int)h2_ihash_count(m->shold), (int)h2_ihash_count(m->spurge), m->tasks_active); +- while (!h2_ihash_iter(m->streams, stream_cancel_iter, m)) { ++ while (!h2_ihash_iter(m->streams, m_stream_cancel_iter, m)) { + /* until empty */ + } + +@@ -463,7 +469,7 @@ void h2_mplx_release_and_join(h2_mplx *m + ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, m->c, APLOGNO(03198) + "h2_mplx(%ld): waited %d sec for %d tasks", + m->id, i*wait_secs, (int)h2_ihash_count(m->shold)); +- h2_ihash_iter(m->shold, report_stream_iter, m); ++ h2_ihash_iter(m->shold, m_report_stream_iter, m); + } + } + m->join_wait = NULL; +@@ -474,7 +480,7 @@ void h2_mplx_release_and_join(h2_mplx *m + ap_log_cerror(APLOG_MARK, APLOG_WARNING, 0, m->c, APLOGNO(03516) + "h2_mplx(%ld): unexpected %d streams in hold", + m->id, (int)h2_ihash_count(m->shold)); +- h2_ihash_iter(m->shold, unexpected_stream_iter, m); ++ h2_ihash_iter(m->shold, m_unexpected_stream_iter, m); + } + + m->c->aborted = old_aborted; +@@ -483,39 +489,39 @@ void h2_mplx_release_and_join(h2_mplx *m + ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, m->c, "h2_mplx(%ld): released", m->id); + } + +-apr_status_t h2_mplx_stream_cleanup(h2_mplx *m, h2_stream *stream) ++apr_status_t h2_mplx_m_stream_cleanup(h2_mplx *m, h2_stream *stream) + { + H2_MPLX_ENTER(m); + + ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, m->c, + H2_STRM_MSG(stream, "cleanup")); +- stream_cleanup(m, stream); ++ m_stream_cleanup(m, stream); + + H2_MPLX_LEAVE(m); + return APR_SUCCESS; + } + +-h2_stream *h2_mplx_stream_get(h2_mplx *m, int id) ++h2_stream *h2_mplx_t_stream_get(h2_mplx *m, h2_task *task) + { + h2_stream *s = NULL; + + H2_MPLX_ENTER_ALWAYS(m); + +- s = h2_ihash_get(m->streams, id); ++ s = h2_ihash_get(m->streams, task->stream_id); + + H2_MPLX_LEAVE(m); + return s; + } + +-static void output_produced(void *ctx, h2_bucket_beam *beam, apr_off_t bytes) ++static void mst_output_produced(void *ctx, h2_bucket_beam *beam, apr_off_t bytes) + { + h2_stream *stream = ctx; + h2_mplx *m = stream->session->mplx; + +- check_data_for(m, stream, 0); ++ mst_check_data_for(m, stream, 0); + } + +-static apr_status_t out_open(h2_mplx *m, int stream_id, h2_bucket_beam *beam) ++static apr_status_t t_out_open(h2_mplx *m, int stream_id, h2_bucket_beam *beam) + { + h2_stream *stream = h2_ihash_get(m->streams, stream_id); + +@@ -527,26 +533,26 @@ static apr_status_t out_open(h2_mplx *m, + stream->output = beam; + + if (APLOGctrace2(m->c)) { +- h2_beam_log(beam, m->c, APLOG_TRACE2, "out_open"); ++ h2_beam_log(beam, stream->task->c, APLOG_TRACE2, "out_open"); + } + else { +- ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, m->c, ++ ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, stream->task->c, + "h2_mplx(%s): out open", stream->task->id); + } + +- h2_beam_on_consumed(stream->output, NULL, stream_output_consumed, stream); +- h2_beam_on_produced(stream->output, output_produced, stream); ++ h2_beam_on_consumed(stream->output, NULL, mst_stream_output_consumed, stream); ++ h2_beam_on_produced(stream->output, mst_output_produced, stream); + if (stream->task->output.copy_files) { + h2_beam_on_file_beam(stream->output, h2_beam_no_files, NULL); + } + + /* we might see some file buckets in the output, see + * if we have enough handles reserved. */ +- check_data_for(m, stream, 1); ++ mst_check_data_for(m, stream, 1); + return APR_SUCCESS; + } + +-apr_status_t h2_mplx_out_open(h2_mplx *m, int stream_id, h2_bucket_beam *beam) ++apr_status_t h2_mplx_t_out_open(h2_mplx *m, int stream_id, h2_bucket_beam *beam) + { + apr_status_t status; + +@@ -556,14 +562,14 @@ apr_status_t h2_mplx_out_open(h2_mplx *m + status = APR_ECONNABORTED; + } + else { +- status = out_open(m, stream_id, beam); ++ status = t_out_open(m, stream_id, beam); + } + + H2_MPLX_LEAVE(m); + return status; + } + +-static apr_status_t out_close(h2_mplx *m, h2_task *task) ++static apr_status_t s_out_close(h2_mplx *m, h2_task *task) + { + apr_status_t status = APR_SUCCESS; + h2_stream *stream; +@@ -580,17 +586,17 @@ static apr_status_t out_close(h2_mplx *m + return APR_ECONNABORTED; + } + +- ap_log_cerror(APLOG_MARK, APLOG_TRACE2, status, m->c, ++ ap_log_cerror(APLOG_MARK, APLOG_TRACE2, status, task->c, + "h2_mplx(%s): close", task->id); + status = h2_beam_close(task->output.beam); +- h2_beam_log(task->output.beam, m->c, APLOG_TRACE2, "out_close"); +- output_consumed_signal(m, task); +- check_data_for(m, stream, 1); ++ h2_beam_log(task->output.beam, task->c, APLOG_TRACE2, "out_close"); ++ s_output_consumed_signal(m, task); ++ mst_check_data_for(m, stream, 1); + return status; + } + +-apr_status_t h2_mplx_out_trywait(h2_mplx *m, apr_interval_time_t timeout, +- apr_thread_cond_t *iowait) ++apr_status_t h2_mplx_m_out_trywait(h2_mplx *m, apr_interval_time_t timeout, ++ apr_thread_cond_t *iowait) + { + apr_status_t status; + +@@ -599,12 +605,12 @@ apr_status_t h2_mplx_out_trywait(h2_mplx + if (m->aborted) { + status = APR_ECONNABORTED; + } +- else if (h2_mplx_has_master_events(m)) { ++ else if (h2_mplx_m_has_master_events(m)) { + status = APR_SUCCESS; + } + else { +- purge_streams(m, 0); +- h2_ihash_iter(m->streams, report_consumption_iter, m); ++ m_purge_streams(m, 0); ++ h2_ihash_iter(m->streams, m_report_consumption_iter, m); + m->added_output = iowait; + status = apr_thread_cond_timedwait(m->added_output, m->lock, timeout); + if (APLOGctrace2(m->c)) { +@@ -619,7 +625,7 @@ apr_status_t h2_mplx_out_trywait(h2_mplx + return status; + } + +-static void check_data_for(h2_mplx *m, h2_stream *stream, int mplx_is_locked) ++static void mst_check_data_for(h2_mplx *m, h2_stream *stream, int mplx_is_locked) + { + /* If m->lock is already held, we must release during h2_ififo_push() + * which can wait on its not_full condition, causing a deadlock because +@@ -639,7 +645,7 @@ static void check_data_for(h2_mplx *m, h + } + } + +-apr_status_t h2_mplx_reprioritize(h2_mplx *m, h2_stream_pri_cmp *cmp, void *ctx) ++apr_status_t h2_mplx_m_reprioritize(h2_mplx *m, h2_stream_pri_cmp *cmp, void *ctx) + { + apr_status_t status; + +@@ -659,22 +665,22 @@ apr_status_t h2_mplx_reprioritize(h2_mpl + return status; + } + +-static void register_if_needed(h2_mplx *m) ++static void ms_register_if_needed(h2_mplx *m, int from_master) + { + if (!m->aborted && !m->is_registered && !h2_iq_empty(m->q)) { + apr_status_t status = h2_workers_register(m->workers, m); + if (status == APR_SUCCESS) { + m->is_registered = 1; + } +- else { ++ else if (from_master) { + ap_log_cerror(APLOG_MARK, APLOG_ERR, status, m->c, APLOGNO(10021) + "h2_mplx(%ld): register at workers", m->id); + } + } + } + +-apr_status_t h2_mplx_process(h2_mplx *m, struct h2_stream *stream, +- h2_stream_pri_cmp *cmp, void *ctx) ++apr_status_t h2_mplx_m_process(h2_mplx *m, struct h2_stream *stream, ++ h2_stream_pri_cmp *cmp, void *ctx) + { + apr_status_t status; + +@@ -688,13 +694,13 @@ apr_status_t h2_mplx_process(h2_mplx *m, + h2_ihash_add(m->streams, stream); + if (h2_stream_is_ready(stream)) { + /* already have a response */ +- check_data_for(m, stream, 1); ++ mst_check_data_for(m, stream, 1); + ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, m->c, + H2_STRM_MSG(stream, "process, add to readyq")); + } + else { + h2_iq_add(m->q, stream->id, cmp, ctx); +- register_if_needed(m); ++ ms_register_if_needed(m, 1); + ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, m->c, + H2_STRM_MSG(stream, "process, added to q")); + } +@@ -704,7 +710,7 @@ apr_status_t h2_mplx_process(h2_mplx *m, + return status; + } + +-static h2_task *next_stream_task(h2_mplx *m) ++static h2_task *s_next_stream_task(h2_mplx *m) + { + h2_stream *stream; + int sid; +@@ -713,15 +719,15 @@ static h2_task *next_stream_task(h2_mplx + + stream = h2_ihash_get(m->streams, sid); + if (stream) { +- conn_rec *slave, **pslave; ++ conn_rec *secondary, **psecondary; + +- pslave = (conn_rec **)apr_array_pop(m->spare_slaves); +- if (pslave) { +- slave = *pslave; +- slave->aborted = 0; ++ psecondary = (conn_rec **)apr_array_pop(m->spare_secondary); ++ if (psecondary) { ++ secondary = *psecondary; ++ secondary->aborted = 0; + } + else { +- slave = h2_slave_create(m->c, stream->id, m->pool); ++ secondary = h2_secondary_create(m->c, stream->id, m->pool); + } + + if (!stream->task) { +@@ -729,16 +735,16 @@ static h2_task *next_stream_task(h2_mplx + m->max_stream_started = sid; + } + if (stream->input) { +- h2_beam_on_consumed(stream->input, stream_input_ev, +- stream_input_consumed, stream); ++ h2_beam_on_consumed(stream->input, mst_stream_input_ev, ++ m_stream_input_consumed, stream); + } + +- stream->task = h2_task_create(slave, stream->id, ++ stream->task = h2_task_create(secondary, stream->id, + stream->request, m, stream->input, + stream->session->s->timeout, + m->stream_max_mem); + if (!stream->task) { +- ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_ENOMEM, slave, ++ ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_ENOMEM, secondary, + H2_STRM_LOG(APLOGNO(02941), stream, + "create task")); + return NULL; +@@ -753,7 +759,7 @@ static h2_task *next_stream_task(h2_mplx + return NULL; + } + +-apr_status_t h2_mplx_pop_task(h2_mplx *m, h2_task **ptask) ++apr_status_t h2_mplx_s_pop_task(h2_mplx *m, h2_task **ptask) + { + apr_status_t rv = APR_EOF; + +@@ -769,7 +775,7 @@ apr_status_t h2_mplx_pop_task(h2_mplx *m + rv = APR_EOF; + } + else { +- *ptask = next_stream_task(m); ++ *ptask = s_next_stream_task(m); + rv = (*ptask != NULL && !h2_iq_empty(m->q))? APR_EAGAIN : APR_SUCCESS; + } + if (APR_EAGAIN != rv) { +@@ -779,22 +785,22 @@ apr_status_t h2_mplx_pop_task(h2_mplx *m + return rv; + } + +-static void task_done(h2_mplx *m, h2_task *task) ++static void s_task_done(h2_mplx *m, h2_task *task) + { + h2_stream *stream; + +- ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, m->c, ++ ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, task->c, + "h2_mplx(%ld): task(%s) done", m->id, task->id); +- out_close(m, task); ++ s_out_close(m, task); + + task->worker_done = 1; + task->done_at = apr_time_now(); +- ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, m->c, ++ ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, task->c, + "h2_mplx(%s): request done, %f ms elapsed", task->id, + (task->done_at - task->started_at) / 1000.0); + + if (task->c && !task->c->aborted && task->started_at > m->last_mood_change) { +- mplx_be_happy(m); ++ s_mplx_be_happy(m, task); + } + + ap_assert(task->done_done == 0); +@@ -806,60 +812,60 @@ static void task_done(h2_mplx *m, h2_tas + /* reset and schedule again */ + h2_task_redo(task); + h2_iq_add(m->q, stream->id, NULL, NULL); +- ap_log_cerror(APLOG_MARK, APLOG_INFO, 0, m->c, ++ ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, task->c, + H2_STRM_MSG(stream, "redo, added to q")); + } + else { + /* stream not cleaned up, stay around */ + task->done_done = 1; +- ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, m->c, ++ ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, task->c, + H2_STRM_MSG(stream, "task_done, stream open")); + if (stream->input) { + h2_beam_leave(stream->input); + } + + /* more data will not arrive, resume the stream */ +- check_data_for(m, stream, 1); ++ mst_check_data_for(m, stream, 1); + } + } + else if ((stream = h2_ihash_get(m->shold, task->stream_id)) != NULL) { + /* stream is done, was just waiting for this. */ + task->done_done = 1; +- ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, m->c, ++ ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, task->c, + H2_STRM_MSG(stream, "task_done, in hold")); + if (stream->input) { + h2_beam_leave(stream->input); + } +- stream_joined(m, stream); ++ ms_stream_joined(m, stream); + } + else if ((stream = h2_ihash_get(m->spurge, task->stream_id)) != NULL) { +- ap_log_cerror(APLOG_MARK, APLOG_WARNING, 0, m->c, ++ ap_log_cerror(APLOG_MARK, APLOG_WARNING, 0, task->c, + H2_STRM_LOG(APLOGNO(03517), stream, "already in spurge")); + ap_assert("stream should not be in spurge" == NULL); + } + else { +- ap_log_cerror(APLOG_MARK, APLOG_WARNING, 0, m->c, APLOGNO(03518) ++ ap_log_cerror(APLOG_MARK, APLOG_WARNING, 0, task->c, APLOGNO(03518) + "h2_mplx(%s): task_done, stream not found", + task->id); + ap_assert("stream should still be available" == NULL); + } + } + +-void h2_mplx_task_done(h2_mplx *m, h2_task *task, h2_task **ptask) ++void h2_mplx_s_task_done(h2_mplx *m, h2_task *task, h2_task **ptask) + { + H2_MPLX_ENTER_ALWAYS(m); + + --m->tasks_active; +- task_done(m, task); ++ s_task_done(m, task); + + if (m->join_wait) { + apr_thread_cond_signal(m->join_wait); + } + if (ptask) { + /* caller wants another task */ +- *ptask = next_stream_task(m); ++ *ptask = s_next_stream_task(m); + } +- register_if_needed(m); ++ ms_register_if_needed(m, 0); + + H2_MPLX_LEAVE(m); + } +@@ -868,7 +874,7 @@ void h2_mplx_task_done(h2_mplx *m, h2_ta + * h2_mplx DoS protection + ******************************************************************************/ + +-static int timed_out_busy_iter(void *data, void *val) ++static int m_timed_out_busy_iter(void *data, void *val) + { + stream_iter_ctx *ctx = data; + h2_stream *stream = val; +@@ -881,17 +887,17 @@ static int timed_out_busy_iter(void *dat + return 1; + } + +-static h2_stream *get_timed_out_busy_stream(h2_mplx *m) ++static h2_stream *m_get_timed_out_busy_stream(h2_mplx *m) + { + stream_iter_ctx ctx; + ctx.m = m; + ctx.stream = NULL; + ctx.now = apr_time_now(); +- h2_ihash_iter(m->streams, timed_out_busy_iter, &ctx); ++ h2_ihash_iter(m->streams, m_timed_out_busy_iter, &ctx); + return ctx.stream; + } + +-static int latest_repeatable_unsubmitted_iter(void *data, void *val) ++static int m_latest_repeatable_unsubmitted_iter(void *data, void *val) + { + stream_iter_ctx *ctx = data; + h2_stream *stream = val; +@@ -917,7 +923,7 @@ leave: + return 1; + } + +-static apr_status_t assess_task_to_throttle(h2_task **ptask, h2_mplx *m) ++static apr_status_t m_assess_task_to_throttle(h2_task **ptask, h2_mplx *m) + { + stream_iter_ctx ctx; + +@@ -927,7 +933,7 @@ static apr_status_t assess_task_to_throt + ctx.m = m; + ctx.stream = NULL; + ctx.count = 0; +- h2_ihash_iter(m->streams, latest_repeatable_unsubmitted_iter, &ctx); ++ h2_ihash_iter(m->streams, m_latest_repeatable_unsubmitted_iter, &ctx); + if (m->tasks_active - ctx.count > m->limit_active) { + /* we are above the limit of running tasks, accounting for the ones + * already throttled. */ +@@ -936,7 +942,7 @@ static apr_status_t assess_task_to_throt + return APR_EAGAIN; + } + /* above limit, be seeing no candidate for easy throttling */ +- if (get_timed_out_busy_stream(m)) { ++ if (m_get_timed_out_busy_stream(m)) { + /* Too many busy workers, unable to cancel enough streams + * and with a busy, timed out stream, we tell the client + * to go away... */ +@@ -946,7 +952,7 @@ static apr_status_t assess_task_to_throt + return APR_SUCCESS; + } + +-static apr_status_t unschedule_slow_tasks(h2_mplx *m) ++static apr_status_t m_unschedule_slow_tasks(h2_mplx *m) + { + h2_task *task; + apr_status_t rv; +@@ -954,7 +960,7 @@ static apr_status_t unschedule_slow_task + /* Try to get rid of streams that occupy workers. Look for safe requests + * that are repeatable. If none found, fail the connection. + */ +- while (APR_EAGAIN == (rv = assess_task_to_throttle(&task, m))) { ++ while (APR_EAGAIN == (rv = m_assess_task_to_throttle(&task, m))) { + ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, m->c, + "h2_mplx(%s): unschedule, resetting task for redo later", + task->id); +@@ -965,7 +971,7 @@ static apr_status_t unschedule_slow_task + return rv; + } + +-static apr_status_t mplx_be_happy(h2_mplx *m) ++static apr_status_t s_mplx_be_happy(h2_mplx *m, h2_task *task) + { + apr_time_t now; + +@@ -977,14 +983,14 @@ static apr_status_t mplx_be_happy(h2_mpl + m->limit_active = H2MIN(m->limit_active * 2, m->max_active); + m->last_mood_change = now; + m->irritations_since = 0; +- ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, m->c, ++ ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, task->c, + "h2_mplx(%ld): mood update, increasing worker limit to %d", + m->id, m->limit_active); + } + return APR_SUCCESS; + } + +-static apr_status_t mplx_be_annoyed(h2_mplx *m) ++static apr_status_t m_be_annoyed(h2_mplx *m) + { + apr_status_t status = APR_SUCCESS; + apr_time_t now; +@@ -1015,12 +1021,12 @@ static apr_status_t mplx_be_annoyed(h2_m + } + + if (m->tasks_active > m->limit_active) { +- status = unschedule_slow_tasks(m); ++ status = m_unschedule_slow_tasks(m); + } + return status; + } + +-apr_status_t h2_mplx_idle(h2_mplx *m) ++apr_status_t h2_mplx_m_idle(h2_mplx *m) + { + apr_status_t status = APR_SUCCESS; + apr_size_t scount; +@@ -1042,7 +1048,7 @@ apr_status_t h2_mplx_idle(h2_mplx *m) + * of busy workers we allow for this connection until it + * well behaves. + */ +- status = mplx_be_annoyed(m); ++ status = m_be_annoyed(m); + } + else if (!h2_iq_empty(m->q)) { + ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, m->c, +@@ -1072,14 +1078,14 @@ apr_status_t h2_mplx_idle(h2_mplx *m) + h2_beam_is_closed(stream->output), + (long)h2_beam_get_buffered(stream->output)); + h2_ihash_add(m->streams, stream); +- check_data_for(m, stream, 1); ++ mst_check_data_for(m, stream, 1); + stream->out_checked = 1; + status = APR_EAGAIN; + } + } + } + } +- register_if_needed(m); ++ ms_register_if_needed(m, 1); + + H2_MPLX_LEAVE(m); + return status; +@@ -1089,14 +1095,13 @@ apr_status_t h2_mplx_idle(h2_mplx *m) + * mplx master events dispatching + ******************************************************************************/ + +-int h2_mplx_has_master_events(h2_mplx *m) ++int h2_mplx_m_has_master_events(h2_mplx *m) + { + return apr_atomic_read32(&m->event_pending) > 0; + } + +-apr_status_t h2_mplx_dispatch_master_events(h2_mplx *m, +- stream_ev_callback *on_resume, +- void *on_ctx) ++apr_status_t h2_mplx_m_dispatch_master_events(h2_mplx *m, stream_ev_callback *on_resume, ++ void *on_ctx) + { + h2_stream *stream; + int n, id; +@@ -1106,8 +1111,8 @@ apr_status_t h2_mplx_dispatch_master_eve + apr_atomic_set32(&m->event_pending, 0); + + /* update input windows for streams */ +- h2_ihash_iter(m->streams, report_consumption_iter, m); +- purge_streams(m, 1); ++ h2_ihash_iter(m->streams, m_report_consumption_iter, m); ++ m_purge_streams(m, 1); + + n = h2_ififo_count(m->readyq); + while (n > 0 +@@ -1122,13 +1127,13 @@ apr_status_t h2_mplx_dispatch_master_eve + return APR_SUCCESS; + } + +-apr_status_t h2_mplx_keep_active(h2_mplx *m, h2_stream *stream) ++apr_status_t h2_mplx_m_keep_active(h2_mplx *m, h2_stream *stream) + { +- check_data_for(m, stream, 0); ++ mst_check_data_for(m, stream, 0); + return APR_SUCCESS; + } + +-int h2_mplx_awaits_data(h2_mplx *m) ++int h2_mplx_m_awaits_data(h2_mplx *m) + { + int waiting = 1; + +@@ -1145,7 +1150,7 @@ int h2_mplx_awaits_data(h2_mplx *m) + return waiting; + } + +-apr_status_t h2_mplx_client_rst(h2_mplx *m, int stream_id) ++apr_status_t h2_mplx_m_client_rst(h2_mplx *m, int stream_id) + { + h2_stream *stream; + apr_status_t status = APR_SUCCESS; +@@ -1153,7 +1158,7 @@ apr_status_t h2_mplx_client_rst(h2_mplx + H2_MPLX_ENTER_ALWAYS(m); + stream = h2_ihash_get(m->streams, stream_id); + if (stream && stream->task) { +- status = mplx_be_annoyed(m); ++ status = m_be_annoyed(m); + } + H2_MPLX_LEAVE(m); + return status; +--- a/modules/http2/h2_mplx.h ++++ b/modules/http2/h2_mplx.h +@@ -31,8 +31,10 @@ + * queued in the multiplexer. If a task thread tries to write more + * data, it is blocked until space becomes available. + * +- * Writing input is never blocked. In order to use flow control on the input, +- * the mplx can be polled for input data consumption. ++ * Naming Convention: ++ * "h2_mplx_m_" are methods only to be called by the main connection ++ * "h2_mplx_s_" are method only to be called by a secondary connection ++ * "h2_mplx_t_" are method only to be called by a task handler (can be master or secondary) + */ + + struct apr_pool_t; +@@ -88,25 +90,23 @@ struct h2_mplx { + apr_size_t stream_max_mem; + + apr_pool_t *spare_io_pool; +- apr_array_header_t *spare_slaves; /* spare slave connections */ ++ apr_array_header_t *spare_secondary; /* spare secondary connections */ + + struct h2_workers *workers; + }; + +- +- + /******************************************************************************* +- * Object lifecycle and information. ++ * From the main connection processing: h2_mplx_m_* + ******************************************************************************/ + +-apr_status_t h2_mplx_child_init(apr_pool_t *pool, server_rec *s); ++apr_status_t h2_mplx_m_child_init(apr_pool_t *pool, server_rec *s); + + /** + * Create the multiplexer for the given HTTP2 session. + * Implicitly has reference count 1. + */ +-h2_mplx *h2_mplx_create(conn_rec *c, server_rec *s, apr_pool_t *master, +- struct h2_workers *workers); ++h2_mplx *h2_mplx_m_create(conn_rec *c, server_rec *s, apr_pool_t *master, ++ struct h2_workers *workers); + + /** + * Decreases the reference counter of this mplx and waits for it +@@ -116,26 +116,14 @@ h2_mplx *h2_mplx_create(conn_rec *c, ser + * @param m the mplx to be released and destroyed + * @param wait condition var to wait on for ref counter == 0 + */ +-void h2_mplx_release_and_join(h2_mplx *m, struct apr_thread_cond_t *wait); +- +-apr_status_t h2_mplx_pop_task(h2_mplx *m, struct h2_task **ptask); +- +-void h2_mplx_task_done(h2_mplx *m, struct h2_task *task, struct h2_task **ptask); ++void h2_mplx_m_release_and_join(h2_mplx *m, struct apr_thread_cond_t *wait); + + /** + * Shut down the multiplexer gracefully. Will no longer schedule new streams + * but let the ongoing ones finish normally. + * @return the highest stream id being/been processed + */ +-int h2_mplx_shutdown(h2_mplx *m); +- +-int h2_mplx_is_busy(h2_mplx *m); +- +-/******************************************************************************* +- * IO lifetime of streams. +- ******************************************************************************/ +- +-struct h2_stream *h2_mplx_stream_get(h2_mplx *m, int id); ++int h2_mplx_m_shutdown(h2_mplx *m); + + /** + * Notifies mplx that a stream has been completely handled on the main +@@ -144,20 +132,16 @@ struct h2_stream *h2_mplx_stream_get(h2_ + * @param m the mplx itself + * @param stream the stream ready for cleanup + */ +-apr_status_t h2_mplx_stream_cleanup(h2_mplx *m, struct h2_stream *stream); ++apr_status_t h2_mplx_m_stream_cleanup(h2_mplx *m, struct h2_stream *stream); + + /** + * Waits on output data from any stream in this session to become available. + * Returns APR_TIMEUP if no data arrived in the given time. + */ +-apr_status_t h2_mplx_out_trywait(h2_mplx *m, apr_interval_time_t timeout, +- struct apr_thread_cond_t *iowait); +- +-apr_status_t h2_mplx_keep_active(h2_mplx *m, struct h2_stream *stream); ++apr_status_t h2_mplx_m_out_trywait(h2_mplx *m, apr_interval_time_t timeout, ++ struct apr_thread_cond_t *iowait); + +-/******************************************************************************* +- * Stream processing. +- ******************************************************************************/ ++apr_status_t h2_mplx_m_keep_active(h2_mplx *m, struct h2_stream *stream); + + /** + * Process a stream request. +@@ -168,8 +152,8 @@ apr_status_t h2_mplx_keep_active(h2_mplx + * @param cmp the stream priority compare function + * @param ctx context data for the compare function + */ +-apr_status_t h2_mplx_process(h2_mplx *m, struct h2_stream *stream, +- h2_stream_pri_cmp *cmp, void *ctx); ++apr_status_t h2_mplx_m_process(h2_mplx *m, struct h2_stream *stream, ++ h2_stream_pri_cmp *cmp, void *ctx); + + /** + * Stream priorities have changed, reschedule pending requests. +@@ -178,7 +162,7 @@ apr_status_t h2_mplx_process(h2_mplx *m, + * @param cmp the stream priority compare function + * @param ctx context data for the compare function + */ +-apr_status_t h2_mplx_reprioritize(h2_mplx *m, h2_stream_pri_cmp *cmp, void *ctx); ++apr_status_t h2_mplx_m_reprioritize(h2_mplx *m, h2_stream_pri_cmp *cmp, void *ctx); + + typedef apr_status_t stream_ev_callback(void *ctx, struct h2_stream *stream); + +@@ -186,7 +170,7 @@ typedef apr_status_t stream_ev_callback( + * Check if the multiplexer has events for the master connection pending. + * @return != 0 iff there are events pending + */ +-int h2_mplx_has_master_events(h2_mplx *m); ++int h2_mplx_m_has_master_events(h2_mplx *m); + + /** + * Dispatch events for the master connection, such as +@@ -194,108 +178,46 @@ int h2_mplx_has_master_events(h2_mplx *m + * @param on_resume new output data has arrived for a suspended stream + * @param ctx user supplied argument to invocation. + */ +-apr_status_t h2_mplx_dispatch_master_events(h2_mplx *m, +- stream_ev_callback *on_resume, +- void *ctx); ++apr_status_t h2_mplx_m_dispatch_master_events(h2_mplx *m, stream_ev_callback *on_resume, ++ void *ctx); + +-int h2_mplx_awaits_data(h2_mplx *m); ++int h2_mplx_m_awaits_data(h2_mplx *m); + + typedef int h2_mplx_stream_cb(struct h2_stream *s, void *ctx); + +-apr_status_t h2_mplx_stream_do(h2_mplx *m, h2_mplx_stream_cb *cb, void *ctx); ++apr_status_t h2_mplx_m_stream_do(h2_mplx *m, h2_mplx_stream_cb *cb, void *ctx); + +-apr_status_t h2_mplx_client_rst(h2_mplx *m, int stream_id); +- +-/******************************************************************************* +- * Output handling of streams. +- ******************************************************************************/ ++apr_status_t h2_mplx_m_client_rst(h2_mplx *m, int stream_id); + + /** +- * Opens the output for the given stream with the specified response. ++ * Master connection has entered idle mode. ++ * @param m the mplx instance of the master connection ++ * @return != SUCCESS iff connection should be terminated + */ +-apr_status_t h2_mplx_out_open(h2_mplx *mplx, int stream_id, +- struct h2_bucket_beam *beam); ++apr_status_t h2_mplx_m_idle(h2_mplx *m); + + /******************************************************************************* +- * h2_mplx list Manipulation. ++ * From a secondary connection processing: h2_mplx_s_* + ******************************************************************************/ ++apr_status_t h2_mplx_s_pop_task(h2_mplx *m, struct h2_task **ptask); ++void h2_mplx_s_task_done(h2_mplx *m, struct h2_task *task, struct h2_task **ptask); + +-/** +- * The magic pointer value that indicates the head of a h2_mplx list +- * @param b The mplx list +- * @return The magic pointer value +- */ +-#define H2_MPLX_LIST_SENTINEL(b) APR_RING_SENTINEL((b), h2_mplx, link) +- +-/** +- * Determine if the mplx list is empty +- * @param b The list to check +- * @return true or false +- */ +-#define H2_MPLX_LIST_EMPTY(b) APR_RING_EMPTY((b), h2_mplx, link) +- +-/** +- * Return the first mplx in a list +- * @param b The list to query +- * @return The first mplx in the list +- */ +-#define H2_MPLX_LIST_FIRST(b) APR_RING_FIRST(b) +- +-/** +- * Return the last mplx in a list +- * @param b The list to query +- * @return The last mplx int he list +- */ +-#define H2_MPLX_LIST_LAST(b) APR_RING_LAST(b) +- +-/** +- * Insert a single mplx at the front of a list +- * @param b The list to add to +- * @param e The mplx to insert +- */ +-#define H2_MPLX_LIST_INSERT_HEAD(b, e) do { \ +-h2_mplx *ap__b = (e); \ +-APR_RING_INSERT_HEAD((b), ap__b, h2_mplx, link); \ +-} while (0) +- +-/** +- * Insert a single mplx at the end of a list +- * @param b The list to add to +- * @param e The mplx to insert +- */ +-#define H2_MPLX_LIST_INSERT_TAIL(b, e) do { \ +-h2_mplx *ap__b = (e); \ +-APR_RING_INSERT_TAIL((b), ap__b, h2_mplx, link); \ +-} while (0) ++/******************************************************************************* ++ * From a h2_task owner: h2_mplx_s_* ++ * (a task is transfered from master to secondary connection and back in ++ * its normal lifetime). ++ ******************************************************************************/ + + /** +- * Get the next mplx in the list +- * @param e The current mplx +- * @return The next mplx +- */ +-#define H2_MPLX_NEXT(e) APR_RING_NEXT((e), link) +-/** +- * Get the previous mplx in the list +- * @param e The current mplx +- * @return The previous mplx ++ * Opens the output for the given stream with the specified response. + */ +-#define H2_MPLX_PREV(e) APR_RING_PREV((e), link) ++apr_status_t h2_mplx_t_out_open(h2_mplx *mplx, int stream_id, ++ struct h2_bucket_beam *beam); + + /** +- * Remove a mplx from its list +- * @param e The mplx to remove ++ * Get the stream that belongs to the given task. + */ +-#define H2_MPLX_REMOVE(e) APR_RING_REMOVE((e), link) +- +-/******************************************************************************* +- * h2_mplx DoS protection +- ******************************************************************************/ ++struct h2_stream *h2_mplx_t_stream_get(h2_mplx *m, struct h2_task *task); + +-/** +- * Master connection has entered idle mode. +- * @param m the mplx instance of the master connection +- * @return != SUCCESS iff connection should be terminated +- */ +-apr_status_t h2_mplx_idle(h2_mplx *m); + + #endif /* defined(__mod_h2__h2_mplx__) */ +--- a/modules/http2/h2_request.c ++++ b/modules/http2/h2_request.c +@@ -288,6 +288,9 @@ request_rec *h2_request_create_rec(const + if (r->method_number == M_GET && r->method[0] == 'H') { + r->header_only = 1; + } ++ r->the_request = apr_psprintf(r->pool, "%s %s HTTP/2.0", ++ req->method, req->path ? req->path : ""); ++ r->headers_in = apr_table_clone(r->pool, req->headers); + + rpath = (req->path ? req->path : ""); + ap_parse_uri(r, rpath); +@@ -304,7 +307,9 @@ request_rec *h2_request_create_rec(const + */ + r->hostname = NULL; + ap_update_vhost_from_headers(r); +- ++ r->protocol = "HTTP/2.0"; ++ r->proto_num = HTTP_VERSION(2, 0); ++ + /* we may have switched to another server */ + r->per_dir_config = r->server->lookup_defaults; + +--- a/modules/http2/h2_session.c ++++ b/modules/http2/h2_session.c +@@ -106,7 +106,7 @@ static int rst_unprocessed_stream(h2_str + + static void cleanup_unprocessed_streams(h2_session *session) + { +- h2_mplx_stream_do(session->mplx, rst_unprocessed_stream, session); ++ h2_mplx_m_stream_do(session->mplx, rst_unprocessed_stream, session); + } + + static h2_stream *h2_session_open_stream(h2_session *session, int stream_id, +@@ -397,7 +397,7 @@ static int on_frame_recv_cb(nghttp2_sess + else { + /* A stream reset on a request it sent us. Could happen in a browser + * when the user navigates away or cancels loading - maybe. */ +- h2_mplx_client_rst(session->mplx, frame->hd.stream_id); ++ h2_mplx_m_client_rst(session->mplx, frame->hd.stream_id); + ++session->streams_reset; + } + break; +@@ -467,7 +467,7 @@ static int on_frame_recv_cb(nghttp2_sess + } + + static int h2_session_continue_data(h2_session *session) { +- if (h2_mplx_has_master_events(session->mplx)) { ++ if (h2_mplx_m_has_master_events(session->mplx)) { + return 0; + } + if (h2_conn_io_needs_flush(&session->io)) { +@@ -729,7 +729,7 @@ static apr_status_t h2_session_shutdown( + * Remove all streams greater than this number without submitting + * a RST_STREAM frame, since that should be clear from the GOAWAY + * we send. */ +- session->local.accepted_max = h2_mplx_shutdown(session->mplx); ++ session->local.accepted_max = h2_mplx_m_shutdown(session->mplx); + session->local.error = error; + } + else { +@@ -779,7 +779,7 @@ static apr_status_t session_cleanup(h2_s + } + + transit(session, trigger, H2_SESSION_ST_CLEANUP); +- h2_mplx_release_and_join(session->mplx, session->iowait); ++ h2_mplx_m_release_and_join(session->mplx, session->iowait); + session->mplx = NULL; + + ap_assert(session->ngh2); +@@ -800,7 +800,7 @@ static apr_status_t session_pool_cleanup + /* if the session is still there, now is the last chance + * to perform cleanup. Normally, cleanup should have happened + * earlier in the connection pre_close. Main reason is that +- * any ongoing requests on slave connections might still access ++ * any ongoing requests on secondary connections might still access + * data which has, at this time, already been freed. An example + * is mod_ssl that uses request hooks. */ + ap_log_cerror(APLOG_MARK, APLOG_WARNING, 0, c, +@@ -893,7 +893,7 @@ apr_status_t h2_session_create(h2_sessio + session->monitor->on_state_event = on_stream_state_event; + session->monitor->on_event = on_stream_event; + +- session->mplx = h2_mplx_create(c, s, session->pool, workers); ++ session->mplx = h2_mplx_m_create(c, s, session->pool, workers); + + /* connection input filter that feeds the session */ + session->cin = h2_filter_cin_create(session); +@@ -1552,7 +1552,7 @@ static void h2_session_in_flush(h2_sessi + if (stream) { + ap_assert(!stream->scheduled); + if (h2_stream_prep_processing(stream) == APR_SUCCESS) { +- h2_mplx_process(session->mplx, stream, stream_pri_cmp, session); ++ h2_mplx_m_process(session->mplx, stream, stream_pri_cmp, session); + } + else { + h2_stream_rst(stream, H2_ERR_INTERNAL_ERROR); +@@ -1824,7 +1824,7 @@ static void h2_session_ev_no_io(h2_sessi + session->open_streams); + h2_conn_io_flush(&session->io); + if (session->open_streams > 0) { +- if (h2_mplx_awaits_data(session->mplx)) { ++ if (h2_mplx_m_awaits_data(session->mplx)) { + /* waiting for at least one stream to produce data */ + transit(session, "no io", H2_SESSION_ST_WAIT); + } +@@ -1983,7 +1983,7 @@ static void on_stream_state_enter(void * + break; + case H2_SS_CLEANUP: + nghttp2_session_set_stream_user_data(session->ngh2, stream->id, NULL); +- h2_mplx_stream_cleanup(session->mplx, stream); ++ h2_mplx_m_stream_cleanup(session->mplx, stream); + break; + default: + break; +@@ -2073,7 +2073,7 @@ static void dispatch_event(h2_session *s + static apr_status_t dispatch_master(h2_session *session) { + apr_status_t status; + +- status = h2_mplx_dispatch_master_events(session->mplx, ++ status = h2_mplx_m_dispatch_master_events(session->mplx, + on_stream_resume, session); + if (status == APR_EAGAIN) { + ap_log_cerror(APLOG_MARK, APLOG_TRACE3, status, session->c, +@@ -2175,7 +2175,7 @@ apr_status_t h2_session_process(h2_sessi + session->have_read = 1; + } + else if (APR_STATUS_IS_EAGAIN(status) || APR_STATUS_IS_TIMEUP(status)) { +- status = h2_mplx_idle(session->mplx); ++ status = h2_mplx_m_idle(session->mplx); + if (status == APR_EAGAIN) { + break; + } +@@ -2205,7 +2205,7 @@ apr_status_t h2_session_process(h2_sessi + /* We wait in smaller increments, using a 1 second timeout. + * That gives us the chance to check for MPMQ_STOPPING often. + */ +- status = h2_mplx_idle(session->mplx); ++ status = h2_mplx_m_idle(session->mplx); + if (status == APR_EAGAIN) { + break; + } +@@ -2319,7 +2319,7 @@ apr_status_t h2_session_process(h2_sessi + "h2_session: wait for data, %ld micros", + (long)session->wait_us); + } +- status = h2_mplx_out_trywait(session->mplx, session->wait_us, ++ status = h2_mplx_m_out_trywait(session->mplx, session->wait_us, + session->iowait); + if (status == APR_SUCCESS) { + session->wait_us = 0; +@@ -2356,7 +2356,7 @@ apr_status_t h2_session_process(h2_sessi + dispatch_event(session, H2_SESSION_EV_NGH2_DONE, 0, NULL); + } + if (session->reprioritize) { +- h2_mplx_reprioritize(session->mplx, stream_pri_cmp, session); ++ h2_mplx_m_reprioritize(session->mplx, stream_pri_cmp, session); + session->reprioritize = 0; + } + } +--- a/modules/http2/h2_session.h ++++ b/modules/http2/h2_session.h +@@ -132,7 +132,7 @@ typedef struct h2_session { + const char *last_status_msg; /* the one already reported */ + + struct h2_iqueue *in_pending; /* all streams with input pending */ +- struct h2_iqueue *in_process; /* all streams ready for processing on slave */ ++ struct h2_iqueue *in_process; /* all streams ready for processing on a secondary */ + + } h2_session; + +--- a/modules/http2/h2_stream.c ++++ b/modules/http2/h2_stream.c +@@ -903,7 +903,7 @@ apr_status_t h2_stream_out_prepare(h2_st + + if (status == APR_EAGAIN) { + /* TODO: ugly, someone needs to retrieve the response first */ +- h2_mplx_keep_active(stream->session->mplx, stream); ++ h2_mplx_m_keep_active(stream->session->mplx, stream); + ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c, + H2_STRM_MSG(stream, "prep, response eagain")); + return status; +--- a/modules/http2/h2_task.c ++++ b/modules/http2/h2_task.c +@@ -86,7 +86,7 @@ static apr_status_t open_output(h2_task + task->request->authority, + task->request->path); + task->output.opened = 1; +- return h2_mplx_out_open(task->mplx, task->stream_id, task->output.beam); ++ return h2_mplx_t_out_open(task->mplx, task->stream_id, task->output.beam); + } + + static apr_status_t send_out(h2_task *task, apr_bucket_brigade* bb, int block) +@@ -126,8 +126,8 @@ static apr_status_t send_out(h2_task *ta + * request_rec out filter chain) into the h2_mplx for further sending + * on the master connection. + */ +-static apr_status_t slave_out(h2_task *task, ap_filter_t* f, +- apr_bucket_brigade* bb) ++static apr_status_t secondary_out(h2_task *task, ap_filter_t* f, ++ apr_bucket_brigade* bb) + { + apr_bucket *b; + apr_status_t rv = APR_SUCCESS; +@@ -175,7 +175,7 @@ send: + if (APR_SUCCESS == rv) { + /* could not write all, buffer the rest */ + ap_log_cerror(APLOG_MARK, APLOG_DEBUG, rv, task->c, APLOGNO(03405) +- "h2_slave_out(%s): saving brigade", task->id); ++ "h2_secondary_out(%s): saving brigade", task->id); + ap_assert(NULL); + rv = ap_save_brigade(f, &task->output.bb, &bb, task->pool); + flush = 1; +@@ -189,7 +189,7 @@ send: + } + out: + ap_log_cerror(APLOG_MARK, APLOG_TRACE2, rv, task->c, +- "h2_slave_out(%s): slave_out leave", task->id); ++ "h2_secondary_out(%s): secondary_out leave", task->id); + return rv; + } + +@@ -202,14 +202,14 @@ static apr_status_t output_finish(h2_tas + } + + /******************************************************************************* +- * task slave connection filters ++ * task secondary connection filters + ******************************************************************************/ + +-static apr_status_t h2_filter_slave_in(ap_filter_t* f, +- apr_bucket_brigade* bb, +- ap_input_mode_t mode, +- apr_read_type_e block, +- apr_off_t readbytes) ++static apr_status_t h2_filter_secondary_in(ap_filter_t* f, ++ apr_bucket_brigade* bb, ++ ap_input_mode_t mode, ++ apr_read_type_e block, ++ apr_off_t readbytes) + { + h2_task *task; + apr_status_t status = APR_SUCCESS; +@@ -224,7 +224,7 @@ static apr_status_t h2_filter_slave_in(a + + if (trace1) { + ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, f->c, +- "h2_slave_in(%s): read, mode=%d, block=%d, readbytes=%ld", ++ "h2_secondary_in(%s): read, mode=%d, block=%d, readbytes=%ld", + task->id, mode, block, (long)readbytes); + } + +@@ -254,7 +254,7 @@ static apr_status_t h2_filter_slave_in(a + /* Get more input data for our request. */ + if (trace1) { + ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, f->c, +- "h2_slave_in(%s): get more data from mplx, block=%d, " ++ "h2_secondary_in(%s): get more data from mplx, block=%d, " + "readbytes=%ld", task->id, block, (long)readbytes); + } + if (task->input.beam) { +@@ -267,7 +267,7 @@ static apr_status_t h2_filter_slave_in(a + + if (trace1) { + ap_log_cerror(APLOG_MARK, APLOG_TRACE2, status, f->c, +- "h2_slave_in(%s): read returned", task->id); ++ "h2_secondary_in(%s): read returned", task->id); + } + if (APR_STATUS_IS_EAGAIN(status) + && (mode == AP_MODE_GETLINE || block == APR_BLOCK_READ)) { +@@ -306,7 +306,7 @@ static apr_status_t h2_filter_slave_in(a + if (APR_BRIGADE_EMPTY(task->input.bb)) { + if (trace1) { + ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, f->c, +- "h2_slave_in(%s): no data", task->id); ++ "h2_secondary_in(%s): no data", task->id); + } + return (block == APR_NONBLOCK_READ)? APR_EAGAIN : APR_EOF; + } +@@ -334,7 +334,7 @@ static apr_status_t h2_filter_slave_in(a + buffer[len] = 0; + if (trace1) { + ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, f->c, +- "h2_slave_in(%s): getline: %s", ++ "h2_secondary_in(%s): getline: %s", + task->id, buffer); + } + } +@@ -344,7 +344,7 @@ static apr_status_t h2_filter_slave_in(a + * to support it. Seems to work. */ + ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_ENOTIMPL, f->c, + APLOGNO(03472) +- "h2_slave_in(%s), unsupported READ mode %d", ++ "h2_secondary_in(%s), unsupported READ mode %d", + task->id, mode); + status = APR_ENOTIMPL; + } +@@ -352,19 +352,19 @@ static apr_status_t h2_filter_slave_in(a + if (trace1) { + apr_brigade_length(bb, 0, &bblen); + ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, f->c, +- "h2_slave_in(%s): %ld data bytes", task->id, (long)bblen); ++ "h2_secondary_in(%s): %ld data bytes", task->id, (long)bblen); + } + return status; + } + +-static apr_status_t h2_filter_slave_output(ap_filter_t* filter, +- apr_bucket_brigade* brigade) ++static apr_status_t h2_filter_secondary_output(ap_filter_t* filter, ++ apr_bucket_brigade* brigade) + { + h2_task *task = h2_ctx_get_task(filter->c); + apr_status_t status; + + ap_assert(task); +- status = slave_out(task, filter, brigade); ++ status = secondary_out(task, filter, brigade); + if (status != APR_SUCCESS) { + h2_task_rst(task, H2_ERR_INTERNAL_ERROR); + } +@@ -456,9 +456,9 @@ void h2_task_register_hooks(void) + ap_hook_process_connection(h2_task_process_conn, + NULL, NULL, APR_HOOK_FIRST); + +- ap_register_input_filter("H2_SLAVE_IN", h2_filter_slave_in, ++ ap_register_input_filter("H2_SECONDARY_IN", h2_filter_secondary_in, + NULL, AP_FTYPE_NETWORK); +- ap_register_output_filter("H2_SLAVE_OUT", h2_filter_slave_output, ++ ap_register_output_filter("H2_SECONDARY_OUT", h2_filter_secondary_output, + NULL, AP_FTYPE_NETWORK); + ap_register_output_filter("H2_PARSE_H1", h2_filter_parse_h1, + NULL, AP_FTYPE_NETWORK); +@@ -492,15 +492,15 @@ static int h2_task_pre_conn(conn_rec* c, + (void)arg; + if (ctx->task) { + ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, c, +- "h2_slave(%s), pre_connection, adding filters", c->log_id); +- ap_add_input_filter("H2_SLAVE_IN", NULL, NULL, c); ++ "h2_secondary(%s), pre_connection, adding filters", c->log_id); ++ ap_add_input_filter("H2_SECONDARY_IN", NULL, NULL, c); + ap_add_output_filter("H2_PARSE_H1", NULL, NULL, c); +- ap_add_output_filter("H2_SLAVE_OUT", NULL, NULL, c); ++ ap_add_output_filter("H2_SECONDARY_OUT", NULL, NULL, c); + } + return OK; + } + +-h2_task *h2_task_create(conn_rec *slave, int stream_id, ++h2_task *h2_task_create(conn_rec *secondary, int stream_id, + const h2_request *req, h2_mplx *m, + h2_bucket_beam *input, + apr_interval_time_t timeout, +@@ -509,10 +509,10 @@ h2_task *h2_task_create(conn_rec *slave, + apr_pool_t *pool; + h2_task *task; + +- ap_assert(slave); ++ ap_assert(secondary); + ap_assert(req); + +- apr_pool_create(&pool, slave->pool); ++ apr_pool_create(&pool, secondary->pool); + apr_pool_tag(pool, "h2_task"); + task = apr_pcalloc(pool, sizeof(h2_task)); + if (task == NULL) { +@@ -520,7 +520,7 @@ h2_task *h2_task_create(conn_rec *slave, + } + task->id = "000"; + task->stream_id = stream_id; +- task->c = slave; ++ task->c = secondary; + task->mplx = m; + task->pool = pool; + task->request = req; +@@ -557,7 +557,7 @@ apr_status_t h2_task_do(h2_task *task, a + if (c->master) { + /* Each conn_rec->id is supposed to be unique at a point in time. Since + * some modules (and maybe external code) uses this id as an identifier +- * for the request_rec they handle, it needs to be unique for slave ++ * for the request_rec they handle, it needs to be unique for secondary + * connections also. + * The connection id is generated by the MPM and most MPMs use the formula + * id := (child_num * max_threads) + thread_num +@@ -600,7 +600,7 @@ apr_status_t h2_task_do(h2_task *task, a + h2_ctx_create_for(c, task); + apr_table_setn(c->notes, H2_TASK_ID_NOTE, task->id); + +- h2_slave_run_pre_connection(c, ap_get_conn_socket(c)); ++ h2_secondary_run_pre_connection(c, ap_get_conn_socket(c)); + + task->input.bb = apr_brigade_create(task->pool, c->bucket_alloc); + if (task->request->serialize) { +@@ -708,7 +708,7 @@ static int h2_task_process_conn(conn_rec + } + else { + ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c, +- "slave_conn(%ld): has no task", c->id); ++ "secondary_conn(%ld): has no task", c->id); + } + return DECLINED; + } +--- a/modules/http2/h2_task.h ++++ b/modules/http2/h2_task.h +@@ -90,7 +90,7 @@ struct h2_task { + apr_bucket *eor; + }; + +-h2_task *h2_task_create(conn_rec *slave, int stream_id, ++h2_task *h2_task_create(conn_rec *secondary, int stream_id, + const h2_request *req, struct h2_mplx *m, + struct h2_bucket_beam *input, + apr_interval_time_t timeout, +--- a/modules/http2/h2_workers.c ++++ b/modules/http2/h2_workers.c +@@ -155,7 +155,7 @@ static apr_status_t slot_pull_task(h2_sl + { + apr_status_t rv; + +- rv = h2_mplx_pop_task(m, &slot->task); ++ rv = h2_mplx_s_pop_task(m, &slot->task); + if (slot->task) { + /* Ok, we got something to give back to the worker for execution. + * If we still have idle workers, we let the worker be sticky, +@@ -234,10 +234,10 @@ static void* APR_THREAD_FUNC slot_run(ap + * mplx the opportunity to give us back a new task right away. + */ + if (!slot->aborted && (--slot->sticks > 0)) { +- h2_mplx_task_done(slot->task->mplx, slot->task, &slot->task); ++ h2_mplx_s_task_done(slot->task->mplx, slot->task, &slot->task); + } + else { +- h2_mplx_task_done(slot->task->mplx, slot->task, NULL); ++ h2_mplx_s_task_done(slot->task->mplx, slot->task, NULL); + slot->task = NULL; + } + } +--- a/modules/http2/mod_http2.c ++++ b/modules/http2/mod_http2.c +@@ -237,7 +237,7 @@ static const char *val_H2_PUSH(apr_pool_ + if (ctx) { + if (r) { + if (ctx->task) { +- h2_stream *stream = h2_mplx_stream_get(ctx->task->mplx, ctx->task->stream_id); ++ h2_stream *stream = h2_mplx_t_stream_get(ctx->task->mplx, ctx->task); + if (stream && stream->push_policy != H2_PUSH_NONE) { + return "on"; + } +@@ -271,7 +271,7 @@ static const char *val_H2_PUSHED_ON(apr_ + { + if (ctx) { + if (ctx->task && !H2_STREAM_CLIENT_INITIATED(ctx->task->stream_id)) { +- h2_stream *stream = h2_mplx_stream_get(ctx->task->mplx, ctx->task->stream_id); ++ h2_stream *stream = h2_mplx_t_stream_get(ctx->task->mplx, ctx->task); + if (stream) { + return apr_itoa(p, stream->initiated_on); + } diff -Nru apache2-2.4.29/debian/patches/CVE-2020-11993-pre1.patch apache2-2.4.29/debian/patches/CVE-2020-11993-pre1.patch --- apache2-2.4.29/debian/patches/CVE-2020-11993-pre1.patch 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.29/debian/patches/CVE-2020-11993-pre1.patch 2020-08-12 21:32:38.000000000 +0000 @@ -0,0 +1,406 @@ +Backport of: + +From 3060a9dd4d160af2e82829ce801a3a89127e36a1 Mon Sep 17 00:00:00 2001 +From: Jim Jagielski +Date: Thu, 30 Jan 2020 15:14:40 +0000 +Subject: [PATCH] Merge r1871810 from trunk: + + *) mod_http2: Fixed rare cases where a h2 worker could deadlock the main connection. + + +Submitted by: icing +Reviewed by: icing, jim, steffenal + + +git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x@1873368 13f79535-47bb-0310-9956-ffa450edef68 +--- + CHANGES | 3 ++ + STATUS | 4 -- + modules/http2/h2_mplx.c | 54 ++++++++++++-------- + modules/http2/h2_session.c | 2 +- + modules/http2/h2_util.c | 89 ++++++++++----------------------- + modules/http2/h2_util.h | 2 - + modules/http2/h2_version.h | 4 +- + modules/http2/h2_workers.c | 1 - + modules/http2/mod_proxy_http2.c | 8 +++ + 9 files changed, 72 insertions(+), 95 deletions(-) + +#diff --git a/CHANGES b/CHANGES +#index ded0ecce34c..e12ef389501 100644 +#--- a/CHANGES +#+++ b/CHANGES +#@@ -5,6 +5,9 @@ Changes with Apache 2.4.42 +# r:notes_table, r:subprocess_env_table as read-only native table alternatives +# that can be iterated over. [Eric Covener] +# +#+ *) mod_http2: Fixed rare cases where a h2 worker could deadlock the main connection. +#+ [Yann Ylavic, Stefan Eissing] +#+ +# *) mod_lua: Accept nil assignments to the exposed tables (r.subprocess_env, +# r.headers_out, etc) to remove the key from the table. PR63971. +# [Eric Covener] +#diff --git a/STATUS b/STATUS +#index 991ed406b23..a610953ed89 100644 +#--- a/STATUS +#+++ b/STATUS +#@@ -132,10 +132,6 @@ RELEASE SHOWSTOPPERS: +# PATCHES ACCEPTED TO BACKPORT FROM TRUNK: +# [ start all new proposals below, under PATCHES PROPOSED. ] +# +#- *) mod_http2: Fixed rare cases where a h2 worker could deadlock the main connection. +#- trunk patch: http://svn.apache.org/r1871810 +#- 2.4.x patch: svn merge -c 1871810 ^/httpd/httpd/trunk +#- +1: icing, jim, steffenal(tested with 1.15.5-git) +# +# PATCHES PROPOSED TO BACKPORT FROM TRUNK: +# [ New proposals should be added at the end of the list ] +--- a/modules/http2/h2_mplx.c ++++ b/modules/http2/h2_mplx.c +@@ -75,13 +75,13 @@ apr_status_t h2_mplx_child_init(apr_pool + #define H2_MPLX_ENTER_ALWAYS(m) \ + apr_thread_mutex_lock(m->lock) + +-#define H2_MPLX_ENTER_MAYBE(m, lock) \ +- if (lock) apr_thread_mutex_lock(m->lock) ++#define H2_MPLX_ENTER_MAYBE(m, dolock) \ ++ if (dolock) apr_thread_mutex_lock(m->lock) + +-#define H2_MPLX_LEAVE_MAYBE(m, lock) \ +- if (lock) apr_thread_mutex_unlock(m->lock) ++#define H2_MPLX_LEAVE_MAYBE(m, dolock) \ ++ if (dolock) apr_thread_mutex_unlock(m->lock) + +-static void check_data_for(h2_mplx *m, h2_stream *stream, int lock); ++static void check_data_for(h2_mplx *m, h2_stream *stream, int mplx_is_locked); + + static void stream_output_consumed(void *ctx, + h2_bucket_beam *beam, apr_off_t length) +@@ -104,6 +104,7 @@ static void stream_joined(h2_mplx *m, h2 + { + ap_assert(!h2_task_has_started(stream->task) || stream->task->worker_done); + ++ h2_ififo_remove(m->readyq, stream->id); + h2_ihash_remove(m->shold, stream->id); + h2_ihash_add(m->spurge, stream); + } +@@ -125,14 +126,16 @@ static void stream_cleanup(h2_mplx *m, h + + h2_ihash_remove(m->streams, stream->id); + h2_iq_remove(m->q, stream->id); +- h2_ififo_remove(m->readyq, stream->id); +- h2_ihash_add(m->shold, stream); + + if (!h2_task_has_started(stream->task) || stream->task->done_done) { + stream_joined(m, stream); + } +- else if (stream->task) { +- stream->task->c->aborted = 1; ++ else { ++ h2_ififo_remove(m->readyq, stream->id); ++ h2_ihash_add(m->shold, stream); ++ if (stream->task) { ++ stream->task->c->aborted = 1; ++ } + } + } + +@@ -509,12 +512,11 @@ static void output_produced(void *ctx, h + h2_stream *stream = ctx; + h2_mplx *m = stream->session->mplx; + +- check_data_for(m, stream, 1); ++ check_data_for(m, stream, 0); + } + + static apr_status_t out_open(h2_mplx *m, int stream_id, h2_bucket_beam *beam) + { +- apr_status_t status = APR_SUCCESS; + h2_stream *stream = h2_ihash_get(m->streams, stream_id); + + if (!stream || !stream->task || m->aborted) { +@@ -528,7 +530,7 @@ static apr_status_t out_open(h2_mplx *m, + h2_beam_log(beam, m->c, APLOG_TRACE2, "out_open"); + } + else { +- ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, m->c, ++ ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, m->c, + "h2_mplx(%s): out open", stream->task->id); + } + +@@ -540,8 +542,8 @@ static apr_status_t out_open(h2_mplx *m, + + /* we might see some file buckets in the output, see + * if we have enough handles reserved. */ +- check_data_for(m, stream, 0); +- return status; ++ check_data_for(m, stream, 1); ++ return APR_SUCCESS; + } + + apr_status_t h2_mplx_out_open(h2_mplx *m, int stream_id, h2_bucket_beam *beam) +@@ -583,7 +585,7 @@ static apr_status_t out_close(h2_mplx *m + status = h2_beam_close(task->output.beam); + h2_beam_log(task->output.beam, m->c, APLOG_TRACE2, "out_close"); + output_consumed_signal(m, task); +- check_data_for(m, stream, 0); ++ check_data_for(m, stream, 1); + return status; + } + +@@ -617,15 +619,23 @@ apr_status_t h2_mplx_out_trywait(h2_mplx + return status; + } + +-static void check_data_for(h2_mplx *m, h2_stream *stream, int lock) ++static void check_data_for(h2_mplx *m, h2_stream *stream, int mplx_is_locked) + { ++ /* If m->lock is already held, we must release during h2_ififo_push() ++ * which can wait on its not_full condition, causing a deadlock because ++ * no one would then be able to acquire m->lock to empty the fifo. ++ */ ++ H2_MPLX_LEAVE_MAYBE(m, mplx_is_locked); + if (h2_ififo_push(m->readyq, stream->id) == APR_SUCCESS) { ++ H2_MPLX_ENTER_ALWAYS(m); + apr_atomic_set32(&m->event_pending, 1); +- H2_MPLX_ENTER_MAYBE(m, lock); + if (m->added_output) { + apr_thread_cond_signal(m->added_output); + } +- H2_MPLX_LEAVE_MAYBE(m, lock); ++ H2_MPLX_LEAVE_MAYBE(m, !mplx_is_locked); ++ } ++ else { ++ H2_MPLX_ENTER_MAYBE(m, mplx_is_locked); + } + } + +@@ -678,7 +688,7 @@ apr_status_t h2_mplx_process(h2_mplx *m, + h2_ihash_add(m->streams, stream); + if (h2_stream_is_ready(stream)) { + /* already have a response */ +- check_data_for(m, stream, 0); ++ check_data_for(m, stream, 1); + ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, m->c, + H2_STRM_MSG(stream, "process, add to readyq")); + } +@@ -809,7 +819,7 @@ static void task_done(h2_mplx *m, h2_tas + } + + /* more data will not arrive, resume the stream */ +- check_data_for(m, stream, 0); ++ check_data_for(m, stream, 1); + } + } + else if ((stream = h2_ihash_get(m->shold, task->stream_id)) != NULL) { +@@ -1062,7 +1072,7 @@ apr_status_t h2_mplx_idle(h2_mplx *m) + h2_beam_is_closed(stream->output), + (long)h2_beam_get_buffered(stream->output)); + h2_ihash_add(m->streams, stream); +- check_data_for(m, stream, 0); ++ check_data_for(m, stream, 1); + stream->out_checked = 1; + status = APR_EAGAIN; + } +@@ -1114,7 +1124,7 @@ apr_status_t h2_mplx_dispatch_master_eve + + apr_status_t h2_mplx_keep_active(h2_mplx *m, h2_stream *stream) + { +- check_data_for(m, stream, 1); ++ check_data_for(m, stream, 0); + return APR_SUCCESS; + } + +--- a/modules/http2/h2_session.c ++++ b/modules/http2/h2_session.c +@@ -2141,7 +2141,7 @@ apr_status_t h2_session_process(h2_sessi + break; + + case H2_SESSION_ST_IDLE: +- if (session->idle_until && (apr_time_now() + session->idle_delay) > session->idle_until) { ++ if (session->idle_until && (now + session->idle_delay) > session->idle_until) { + ap_log_cerror( APLOG_MARK, APLOG_TRACE1, status, c, + H2_SSSN_MSG(session, "idle, timeout reached, closing")); + if (session->idle_delay) { +--- a/modules/http2/h2_util.c ++++ b/modules/http2/h2_util.c +@@ -638,15 +638,6 @@ apr_status_t h2_fifo_term(h2_fifo *fifo) + apr_status_t rv; + if ((rv = apr_thread_mutex_lock(fifo->lock)) == APR_SUCCESS) { + fifo->aborted = 1; +- apr_thread_mutex_unlock(fifo->lock); +- } +- return rv; +-} +- +-apr_status_t h2_fifo_interrupt(h2_fifo *fifo) +-{ +- apr_status_t rv; +- if ((rv = apr_thread_mutex_lock(fifo->lock)) == APR_SUCCESS) { + apr_thread_cond_broadcast(fifo->not_empty); + apr_thread_cond_broadcast(fifo->not_full); + apr_thread_mutex_unlock(fifo->lock); +@@ -710,10 +701,6 @@ static apr_status_t fifo_push(h2_fifo *f + { + apr_status_t rv; + +- if (fifo->aborted) { +- return APR_EOF; +- } +- + if ((rv = apr_thread_mutex_lock(fifo->lock)) == APR_SUCCESS) { + rv = fifo_push_int(fifo, elem, block); + apr_thread_mutex_unlock(fifo->lock); +@@ -754,10 +741,6 @@ static apr_status_t fifo_pull(h2_fifo *f + { + apr_status_t rv; + +- if (fifo->aborted) { +- return APR_EOF; +- } +- + if ((rv = apr_thread_mutex_lock(fifo->lock)) == APR_SUCCESS) { + rv = pull_head(fifo, pelem, block); + apr_thread_mutex_unlock(fifo->lock); +@@ -946,15 +929,6 @@ apr_status_t h2_ififo_term(h2_ififo *fif + apr_status_t rv; + if ((rv = apr_thread_mutex_lock(fifo->lock)) == APR_SUCCESS) { + fifo->aborted = 1; +- apr_thread_mutex_unlock(fifo->lock); +- } +- return rv; +-} +- +-apr_status_t h2_ififo_interrupt(h2_ififo *fifo) +-{ +- apr_status_t rv; +- if ((rv = apr_thread_mutex_lock(fifo->lock)) == APR_SUCCESS) { + apr_thread_cond_broadcast(fifo->not_empty); + apr_thread_cond_broadcast(fifo->not_full); + apr_thread_mutex_unlock(fifo->lock); +@@ -1018,10 +992,6 @@ static apr_status_t ififo_push(h2_ififo + { + apr_status_t rv; + +- if (fifo->aborted) { +- return APR_EOF; +- } +- + if ((rv = apr_thread_mutex_lock(fifo->lock)) == APR_SUCCESS) { + rv = ififo_push_int(fifo, id, block); + apr_thread_mutex_unlock(fifo->lock); +@@ -1062,10 +1032,6 @@ static apr_status_t ififo_pull(h2_ififo + { + apr_status_t rv; + +- if (fifo->aborted) { +- return APR_EOF; +- } +- + if ((rv = apr_thread_mutex_lock(fifo->lock)) == APR_SUCCESS) { + rv = ipull_head(fifo, pi, block); + apr_thread_mutex_unlock(fifo->lock); +@@ -1088,10 +1054,6 @@ static apr_status_t ififo_peek(h2_ififo + apr_status_t rv; + int id; + +- if (fifo->aborted) { +- return APR_EOF; +- } +- + if (APR_SUCCESS == (rv = apr_thread_mutex_lock(fifo->lock))) { + if (APR_SUCCESS == (rv = ipull_head(fifo, &id, block))) { + switch (fn(id, ctx)) { +@@ -1117,39 +1079,40 @@ apr_status_t h2_ififo_try_peek(h2_ififo + return ififo_peek(fifo, fn, ctx, 0); + } + +-apr_status_t h2_ififo_remove(h2_ififo *fifo, int id) ++static apr_status_t ififo_remove(h2_ififo *fifo, int id) + { +- apr_status_t rv; ++ int rc, i; + + if (fifo->aborted) { + return APR_EOF; + } + +- if ((rv = apr_thread_mutex_lock(fifo->lock)) == APR_SUCCESS) { +- int i, rc; +- int e; +- +- rc = 0; +- for (i = 0; i < fifo->count; ++i) { +- e = fifo->elems[inth_index(fifo, i)]; +- if (e == id) { +- ++rc; +- } +- else if (rc) { +- fifo->elems[inth_index(fifo, i-rc)] = e; +- } +- } +- if (rc) { +- fifo->count -= rc; +- if (fifo->count + rc == fifo->nelems) { +- apr_thread_cond_broadcast(fifo->not_full); +- } +- rv = APR_SUCCESS; ++ rc = 0; ++ for (i = 0; i < fifo->count; ++i) { ++ int e = fifo->elems[inth_index(fifo, i)]; ++ if (e == id) { ++ ++rc; + } +- else { +- rv = APR_EAGAIN; ++ else if (rc) { ++ fifo->elems[inth_index(fifo, i-rc)] = e; + } +- ++ } ++ if (!rc) { ++ return APR_EAGAIN; ++ } ++ fifo->count -= rc; ++ if (fifo->count + rc == fifo->nelems) { ++ apr_thread_cond_broadcast(fifo->not_full); ++ } ++ return APR_SUCCESS; ++} ++ ++apr_status_t h2_ififo_remove(h2_ififo *fifo, int id) ++{ ++ apr_status_t rv; ++ ++ if ((rv = apr_thread_mutex_lock(fifo->lock)) == APR_SUCCESS) { ++ rv = ififo_remove(fifo, id); + apr_thread_mutex_unlock(fifo->lock); + } + return rv; +--- a/modules/http2/h2_util.h ++++ b/modules/http2/h2_util.h +@@ -209,7 +209,6 @@ apr_status_t h2_fifo_create(h2_fifo **pf + apr_status_t h2_fifo_set_create(h2_fifo **pfifo, apr_pool_t *pool, int capacity); + + apr_status_t h2_fifo_term(h2_fifo *fifo); +-apr_status_t h2_fifo_interrupt(h2_fifo *fifo); + + int h2_fifo_count(h2_fifo *fifo); + +@@ -280,7 +279,6 @@ apr_status_t h2_ififo_create(h2_ififo ** + apr_status_t h2_ififo_set_create(h2_ififo **pfifo, apr_pool_t *pool, int capacity); + + apr_status_t h2_ififo_term(h2_ififo *fifo); +-apr_status_t h2_ififo_interrupt(h2_ififo *fifo); + + int h2_ififo_count(h2_ififo *fifo); + +--- a/modules/http2/h2_workers.c ++++ b/modules/http2/h2_workers.c +@@ -269,7 +269,6 @@ static apr_status_t workers_pool_cleanup + } + + h2_fifo_term(workers->mplxs); +- h2_fifo_interrupt(workers->mplxs); + + cleanup_zombies(workers); + } diff -Nru apache2-2.4.29/debian/patches/CVE-2020-1927-1.patch apache2-2.4.29/debian/patches/CVE-2020-1927-1.patch --- apache2-2.4.29/debian/patches/CVE-2020-1927-1.patch 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.29/debian/patches/CVE-2020-1927-1.patch 2020-08-12 21:30:07.000000000 +0000 @@ -0,0 +1,93 @@ +From cd26218b944fc702901d1385a581e923d97a879f Mon Sep 17 00:00:00 2001 +From: Jim Jagielski +Date: Tue, 11 Feb 2020 13:16:38 +0000 +Subject: [PATCH] Merge r1873747 from trunk: + +factor out default regex flags + + +Submitted by: covener +Reviewed by: covener, minfrin, jorton + + +git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x@1873905 13f79535-47bb-0310-9956-ffa450edef68 +--- + STATUS | 6 ------ + include/ap_mmn.h | 3 ++- + include/ap_regex.h | 2 ++ + server/core.c | 2 +- + server/util_pcre.c | 3 +-- + 5 files changed, 6 insertions(+), 10 deletions(-) + +#diff --git a/STATUS b/STATUS +#index 26b37e16a2c..4afd862b385 100644 +#--- a/STATUS +#+++ b/STATUS +#@@ -133,12 +133,6 @@ PATCHES ACCEPTED TO BACKPORT FROM TRUNK: +# [ start all new proposals below, under PATCHES PROPOSED. ] +# +# +#- *) factor out default regex flags: +#- trunk patch: http://svn.apache.org/r1873747 +#- 2.4.x patch: http://people.apache.org/~covener/patches/httpd-2.4.x-reg_default.diff +#- (MMN, ap_regex.h conflicts) +#- +1: covener, minfrin, jorton +#- +# *) factor out chunked TE checks +# trunk patch: http://svn.apache.org/r1873748 +# 2.4.x patch: http://people.apache.org/~covener/patches/httpd-2.4.x-chunk.diff +#diff --git a/include/ap_mmn.h b/include/ap_mmn.h +#index e257c606eea..acdf7f1b1ad 100644 +#--- a/include/ap_mmn.h +#+++ b/include/ap_mmn.h +#@@ -531,6 +531,7 @@ +# * 20120211.88 (2.4.40-dev) Add ap_dir_nofnmatch() and ap_dir_fnmatch(). +# * 20120211.89 (2.4.42-dev) Add add dns_pool to proxy_conn_pool and define +# * AP_VOLATILIZE_T. +#+ * 20120211.90 (2.4.42-dev) AP_REG_DEFAULT macro in ap_regex.h +# */ +# +# #define MODULE_MAGIC_COOKIE 0x41503234UL /* "AP24" */ +#@@ -538,7 +539,7 @@ +# #ifndef MODULE_MAGIC_NUMBER_MAJOR +# #define MODULE_MAGIC_NUMBER_MAJOR 20120211 +# #endif +#-#define MODULE_MAGIC_NUMBER_MINOR 89 /* 0...n */ +#+#define MODULE_MAGIC_NUMBER_MINOR 90 /* 0...n */ +# +# /** +# * Determine if the server's current MODULE_MAGIC_NUMBER is at least a +--- a/include/ap_regex.h ++++ b/include/ap_regex.h +@@ -81,6 +81,8 @@ extern "C" { + + #define AP_REG_MATCH "MATCH_" /** suggested prefix for ap_regname */ + ++#define AP_REG_DEFAULT (AP_REG_DOTALL|AP_REG_DOLLAR_ENDONLY) ++ + /* Error values: */ + enum { + AP_REG_ASSERT = 1, /** internal error ? */ +--- a/server/core.c ++++ b/server/core.c +@@ -4925,7 +4925,7 @@ static int core_pre_config(apr_pool_t *p + apr_pool_cleanup_register(pconf, NULL, reset_config_defines, + apr_pool_cleanup_null); + +- ap_regcomp_set_default_cflags(AP_REG_DOLLAR_ENDONLY); ++ ap_regcomp_set_default_cflags(AP_REG_DEFAULT); + + mpm_common_pre_config(pconf); + +--- a/server/util_pcre.c ++++ b/server/util_pcre.c +@@ -111,8 +111,7 @@ AP_DECLARE(void) ap_regfree(ap_regex_t * + * Compile a regular expression * + *************************************************/ + +-static int default_cflags = AP_REG_DOTALL | +- AP_REG_DOLLAR_ENDONLY; ++static int default_cflags = AP_REG_DEFAULT; + + AP_DECLARE(int) ap_regcomp_get_default_cflags(void) + { diff -Nru apache2-2.4.29/debian/patches/CVE-2020-1927-2.patch apache2-2.4.29/debian/patches/CVE-2020-1927-2.patch --- apache2-2.4.29/debian/patches/CVE-2020-1927-2.patch 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.29/debian/patches/CVE-2020-1927-2.patch 2020-08-12 21:30:12.000000000 +0000 @@ -0,0 +1,99 @@ +From 40bf351edc852c86e9a6bb85ef6d7f5b6a2bee3a Mon Sep 17 00:00:00 2001 +From: Eric Covener +Date: Wed, 19 Feb 2020 12:26:31 +0000 +Subject: [PATCH] add AP_REG_NO_DEFAULT to allow opt-out of pcre defaults + +... and use it in mod_substitute to avoid DOTALL + + + +git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x@1874191 13f79535-47bb-0310-9956-ffa450edef68 +--- + include/ap_mmn.h | 3 ++- + include/ap_regex.h | 4 +++- + modules/filters/mod_substitute.c | 6 ++++-- + server/util_pcre.c | 4 +++- + server/util_regex.c | 3 ++- + 5 files changed, 14 insertions(+), 6 deletions(-) + +#diff --git a/include/ap_mmn.h b/include/ap_mmn.h +#index 70300f43308..0af9c9874f5 100644 +#--- a/include/ap_mmn.h +#+++ b/include/ap_mmn.h +#@@ -533,6 +533,7 @@ +# * AP_VOLATILIZE_T. +# * 20120211.90 (2.4.42-dev) AP_REG_DEFAULT macro in ap_regex.h +# * 20120211.91 (2.4.42-dev) Add ap_is_chunked() in httpd.h +#+ * 20120211.92 (2.4.42-dev) AP_REG_NO_DEFAULT macro in ap_regex.h +# */ +# +# #define MODULE_MAGIC_COOKIE 0x41503234UL /* "AP24" */ +#@@ -540,7 +541,7 @@ +# #ifndef MODULE_MAGIC_NUMBER_MAJOR +# #define MODULE_MAGIC_NUMBER_MAJOR 20120211 +# #endif +#-#define MODULE_MAGIC_NUMBER_MINOR 91 /* 0...n */ +#+#define MODULE_MAGIC_NUMBER_MINOR 92 /* 0...n */ +# +# /** +# * Determine if the server's current MODULE_MAGIC_NUMBER is at least a +--- a/include/ap_regex.h ++++ b/include/ap_regex.h +@@ -79,7 +79,9 @@ extern "C" { + + #define AP_REG_DOLLAR_ENDONLY 0x200 /* '$' matches at end of subject string only */ + +-#define AP_REG_MATCH "MATCH_" /** suggested prefix for ap_regname */ ++#define AP_REG_NO_DEFAULT 0x400 /**< Don't implicitely add AP_REG_DEFAULT options */ ++ ++#define AP_REG_MATCH "MATCH_" /**< suggested prefix for ap_regname */ + + #define AP_REG_DEFAULT (AP_REG_DOTALL|AP_REG_DOLLAR_ENDONLY) + +--- a/modules/filters/mod_substitute.c ++++ b/modules/filters/mod_substitute.c +@@ -635,8 +635,10 @@ static const char *set_pattern(cmd_parms + + /* first see if we can compile the regex */ + if (!is_pattern) { +- r = ap_pregcomp(cmd->pool, from, AP_REG_EXTENDED | +- (ignore_case ? AP_REG_ICASE : 0)); ++ int flags = AP_REG_NO_DEFAULT ++ | (ap_regcomp_get_default_cflags() & AP_REG_DOLLAR_ENDONLY) ++ | (ignore_case ? AP_REG_ICASE : 0); ++ r = ap_pregcomp(cmd->pool, from, flags); + if (!r) + return "Substitute could not compile regex"; + } +--- a/server/util_pcre.c ++++ b/server/util_pcre.c +@@ -159,7 +159,9 @@ AP_DECLARE(int) ap_regcomp(ap_regex_t * + int errcode = 0; + int options = PCRE_DUPNAMES; + +- cflags |= default_cflags; ++ if ((cflags & AP_REG_NO_DEFAULT) == 0) ++ cflags |= default_cflags; ++ + if ((cflags & AP_REG_ICASE) != 0) + options |= PCRE_CASELESS; + if ((cflags & AP_REG_NEWLINE) != 0) +--- a/server/util_regex.c ++++ b/server/util_regex.c +@@ -94,6 +94,7 @@ AP_DECLARE(ap_rxplus_t*) ap_rxplus_compi + } + + /* anything after the current delimiter is flags */ ++ ret->flags = ap_regcomp_get_default_cflags() & AP_REG_DOLLAR_ENDONLY; + while (*++endp) { + switch (*endp) { + case 'i': ret->flags |= AP_REG_ICASE; break; +@@ -106,7 +107,7 @@ AP_DECLARE(ap_rxplus_t*) ap_rxplus_compi + default: break; /* we should probably be stricter here */ + } + } +- if (ap_regcomp(&ret->rx, rxstr, ret->flags) == 0) { ++ if (ap_regcomp(&ret->rx, rxstr, AP_REG_NO_DEFAULT | ret->flags) == 0) { + apr_pool_cleanup_register(pool, &ret->rx, rxplus_cleanup, + apr_pool_cleanup_null); + } diff -Nru apache2-2.4.29/debian/patches/CVE-2020-1934.patch apache2-2.4.29/debian/patches/CVE-2020-1934.patch --- apache2-2.4.29/debian/patches/CVE-2020-1934.patch 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.29/debian/patches/CVE-2020-1934.patch 2020-08-12 21:30:18.000000000 +0000 @@ -0,0 +1,103 @@ +From 179492ff35adbc239c2fcdcb3d6af117a11c105b Mon Sep 17 00:00:00 2001 +From: Jim Jagielski +Date: Tue, 11 Feb 2020 13:14:42 +0000 +Subject: [PATCH] Merge r1873745 from trunk: + +trap bad FTP responses + +Submitted by: covener +Reviewed by: covener, minfrin, jorton + + +git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x@1873904 13f79535-47bb-0310-9956-ffa450edef68 +--- + STATUS | 5 ----- + modules/proxy/mod_proxy_ftp.c | 20 +++++++++++++++----- + 2 files changed, 15 insertions(+), 10 deletions(-) + +#diff --git a/STATUS b/STATUS +#index 061debfb283..26b37e16a2c 100644 +#--- a/STATUS +#+++ b/STATUS +#@@ -133,11 +133,6 @@ PATCHES ACCEPTED TO BACKPORT FROM TRUNK: +# [ start all new proposals below, under PATCHES PROPOSED. ] +# +# +#- *) trap mod_proxy_ftp errors +#- trunk patch: http://svn.apache.org/r1873745 +#- 2.4.x patch: svn merge -c 1873745 ^/httpd/httpd/trunk . +#- +1: covener, minfrin, jorton +#- +# *) factor out default regex flags: +# trunk patch: http://svn.apache.org/r1873747 +# 2.4.x patch: http://people.apache.org/~covener/patches/httpd-2.4.x-reg_default.diff +diff --git a/modules/proxy/mod_proxy_ftp.c b/modules/proxy/mod_proxy_ftp.c +index 801e351c2b2..dfcaa808556 100644 +--- a/modules/proxy/mod_proxy_ftp.c ++++ b/modules/proxy/mod_proxy_ftp.c +@@ -218,7 +218,7 @@ static int ftp_check_string(const char *x) + * (EBCDIC) machines either. + */ + static apr_status_t ftp_string_read(conn_rec *c, apr_bucket_brigade *bb, +- char *buff, apr_size_t bufflen, int *eos) ++ char *buff, apr_size_t bufflen, int *eos, apr_size_t *outlen) + { + apr_bucket *e; + apr_status_t rv; +@@ -230,6 +230,7 @@ static apr_status_t ftp_string_read(conn_rec *c, apr_bucket_brigade *bb, + /* start with an empty string */ + buff[0] = 0; + *eos = 0; ++ *outlen = 0; + + /* loop through each brigade */ + while (!found) { +@@ -273,6 +274,7 @@ static apr_status_t ftp_string_read(conn_rec *c, apr_bucket_brigade *bb, + if (len > 0) { + memcpy(pos, response, len); + pos += len; ++ *outlen += len; + } + } + apr_bucket_delete(e); +@@ -385,28 +387,36 @@ static int ftp_getrc_msg(conn_rec *ftp_ctrl, apr_bucket_brigade *bb, char *msgbu + char buff[5]; + char *mb = msgbuf, *me = &msgbuf[msglen]; + apr_status_t rv; ++ apr_size_t nread; ++ + int eos; + +- if (APR_SUCCESS != (rv = ftp_string_read(ftp_ctrl, bb, response, sizeof(response), &eos))) { ++ if (APR_SUCCESS != (rv = ftp_string_read(ftp_ctrl, bb, response, sizeof(response), &eos, &nread))) { + return -1; + } + /* + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL, APLOGNO(03233) + "<%s", response); + */ ++ if (nread < 4) { ++ ap_log_error(APLOG_MARK, APLOG_INFO, 0, NULL, APLOGNO(10229) "Malformed FTP response '%s'", response); ++ *mb = '\0'; ++ return -1; ++ } ++ + if (!apr_isdigit(response[0]) || !apr_isdigit(response[1]) || +- !apr_isdigit(response[2]) || (response[3] != ' ' && response[3] != '-')) ++ !apr_isdigit(response[2]) || (response[3] != ' ' && response[3] != '-')) + status = 0; + else + status = 100 * response[0] + 10 * response[1] + response[2] - 111 * '0'; + + mb = apr_cpystrn(mb, response + 4, me - mb); + +- if (response[3] == '-') { ++ if (response[3] == '-') { /* multi-line reply "123-foo\nbar\n123 baz" */ + memcpy(buff, response, 3); + buff[3] = ' '; + do { +- if (APR_SUCCESS != (rv = ftp_string_read(ftp_ctrl, bb, response, sizeof(response), &eos))) { ++ if (APR_SUCCESS != (rv = ftp_string_read(ftp_ctrl, bb, response, sizeof(response), &eos, &nread))) { + return -1; + } + mb = apr_cpystrn(mb, response + (' ' == response[0] ? 1 : 4), me - mb); diff -Nru apache2-2.4.29/debian/patches/CVE-2020-9490.patch apache2-2.4.29/debian/patches/CVE-2020-9490.patch --- apache2-2.4.29/debian/patches/CVE-2020-9490.patch 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.29/debian/patches/CVE-2020-9490.patch 2020-08-12 21:30:28.000000000 +0000 @@ -0,0 +1,436 @@ +From a61223e9cb906110f35ec144b93fee9eb80ad6e4 Mon Sep 17 00:00:00 2001 +From: Stefan Eissing +Date: Wed, 29 Jul 2020 12:33:53 +0000 +Subject: [PATCH] Merge of r1880395 from trunk: + + *) mod_http2: remote support for abandoned http-wg draft + . + + + +git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x@1880396 13f79535-47bb-0310-9956-ffa450edef68 +--- + CHANGES | 4 + + modules/http2/h2_push.c | 253 ++++--------------------------------- + modules/http2/h2_push.h | 54 +++++--- + modules/http2/h2_version.h | 4 +- + 4 files changed, 69 insertions(+), 246 deletions(-) + +#diff --git a/CHANGES b/CHANGES +#index 6948d2ec571..81a36618bba 100644 +#--- a/CHANGES +#+++ b/CHANGES +#@@ -1,6 +1,10 @@ +# -*- coding: utf-8 -*- +# Changes with Apache 2.4.45 +# +#+ *) mod_http2: remote support for abandoned http-wg draft +#+ . +#+ [Stefan Eissing] +#+ +# Changes with Apache 2.4.44 +# +# *) mod_proxy_uwsgi: Error out on HTTP header larger than 16K (hard +diff --git a/modules/http2/h2_push.c b/modules/http2/h2_push.c +index 60488cf50a2..dc21e1ef648 100644 +--- a/modules/http2/h2_push.c ++++ b/modules/http2/h2_push.c +@@ -464,33 +464,6 @@ apr_array_header_t *h2_push_collect(apr_pool_t *p, const h2_request *req, + return NULL; + } + +-/******************************************************************************* +- * push diary +- * +- * - The push diary keeps track of resources already PUSHed via HTTP/2 on this +- * connection. It records a hash value from the absolute URL of the resource +- * pushed. +- * - Lacking openssl, it uses 'apr_hashfunc_default' for the value +- * - with openssl, it uses SHA256 to calculate the hash value +- * - whatever the method to generate the hash, the diary keeps a maximum of 64 +- * bits per hash, limiting the memory consumption to about +- * H2PushDiarySize * 8 +- * bytes. Entries are sorted by most recently used and oldest entries are +- * forgotten first. +- * - Clients can initialize/replace the push diary by sending a 'Cache-Digest' +- * header. Currently, this is the base64url encoded value of the cache digest +- * as specified in https://datatracker.ietf.org/doc/draft-kazuho-h2-cache-digest/ +- * This draft can be expected to evolve and the definition of the header +- * will be added there and refined. +- * - The cache digest header is a Golomb Coded Set of hash values, but it may +- * limit the amount of bits per hash value even further. For a good description +- * of GCS, read here: +- * http://giovanni.bajo.it/post/47119962313/golomb-coded-sets-smaller-than-bloom-filters +- * - The means that the push diary might be initialized with hash values of much +- * less than 64 bits, leading to more false positives, but smaller digest size. +- ******************************************************************************/ +- +- + #define GCSLOG_LEVEL APLOG_TRACE1 + + typedef struct h2_push_diary_entry { +@@ -617,38 +590,48 @@ static int h2_push_diary_find(h2_push_diary *diary, apr_uint64_t hash) + return -1; + } + +-static h2_push_diary_entry *move_to_last(h2_push_diary *diary, apr_size_t idx) ++static void move_to_last(h2_push_diary *diary, apr_size_t idx) + { + h2_push_diary_entry *entries = (h2_push_diary_entry*)diary->entries->elts; + h2_push_diary_entry e; +- apr_size_t lastidx = diary->entries->nelts-1; ++ int lastidx; + ++ /* Move an existing entry to the last place */ ++ if (diary->entries->nelts <= 0) ++ return; ++ + /* move entry[idx] to the end */ ++ lastidx = diary->entries->nelts - 1; + if (idx < lastidx) { + e = entries[idx]; +- memmove(entries+idx, entries+idx+1, sizeof(e) * (lastidx - idx)); ++ memmove(entries+idx, entries+idx+1, sizeof(h2_push_diary_entry) * (lastidx - idx)); + entries[lastidx] = e; + } +- return &entries[lastidx]; + } + +-static void h2_push_diary_append(h2_push_diary *diary, h2_push_diary_entry *e) ++static void remove_first(h2_push_diary *diary) + { +- h2_push_diary_entry *ne; ++ h2_push_diary_entry *entries = (h2_push_diary_entry*)diary->entries->elts; ++ int lastidx; + +- if (diary->entries->nelts < diary->N) { +- /* append a new diary entry at the end */ +- APR_ARRAY_PUSH(diary->entries, h2_push_diary_entry) = *e; +- ne = &APR_ARRAY_IDX(diary->entries, diary->entries->nelts-1, h2_push_diary_entry); ++ /* move remaining entries to index 0 */ ++ lastidx = diary->entries->nelts - 1; ++ if (lastidx > 0) { ++ --diary->entries->nelts; ++ memmove(entries, entries+1, sizeof(h2_push_diary_entry) * diary->entries->nelts); + } +- else { +- /* replace content with new digest. keeps memory usage constant once diary is full */ +- ne = move_to_last(diary, 0); +- *ne = *e; ++} ++ ++static void h2_push_diary_append(h2_push_diary *diary, h2_push_diary_entry *e) ++{ ++ while (diary->entries->nelts >= diary->N) { ++ remove_first(diary); + } ++ /* append a new diary entry at the end */ ++ APR_ARRAY_PUSH(diary->entries, h2_push_diary_entry) = *e; + /* Intentional no APLOGNO */ + ap_log_perror(APLOG_MARK, GCSLOG_LEVEL, 0, diary->entries->pool, +- "push_diary_append: %"APR_UINT64_T_HEX_FMT, ne->hash); ++ "push_diary_append: %"APR_UINT64_T_HEX_FMT, e->hash); + } + + apr_array_header_t *h2_push_diary_update(h2_session *session, apr_array_header_t *pushes) +@@ -691,30 +674,12 @@ apr_array_header_t *h2_push_collect_update(h2_stream *stream, + const struct h2_request *req, + const struct h2_headers *res) + { +- h2_session *session = stream->session; +- const char *cache_digest = apr_table_get(req->headers, "Cache-Digest"); + apr_array_header_t *pushes; +- apr_status_t status; + +- if (cache_digest && session->push_diary) { +- status = h2_push_diary_digest64_set(session->push_diary, req->authority, +- cache_digest, stream->pool); +- if (status != APR_SUCCESS) { +- ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, session->c, +- H2_SSSN_LOG(APLOGNO(03057), session, +- "push diary set from Cache-Digest: %s"), cache_digest); +- } +- } + pushes = h2_push_collect(stream->pool, req, stream->push_policy, res); + return h2_push_diary_update(stream->session, pushes); + } + +-static apr_int32_t h2_log2inv(unsigned char log2) +-{ +- return log2? (1 << log2) : 1; +-} +- +- + typedef struct { + h2_push_diary *diary; + unsigned char log2p; +@@ -829,11 +794,6 @@ apr_status_t h2_push_diary_digest_get(h2_push_diary *diary, apr_pool_t *pool, + apr_size_t hash_count; + + nelts = diary->entries->nelts; +- +- if (nelts > APR_UINT32_MAX) { +- /* should not happen */ +- return APR_ENOTIMPL; +- } + N = ceil_power_of_2(nelts); + log2n = h2_log2(N); + +@@ -895,166 +855,3 @@ apr_status_t h2_push_diary_digest_get(h2_push_diary *diary, apr_pool_t *pool, + return APR_SUCCESS; + } + +-typedef struct { +- h2_push_diary *diary; +- apr_pool_t *pool; +- unsigned char log2p; +- const unsigned char *data; +- apr_size_t datalen; +- apr_size_t offset; +- unsigned int bit; +- apr_uint64_t last_val; +-} gset_decoder; +- +-static int gset_decode_next_bit(gset_decoder *decoder) +-{ +- if (++decoder->bit >= 8) { +- if (++decoder->offset >= decoder->datalen) { +- return -1; +- } +- decoder->bit = 0; +- } +- return (decoder->data[decoder->offset] & cbit_mask[decoder->bit])? 1 : 0; +-} +- +-static apr_status_t gset_decode_next(gset_decoder *decoder, apr_uint64_t *phash) +-{ +- apr_uint64_t flex = 0, fixed = 0, delta; +- int i; +- +- /* read 1 bits until we encounter 0, then read log2n(diary-P) bits. +- * On a malformed bit-string, this will not fail, but produce results +- * which are pbly too large. Luckily, the diary will modulo the hash. +- */ +- while (1) { +- int bit = gset_decode_next_bit(decoder); +- if (bit == -1) { +- return APR_EINVAL; +- } +- if (!bit) { +- break; +- } +- ++flex; +- } +- +- for (i = 0; i < decoder->log2p; ++i) { +- int bit = gset_decode_next_bit(decoder); +- if (bit == -1) { +- return APR_EINVAL; +- } +- fixed = (fixed << 1) | bit; +- } +- +- delta = (flex << decoder->log2p) | fixed; +- *phash = delta + decoder->last_val; +- decoder->last_val = *phash; +- +- /* Intentional no APLOGNO */ +- ap_log_perror(APLOG_MARK, GCSLOG_LEVEL, 0, decoder->pool, +- "h2_push_diary_digest_dec: val=%"APR_UINT64_T_HEX_FMT", delta=%" +- APR_UINT64_T_HEX_FMT", flex=%d, fixed=%"APR_UINT64_T_HEX_FMT, +- *phash, delta, (int)flex, fixed); +- +- return APR_SUCCESS; +-} +- +-/** +- * Initialize the push diary by a cache digest as described in +- * https://datatracker.ietf.org/doc/draft-kazuho-h2-cache-digest/ +- * . +- * @param diary the diary to set the digest into +- * @param data the binary cache digest +- * @param len the length of the cache digest +- * @return APR_EINVAL if digest was not successfully parsed +- */ +-apr_status_t h2_push_diary_digest_set(h2_push_diary *diary, const char *authority, +- const char *data, apr_size_t len) +-{ +- gset_decoder decoder; +- unsigned char log2n, log2p; +- int N, i; +- apr_pool_t *pool = diary->entries->pool; +- h2_push_diary_entry e; +- apr_status_t status = APR_SUCCESS; +- +- if (len < 2) { +- /* at least this should be there */ +- return APR_EINVAL; +- } +- log2n = data[0]; +- log2p = data[1]; +- diary->mask_bits = log2n + log2p; +- if (diary->mask_bits > 64) { +- /* cannot handle */ +- return APR_ENOTIMPL; +- } +- +- /* whatever is in the digest, it replaces the diary entries */ +- apr_array_clear(diary->entries); +- if (!authority || !strcmp("*", authority)) { +- diary->authority = NULL; +- } +- else if (!diary->authority || strcmp(diary->authority, authority)) { +- diary->authority = apr_pstrdup(diary->entries->pool, authority); +- } +- +- N = h2_log2inv(log2n + log2p); +- +- decoder.diary = diary; +- decoder.pool = pool; +- decoder.log2p = log2p; +- decoder.data = (const unsigned char*)data; +- decoder.datalen = len; +- decoder.offset = 1; +- decoder.bit = 8; +- decoder.last_val = 0; +- +- diary->N = N; +- /* Determine effective N we use for storage */ +- if (!N) { +- /* a totally empty cache digest. someone tells us that she has no +- * entries in the cache at all. Use our own preferences for N+mask +- */ +- diary->N = diary->NMax; +- return APR_SUCCESS; +- } +- else if (N > diary->NMax) { +- /* Store not more than diary is configured to hold. We open us up +- * to DOS attacks otherwise. */ +- diary->N = diary->NMax; +- } +- +- /* Intentional no APLOGNO */ +- ap_log_perror(APLOG_MARK, GCSLOG_LEVEL, 0, pool, +- "h2_push_diary_digest_set: N=%d, log2n=%d, " +- "diary->mask_bits=%d, dec.log2p=%d", +- (int)diary->N, (int)log2n, diary->mask_bits, +- (int)decoder.log2p); +- +- for (i = 0; i < diary->N; ++i) { +- if (gset_decode_next(&decoder, &e.hash) != APR_SUCCESS) { +- /* the data may have less than N values */ +- break; +- } +- h2_push_diary_append(diary, &e); +- } +- +- /* Intentional no APLOGNO */ +- ap_log_perror(APLOG_MARK, GCSLOG_LEVEL, 0, pool, +- "h2_push_diary_digest_set: diary now with %d entries, mask_bits=%d", +- (int)diary->entries->nelts, diary->mask_bits); +- return status; +-} +- +-apr_status_t h2_push_diary_digest64_set(h2_push_diary *diary, const char *authority, +- const char *data64url, apr_pool_t *pool) +-{ +- const char *data; +- apr_size_t len = h2_util_base64url_decode(&data, data64url, pool); +- /* Intentional no APLOGNO */ +- ap_log_perror(APLOG_MARK, GCSLOG_LEVEL, 0, pool, +- "h2_push_diary_digest64_set: digest=%s, dlen=%d", +- data64url, (int)len); +- return h2_push_diary_digest_set(diary, authority, data, len); +-} +- +diff --git a/modules/http2/h2_push.h b/modules/http2/h2_push.h +index bc24e687715..d061dd8d141 100644 +--- a/modules/http2/h2_push.h ++++ b/modules/http2/h2_push.h +@@ -35,6 +35,44 @@ typedef enum { + H2_PUSH_DIGEST_SHA256 + } h2_push_digest_type; + ++/******************************************************************************* ++ * push diary ++ * ++ * - The push diary keeps track of resources already PUSHed via HTTP/2 on this ++ * connection. It records a hash value from the absolute URL of the resource ++ * pushed. ++ * - Lacking openssl, ++ * - with openssl, it uses SHA256 to calculate the hash value, otherwise it ++ * falls back to apr_hashfunc_default() ++ * - whatever the method to generate the hash, the diary keeps a maximum of 64 ++ * bits per hash, limiting the memory consumption to about ++ * H2PushDiarySize * 8 ++ * bytes. Entries are sorted by most recently used and oldest entries are ++ * forgotten first. ++ * - While useful by itself to avoid duplicated PUSHes on the same connection, ++ * the original idea was that clients provided a 'Cache-Digest' header with ++ * the values of *their own* cached resources. This was described in ++ * ++ * and some subsequent revisions that tweaked values but kept the overall idea. ++ * - The draft was abandoned by the IETF http-wg, as support from major clients, ++ * e.g. browsers, was lacking for various reasons. ++ * - For these reasons, mod_h2 abandoned its support for client supplied values ++ * but keeps the diary. It seems to provide value for applications using PUSH, ++ * is configurable in size and defaults to a very moderate amount of memory ++ * used. ++ * - The cache digest header is a Golomb Coded Set of hash values, but it may ++ * limit the amount of bits per hash value even further. For a good description ++ * of GCS, read here: ++ * ++ ******************************************************************************/ ++ ++ ++/* ++ * The push diary is based on the abandoned draft ++ * ++ * that describes how to use golomb filters. ++ */ ++ + typedef struct h2_push_diary h2_push_diary; + + typedef void h2_push_digest_calc(h2_push_diary *diary, apr_uint64_t *phash, h2_push *push); +@@ -101,20 +139,4 @@ apr_status_t h2_push_diary_digest_get(h2_push_diary *diary, apr_pool_t *p, + int maxP, const char *authority, + const char **pdata, apr_size_t *plen); + +-/** +- * Initialize the push diary by a cache digest as described in +- * https://datatracker.ietf.org/doc/draft-kazuho-h2-cache-digest/ +- * . +- * @param diary the diary to set the digest into +- * @param authority the authority to set the data for +- * @param data the binary cache digest +- * @param len the length of the cache digest +- * @return APR_EINVAL if digest was not successfully parsed +- */ +-apr_status_t h2_push_diary_digest_set(h2_push_diary *diary, const char *authority, +- const char *data, apr_size_t len); +- +-apr_status_t h2_push_diary_digest64_set(h2_push_diary *diary, const char *authority, +- const char *data64url, apr_pool_t *pool); +- + #endif /* defined(__mod_h2__h2_push__) */ +#diff --git a/modules/http2/h2_version.h b/modules/http2/h2_version.h +#index 7083f097cba..68fd2239856 100644 +#--- a/modules/http2/h2_version.h +#+++ b/modules/http2/h2_version.h +#@@ -27,7 +27,7 @@ +# * @macro +# * Version number of the http2 module as c string +# */ +#-#define MOD_HTTP2_VERSION "1.15.11" +#+#define MOD_HTTP2_VERSION "1.15.14" +# +# /** +# * @macro +#@@ -35,6 +35,6 @@ +# * release. This is a 24 bit number with 8 bits for major number, 8 bits +# * for minor and 8 bits for patch. Version 1.2.3 becomes 0x010203. +# */ +#-#define MOD_HTTP2_VERSION_NUM 0x010f0c +#+#define MOD_HTTP2_VERSION_NUM 0x010f0e +# +# #endif /* mod_h2_h2_version_h */ diff -Nru apache2-2.4.29/debian/patches/series apache2-2.4.29/debian/patches/series --- apache2-2.4.29/debian/patches/series 2020-03-13 12:26:05.000000000 +0000 +++ apache2-2.4.29/debian/patches/series 2020-08-12 21:32:55.000000000 +0000 @@ -93,3 +93,9 @@ tlsv1.3-support-3.patch tlsv1.3-support-4.patch tlsv1.3-support-5.patch +CVE-2020-1927-1.patch +CVE-2020-1927-2.patch +CVE-2020-1934.patch +CVE-2020-9490.patch +CVE-2020-11993-pre1.patch +CVE-2020-11993.patch