[PATCH nf 1/1] ipvs: guard estimator enqueue until limits are set

Ren Wei <[email protected]> Tue, 28 Jul 2026 00:58:01 +0800
Newsgroups org.kernel.vger.lvs-devel,org.kernel.vger.netfilter-devel
Message-ID <6359e0031f0ee7cc969bae3483308de362d3c709.1784878022.git.zhilinz@nebusec.ai>
From: Zhiling Zou <[email protected]>

IPVS estimator kthread 0 starts with zeroed chain and tick limits until
its initial calculation phase completes. If network namespace teardown
clears ipvs->enable during that phase, ip_vs_est_calc_phase() can return
without installing positive limits.

The kthread can then drain est_temp_list with zero chain_max, tick_max
and est_max_count values. Each enqueue consumes one available tick row,
but est_count never reaches the zero est_max_count value. After all rows
are consumed, the row lookup returns IPVS_EST_NTICKS and
ip_vs_enqueue_estimator() writes past the ticks and tick_len arrays.

Do not drain temporary estimators before est_chain_max is ready, and
reject enqueue attempts that do not have valid per-kthread limits or a
valid tick row.

Fixes: 705dd3444081 ("ipvs: use kthreads for stats estimation")
Cc: [email protected]
Reported-by: Vega <[email protected]>
Assisted-by: Codex:gpt-5.4
Signed-off-by: Zhiling Zou <[email protected]>
Signed-off-by: Ren Wei <[email protected]>
---
 net/netfilter/ipvs/ip_vs_est.c | 12 +++++++++++-
 1 file changed, 11 insertions(+), 1 deletion(-)

diff --git a/net/netfilter/ipvs/ip_vs_est.c b/net/netfilter/ipvs/ip_vs_est.c
index ab09f51829512..8c8bb2085d1e3 100644
--- a/net/netfilter/ipvs/ip_vs_est.c
+++ b/net/netfilter/ipvs/ip_vs_est.c
@@ -196,7 +196,8 @@ static int ip_vs_estimation_kthread(void *data)
 	}
 
 	while (1) {
-		if (!id && !hlist_empty(&ipvs->est_temp_list))
+		if (!id && READ_ONCE(ipvs->est_chain_max) &&
+		    !hlist_empty(&ipvs->est_temp_list))
 			ip_vs_est_drain_temp_list(ipvs);
 		set_current_state(TASK_IDLE);
 		if (kthread_should_stop())
@@ -421,6 +422,11 @@ static int ip_vs_enqueue_estimator(struct netns_ipvs *ipvs,
 
 add_est:
 	ktid = kd->id;
+	if (!kd->chain_max || !kd->tick_max || !kd->est_max_count) {
+		ret = -EINVAL;
+		goto out;
+	}
+
 	/* For small number of estimators prefer to use few ticks,
 	 * otherwise try to add into the last estimated row.
 	 * est_row and add_row point after the row we should use
@@ -450,6 +456,10 @@ static int ip_vs_enqueue_estimator(struct netns_ipvs *ipvs,
 		if (row >= IPVS_EST_NTICKS)
 			row = find_first_bit(kd->avail, IPVS_EST_NTICKS);
 	}
+	if (row >= IPVS_EST_NTICKS) {
+		ret = -EINVAL;
+		goto out;
+	}
 
 	td = rcu_dereference_protected(kd->ticks[row], 1);
 	if (!td) {
-- 
2.43.0