Blame SOURCES/nghttp2-1.7.1-CVE-2020-11080.patch

6b7b20
diff --git a/lib/includes/nghttp2/nghttp2.h b/lib/includes/nghttp2/nghttp2.h
6b7b20
index 59fc867..3b14027 100644
6b7b20
--- a/lib/includes/nghttp2/nghttp2.h
6b7b20
+++ b/lib/includes/nghttp2/nghttp2.h
6b7b20
@@ -221,6 +221,13 @@ typedef struct {
6b7b20
  */
6b7b20
 #define NGHTTP2_CLIENT_MAGIC_LEN 24
6b7b20
 
6b7b20
+/**
6b7b20
+ * @macro
6b7b20
+ *
6b7b20
+ * The default max number of settings per SETTINGS frame
6b7b20
+ */
6b7b20
+#define NGHTTP2_DEFAULT_MAX_SETTINGS 32
6b7b20
+
6b7b20
 /**
6b7b20
  * @enum
6b7b20
  *
6b7b20
@@ -382,6 +389,11 @@ typedef enum {
6b7b20
    * Unexpected internal error, but recovered.
6b7b20
    */
6b7b20
   NGHTTP2_ERR_INTERNAL = -534,
6b7b20
+  /**
6b7b20
+   * When a local endpoint receives too many settings entries
6b7b20
+   * in a single SETTINGS frame.
6b7b20
+   */
6b7b20
+  NGHTTP2_ERR_TOO_MANY_SETTINGS = -537,
6b7b20
   /**
6b7b20
    * The errors < :enum:`NGHTTP2_ERR_FATAL` mean that the library is
6b7b20
    * under unexpected condition and processing was terminated (e.g.,
6b7b20
@@ -2106,6 +2118,17 @@ NGHTTP2_EXTERN void
6b7b20
 nghttp2_option_set_max_reserved_remote_streams(nghttp2_option *option,
6b7b20
                                                uint32_t val);
6b7b20
 
6b7b20
+/**
6b7b20
+ * @function
6b7b20
+ *
6b7b20
+ * This function sets the maximum number of SETTINGS entries per
6b7b20
+ * SETTINGS frame that will be accepted. If more than those entries
6b7b20
+ * are received, the peer is considered to be misbehaving and session
6b7b20
+ * will be closed. The default value is 32.
6b7b20
+ */
6b7b20
+NGHTTP2_EXTERN void nghttp2_option_set_max_settings(nghttp2_option *option,
6b7b20
+                                                    size_t val);
6b7b20
+
6b7b20
 /**
6b7b20
  * @function
6b7b20
  *
6b7b20
diff --git a/lib/nghttp2_helper.c b/lib/nghttp2_helper.c
6b7b20
index 884abc6..b4d9e55 100644
6b7b20
--- a/lib/nghttp2_helper.c
6b7b20
+++ b/lib/nghttp2_helper.c
6b7b20
@@ -297,6 +297,8 @@ const char *nghttp2_strerror(int error_code) {
6b7b20
   case NGHTTP2_ERR_FLOODED:
6b7b20
     return "Flooding was detected in this HTTP/2 session, and it must be "
6b7b20
            "closed";
6b7b20
+  case NGHTTP2_ERR_TOO_MANY_SETTINGS:
6b7b20
+    return "SETTINGS frame contained more than the maximum allowed entries";
6b7b20
   default:
6b7b20
     return "Unknown error code";
6b7b20
   }
6b7b20
diff --git a/lib/nghttp2_option.c b/lib/nghttp2_option.c
6b7b20
index 04dbbc6..e8ee7b4 100644
6b7b20
--- a/lib/nghttp2_option.c
6b7b20
+++ b/lib/nghttp2_option.c
6b7b20
@@ -62,3 +62,8 @@ void nghttp2_option_set_max_reserved_remote_streams(nghttp2_option *option,
6b7b20
   option->opt_set_mask |= NGHTTP2_OPT_MAX_RESERVED_REMOTE_STREAMS;
6b7b20
   option->max_reserved_remote_streams = val;
6b7b20
 }
6b7b20
+
6b7b20
+void nghttp2_option_set_max_settings(nghttp2_option *option, size_t val) {
6b7b20
+   option->opt_set_mask |= NGHTTP2_OPT_MAX_SETTINGS;
6b7b20
+   option->max_settings = val;
6b7b20
+}
6b7b20
diff --git a/lib/nghttp2_option.h b/lib/nghttp2_option.h
6b7b20
index ebf416a..1ae1a4f 100644
6b7b20
--- a/lib/nghttp2_option.h
6b7b20
+++ b/lib/nghttp2_option.h
6b7b20
@@ -59,7 +59,8 @@ typedef enum {
6b7b20
   NGHTTP2_OPT_PEER_MAX_CONCURRENT_STREAMS = 1 << 1,
6b7b20
   NGHTTP2_OPT_NO_RECV_CLIENT_MAGIC = 1 << 2,
6b7b20
   NGHTTP2_OPT_NO_HTTP_MESSAGING = 1 << 3,
6b7b20
-  NGHTTP2_OPT_MAX_RESERVED_REMOTE_STREAMS = 1 << 4
6b7b20
+  NGHTTP2_OPT_MAX_RESERVED_REMOTE_STREAMS = 1 << 4,
6b7b20
+  NGHTTP2_OPT_MAX_SETTINGS = 1 << 12,
6b7b20
 } nghttp2_option_flag;
6b7b20
 
6b7b20
 /**
6b7b20
@@ -91,6 +92,11 @@ struct nghttp2_option {
6b7b20
    * NGHTTP2_OPT_NO_HTTP_MESSAGING
6b7b20
    */
6b7b20
   int no_http_messaging;
6b7b20
+
6b7b20
+  /**
6b7b20
+   * NGHTTP2_OPT_MAX_SETTINGS
6b7b20
+   */
6b7b20
+  size_t max_settings;
6b7b20
 };
6b7b20
 
6b7b20
 #endif /* NGHTTP2_OPTION_H */
6b7b20
diff --git a/lib/nghttp2_session.c b/lib/nghttp2_session.c
6b7b20
index e42e46b..a3a84b4 100644
6b7b20
--- a/lib/nghttp2_session.c
6b7b20
+++ b/lib/nghttp2_session.c
6b7b20
@@ -375,6 +375,8 @@ static int session_new(nghttp2_session **session_ptr,
6b7b20
   /* Limit max outgoing concurrent streams to sensible value */
6b7b20
   (*session_ptr)->remote_settings.max_concurrent_streams = 100;
6b7b20
 
6b7b20
+  (*session_ptr)->max_settings = NGHTTP2_DEFAULT_MAX_SETTINGS;
6b7b20
+
6b7b20
   if (option) {
6b7b20
     if ((option->opt_set_mask & NGHTTP2_OPT_NO_AUTO_WINDOW_UPDATE) &&
6b7b20
         option->no_auto_window_update) {
6b7b20
@@ -405,6 +407,11 @@ static int session_new(nghttp2_session **session_ptr,
6b7b20
 
6b7b20
       (*session_ptr)->opt_flags |= NGHTTP2_OPTMASK_NO_HTTP_MESSAGING;
6b7b20
     }
6b7b20
+
6b7b20
+    if ((option->opt_set_mask & NGHTTP2_OPT_MAX_SETTINGS) &&
6b7b20
+         option->max_settings) {
6b7b20
+       (*session_ptr)->max_settings = option->max_settings;
6b7b20
+    }
6b7b20
   }
6b7b20
 
6b7b20
   (*session_ptr)->callbacks = *callbacks;
6b7b20
@@ -4837,6 +4844,7 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in,
6b7b20
   ssize_t padlen;
6b7b20
   int rv;
6b7b20
   int busy = 0;
6b7b20
+  int max_niv;
6b7b20
   nghttp2_frame_hd cont_hd;
6b7b20
   nghttp2_stream *stream;
6b7b20
   size_t pri_fieldlen;
6b7b20
@@ -5123,9 +5131,30 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in,
6b7b20
           break;
6b7b20
         }
6b7b20
 
6b7b20
+        /* Check the settings flood counter early to be safe */
6b7b20
+        if (session->obq_flood_counter_ >= NGHTTP2_MAX_OBQ_FLOOD_ITEM &&
6b7b20
+            !(iframe->frame.hd.flags & NGHTTP2_FLAG_ACK)) {
6b7b20
+          return NGHTTP2_ERR_FLOODED;
6b7b20
+        }
6b7b20
+
6b7b20
         iframe->state = NGHTTP2_IB_READ_SETTINGS;
6b7b20
 
6b7b20
         if (iframe->payloadleft) {
6b7b20
+          /* We allocate iv with additional one entry, to store the
6b7b20
+             minimum header table size. */
6b7b20
+          max_niv =
6b7b20
+               iframe->frame.hd.length / NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH + 1;
6b7b20
+
6b7b20
+          if (max_niv - 1 > session->max_settings) {
6b7b20
+            rv = nghttp2_session_terminate_session_with_reason(
6b7b20
+                session, NGHTTP2_ENHANCE_YOUR_CALM,
6b7b20
+                "SETTINGS: too many setting entries");
6b7b20
+            if (nghttp2_is_fatal(rv)) {
6b7b20
+              return rv;
6b7b20
+            }
6b7b20
+            return (ssize_t)inlen;
6b7b20
+          }
6b7b20
+
6b7b20
           inbound_frame_set_mark(iframe, NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH);
6b7b20
           break;
6b7b20
         }
6b7b20
@@ -6531,6 +6560,11 @@ static int nghttp2_session_upgrade_internal(nghttp2_session *session,
6b7b20
   if (settings_payloadlen % NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH) {
6b7b20
     return NGHTTP2_ERR_INVALID_ARGUMENT;
6b7b20
   }
6b7b20
+  /* SETTINGS frame contains too many settings */
6b7b20
+  if (settings_payloadlen / NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH
6b7b20
+        > session->max_settings) {
6b7b20
+    return NGHTTP2_ERR_TOO_MANY_SETTINGS;
6b7b20
+  }
6b7b20
   rv = nghttp2_frame_unpack_settings_payload2(&iv, &niv, settings_payload,
6b7b20
                                               settings_payloadlen, mem);
6b7b20
   if (rv != 0) {
6b7b20
diff --git a/lib/nghttp2_session.h b/lib/nghttp2_session.h
6b7b20
index 204aea9..87aac84 100644
6b7b20
--- a/lib/nghttp2_session.h
6b7b20
+++ b/lib/nghttp2_session.h
6b7b20
@@ -244,6 +244,8 @@ struct nghttp2_session {
6b7b20
   size_t nvbuflen;
6b7b20
   /* Counter for detecting flooding in outbound queue */
6b7b20
   size_t obq_flood_counter_;
6b7b20
+  /* The maximum number of settings accepted per SETTINGS frame. */
6b7b20
+  size_t max_settings;
6b7b20
   /* Next Stream ID. Made unsigned int to detect >= (1 << 31). */
6b7b20
   uint32_t next_stream_id;
6b7b20
   /* The last stream ID this session initiated.  For client session,