changeset 8530:f882b1784f30 quic

QUIC: enforce flow control on incoming STREAM and CRYPTO frames.
author Roman Arutyunyan <arut@nginx.com>
date Tue, 25 Aug 2020 17:22:57 +0300
parents eaea7dac3292
children 4ff2a0b747d1
files src/event/ngx_event_quic.c
diffstat 1 files changed, 41 insertions(+), 13 deletions(-) [+]
line wrap: on
line diff
--- a/src/event/ngx_event_quic.c
+++ b/src/event/ngx_event_quic.c
@@ -2802,13 +2802,6 @@ ngx_quic_buffer_frame(ngx_connection_t *
 
     /* frame start offset is in the future, buffer it */
 
-    /* check limit on total size used by all buffered frames, not actual data */
-    if (NGX_QUIC_MAX_BUFFERED - fs->total < f->length) {
-        ngx_log_error(NGX_LOG_INFO, c->log, 0,
-                      "quic ordered input buffer limit exceeded");
-        return NGX_ERROR;
-    }
-
     dst = ngx_quic_alloc_frame(c, f->length);
     if (dst == NULL) {
         return NGX_ERROR;
@@ -2857,11 +2850,22 @@ static ngx_int_t
 ngx_quic_handle_crypto_frame(ngx_connection_t *c, ngx_quic_header_t *pkt,
     ngx_quic_frame_t *frame)
 {
+    uint64_t                   last;
     ngx_quic_connection_t     *qc;
+    ngx_quic_crypto_frame_t   *f;
     ngx_quic_frames_stream_t  *fs;
 
     qc = c->quic;
     fs = &qc->crypto[pkt->level];
+    f = &frame->u.crypto;
+
+    /* no overflow since both values are 62-bit */
+    last = f->offset + f->length;
+
+    if (last > fs->received && last - fs->received > NGX_QUIC_MAX_BUFFERED) {
+        c->quic->error = NGX_QUIC_ERR_CRYPTO_BUFFER_EXCEEDED;
+        return NGX_ERROR;
+    }
 
     return ngx_quic_handle_ordered_frame(c, fs, frame, ngx_quic_crypto_input,
                                          NULL);
@@ -2978,6 +2982,9 @@ static ngx_int_t
 ngx_quic_handle_stream_frame(ngx_connection_t *c, ngx_quic_header_t *pkt,
     ngx_quic_frame_t *frame)
 {
+    size_t                     window;
+    uint64_t                   last;
+    ngx_buf_t                 *b;
     ngx_pool_t                *pool;
     ngx_connection_t          *sc;
     ngx_quic_stream_t         *sn;
@@ -2995,6 +3002,9 @@ ngx_quic_handle_stream_frame(ngx_connect
         return NGX_ERROR;
     }
 
+    /* no overflow since both values are 62-bit */
+    last = f->offset + f->length;
+
     sn = ngx_quic_find_stream(&qc->streams.tree, f->stream_id);
 
     if (sn == NULL) {
@@ -3010,17 +3020,19 @@ ngx_quic_handle_stream_frame(ngx_connect
 
         sc = sn->c;
         fs = &sn->fs;
+        b = sn->b;
+        window = b->end - b->last;
+
+        if (last > window) {
+            c->quic->error = NGX_QUIC_ERR_FLOW_CONTROL_ERROR;
+            goto cleanup;
+        }
 
         if (ngx_quic_handle_ordered_frame(c, fs, frame, ngx_quic_stream_input,
                                           sn)
             != NGX_OK)
         {
-            pool = sc->pool;
-
-            ngx_close_connection(sc);
-            ngx_destroy_pool(pool);
-
-            return NGX_ERROR;
+            goto cleanup;
         }
 
         sc->listening->handler(sc);
@@ -3029,9 +3041,25 @@ ngx_quic_handle_stream_frame(ngx_connect
     }
 
     fs = &sn->fs;
+    b = sn->b;
+    window = (b->pos - b->start) + (b->end - b->last);
+
+    if (last > fs->received && last - fs->received > window) {
+        c->quic->error = NGX_QUIC_ERR_FLOW_CONTROL_ERROR;
+        return NGX_ERROR;
+    }
 
     return ngx_quic_handle_ordered_frame(c, fs, frame, ngx_quic_stream_input,
                                          sn);
+
+cleanup:
+
+    pool = sc->pool;
+
+    ngx_close_connection(sc);
+    ngx_destroy_pool(pool);
+
+    return NGX_ERROR;
 }