Commit 78f348a5 authored by Stefan Eissing's avatar Stefan Eissing
Browse files

Merge of r771160,1772576 from trunk:

SECURITY: CVE-2016-8740

mod_http2: properly crafted, endless HTTP/2 CONTINUATION frames could be used to exhaust all server's memory.

Reported by: Naveen Tiwari <naveen.tiwari@asu.edu> and CDF/SEFCOM at Arizona State University

mod_http2: wseaking cleanup assertion on streams that have never been scheduled


git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x@1772579 13f79535-47bb-0310-9956-ffa450edef68
parent ec0060f2
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -2,6 +2,10 @@

Changes with Apache 2.4.24

  *) mod_http2: CVE-2016-8740: Mitigate DoS memory exhaustion via endless
     CONTINUATION frames.
     [Naveen Tiwari <naveen.tiwari@asu.edu> and CDF/SEFCOM at Arizona State University, Stefan Eissing]

  *) mod_socache_memcache: Pass expiration time through to memcached.
     [Faidon Liambotis <paravoid debian.org>, Joe Orton]

+1 −0
Original line number Diff line number Diff line
@@ -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) {
+9 −2
Original line number Diff line number Diff line
@@ -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;
+36 −29
Original line number Diff line number Diff line
@@ -200,6 +200,7 @@ 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,17 +333,20 @@ 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 (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): pseudo header %s too long", 
                          stream->session->id, stream->id, name);
                return h2_stream_set_error(stream, 
                                           HTTP_REQUEST_URI_TOO_LARGE);
            error = HTTP_REQUEST_URI_TOO_LARGE;
        }
    }
    else if ((nlen + 2 + vlen) > stream->session->s->limit_req_fieldsize) {
@@ -350,27 +354,29 @@ apr_status_t h2_stream_add_header(h2_stream *stream,
        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);
            return h2_stream_set_error(stream, 
                                       HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE);
        error = HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE;
    }
    
        if (name[0] != ':') {
            ++stream->request_headers_added;
    if (stream->request_headers_added 
                > stream->session->s->limit_req_fields) {
                /* too many header lines */
        > 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);
                return h2_stream_set_error(stream, 
                                           HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE);
            }
        }
        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);
            
+2 −2
Original line number Diff line number Diff line
@@ -26,7 +26,7 @@
 * @macro
 * Version number of the http2 module as c string
 */
#define MOD_HTTP2_VERSION "1.8.2"
#define MOD_HTTP2_VERSION "1.8.3"

/**
 * @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 */