]> git.ipfire.org Git - thirdparty/apache/httpd.git/commitdiff
Merge of r771160,1772576 from trunk:
authorStefan Eissing <icing@apache.org>
Sun, 4 Dec 2016 22:28:45 +0000 (22:28 +0000)
committerStefan Eissing <icing@apache.org>
Sun, 4 Dec 2016 22:28:45 +0000 (22:28 +0000)
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

CHANGES
modules/http2/h2_mplx.c
modules/http2/h2_session.c
modules/http2/h2_stream.c
modules/http2/h2_version.h

diff --git a/CHANGES b/CHANGES
index dd69e7c0af1fb8ab9ba00a6ee3648a8574676149..404dbf8c90b38e64da0bd85d02e5fd91cbf2fe6a 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -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]
 
index 7150058558051282df92baa4476872955ebb30bf..dd9c563ac37968080362d41fab4cb5efbaf9feed 100644 (file)
@@ -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) {
index 0a29a3b18cca9eaa7c6aff767fba5974e8bc0d72..44eed633256aa6cc85b9dd9ae1a53975dec7b319 100644 (file)
@@ -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;
index d3dd50cb653a848aa5e8617efe99b4910af75a04..3f309492f1080c4f751d9cfe686d1800f606b2a0 100644 (file)
@@ -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);
             
index f272c3c71fe499eb37c0a81b27119d6a10865306..46906d6ec49def42890e4898a59553d285f6d95a 100644 (file)
@@ -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 */