]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
HTTP/2, HTTP/3: handle detach of onoing transfers
authorStefan Eissing <stefan@eissing.org>
Mon, 20 Nov 2023 10:32:19 +0000 (11:32 +0100)
committerDaniel Stenberg <daniel@haxx.se>
Tue, 21 Nov 2023 06:55:16 +0000 (07:55 +0100)
- refs #12356 where a UAF is reported when closing a connection
  with a stream whose easy handle was cleaned up already
- handle DETACH events same as DONE events in h2/h3 filters

Fixes #12356
Reported-by: Paweł Wegner
Closes #12364

lib/http2.c
lib/vquic/curl_ngtcp2.c
lib/vquic/curl_quiche.c

index c188f32a715ed0cc25ee1342f70ad0881d2a9425..973848484a44c77f78259fcf104f53ca98d04157 100644 (file)
@@ -2486,14 +2486,15 @@ static CURLcode cf_h2_cntrl(struct Curl_cfilter *cf,
   case CF_CTRL_DATA_PAUSE:
     result = http2_data_pause(cf, data, (arg1 != 0));
     break;
-  case CF_CTRL_DATA_DONE_SEND: {
+  case CF_CTRL_DATA_DONE_SEND:
     result = http2_data_done_send(cf, data);
     break;
-  }
-  case CF_CTRL_DATA_DONE: {
+  case CF_CTRL_DATA_DETACH:
+    http2_data_done(cf, data, TRUE);
+    break;
+  case CF_CTRL_DATA_DONE:
     http2_data_done(cf, data, arg1 != 0);
     break;
-  }
   default:
     break;
   }
index 68bbd13f2612ce51661f00444989f97b8522bde7..21afdabfaf7051f8c0db4251fbe3e159dcefa56c 100644 (file)
@@ -238,11 +238,20 @@ static CURLcode h3_data_setup(struct Curl_cfilter *cf,
 
 static void h3_data_done(struct Curl_cfilter *cf, struct Curl_easy *data)
 {
+  struct cf_ngtcp2_ctx *ctx = cf->ctx;
   struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
 
   (void)cf;
   if(stream) {
     CURL_TRC_CF(data, cf, "[%"PRId64"] easy handle is done", stream->id);
+    if(ctx->h3conn && !stream->closed) {
+      nghttp3_conn_shutdown_stream_read(ctx->h3conn, stream->id);
+      nghttp3_conn_close_stream(ctx->h3conn, stream->id,
+                                NGHTTP3_H3_REQUEST_CANCELLED);
+      nghttp3_conn_set_stream_user_data(ctx->h3conn, stream->id, NULL);
+      stream->closed = TRUE;
+    }
+
     Curl_bufq_free(&stream->sendbuf);
     Curl_bufq_free(&stream->recvbuf);
     Curl_h1_req_parse_free(&stream->h1);
@@ -2323,10 +2332,12 @@ static CURLcode cf_ngtcp2_data_event(struct Curl_cfilter *cf,
   case CF_CTRL_DATA_PAUSE:
     result = h3_data_pause(cf, data, (arg1 != 0));
     break;
-  case CF_CTRL_DATA_DONE: {
+  case CF_CTRL_DATA_DETACH:
+    h3_data_done(cf, data);
+    break;
+  case CF_CTRL_DATA_DONE:
     h3_data_done(cf, data);
     break;
-  }
   case CF_CTRL_DATA_DONE_SEND: {
     struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
     if(stream && !stream->send_closed) {
index 7959b0aaa1012073b111cd7b9756212b4a781f15..41cc8117f1161612478a71e4416d65cb14a88979 100644 (file)
@@ -55,7 +55,8 @@
 #include "curl_memory.h"
 #include "memdebug.h"
 
-/* #define DEBUG_QUICHE */
+/* HTTP/3 error values defined in RFC 9114, ch. 8.1 */
+#define CURL_H3_NO_ERROR  (0x0100)
 
 #define QUIC_MAX_STREAMS              (100)
 
@@ -303,11 +304,22 @@ static CURLcode h3_data_setup(struct Curl_cfilter *cf,
 
 static void h3_data_done(struct Curl_cfilter *cf, struct Curl_easy *data)
 {
+  struct cf_quiche_ctx *ctx = cf->ctx;
   struct stream_ctx *stream = H3_STREAM_CTX(data);
 
   (void)cf;
   if(stream) {
     CURL_TRC_CF(data, cf, "[%"PRId64"] easy handle is done", stream->id);
+    if(ctx->qconn && !stream->closed) {
+      quiche_conn_stream_shutdown(ctx->qconn, stream->id,
+                                  QUICHE_SHUTDOWN_READ, CURL_H3_NO_ERROR);
+      if(!stream->send_closed) {
+        quiche_conn_stream_shutdown(ctx->qconn, stream->id,
+                                    QUICHE_SHUTDOWN_WRITE, CURL_H3_NO_ERROR);
+        stream->send_closed = TRUE;
+      }
+      stream->closed = TRUE;
+    }
     Curl_bufq_free(&stream->recvbuf);
     Curl_h1_req_parse_free(&stream->h1);
     free(stream);
@@ -1213,10 +1225,12 @@ static CURLcode cf_quiche_data_event(struct Curl_cfilter *cf,
   case CF_CTRL_DATA_PAUSE:
     result = h3_data_pause(cf, data, (arg1 != 0));
     break;
-  case CF_CTRL_DATA_DONE: {
+  case CF_CTRL_DATA_DETACH:
+    h3_data_done(cf, data);
+    break;
+  case CF_CTRL_DATA_DONE:
     h3_data_done(cf, data);
     break;
-  }
   case CF_CTRL_DATA_DONE_SEND: {
     struct stream_ctx *stream = H3_STREAM_CTX(data);
     if(stream && !stream->send_closed) {