summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStefan Eissing <stefan@eissing.org>2023-04-13 11:03:50 +0200
committerDaniel Stenberg <daniel@haxx.se>2023-04-13 23:53:36 +0200
commitbe800a6cabe5c3e8968549b3481ddc5d73f12721 (patch)
tree89e6cd750cfec5aaecb9e214bb814796fc4c8287
parent7e68133d041376c1012571e5a2386bb03a620a8e (diff)
downloadcurl-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.c39
-rw-r--r--lib/vquic/curl_ngtcp2.c38
-rw-r--r--lib/vquic/curl_quiche.c58
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: