git: 8a7404b2aeeb - main - tcp: fix leaks in tcp_chg_pacing_rate error paths

From: Andrew Gallatin <gallatin_at_FreeBSD.org>
Date: Thu, 27 Jan 2022 15:42:54 UTC
The branch main has been updated by gallatin:

URL: https://cgit.FreeBSD.org/src/commit/?id=8a7404b2aeeb6345bd82c13c432e56d8cbfba869

commit 8a7404b2aeeb6345bd82c13c432e56d8cbfba869
Author:     Andrew Gallatin <gallatin@FreeBSD.org>
AuthorDate: 2022-01-27 15:35:03 +0000
Commit:     Andrew Gallatin <gallatin@FreeBSD.org>
CommitDate: 2022-01-27 15:35:03 +0000

    tcp: fix leaks in tcp_chg_pacing_rate error paths
    
    tcp_chg_pacing_rate() is expected to release the hw rate limit table,
    but failed to do so in several error cases, leading to ever
    increasing counts of flows using the rate.
    
    This patch was mostly done by rrs
    
    Sponsored by: Netflix
    Differential Revision: https://reviews.freebsd.org/D34058
    Reviewed by: hselasky, rrs,  jhb (inital version, outside of Differential)
---
 sys/netinet/tcp_ratelimit.c | 21 +++++++++++++++++++++
 1 file changed, 21 insertions(+)

diff --git a/sys/netinet/tcp_ratelimit.c b/sys/netinet/tcp_ratelimit.c
index 96a38b6afd54..2f36cea4faed 100644
--- a/sys/netinet/tcp_ratelimit.c
+++ b/sys/netinet/tcp_ratelimit.c
@@ -1411,6 +1411,7 @@ tcp_chg_pacing_rate(const struct tcp_hwrate_limit_table *crte,
 			 * tags if it didn't allocate one when an
 			 * existing rate was present, so ignore.
 			 */
+			tcp_rel_pacing_rate(crte, tp);
 			if (error)
 				*error = EOPNOTSUPP;
 			return (NULL);
@@ -1419,6 +1420,7 @@ tcp_chg_pacing_rate(const struct tcp_hwrate_limit_table *crte,
 #endif
 	if (tp->t_inpcb->inp_snd_tag == NULL) {
 		/* Wrong interface */
+		tcp_rel_pacing_rate(crte, tp);
 		if (error)
 			*error = EINVAL;
 		return (NULL);
@@ -1457,10 +1459,29 @@ tcp_chg_pacing_rate(const struct tcp_hwrate_limit_table *crte,
 #endif
 		err = in_pcbmodify_txrtlmt(tp->t_inpcb, nrte->rate);
 	if (err) {
+		struct tcp_rate_set *lrs;
+		uint64_t pre;
+
 		rl_decrement_using(nrte);
+		lrs = __DECONST(struct tcp_rate_set *, rs);
+		pre = atomic_fetchadd_64(&lrs->rs_flows_using, -1);
 		/* Do we still have a snd-tag attached? */
 		if (tp->t_inpcb->inp_snd_tag)
 			in_pcbdetach_txrtlmt(tp->t_inpcb);
+
+		if (pre == 1) {
+			struct epoch_tracker et;
+
+			NET_EPOCH_ENTER(et);
+			mtx_lock(&rs_mtx);
+			/*
+			 * Is it dead?
+			 */
+			if (lrs->rs_flags & RS_IS_DEAD)
+				rs_defer_destroy(lrs);
+			mtx_unlock(&rs_mtx);
+			NET_EPOCH_EXIT(et);
+		}
 		if (error)
 			*error = err;
 		return (NULL);