changeset 8282:4cf00c14f11a quic

Safe QUIC stream creation.
author Roman Arutyunyan <arut@nginx.com>
date Wed, 25 Mar 2020 12:56:21 +0300
parents 618a65de08b3
children dadbc66e9fca
files src/event/ngx_event_quic.c src/event/ngx_event_quic.h
diffstat 2 files changed, 54 insertions(+), 56 deletions(-) [+]
line wrap: on
line diff
--- a/src/event/ngx_event_quic.c
+++ b/src/event/ngx_event_quic.c
@@ -16,9 +16,6 @@ typedef enum {
 } ngx_quic_state_t;
 
 
-#define NGX_QUIC_STREAM_BUFSIZE        16384
-
-
 typedef struct {
     ngx_rbtree_t                      tree;
     ngx_rbtree_node_t                 sentinel;
@@ -122,7 +119,7 @@ static void ngx_quic_rbtree_insert_strea
 static ngx_quic_stream_t *ngx_quic_find_stream(ngx_rbtree_t *rbtree,
     ngx_uint_t key);
 static ngx_quic_stream_t *ngx_quic_create_stream(ngx_connection_t *c,
-    ngx_uint_t id);
+    uint64_t id, size_t rcvbuf_size);
 static ssize_t ngx_quic_stream_recv(ngx_connection_t *c, u_char *buf,
     size_t size);
 static ssize_t ngx_quic_stream_send(ngx_connection_t *c, u_char *buf,
@@ -1096,6 +1093,7 @@ static ngx_int_t
 ngx_quic_handle_stream_frame(ngx_connection_t *c,
     ngx_quic_header_t *pkt, ngx_quic_stream_frame_t *f)
 {
+    size_t                  n;
     ngx_buf_t              *b;
     ngx_event_t            *rev;
     ngx_quic_stream_t      *sn;
@@ -1137,15 +1135,28 @@ ngx_quic_handle_stream_frame(ngx_connect
 
     ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, "stream is new");
 
-    sn = ngx_quic_create_stream(c, f->stream_id);
+    n = (f->stream_id & NGX_QUIC_STREAM_UNIDIRECTIONAL)
+        ? qc->tp.initial_max_stream_data_uni
+        : qc->tp.initial_max_stream_data_bidi_remote;
+
+    if (n < NGX_QUIC_STREAM_BUFSIZE) {
+        n = NGX_QUIC_STREAM_BUFSIZE;
+    }
+
+    if (n < f->length) {
+        ngx_log_error(NGX_LOG_INFO, c->log, 0, "no space in stream buffer");
+        return NGX_ERROR;
+    }
+
+    sn = ngx_quic_create_stream(c, f->stream_id, n);
     if (sn == NULL) {
         return NGX_ERROR;
     }
 
     b = sn->b;
+    b->last = ngx_cpymem(b->last, f->data, f->length);
 
-    ngx_memcpy(b->start, f->data, f->length);
-    b->last = b->start + f->length;
+    sn->c->read->ready = 1;
 
     qc->streams.handler(sn->c);
 
@@ -1419,7 +1430,7 @@ ngx_quic_create_uni_stream(ngx_connectio
 
     qc->streams.id_counter++;
 
-    sn = ngx_quic_create_stream(qs->parent, id);
+    sn = ngx_quic_create_stream(qs->parent, id, 0);
     if (sn == NULL) {
         return NULL;
     }
@@ -1494,82 +1505,65 @@ ngx_quic_find_stream(ngx_rbtree_t *rbtre
 
 
 static ngx_quic_stream_t *
-ngx_quic_create_stream(ngx_connection_t *c, ngx_uint_t id)
+ngx_quic_create_stream(ngx_connection_t *c, uint64_t id, size_t rcvbuf_size)
 {
-    size_t                  n;
-    ngx_log_t              *log;
-    ngx_pool_t             *pool;
-    ngx_event_t            *rev, *wev;
-    ngx_quic_stream_t      *sn;
-    ngx_pool_cleanup_t     *cln;
-    ngx_quic_connection_t  *qc;
+    ngx_log_t           *log;
+    ngx_pool_t          *pool;
+    ngx_quic_stream_t   *sn;
+    ngx_pool_cleanup_t  *cln;
 
-    qc = c->quic;
-
-    sn = ngx_pcalloc(c->pool, sizeof(ngx_quic_stream_t));
-    if (sn == NULL) {
+    pool = ngx_create_pool(NGX_DEFAULT_POOL_SIZE, c->log);
+    if (pool == NULL) {
         return NULL;
     }
 
-    sn->c = ngx_get_connection(-1, c->log); // TODO: free on connection termination
-    if (sn->c == NULL) {
+    sn = ngx_pcalloc(pool, sizeof(ngx_quic_stream_t));
+    if (sn == NULL) {
+        ngx_destroy_pool(pool);
         return NULL;
     }
 
-    pool = ngx_create_pool(NGX_DEFAULT_POOL_SIZE, c->log);
-    if (pool == NULL) {
-        /* XXX free connection */
-        // TODO: add pool cleanup handdler
+    sn->node.key = id;
+    sn->parent = c;
+    sn->id = id;
+
+    sn->b = ngx_create_temp_buf(pool, rcvbuf_size);
+    if (sn->b == NULL) {
+        ngx_destroy_pool(pool);
         return NULL;
     }
 
     log = ngx_palloc(pool, sizeof(ngx_log_t));
     if (log == NULL) {
-        /* XXX free pool and connection */
+        ngx_destroy_pool(pool);
         return NULL;
     }
 
     *log = *c->log;
     pool->log = log;
 
-    sn->c->log = log;
-    sn->c->pool = pool;
-
-    sn->c->listening = c->listening;
-    sn->c->sockaddr = c->sockaddr;
-    sn->c->local_sockaddr = c->local_sockaddr;
-    sn->c->addr_text = c->addr_text;
-    sn->c->ssl = c->ssl;
-
-    rev = sn->c->read;
-    wev = sn->c->write;
-
-    rev->ready = 1;
-
-    rev->log = c->log;
-    wev->log = c->log;
-
-    sn->c->number = ngx_atomic_fetch_add(ngx_connection_counter, 1);
-
-    n = ngx_max(NGX_QUIC_STREAM_BUFSIZE,
-                qc->tp.initial_max_stream_data_bidi_remote);
-
-    sn->node.key =id;
-    sn->b = ngx_create_temp_buf(pool, n);
-    if (sn->b == NULL) {
+    sn->c = ngx_get_connection(-1, log);
+    if (sn->c == NULL) {
+        ngx_destroy_pool(pool);
         return NULL;
     }
 
-    ngx_rbtree_insert(&qc->streams.tree, &sn->node);
-
-    sn->id = id;
-    sn->parent = c;
     sn->c->qs = sn;
+    sn->c->pool = pool;
+    sn->c->ssl = c->ssl;
+    sn->c->sockaddr = c->sockaddr;
+    sn->c->listening = c->listening;
+    sn->c->addr_text = c->addr_text;
+    sn->c->local_sockaddr = c->local_sockaddr;
+    sn->c->number = ngx_atomic_fetch_add(ngx_connection_counter, 1);
 
     sn->c->recv = ngx_quic_stream_recv;
     sn->c->send = ngx_quic_stream_send;
     sn->c->send_chain = ngx_quic_stream_send_chain;
 
+    sn->c->read->log = c->log;
+    sn->c->write->log = c->log;
+
     cln = ngx_pool_cleanup_add(pool, 0);
     if (cln == NULL) {
         ngx_close_connection(sn->c);
@@ -1580,6 +1574,8 @@ ngx_quic_create_stream(ngx_connection_t 
     cln->handler = ngx_quic_stream_cleanup_handler;
     cln->data = sn->c;
 
+    ngx_rbtree_insert(&c->quic->streams.tree, &sn->node);
+
     return sn;
 }
 
--- a/src/event/ngx_event_quic.h
+++ b/src/event/ngx_event_quic.h
@@ -24,6 +24,8 @@
 #define NGX_QUIC_STREAM_SERVER_INITIATED     0x01
 #define NGX_QUIC_STREAM_UNIDIRECTIONAL       0x02
 
+#define NGX_QUIC_STREAM_BUFSIZE              16384
+
 
 typedef struct {
     /* configurable */