diff options
author | Stefan Eissing <stefan@eissing.org> | 2023-04-13 11:03:50 +0200 |
---|---|---|
committer | Daniel Stenberg <daniel@haxx.se> | 2023-04-13 23:53:36 +0200 |
commit | be800a6cabe5c3e8968549b3481ddc5d73f12721 (patch) | |
tree | 89e6cd750cfec5aaecb9e214bb814796fc4c8287 | |
parent | 7e68133d041376c1012571e5a2386bb03a620a8e (diff) | |
download | curl-be800a6cabe5c3e8968549b3481ddc5d73f12721.tar.gz |
http3: check stream_ctx more thoroughly in all backends
- 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
-rw-r--r-- | lib/vquic/curl_msh3.c | 39 | ||||
-rw-r--r-- | lib/vquic/curl_ngtcp2.c | 38 | ||||
-rw-r--r-- | lib/vquic/curl_quiche.c | 58 |
3 files changed, 109 insertions, 26 deletions
diff --git a/lib/vquic/curl_msh3.c b/lib/vquic/curl_msh3.c index d6aaa47bb..1c35291d8 100644 --- a/lib/vquic/curl_msh3.c +++ b/lib/vquic/curl_msh3.c @@ -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; diff --git a/lib/vquic/curl_ngtcp2.c b/lib/vquic/curl_ngtcp2.c index 26bc80c9c..47b66efa5 100644 --- a/lib/vquic/curl_ngtcp2.c +++ b/lib/vquic/curl_ngtcp2.c @@ -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: diff --git a/lib/vquic/curl_quiche.c b/lib/vquic/curl_quiche.c index 69a65f01b..31a7174bf 100644 --- a/lib/vquic/curl_quiche.c +++ b/lib/vquic/curl_quiche.c @@ -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: |