]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
http3: check stream_ctx more thoroughly in all backends
authorStefan Eissing <stefan@eissing.org>
Thu, 13 Apr 2023 09:03:50 +0000 (11:03 +0200)
committerDaniel Stenberg <daniel@haxx.se>
Thu, 13 Apr 2023 21:53:36 +0000 (23:53 +0200)
- callbacks and filter methods might be invoked at unexpected
  times, e.g. when the transfer's stream_ctx has not been initialized
  yet or, more likely, has already been taken down.
- check for existance of stream_ctx in such places and return
  an error or silently succeed the call.

Closes #10951

lib/vquic/curl_msh3.c
lib/vquic/curl_ngtcp2.c
lib/vquic/curl_quiche.c

index d6aaa47bbaee5da9fe5774d818f998370ce7077c..1c35291d89fe775384ab3c4e5b8517960527ee7a 100644 (file)
@@ -288,6 +288,9 @@ static CURLcode write_resp_raw(struct Curl_easy *data,
   CURLcode result = CURLE_OK;
   ssize_t nwritten;
 
+  if(!stream)
+    return CURLE_RECV_ERROR;
+
   nwritten = Curl_bufq_write(&stream->recvbuf, mem, memlen, &result);
   if(nwritten < 0) {
     return result;
@@ -311,7 +314,7 @@ static void MSH3_CALL msh3_header_received(MSH3_REQUEST *Request,
   CURLcode result;
   (void)Request;
 
-  if(stream->recv_header_complete) {
+  if(!stream || stream->recv_header_complete) {
     return;
   }
 
@@ -366,6 +369,9 @@ static bool MSH3_CALL msh3_data_received(MSH3_REQUEST *Request,
    * length (buflen is an inout parameter).
    */
   (void)Request;
+  if(!stream)
+    return FALSE;
+
   msh3_lock_acquire(&stream->recv_lock);
 
   if(!stream->recv_header_complete) {
@@ -395,6 +401,8 @@ static void MSH3_CALL msh3_complete(MSH3_REQUEST *Request, void *IfContext,
   struct stream_ctx *stream = H3_STREAM_CTX(data);
 
   (void)Request;
+  if(!stream)
+    return;
   msh3_lock_acquire(&stream->recv_lock);
   stream->closed = TRUE;
   stream->recv_header_complete = true;
@@ -410,6 +418,9 @@ static void MSH3_CALL msh3_shutdown_complete(MSH3_REQUEST *Request,
 {
   struct Curl_easy *data = IfContext;
   struct stream_ctx *stream = H3_STREAM_CTX(data);
+
+  if(!stream)
+    return;
   (void)Request;
   (void)stream;
 }
@@ -419,6 +430,8 @@ static void MSH3_CALL msh3_data_sent(MSH3_REQUEST *Request,
 {
   struct Curl_easy *data = IfContext;
   struct stream_ctx *stream = H3_STREAM_CTX(data);
+  if(!stream)
+    return;
   (void)Request;
   (void)stream;
   (void)SendContext;
@@ -431,6 +444,10 @@ static ssize_t recv_closed_stream(struct Curl_cfilter *cf,
   struct stream_ctx *stream = H3_STREAM_CTX(data);
   ssize_t nread = -1;
 
+  if(!stream) {
+    *err = CURLE_RECV_ERROR;
+    return -1;
+  }
   (void)cf;
   if(stream->reset) {
     failf(data, "HTTP/3 stream reset by server");
@@ -480,6 +497,10 @@ static ssize_t cf_msh3_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
   struct cf_call_data save;
 
   (void)cf;
+  if(!stream) {
+    *err = CURLE_RECV_ERROR;
+    return -1;
+  }
   CF_DATA_SAVE(save, cf, data);
   DEBUGF(LOG_CF(data, cf, "req: recv with %zu byte buffer", len));
 
@@ -535,6 +556,7 @@ static ssize_t cf_msh3_send(struct Curl_cfilter *cf, struct Curl_easy *data,
   CF_DATA_SAVE(save, cf, data);
 
   /* Sizes must match for cast below to work" */
+  DEBUGASSERT(stream);
   DEBUGASSERT(sizeof(MSH3_HEADER) == sizeof(struct h2h3pseudo));
   DEBUGF(LOG_CF(data, cf, "req: send %zu bytes", len));
 
@@ -628,7 +650,7 @@ static bool cf_msh3_data_pending(struct Curl_cfilter *cf,
   CF_DATA_SAVE(save, cf, data);
 
   (void)cf;
-  if(stream->req) {
+  if(stream && stream->req) {
     msh3_lock_acquire(&stream->recv_lock);
     DEBUGF(LOG_CF((struct Curl_easy *)data, cf, "data pending = %zu",
                   Curl_bufq_len(&stream->recvbuf)));
@@ -678,11 +700,14 @@ static CURLcode cf_msh3_data_event(struct Curl_cfilter *cf,
     break;
   case CF_CTRL_DATA_DONE_SEND:
     DEBUGF(LOG_CF(data, cf, "req: send done"));
-    stream->upload_done = TRUE;
-    if(stream && stream->req) {
-      char buf[1];
-      if(!MsH3RequestSend(stream->req, MSH3_REQUEST_FLAG_FIN, buf, 0, data)) {
-        result = CURLE_SEND_ERROR;
+    if(stream) {
+      stream->upload_done = TRUE;
+      if(stream->req) {
+        char buf[1];
+        if(!MsH3RequestSend(stream->req, MSH3_REQUEST_FLAG_FIN,
+                            buf, 0, data)) {
+          result = CURLE_SEND_ERROR;
+        }
       }
     }
     break;
index 26bc80c9c4bf5140ed8e36bcac227171f36d4233..47b66efa53c5e225c7d7483276ccf442e79657b7 100644 (file)
@@ -688,6 +688,8 @@ static void report_consumed_data(struct Curl_cfilter *cf,
   struct stream_ctx *stream = H3_STREAM_CTX(data);
   struct cf_ngtcp2_ctx *ctx = cf->ctx;
 
+  if(!stream)
+    return;
   /* the HTTP/1.1 response headers are written to the buffer, but
    * consuming those does not count against flow control. */
   if(stream->recv_buf_nonflow) {
@@ -984,7 +986,7 @@ static int cf_ngtcp2_get_select_socks(struct Curl_cfilter *cf,
   if((k->keepon & KEEP_SENDBITS) == KEEP_SEND &&
      ngtcp2_conn_get_cwnd_left(ctx->qconn) &&
      ngtcp2_conn_get_max_data_left(ctx->qconn) &&
-     nghttp3_conn_is_stream_writable(ctx->h3conn, stream->id))
+     stream && nghttp3_conn_is_stream_writable(ctx->h3conn, stream->id))
     rv |= GETSOCK_WRITESOCK(0);
 
   DEBUGF(LOG_CF(data, cf, "get_select_socks -> %x (sock=%d)",
@@ -1015,6 +1017,10 @@ static int cb_h3_stream_close(nghttp3_conn *conn, int64_t stream_id,
   (void)app_error_code;
   (void)cf;
 
+  /* we might be called by nghttp3 after we already cleaned up */
+  if(!stream)
+    return 0;
+
   DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] h3 close(err=%" PRId64 ")",
                 stream_id, app_error_code));
   stream->closed = TRUE;
@@ -1041,6 +1047,9 @@ static CURLcode write_resp_raw(struct Curl_cfilter *cf,
   ssize_t nwritten;
 
   (void)cf;
+  if(!stream) {
+    return CURLE_RECV_ERROR;
+  }
   nwritten = Curl_bufq_write(&stream->recvbuf, mem, memlen, &result);
   /* DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] add recvbuf(len=%zu) "
                 "-> %zd, %d", stream->id, memlen, nwritten, result));
@@ -1107,6 +1116,8 @@ static int cb_h3_end_headers(nghttp3_conn *conn, int64_t stream_id,
   (void)fin;
   (void)cf;
 
+  if(!stream)
+    return 0;
   /* add a CRLF only if we've received some headers */
   result = write_resp_raw(cf, data, "\r\n", 2, FALSE);
   if(result) {
@@ -1141,6 +1152,10 @@ static int cb_h3_recv_header(nghttp3_conn *conn, int64_t stream_id,
   (void)flags;
   (void)cf;
 
+  /* we might have cleaned up this transfer already */
+  if(!stream)
+    return 0;
+
   if(token == NGHTTP3_QPACK_TOKEN__STATUS) {
     char line[14]; /* status line is always 13 characters long */
     size_t ncopy;
@@ -1306,7 +1321,7 @@ static ssize_t recv_closed_stream(struct Curl_cfilter *cf,
   ssize_t nread = -1;
 
   (void)cf;
-
+  DEBUGASSERT(stream);
   if(stream->reset) {
     failf(data,
           "HTTP/3 stream %" PRId64 " reset by server", stream->id);
@@ -1365,6 +1380,11 @@ static ssize_t cf_ngtcp2_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
   DEBUGASSERT(ctx->h3conn);
   *err = CURLE_OK;
 
+  if(!stream) {
+    *err = CURLE_RECV_ERROR;
+    goto out;
+  }
+
   if(!Curl_bufq_is_empty(&stream->recvbuf)) {
     nread = Curl_bufq_read(&stream->recvbuf,
                            (unsigned char *)buf, len, err);
@@ -1414,7 +1434,7 @@ out:
     goto out;
   }
   DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] cf_recv(len=%zu) -> %zd, %d",
-                stream->id, len, nread, *err));
+                stream? stream->id : -1, len, nread, *err));
   CF_DATA_RESTORE(cf, save);
   return nread;
 }
@@ -1428,6 +1448,8 @@ static int cb_h3_acked_req_body(nghttp3_conn *conn, int64_t stream_id,
   struct stream_ctx *stream = H3_STREAM_CTX(data);
 
   (void)cf;
+  if(!stream)
+    return 0;
   /* The server ackknowledged `datalen` of bytes from our request body.
    * This is a delta. We have kept this data in `sendbuf` for
    * re-transmissions and can free it now. */
@@ -1471,6 +1493,8 @@ cb_h3_read_req_body(nghttp3_conn *conn, int64_t stream_id,
   (void)user_data;
   (void)veccnt;
 
+  if(!stream)
+    return NGHTTP3_ERR_CALLBACK_FAILURE;
   /* nghttp3 keeps references to the sendbuf data until it is ACKed
    * by the server (see `cb_h3_acked_req_body()` for updates).
    * `sendbuf_len_in_flight` is the amount of bytes in `sendbuf`
@@ -2065,7 +2089,7 @@ static bool cf_ngtcp2_data_pending(struct Curl_cfilter *cf,
 {
   const struct stream_ctx *stream = H3_STREAM_CTX(data);
   (void)cf;
-  return !Curl_bufq_is_empty(&stream->recvbuf);
+  return stream && !Curl_bufq_is_empty(&stream->recvbuf);
 }
 
 static CURLcode cf_ngtcp2_data_event(struct Curl_cfilter *cf,
@@ -2088,8 +2112,10 @@ static CURLcode cf_ngtcp2_data_event(struct Curl_cfilter *cf,
   }
   case CF_CTRL_DATA_DONE_SEND: {
     struct stream_ctx *stream = H3_STREAM_CTX(data);
-    stream->upload_done = TRUE;
-    (void)nghttp3_conn_resume_stream(ctx->h3conn, stream->id);
+    if(stream) {
+      stream->upload_done = TRUE;
+      (void)nghttp3_conn_resume_stream(ctx->h3conn, stream->id);
+    }
     break;
   }
   case CF_CTRL_DATA_IDLE:
index 69a65f01bce0c86a6d4706abaa908e65a8f8fd06..31a7174bf81b1efc1af2fde6f3bd4e0c4a950e26 100644 (file)
@@ -344,6 +344,8 @@ static CURLcode write_resp_raw(struct Curl_cfilter *cf,
   ssize_t nwritten;
 
   (void)cf;
+  if(!stream)
+    return CURLE_RECV_ERROR;
   nwritten = Curl_bufq_write(&stream->recvbuf, mem, memlen, &result);
   if(nwritten < 0)
     return result;
@@ -390,7 +392,7 @@ static int cb_each_header(uint8_t *name, size_t name_len,
   if(result) {
     DEBUGF(LOG_CF(x->data, x->cf,
                   "[h3sid=%"PRId64"][HEADERS][%.*s: %.*s] error %d",
-                  stream->id, (int)name_len, name,
+                  stream? stream->id : -1, (int)name_len, name,
                   (int)value_len, value, result));
   }
   return result;
@@ -405,6 +407,11 @@ static ssize_t stream_resp_read(void *reader_ctx,
   struct stream_ctx *stream = H3_STREAM_CTX(x->data);
   ssize_t nread;
 
+  if(!stream) {
+    *err = CURLE_RECV_ERROR;
+    return -1;
+  }
+
   nread = quiche_h3_recv_body(ctx->h3c, ctx->qconn, stream->id,
                               buf, len);
   if(nread >= 0) {
@@ -429,6 +436,9 @@ static CURLcode cf_recv_body(struct Curl_cfilter *cf,
   struct cb_ctx cb_ctx;
   CURLcode result = CURLE_OK;
 
+  if(!stream)
+    return CURLE_RECV_ERROR;
+
   if(!stream->resp_hds_complete) {
     result = write_resp_raw(cf, data, "\r\n", 2);
     if(result)
@@ -486,6 +496,8 @@ static CURLcode h3_process_event(struct Curl_cfilter *cf,
   CURLcode result = CURLE_OK;
   int rc;
 
+  if(!stream)
+    return CURLE_OK;
   DEBUGASSERT(stream3_id == stream->id);
   switch(quiche_h3_event_type(ev)) {
   case QUICHE_H3_EVENT_HEADERS:
@@ -764,6 +776,7 @@ static ssize_t recv_closed_stream(struct Curl_cfilter *cf,
   struct stream_ctx *stream = H3_STREAM_CTX(data);
   ssize_t nread = -1;
 
+  DEBUGASSERT(stream);
   if(stream->reset) {
     failf(data,
           "HTTP/3 stream %" PRId64 " reset by server", stream->id);
@@ -798,6 +811,11 @@ static ssize_t cf_quiche_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
   ssize_t nread = -1;
   CURLcode result;
 
+  if(!stream) {
+    *err = CURLE_RECV_ERROR;
+    goto out;
+  }
+
   if(!Curl_bufq_is_empty(&stream->recvbuf)) {
     nread = Curl_bufq_read(&stream->recvbuf,
                            (unsigned char *)buf, len, err);
@@ -868,6 +886,14 @@ static ssize_t h3_open_stream(struct Curl_cfilter *cf,
   quiche_h3_header *nva = NULL;
   struct h2h3req *hreq = NULL;
 
+  if(!stream) {
+    *err = h3_data_setup(cf, data);
+    if(*err)
+      goto fail;
+    stream = H3_STREAM_CTX(data);
+    DEBUGASSERT(stream);
+  }
+
   if(!stream->req_hds_len) {
     stream->req_hds_len = len;  /* fist call */
   }
@@ -969,10 +995,11 @@ static ssize_t cf_quiche_send(struct Curl_cfilter *cf, struct Curl_easy *data,
     goto out;
   }
 
-  if(stream->id < 0) {
+  if(!stream || stream->id < 0) {
     nwritten = h3_open_stream(cf, data, buf, len, err);
     if(nwritten < 0)
       goto out;
+    stream = H3_STREAM_CTX(data);
   }
   else {
     nwritten = quiche_h3_send_body(ctx->h3c, ctx->qconn, stream->id,
@@ -1019,7 +1046,7 @@ out:
     nwritten = -1;
   }
   DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] cf_send(len=%zu) -> %zd, %d",
-                stream->id, len, nwritten, *err));
+                stream? stream->id : -1, len, nwritten, *err));
   return nwritten;
 }
 
@@ -1028,10 +1055,13 @@ static bool stream_is_writeable(struct Curl_cfilter *cf,
 {
   struct cf_quiche_ctx *ctx = cf->ctx;
   struct stream_ctx *stream = H3_STREAM_CTX(data);
+  quiche_stream_iter *qiter;
   bool is_writable = FALSE;
 
+  if(!stream)
+    return FALSE;
   /* surely, there must be a better way */
-  quiche_stream_iter *qiter = quiche_conn_writable(ctx->qconn);
+  qiter = quiche_conn_writable(ctx->qconn);
   if(qiter) {
     uint64_t stream_id;
     while(quiche_stream_iter_next(qiter, &stream_id)) {
@@ -1076,7 +1106,7 @@ static bool cf_quiche_data_pending(struct Curl_cfilter *cf,
 {
   const struct stream_ctx *stream = H3_STREAM_CTX(data);
   (void)cf;
-  return !Curl_bufq_is_empty(&stream->recvbuf);
+  return stream && !Curl_bufq_is_empty(&stream->recvbuf);
 }
 
 static CURLcode cf_quiche_data_event(struct Curl_cfilter *cf,
@@ -1098,14 +1128,16 @@ static CURLcode cf_quiche_data_event(struct Curl_cfilter *cf,
   }
   case CF_CTRL_DATA_DONE_SEND: {
     struct stream_ctx *stream = H3_STREAM_CTX(data);
-    unsigned char body[1];
-    ssize_t sent;
-    stream->upload_done = TRUE;
-
-    body[0] = 'X';
-    sent = cf_quiche_send(cf, data, body, 0, &result);
-    DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"] DONE_SEND -> %zd, %d",
-                  stream->id, sent, result));
+    if(stream) {
+      unsigned char body[1];
+      ssize_t sent;
+      stream->upload_done = TRUE;
+
+      body[0] = 'X';
+      sent = cf_quiche_send(cf, data, body, 0, &result);
+      DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"] DONE_SEND -> %zd, %d",
+                    stream->id, sent, result));
+    }
     break;
   }
   case CF_CTRL_DATA_IDLE: