mirror of
http://git.haproxy.org/git/haproxy.git/
synced 2025-02-19 04:07:04 +00:00
BUG/MEDIUM: quic: fix handshake freeze under high traffic
QUIC relies on SSL_do_hanshake() to be able to validate handshake. As
this function is computation heavy, it is since 2.9 called only under
TASK_HEAVY. This has been implemented by the following patch :
94d20be138
MEDIUM: quic: Heavy task mode during handshake
Instead of handling CRYPTO frames immediately during reception, this
patch delays the process to run under TASK_HEAVY tasklet. A frame copy
is stored in qel.rx.crypto_frms list. However, this frame still
reference the receive buffer. If the receive buffer is cleared before
the tasklet is rescheduled, it will point to garbage data, resulting in
haproxy decryption error. This happens if a fair amount of data is
received constantly to preempt the quic_conn tasklet execution.
This bug can be reproduced with a fair amount of clients. It is
exhibited by 'show quic full' which can report connections blocked on
handshake. Using the following commands result in h2load non able to
complete the last connections.
$ h2load --alpn-list h3 -t 8 -c 800 -m 10 -w 10 -n 8000 "https://127.0.0.1:20443/?s=10k"
Also, haproxy QUIC listener socket mode was active to trigger the issue.
This forces several connections to share the same reception buffer,
rendering the bug even more plausible to occur. It should be possible to
reproduce it with connection socket if increasing the clients amount.
To fix this bug, define a new buffer under quic_cstream. It is used
exclusively to copy CRYPTO data for in-order frame if ncbuf is empty.
This ensures data remains accessible even if receive buffer is cleared.
Note that this fix is only a temporary step. Indeed, a ncbuf is also
already used for out-of-order data. It should be possible to unify its
usage for both in and out-of-order data, rendering this new buffer
instance unnecessary. In this case, several unneeded elements will
become obsolete such as qel.rx.crypto_frms list. This will be done in a
future refactoring patch.
This must be backported up to 2.9.
This commit is contained in:
parent
08ac282375
commit
81f118cec0
@ -226,6 +226,7 @@ struct quic_cstream {
|
||||
struct {
|
||||
uint64_t offset; /* absolute current base offset of ncbuf */
|
||||
struct ncbuf ncbuf; /* receive buffer - can handle out-of-order offset frames */
|
||||
struct buffer buf; /* receive buffer - handle only in-order data */
|
||||
} rx;
|
||||
struct {
|
||||
uint64_t offset; /* last offset of data ready to be sent */
|
||||
|
@ -720,8 +720,14 @@ static int qc_handle_crypto_frm(struct quic_conn *qc,
|
||||
}
|
||||
|
||||
if (crypto_frm->offset == cstream->rx.offset && ncb_is_empty(ncbuf)) {
|
||||
struct buffer *buf = &qel->cstream->rx.buf;
|
||||
struct qf_crypto *qf_crypto;
|
||||
|
||||
if (!b_alloc(buf)) {
|
||||
TRACE_ERROR("in-order buffer allocation failed", QUIC_EV_CONN_PRSHPKT, qc);
|
||||
goto leave;
|
||||
}
|
||||
|
||||
qf_crypto = pool_alloc(pool_head_qf_crypto);
|
||||
if (!qf_crypto) {
|
||||
TRACE_ERROR("CRYPTO frame allocation failed", QUIC_EV_CONN_PRSHPKT, qc);
|
||||
@ -730,7 +736,8 @@ static int qc_handle_crypto_frm(struct quic_conn *qc,
|
||||
|
||||
qf_crypto->offset = crypto_frm->offset;
|
||||
qf_crypto->len = crypto_frm->len;
|
||||
qf_crypto->data = crypto_frm->data;
|
||||
qf_crypto->data = (unsigned char *)b_tail(buf);
|
||||
b_putblk(buf, (char *)crypto_frm->data, crypto_frm->len);
|
||||
qf_crypto->qel = qel;
|
||||
LIST_APPEND(&qel->rx.crypto_frms, &qf_crypto->list);
|
||||
|
||||
|
@ -671,6 +671,7 @@ int qc_ssl_provide_all_quic_data(struct quic_conn *qc, struct ssl_sock_ctx *ctx)
|
||||
TRACE_ENTER(QUIC_EV_CONN_PHPKTS, qc);
|
||||
list_for_each_entry(qel, &qc->qel_list, list) {
|
||||
struct qf_crypto *qf_crypto, *qf_back;
|
||||
struct quic_cstream *cstream = qel->cstream;
|
||||
|
||||
list_for_each_entry_safe(qf_crypto, qf_back, &qel->rx.crypto_frms, list) {
|
||||
const unsigned char *crypto_data = qf_crypto->data;
|
||||
@ -688,9 +689,12 @@ int qc_ssl_provide_all_quic_data(struct quic_conn *qc, struct ssl_sock_ctx *ctx)
|
||||
QUIC_EV_CONN_PHPKTS, qc, qel);
|
||||
}
|
||||
|
||||
if (!qel->cstream)
|
||||
if (!cstream)
|
||||
continue;
|
||||
|
||||
b_free(&cstream->rx.buf);
|
||||
cstream->rx.buf = BUF_NULL;
|
||||
|
||||
if (!qc_treat_rx_crypto_frms(qc, qel, ctx))
|
||||
goto leave;
|
||||
}
|
||||
|
@ -124,6 +124,7 @@ void quic_cstream_free(struct quic_cstream *cs)
|
||||
}
|
||||
|
||||
quic_free_ncbuf(&cs->rx.ncbuf);
|
||||
b_free(&cs->rx.buf);
|
||||
|
||||
qc_stream_desc_release(cs->desc, 0);
|
||||
pool_free(pool_head_quic_cstream, cs);
|
||||
@ -145,6 +146,7 @@ struct quic_cstream *quic_cstream_new(struct quic_conn *qc)
|
||||
|
||||
cs->rx.offset = 0;
|
||||
cs->rx.ncbuf = NCBUF_NULL;
|
||||
cs->rx.buf = BUF_NULL;
|
||||
cs->rx.offset = 0;
|
||||
|
||||
cs->tx.offset = 0;
|
||||
|
Loading…
Reference in New Issue
Block a user