BUG/MAJOR: quic: reject too large CRYPTO frames

Received CRYPTO frames are inserted in a ncbuf to handle out-of-order
reception via ncb_add(). They are stored on the position relative to the
frame offset, minus a base offset which corresponds to the in-order data
length already handled.

Previouly, no check was implemented on the frame offset value prior to
ncb_add(), which could easily trigger a crash if relative offset was too
large. Fix this by ensuring first that the frame can be stored in the
buffer before ncb_add() invokation. If this is not the case, connection
is closed with error CRYPTO_BUFFER_EXCEEDED, as required by QUIC
specification.

This should fix github issue #2842.

This must be backported up to 2.6.
This commit is contained in:
Amaury Denoyelle 2025-01-20 10:15:12 +01:00
parent 0486b9e491
commit c3a4a4d166

View File

@ -663,6 +663,7 @@ static enum quic_rx_ret_frm qc_handle_crypto_frm(struct quic_conn *qc,
}; };
struct quic_cstream *cstream = qel->cstream; struct quic_cstream *cstream = qel->cstream;
struct ncbuf *ncbuf = &qel->cstream->rx.ncbuf; struct ncbuf *ncbuf = &qel->cstream->rx.ncbuf;
uint64_t off_rel;
TRACE_ENTER(QUIC_EV_CONN_PRSHPKT, qc); TRACE_ENTER(QUIC_EV_CONN_PRSHPKT, qc);
@ -692,8 +693,24 @@ static enum quic_rx_ret_frm qc_handle_crypto_frm(struct quic_conn *qc,
} }
/* crypto_frm->offset > cstream-trx.offset */ /* crypto_frm->offset > cstream-trx.offset */
ncb_ret = ncb_add(ncbuf, crypto_frm->offset - cstream->rx.offset, off_rel = crypto_frm->offset - cstream->rx.offset;
(const char *)crypto_frm->data, crypto_frm->len, NCB_ADD_COMPARE);
/* RFC 9000 7.5. Cryptographic Message Buffering
*
* Being unable to buffer CRYPTO frames during the handshake can lead to
* a connection failure. If an endpoint's buffer is exceeded during the
* handshake, it can expand its buffer temporarily to complete the
* handshake. If an endpoint does not expand its buffer, it MUST close
* the connection with a CRYPTO_BUFFER_EXCEEDED error code.
*/
if (off_rel + crypto_frm->len > ncb_size(ncbuf)) {
TRACE_ERROR("CRYPTO frame too large", QUIC_EV_CONN_PRSHPKT, qc);
quic_set_connection_close(qc, quic_err_transport(QC_ERR_CRYPTO_BUFFER_EXCEEDED));
goto err;
}
ncb_ret = ncb_add(ncbuf, off_rel, (const char *)crypto_frm->data,
crypto_frm->len, NCB_ADD_COMPARE);
if (ncb_ret != NCB_RET_OK) { if (ncb_ret != NCB_RET_OK) {
if (ncb_ret == NCB_RET_DATA_REJ) { if (ncb_ret == NCB_RET_DATA_REJ) {
TRACE_ERROR("overlapping data rejected", QUIC_EV_CONN_PRSHPKT, qc); TRACE_ERROR("overlapping data rejected", QUIC_EV_CONN_PRSHPKT, qc);