You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@httpd.apache.org by ic...@apache.org on 2016/12/04 22:06:31 UTC

svn commit: r1772576 - in /httpd/httpd/trunk: CHANGES modules/http2/h2_session.c modules/http2/h2_stream.c

Author: icing
Date: Sun Dec  4 22:06:30 2016
New Revision: 1772576

URL: http://svn.apache.org/viewvc?rev=1772576&view=rev
Log:
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 <na...@asu.edu> and CDF/SEFCOM at Arizona State University


Modified:
    httpd/httpd/trunk/CHANGES
    httpd/httpd/trunk/modules/http2/h2_session.c
    httpd/httpd/trunk/modules/http2/h2_stream.c

Modified: httpd/httpd/trunk/CHANGES
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/CHANGES?rev=1772576&r1=1772575&r2=1772576&view=diff
==============================================================================
--- httpd/httpd/trunk/CHANGES [utf-8] (original)
+++ httpd/httpd/trunk/CHANGES [utf-8] Sun Dec  4 22:06:30 2016
@@ -1,6 +1,10 @@
                                                          -*- coding: utf-8 -*-
 Changes with Apache 2.5.0
 
+  *) mod_http2: CVE-2016-8740: Mitigate DoS memory exhaustion via endless
+     CONTINUATION frames.
+     [Naveen Tiwari <na...@asu.edu> and CDF/SEFCOM at Arizona State University, Stefan Eissing]
+
   *) mod_lua: Fix default value of LuaInherit directive. It should be 
      'parent-first' instead of 'none', as per documentation.  PR 60419
      [Christophe Jaillet]

Modified: httpd/httpd/trunk/modules/http2/h2_session.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/h2_session.c?rev=1772576&r1=1772575&r2=1772576&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/http2/h2_session.c (original)
+++ httpd/httpd/trunk/modules/http2/h2_session.c Sun Dec  4 22:06:30 2016
@@ -394,7 +394,7 @@ static int on_header_cb(nghttp2_session
     (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
     
     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;

Modified: httpd/httpd/trunk/modules/http2/h2_stream.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/h2_stream.c?rev=1772576&r1=1772575&r2=1772576&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/http2/h2_stream.c (original)
+++ httpd/httpd/trunk/modules/http2/h2_stream.c Sun Dec  4 22:06:30 2016
@@ -333,45 +333,50 @@ apr_status_t h2_stream_add_header(h2_str
                                   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,