HTTP/2, HTTP/3: handle detach of onoing transfers

- 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
This commit is contained in:
Stefan Eissing 2023-11-20 11:32:19 +01:00 committed by Daniel Stenberg
parent 413a0fedd0
commit b06b6216a3
No known key found for this signature in database
GPG Key ID: 5CC908FDB71E12C2
3 changed files with 35 additions and 9 deletions

View 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;
}

View 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) {

View 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) {