BUG/MAJOR: quic: remove qc from receiver cids tree on free

Remove the quic_conn from the receiver connection_ids tree on
quic_conn_free. This fixes a crash due to dangling references in the
tree after a quic connection release.

This operation must be conducted under the listener lock. For this
reason, the quic_conn now contains a reference to its attached listener.
diff --git a/include/haproxy/xprt_quic-t.h b/include/haproxy/xprt_quic-t.h
index 6e83594..9e282f5 100644
--- a/include/haproxy/xprt_quic-t.h
+++ b/include/haproxy/xprt_quic-t.h
@@ -657,6 +657,7 @@
 	struct quic_path paths[1];
 	struct quic_path *path;
 
+	struct listener *li; /* only valid for frontend connections */
 	/* MUX */
 	struct qcc *qcc;
 	struct task *timer_task;
diff --git a/src/xprt_quic.c b/src/xprt_quic.c
index cee9322..3fd4632 100644
--- a/src/xprt_quic.c
+++ b/src/xprt_quic.c
@@ -2903,6 +2903,13 @@
 		return;
 
 	free_quic_conn_cids(conn);
+
+	/* remove the connection from receiver cids trees */
+	HA_RWLOCK_WRLOCK(OTHER_LOCK, &conn->li->rx.cids_lock);
+	ebmb_delete(&conn->odcid_node);
+	ebmb_delete(&conn->scid_node);
+	HA_RWLOCK_WRUNLOCK(OTHER_LOCK, &conn->li->rx.cids_lock);
+
 	for (i = 0; i < QUIC_TLS_ENC_LEVEL_MAX; i++)
 		quic_conn_enc_level_uninit(&conn->els[i]);
 	if (conn->timer_task)
@@ -3004,6 +3011,7 @@
 			memcpy(qc->dcid.data, scid, scid_len);
 		qc->dcid.len = scid_len;
 		qc->tx.qring_list = &l->rx.tx_qrings;
+		qc->li = l;
 	}
 	/* QUIC Client (outgoing connection to servers) */
 	else {