BUG/MEDIUM: quic: Avoid some crashes upon TX packet allocation failures
If a TX packet cannot be allocated (by qc_build_pkt()), as it can be coalesced
to another one, this leads the TX buffer to have remaining not sent prepared data.
Then haproxy crashes upon a BUG_ON() triggered by the next call to qc_txb_release().
This may happen only during handshakes.
To fix this, qc_build_pkt() returns a new -3 error to dected such allocation
failures followed which is for now on followed by a call to qc_purge_txbuf() to
send the TX prepared data and purge the TX buffer.
Must be backported as far as 2.6.
(cherry picked from commit 819690303de91804e009dea31d1c4c9116666396)
[cf: Applied on quic_conn.c instead of quic_tx.c. In addition forward
declarations for qc_purge_txbuf() and qc_purge_tx_buf() were added and call
to qc_purge_tx_buf() was adapted]
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
diff --git a/src/quic_conn.c b/src/quic_conn.c
index deb1e10..93ff3ee 100644
--- a/src/quic_conn.c
+++ b/src/quic_conn.c
@@ -237,6 +237,8 @@
struct list *frms, struct quic_conn *qc,
const struct quic_version *ver, size_t dglen, int pkt_type,
int must_ack, int padding, int probe, int cc, int *err);
+static int qc_purge_txbuf(struct quic_conn *qc, struct buffer *buf);
+static void qc_purge_tx_buf(struct buffer *buf);
struct task *quic_conn_app_io_cb(struct task *t, void *context, unsigned int state);
static void qc_idle_timer_do_rearm(struct quic_conn *qc, int arm_ack);
static void qc_idle_timer_rearm(struct quic_conn *qc, int read, int arm_ack);
@@ -3583,6 +3585,9 @@
pkt = qc_build_pkt(&pos, end, qel, &qel->tls_ctx, frms, qc, NULL, 0,
QUIC_PACKET_TYPE_SHORT, must_ack, 0, probe, cc, &err);
switch (err) {
+ case -3:
+ qc_purge_txbuf(qc, buf);
+ goto leave;
case -2:
// trace already emitted by function above
goto leave;
@@ -3735,6 +3740,9 @@
qc, ver, dglen, pkt_type,
must_ack, padding, probe, cc, &err);
switch (err) {
+ case -3:
+ qc_purge_tx_buf(buf);
+ goto leave;
case -2:
// trace already emitted by function above
goto leave;
@@ -8274,8 +8282,8 @@
* the end of this buffer, with <pkt_type> as packet type for <qc> QUIC connection
* at <qel> encryption level with <frms> list of prebuilt frames.
*
- * Return -2 if the packet could not be allocated or encrypted for any reason,
- * -1 if there was not enough room to build a packet.
+ + * Return -3 if the packet could not be allocated, -2 if could not be encrypted for
+ + * any reason, -1 if there was not enough room to build a packet.
* XXX NOTE XXX
* If you provide provide qc_build_pkt() with a big enough buffer to build a packet as big as
* possible (to fill an MTU), the unique reason why this function may fail is the congestion
@@ -8304,7 +8312,7 @@
pkt = pool_alloc(pool_head_quic_tx_packet);
if (!pkt) {
TRACE_DEVEL("Not enough memory for a new packet", QUIC_EV_CONN_TXPKT, qc);
- *err = -2;
+ *err = -3;
goto err;
}