diff --git a/ChangeLog b/ChangeLog index 45c804fc..d36e3d00 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +v1.8.3 +-------------------------------------------------------------------------------- + * CVE-2016-8740: Mitigate DoS memory exhaustion via endless + CONTINUATION frames. Reported by Naveen Tiwari and + CDF/SEFCOM at Arizona State University + * Removing some warning when streams are cleaned up that were never scheduled + + v1.8.2 -------------------------------------------------------------------------------- * new directive 'H2EarlyHints' to enable sending of HTTP status 103 interim diff --git a/configure.ac b/configure.ac index 3525d852..932909c6 100644 --- a/configure.ac +++ b/configure.ac @@ -14,7 +14,7 @@ # AC_PREREQ([2.69]) -AC_INIT([mod_http2], [1.8.2], [stefan.eissing@greenbytes.de]) +AC_INIT([mod_http2], [1.8.3], [stefan.eissing@greenbytes.de]) LT_PREREQ([2.2.6]) LT_INIT() diff --git a/mod_http2/h2_mplx.c b/mod_http2/h2_mplx.c index 71500585..dd9c563a 100644 --- a/mod_http2/h2_mplx.c +++ b/mod_http2/h2_mplx.c @@ -899,6 +899,7 @@ static h2_task *next_stream_task(h2_mplx *m) h2_slave_run_pre_connection(slave, ap_get_conn_socket(slave)); } stream->started = 1; + stream->can_be_cleaned = 0; task->worker_started = 1; task->started_at = apr_time_now(); if (sid > m->max_stream_started) { diff --git a/mod_http2/h2_session.c b/mod_http2/h2_session.c index 0a29a3b1..44eed633 100644 --- a/mod_http2/h2_session.c +++ b/mod_http2/h2_session.c @@ -394,7 +394,7 @@ static int on_header_cb(nghttp2_session *ngh2, const nghttp2_frame *frame, (void)flags; stream = get_stream(session, frame->hd.stream_id); if (!stream) { - ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, session->c, + ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, APLOGNO(02920) "h2_session: stream(%ld-%d): on_header unknown stream", session->id, (int)frame->hd.stream_id); @@ -403,7 +403,14 @@ static int on_header_cb(nghttp2_session *ngh2, const nghttp2_frame *frame, status = h2_stream_add_header(stream, (const char *)name, namelen, (const char *)value, valuelen); - if (status != APR_SUCCESS && !h2_stream_is_ready(stream)) { + if (status == APR_ECONNRESET) { + ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, session->c, + "h2-stream(%ld-%d): on_header, reset stream", + session->id, stream->id); + nghttp2_submit_rst_stream(ngh2, NGHTTP2_FLAG_NONE, stream->id, + NGHTTP2_INTERNAL_ERROR); + } + else if (status != APR_SUCCESS && !h2_stream_is_ready(stream)) { return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; } return 0; diff --git a/mod_http2/h2_stream.c b/mod_http2/h2_stream.c index d3dd50cb..3f309492 100644 --- a/mod_http2/h2_stream.c +++ b/mod_http2/h2_stream.c @@ -200,7 +200,8 @@ h2_stream *h2_stream_open(int id, apr_pool_t *pool, h2_session *session, stream->state = H2_STREAM_ST_IDLE; stream->pool = pool; stream->session = session; - + stream->can_be_cleaned = 1; + h2_beam_create(&stream->input, pool, id, "input", H2_BEAM_OWNER_SEND, 0); h2_beam_create(&stream->output, pool, id, "output", H2_BEAM_OWNER_RECV, 0); @@ -332,45 +333,50 @@ apr_status_t h2_stream_add_header(h2_stream *stream, const char *name, size_t nlen, const char *value, size_t vlen) { + int error = 0; ap_assert(stream); - if (!stream->has_response) { - if (name[0] == ':') { - if ((vlen) > stream->session->s->limit_req_line) { - /* pseudo header: approximation of request line size check */ - ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, stream->session->c, - "h2_stream(%ld-%d): pseudo header %s too long", - stream->session->id, stream->id, name); - return h2_stream_set_error(stream, - HTTP_REQUEST_URI_TOO_LARGE); - } - } - else if ((nlen + 2 + vlen) > stream->session->s->limit_req_fieldsize) { - /* header too long */ + if (stream->has_response) { + return APR_EINVAL; + } + ++stream->request_headers_added; + if (name[0] == ':') { + if ((vlen) > stream->session->s->limit_req_line) { + /* pseudo header: approximation of request line size check */ ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, stream->session->c, - "h2_stream(%ld-%d): header %s too long", + "h2_stream(%ld-%d): pseudo header %s too long", stream->session->id, stream->id, name); - return h2_stream_set_error(stream, - HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE); + error = HTTP_REQUEST_URI_TOO_LARGE; } - - if (name[0] != ':') { - ++stream->request_headers_added; - if (stream->request_headers_added - > stream->session->s->limit_req_fields) { - /* too many header lines */ - ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, stream->session->c, - "h2_stream(%ld-%d): too many header lines", - stream->session->id, stream->id); - return h2_stream_set_error(stream, - HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE); - } + } + else if ((nlen + 2 + vlen) > stream->session->s->limit_req_fieldsize) { + /* header too long */ + ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, stream->session->c, + "h2_stream(%ld-%d): header %s too long", + stream->session->id, stream->id, name); + error = HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE; + } + + if (stream->request_headers_added + > stream->session->s->limit_req_fields + 4) { + /* too many header lines, include 4 pseudo headers */ + if (stream->request_headers_added + > stream->session->s->limit_req_fields + 4 + 100) { + /* yeah, right */ + return APR_ECONNRESET; } + ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, stream->session->c, + "h2_stream(%ld-%d): too many header lines", + stream->session->id, stream->id); + error = HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE; } if (h2_stream_is_scheduled(stream)) { return add_trailer(stream, name, nlen, value, vlen); } + else if (error) { + return h2_stream_set_error(stream, error); + } else { if (!stream->rtmp) { stream->rtmp = h2_req_create(stream->id, stream->pool, @@ -412,6 +418,7 @@ apr_status_t h2_stream_schedule(h2_stream *stream, int eos, int push_enabled, stream->request = stream->rtmp; stream->rtmp = NULL; stream->scheduled = 1; + stream->push_policy = h2_push_policy_determine(stream->request->headers, stream->pool, push_enabled); diff --git a/mod_http2/h2_version.h b/mod_http2/h2_version.h index 19167bea..2e3972ed 100644 --- a/mod_http2/h2_version.h +++ b/mod_http2/h2_version.h @@ -26,7 +26,7 @@ * @macro * Version number of the http2 module as c string */ -#define MOD_HTTP2_VERSION "1.8.2-git" +#define MOD_HTTP2_VERSION "1.8.3-git" /** * @macro @@ -34,7 +34,7 @@ * 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 0x010802 +#define MOD_HTTP2_VERSION_NUM 0x010803 #endif /* mod_h2_h2_version_h */