BUG/MEDIUM: h3: do not access QCS <sd> if not allocated

Since the following commit, allocation of QCS stream-endpoint on FE side
has been delayed. The objective is to allocate it only for QCS attached
to an upper stream object. Stream-endpoint allocation is now performed
on qcs_attach_sc() called during HEADERS parsing.

  commit e6064c561684d9b079e3b5725d38dc3b5c1b5cd5
  OPTIM: mux-quic: delay FE sedesc alloc to stream creation

Also, stream-endpoint is accessed through the QCS instance after HEADERS
or DATA frames parsing, to update the known input payload length. The
above patch triggered regressions as in some code paths, <sd> field is
dereferenced while still being NULL.

This patch fixes this by restricting access to <sd> field after newer
conditions.

First, after HEADERS parsing, known input length is only updated if
h3_req_headers_to_htx() previously returned a success value, which
guarantee that qcs_attach_sc() has been executed.

After DATA parsing, <sd> is only accessed after the frame validity
check. This ensures that HEADERS were already parsed, thus guaranteing
that stream-endpoint is allocated.

This should fix github issue #3211.

This must be backported up to 3.3. This is sufficient, unless above
patch is backported to previous releases, in which case the current one
must be picked with it.
This commit is contained in:
Amaury Denoyelle 2025-12-09 11:18:54 +01:00
parent 18625f7ff3
commit a15f0461a0

View File

@ -1767,7 +1767,14 @@ static ssize_t h3_rcv_buf(struct qcs *qcs, struct buffer *b, int fin)
total += hlen;
TRACE_PROTO("parsing a new frame", H3_EV_RX_FRAME, qcs->qcc->conn, qcs);
if (ftype == H3_FT_DATA) {
if ((ret = h3_check_frame_valid(h3c, qcs, ftype))) {
TRACE_ERROR("received an invalid frame", H3_EV_RX_FRAME, qcs->qcc->conn, qcs);
qcc_set_error(qcs->qcc, ret, 1);
qcc_report_glitch(qcs->qcc, 1);
goto err;
}
if (h3s->type == H3S_T_REQ && ftype == H3_FT_DATA) {
h3s->data_len += flen;
if (h3s->flags & H3_SF_HAVE_CLEN) {
@ -1778,18 +1785,14 @@ static ssize_t h3_rcv_buf(struct qcs *qcs, struct buffer *b, int fin)
break;
}
else {
/* content-length not present, update estimated payload length. */
/* content-length not present, update estimated payload length.
* <qcs.sd> is valid here as HEADERS frame were already parsed,
* guaranteed by h3_check_frame_valid().
*/
qcs->sd->kip = h3s->data_len;
}
}
if ((ret = h3_check_frame_valid(h3c, qcs, ftype))) {
TRACE_ERROR("received an invalid frame", H3_EV_RX_FRAME, qcs->qcc->conn, qcs);
qcc_set_error(qcs->qcc, ret, 1);
qcc_report_glitch(qcs->qcc, 1);
goto err;
}
if (!b_data(b))
break;
}
@ -1849,8 +1852,10 @@ static ssize_t h3_rcv_buf(struct qcs *qcs, struct buffer *b, int fin)
}
}
/* Update estimated payload with content-length value if present. */
if (h3s->flags & H3_SF_HAVE_CLEN)
/* Update estimated payload with content-length value if present.
* <sd> must be allocated on h3_req_headers_to_htx() success.
*/
if (ret >= 0 && h3s->flags & H3_SF_HAVE_CLEN)
qcs->sd->kip = h3s->body_len;
}
else {