From: Amaury Denoyelle Date: Wed, 6 Apr 2022 15:22:12 +0000 (+0200) Subject: BUG/MEDIUM: quic: ensure quic-conn survives to the MUX X-Git-Tag: v2.6-dev5~45 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=db71e3bd09d6f0a64a9615a0fb6e7c477e82e7c7;p=thirdparty%2Fhaproxy.git BUG/MEDIUM: quic: ensure quic-conn survives to the MUX Rationalize the lifetime of the quic-conn regarding with the MUX. The quic-conn must not be freed if the MUX is still allocated. This simplify the MUX code when accessing the quic-conn and removed possible segfaults. To implement this, if the quic-conn timer expired, the quic-conn is released only if the MUX is not allocated. Else, the quic-conn is flagged with QUIC_FL_CONN_EXP_TIMER. The MUX is then responsible to call quic_close() which will free the flagged quic-conn. --- diff --git a/include/haproxy/xprt_quic-t.h b/include/haproxy/xprt_quic-t.h index fad3722c86..fbe2ff7735 100644 --- a/include/haproxy/xprt_quic-t.h +++ b/include/haproxy/xprt_quic-t.h @@ -664,6 +664,7 @@ enum qc_mux_state { #define QUIC_FL_CONN_LISTENER (1U << 3) #define QUIC_FL_CONN_ACCEPT_REGISTERED (1U << 4) #define QUIC_FL_CONN_IDLE_TIMER_RESTARTED_AFTER_READ (1U << 6) +#define QUIC_FL_CONN_EXP_TIMER (1U << 28) /* timer has expired, quic-conn can be freed */ #define QUIC_FL_CONN_CLOSING (1U << 29) #define QUIC_FL_CONN_DRAINING (1U << 30) #define QUIC_FL_CONN_IMMEDIATE_CLOSE (1U << 31) diff --git a/src/xprt_quic.c b/src/xprt_quic.c index 6d4262c16b..a86e7f7546 100644 --- a/src/xprt_quic.c +++ b/src/xprt_quic.c @@ -3802,6 +3802,9 @@ static void quic_conn_release(struct quic_conn *qc) struct eb64_node *node; struct quic_tls_ctx *app_tls_ctx; + /* We must not free the quic-conn if the MUX is still allocated. */ + BUG_ON(qc->mux_state == QC_MUX_READY); + /* free remaining stream descriptors */ node = eb64_first(&qc->streams_by_id); while (node) { @@ -3866,6 +3869,13 @@ void quic_close(struct connection *conn, void *xprt_ctx) /* Next application data can be dropped. */ qc->mux_state = QC_MUX_RELEASED; + /* If the quic-conn timer has already expired free the quic-conn. */ + if (qc->flags & QUIC_FL_CONN_EXP_TIMER) { + quic_conn_release(qc); + TRACE_LEAVE(QUIC_EV_CONN_CLOSE); + return; + } + TRACE_LEAVE(QUIC_EV_CONN_CLOSE, qc); } @@ -4107,7 +4117,16 @@ static struct task *qc_idle_timer_task(struct task *t, void *ctx, unsigned int s { struct quic_conn *qc = ctx; - quic_conn_release(qc); + /* If the MUX is still alive, keep the quic-conn. The MUX is + * responsible to call quic_close to release it. + */ + qc->flags |= QUIC_FL_CONN_EXP_TIMER; + if (qc->mux_state != QC_MUX_READY) + quic_conn_release(qc); + + /* TODO if the quic-conn cannot be freed because of the MUX, we may at + * least clean some parts of it such as the tasklet. + */ return NULL; }