diff --git a/CVE-2020-11984.patch b/CVE-2020-11984.patch new file mode 100644 index 0000000000000000000000000000000000000000..9189c378104abd7c7c2d3a4e889474d9dd7fb19c --- /dev/null +++ b/CVE-2020-11984.patch @@ -0,0 +1,61 @@ +From 0c543e3f5b3881d515d6235f152aacaaaf3aba72 Mon Sep 17 00:00:00 2001 +From: Yann Ylavic +Date: Fri, 24 Jul 2020 09:35:25 +0000 +Subject: [PATCH] Merge r1880205, r1880214 from trunk: + +mod_proxy_uwsgi: Error out on HTTP header larger than 16K + +The uwsgi protocol does not let us serialize more than 16K of HTTP header, +so fail early with 500 if it happens. + + +Follow up to r1880205, APLOGNO(). + + +Submitted by: ylavic +Reviewed by: ylavic, covener, icing + + +git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x@1880251 13f79535-47bb-0310-9956-ffa450edef68 +--- + modules/proxy/mod_proxy_uwsgi.c | 13 ++++++++++--- + 1 files changed, 10 insertions(+), 3 deletions(-) + +diff --git a/modules/proxy/mod_proxy_uwsgi.c b/modules/proxy/mod_proxy_uwsgi.c +index 2ac2a95d2ef..0209ac4062e 100644 +--- a/modules/proxy/mod_proxy_uwsgi.c ++++ b/modules/proxy/mod_proxy_uwsgi.c +@@ -136,7 +136,7 @@ static int uwsgi_send_headers(request_rec *r, proxy_conn_rec * conn) + int j; + + apr_size_t headerlen = 4; +- apr_uint16_t pktsize, keylen, vallen; ++ apr_size_t pktsize, keylen, vallen; + const char *script_name; + const char *path_info; + const char *auth; +@@ -178,6 +178,15 @@ static int uwsgi_send_headers(request_rec *r, proxy_conn_rec * conn) + headerlen += 2 + strlen(env[j].key) + 2 + strlen(env[j].val); + } + ++ pktsize = headerlen - 4; ++ if (pktsize > APR_UINT16_MAX) { ++ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10259) ++ "can't send headers to %s:%u: packet size too " ++ "large (%" APR_SIZE_T_FMT ")", ++ conn->hostname, conn->port, pktsize); ++ return HTTP_INTERNAL_SERVER_ERROR; ++ } ++ + ptr = buf = apr_palloc(r->pool, headerlen); + + ptr += 4; +@@ -196,8 +205,6 @@ static int uwsgi_send_headers(request_rec *r, proxy_conn_rec * conn) + ptr += vallen; + } + +- pktsize = headerlen - 4; +- + buf[0] = 0; + buf[1] = (apr_byte_t) (pktsize & 0xff); + buf[2] = (apr_byte_t) ((pktsize >> 8) & 0xff); diff --git a/CVE-2020-11993.patch b/CVE-2020-11993.patch new file mode 100644 index 0000000000000000000000000000000000000000..b1e3ee9d8ec373035b7399110f8a67a893bec59f --- /dev/null +++ b/CVE-2020-11993.patch @@ -0,0 +1,1902 @@ +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 +--- + 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 +- + 14 files changed, 285 insertions(+), 353 deletions(-) + +diff --git a/modules/http2/h2_conn.c b/modules/http2/h2_conn.c +index a330cc8..17a2d48 100644 +--- 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_t *pool, server_rec *s) + 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_ctx *ctx, conn_rec *c) + 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 *master, int slave_id, apr_pool_t *parent) + + 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 *master, int slave_id, apr_pool_t *parent) + 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 *master, int slave_id, apr_pool_t *parent) + 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 *master, int slave_id, apr_pool_t *parent) + } + + 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 unnecessary 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; + } + +diff --git a/modules/http2/h2_conn.h b/modules/http2/h2_conn.h +index c560405..3b8b33e 100644 +--- 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__) */ +diff --git a/modules/http2/h2_filter.c b/modules/http2/h2_filter.c +index 2fc5e12..d9257fa 100644 +--- a/modules/http2/h2_filter.c ++++ b/modules/http2/h2_filter.c +@@ -370,7 +370,7 @@ static void add_streams(apr_bucket_brigade *bb, h2_session *s, int last) + 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 *bb, h2_session *s, + 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; + +diff --git a/modules/http2/h2_h2.c b/modules/http2/h2_h2.c +index 1b69fe3..96704b4 100644 +--- a/modules/http2/h2_h2.c ++++ b/modules/http2/h2_h2.c +@@ -666,7 +666,7 @@ static int h2_h2_pre_close_conn(conn_rec *c) + { + h2_ctx *ctx; + +- /* slave connection? */ ++ /* secondary connection? */ + if (c->master) { + return DECLINED; + } +@@ -710,7 +710,7 @@ static void check_push(request_rec *r, const char *tag) + + 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 +@@ -729,7 +729,7 @@ static int h2_h2_post_read_req(request_rec *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; + } +@@ -743,7 +743,7 @@ static int h2_h2_post_read_req(request_rec *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) { +@@ -751,7 +751,7 @@ static int h2_h2_late_fixups(request_rec *r) + 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"); +diff --git a/modules/http2/h2_mplx.c b/modules/http2/h2_mplx.c +index 9a5f355..62c381d 100644 +--- 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_t *pool, server_rec *s) + #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_stream *stream) + 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, h2_stream *stream) + 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, h2_stream *stream) + * 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, server_rec *s, apr_pool_t *parent, + 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 independent 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, server_rec *s, apr_pool_t *parent, + 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 *m, h2_stream *stream) + 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 *ctx, void *val) + 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_mplx *m, h2_task *task) + 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, void *val) + + 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, void *val) + + 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, void *val) + 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, void *val) + 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 *m, h2_mplx_stream_cb *cb, void *ctx) + + 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, void *val) { + 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 *ctx, void *val) { + 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, void *val) { + 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, apr_thread_cond_t *wait) + + 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, apr_thread_cond_t *wait) + "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, apr_thread_cond_t *wait) + 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, apr_thread_cond_t *wait) + 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, apr_thread_cond_t *wait) + 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, int stream_id, h2_bucket_beam *beam) + 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, int stream_id, h2_bucket_beam *beam) + 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, h2_task *task) + 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 *m, apr_interval_time_t timeout, + 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 *m, apr_interval_time_t timeout, + 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, h2_stream *stream, int mplx_is_locked) + } + } + +-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_mplx *m, h2_stream_pri_cmp *cmp, void *ctx) + 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, struct h2_stream *stream, + 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, struct h2_stream *stream, + 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 *m) + + 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) + 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 *m) + 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, h2_task **ptask) + 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, h2_task **ptask) + 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_task *task) + /* 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_task *task, h2_task **ptask) + * 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 *data, void *val) + 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_throttle(h2_task **ptask, h2_mplx *m) + 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_throttle(h2_task **ptask, h2_mplx *m) + 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_throttle(h2_task **ptask, h2_mplx *m) + 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_tasks(h2_mplx *m) + /* 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_tasks(h2_mplx *m) + 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_mplx *m) + 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_mplx *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_events(h2_mplx *m, + 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_events(h2_mplx *m, + 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 *m, int stream_id) + 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; +diff --git a/modules/http2/h2_mplx.h b/modules/http2/h2_mplx.h +index 8a4f63f..c61629d 100644 +--- 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, server_rec *s, apr_pool_t *master, + * @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_mplx *m, int id); + * @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 *m, struct h2_stream *stream); + * @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, struct h2_stream *stream, + * @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(void *ctx, struct h2_stream *stream); + * 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__) */ +diff --git a/modules/http2/h2_request.c b/modules/http2/h2_request.c +index 6394502..202d560 100644 +--- a/modules/http2/h2_request.c ++++ b/modules/http2/h2_request.c +@@ -288,6 +288,9 @@ request_rec *h2_request_create_rec(const h2_request *req, conn_rec *c) + 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 h2_request *req, conn_rec *c) + */ + 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; + +diff --git a/modules/http2/h2_session.c b/modules/http2/h2_session.c +index de54ac7..d657fce 100644 +--- a/modules/http2/h2_session.c ++++ b/modules/http2/h2_session.c +@@ -106,7 +106,7 @@ static int rst_unprocessed_stream(h2_stream *stream, void *ctx) + + 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_session *ng2s, + 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_session *ng2s, + } + + 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(h2_session *session, int error, + * 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_session *session, const char *trigger) + } + + 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(void *data) + /* 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_session **psession, conn_rec *c, request_rec * + 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_session *session) + 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_session *session, int arg, const char *msg) + 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 *ctx, h2_stream *stream) + 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 *session, h2_session_event_t ev, + 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_session *session, int async) + 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_session *session, int async) + /* 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_session *session, int async) + "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_session *session, int async) + 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; + } + } +diff --git a/modules/http2/h2_session.h b/modules/http2/h2_session.h +index 1bf6f05..4f74b56 100644 +--- 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; + +diff --git a/modules/http2/h2_stream.c b/modules/http2/h2_stream.c +index 4603513..eb61add 100644 +--- a/modules/http2/h2_stream.c ++++ b/modules/http2/h2_stream.c +@@ -911,7 +911,7 @@ apr_status_t h2_stream_out_prepare(h2_stream *stream, apr_off_t *plen, + + 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; +diff --git a/modules/http2/h2_task.c b/modules/http2/h2_task.c +index d610895..46d4a57 100644 +--- 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) + 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 *task, apr_bucket_brigade* bb, int block) + * 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_task *task) + } + + /******************************************************************************* +- * 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(ap_filter_t* f, + + 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(ap_filter_t* f, + /* 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(ap_filter_t* f, + + 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(ap_filter_t* f, + 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(ap_filter_t* f, + 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(ap_filter_t* f, + * 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(ap_filter_t* f, + 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) + (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, int stream_id, + 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, int stream_id, + } + task->id = "000"; + task->stream_id = stream_id; +- task->c = slave; ++ task->c = secondary; + task->mplx = m; + task->pool = pool; + task->request = req; +@@ -559,7 +559,7 @@ apr_status_t h2_task_do(h2_task *task, apr_thread_t *thread, int worker_id) + * + * 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 MPM module assigns the connection ids and mod_unique_id is using +@@ -567,7 +567,7 @@ apr_status_t h2_task_do(h2_task *task, apr_thread_t *thread, int worker_id) + * works for HTTP/1.x, the parallel execution of several requests per + * connection will generate duplicate identifiers on load. + * +- * The original implementation for slave connection identifiers used ++ * The original implementation for secondary connection identifiers used + * to shift the master connection id up and assign the stream id to the + * lower bits. This was cramped on 32 bit systems, but on 64bit there was + * enough space. +@@ -599,7 +599,7 @@ apr_status_t h2_task_do(h2_task *task, apr_thread_t *thread, int worker_id) + 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) { +@@ -707,7 +707,7 @@ static int h2_task_process_conn(conn_rec* c) + } + 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; + } +diff --git a/modules/http2/h2_task.h b/modules/http2/h2_task.h +index 6e8775f..efdbff6 100644 +--- 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, +diff --git a/modules/http2/h2_workers.c b/modules/http2/h2_workers.c +index 52f1a70..96c20a8 100644 +--- a/modules/http2/h2_workers.c ++++ b/modules/http2/h2_workers.c +@@ -155,7 +155,7 @@ static apr_status_t slot_pull_task(h2_slot *slot, h2_mplx *m) + { + 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(apr_thread_t *thread, void *wctx) + * 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; + } + } +diff --git a/modules/http2/mod_http2.c b/modules/http2/mod_http2.c +index 5664f39..9f087ab 100644 +--- a/modules/http2/mod_http2.c ++++ b/modules/http2/mod_http2.c +@@ -237,7 +237,7 @@ static const char *val_H2_PUSH(apr_pool_t *p, server_rec *s, + 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_pool_t *p, server_rec *s, + { + 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 --git a/CVE-2020-9490.patch b/CVE-2020-9490.patch new file mode 100644 index 0000000000000000000000000000000000000000..d0ef9625d9dc43078efb6847984d06292d059262 --- /dev/null +++ b/CVE-2020-9490.patch @@ -0,0 +1,394 @@ +From f1e4032670b82a84a469f6506de9052fd9df54f8 Mon Sep 17 00:00:00 2001 +From: Stefan Eissing +Date: Wed, 29 Jul 2020 12:15:58 +0000 +Subject: [PATCH] *) mod_http2: remote support for abandoned http-wg draft + . + +git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1880395 13f79535-47bb-0310-9956-ffa450edef68 +--- + modules/http2/h2_push.c | 255 ++++--------------------------------- + modules/http2/h2_push.h | 54 +++++--- + 2 files changed, 64 insertions(+), 245 deletions(-) + +diff --git a/modules/http2/h2_push.c b/modules/http2/h2_push.c +index 60488cf..dc21e1e 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 bc24e68..d061dd8 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/httpd.spec b/httpd.spec index 26511b3d9ed83ac53c0cd3a535f1ae890b379efa..942d8f8a942ea3296ccf29245e6f3cec23d42480 100644 --- a/httpd.spec +++ b/httpd.spec @@ -8,7 +8,7 @@ Name: httpd Summary: Apache HTTP Server Version: 2.4.43 -Release: 2 +Release: 3 License: ASL 2.0 URL: https://httpd.apache.org/ Source0: https://archive.apache.org/dist/httpd/httpd-%{version}.tar.bz2 @@ -66,6 +66,9 @@ Patch12: httpd-2.4.34-sslprotdefault.patch Patch13: httpd-2.4.34-enable-sslv3.patch Patch14: layout_add_openEuler.patch Patch15: httpd-2.4.43-lua-resume.patch +Patch16: CVE-2020-11984.patch +Patch17: CVE-2020-11993.patch +Patch18: CVE-2020-9490.patch BuildRequires: gcc autoconf pkgconfig findutils xmlto perl-interpreter perl-generators systemd-devel BuildRequires: zlib-devel libselinux-devel lua-devel brotli-devel @@ -502,6 +505,12 @@ exit $rv %{_rpmconfigdir}/macros.d/macros.httpd %changelog +* Sun Sep 27 2020 yuboyun - 2.4.43-3 +- Type:cves +- ID:CVE-2020-9490 CVE-2020-11984 CVE-2020-11993 +- SUG:restart +- DESC:fix CVE-2020-9490CVE-2020-11984CVE-2020-11993 + * Sat Sep 5 2020 zhaowei - 2.4.43-2 - Type:enhancement - ID:NA