BUG/MINOR: quic: Possible memory leak from TX packets
This bug arrived with this commit which was not sufficient:
BUG/MEDIUM: quic: Missing TX buffer draining from qc_send_ppkts()
Indeed, there were also remaining allocated TX packets to be released and
their TX frames.
Implement qc_purge_tx_buf() to do so which depends on qc_free_tx_coalesced_pkts()
and qc_free_frm_list().
Must be backported to 2.7.
diff --git a/src/quic_conn.c b/src/quic_conn.c
index 064beae..6b66c22 100644
--- a/src/quic_conn.c
+++ b/src/quic_conn.c
@@ -3741,6 +3741,48 @@
return ret;
}
+/* Free all frames in <l> list. In addition also remove all these frames
+ * from the original ones if they are the results of duplications.
+ */
+static inline void qc_free_frm_list(struct list *l, struct quic_conn *qc)
+{
+ struct quic_frame *frm, *frmbak;
+
+ list_for_each_entry_safe(frm, frmbak, l, list) {
+ LIST_DEL_INIT(&frm->ref);
+ qc_frm_free(&frm);
+ }
+}
+
+/* Free <pkt> TX packet and all the packets coalesced to it. */
+static inline void qc_free_tx_coalesced_pkts(struct quic_conn *qc, struct quic_tx_packet *p)
+{
+ struct quic_tx_packet *pkt, *nxt_pkt;
+
+ for (pkt = p; pkt; pkt = nxt_pkt) {
+ qc_free_frm_list(&pkt->frms, qc);
+ nxt_pkt = pkt->next;
+ pool_free(pool_head_quic_tx_packet, pkt);
+ }
+}
+
+/* Purge <buf> TX buffer from its prepare packets. */
+static void qc_purge_tx_buf(struct quic_conn *qc, struct buffer *buf)
+{
+ while (b_contig_data(buf, 0)) {
+ uint16_t dglen;
+ struct quic_tx_packet *pkt;
+ size_t headlen = sizeof dglen + sizeof pkt;
+
+ dglen = read_u16(b_head(buf));
+ pkt = read_ptr(b_head(buf) + sizeof dglen);
+ qc_free_tx_coalesced_pkts(qc, pkt);
+ b_del(buf, dglen + headlen);
+ }
+
+ BUG_ON(b_data(buf));
+}
+
/* Send datagrams stored in <buf>.
*
* This function returns 1 for success. On error, there is several behavior
@@ -3792,9 +3834,11 @@
if (!skip_sendto) {
int ret = qc_snd_buf(qc, &tmpbuf, tmpbuf.data, 0);
if (ret < 0) {
- TRACE_ERROR("sendto fatal error", QUIC_EV_CONN_SPPKTS, qc);
+ TRACE_ERROR("sendto fatal error", QUIC_EV_CONN_SPPKTS, qc, first_pkt);
qc_kill_conn(qc);
- b_del(buf, buf->data);
+ qc_free_tx_coalesced_pkts(qc, first_pkt);
+ b_del(buf, dglen + headlen);
+ qc_purge_tx_buf(qc, buf);
goto leave;
}
else if (!ret) {