A QUIC stream could be destroyed by handler while in ngx_quic_stream_input().
To detect this, ngx_quic_find_stream() is used to check that it still exists.
Previously, a stream id was passed to this routine off the frame structure.
In case of stream cleanup, it is freed along with other frames belonging to
the stream on cleanup. Then, a cleanup handler reuses last frames to update
MAX_STREAMS and serve other purpose. Thus, ngx_quic_find_stream() is passed
a reused frame with zeroed out part pointed by stream_id. If a stream with
id 0x0 still exists, this leads to use-after-free.
static ngx_int_t
ngx_quic_stream_input(ngx_connection_t *c, ngx_quic_frame_t *frame, void *data)
{
+ uint64_t id;
ngx_buf_t *b;
ngx_event_t *rev;
ngx_quic_stream_t *sn;
sn = data;
f = &frame->u.stream;
+ id = f->stream_id;
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, "quic existing stream");
}
/* check if stream was destroyed by handler */
- if (ngx_quic_find_stream(&qc->streams.tree, f->stream_id) == NULL) {
+ if (ngx_quic_find_stream(&qc->streams.tree, id) == NULL) {
return NGX_DONE;
}