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

Julian Anastasov <[email protected]> Mon, 27 Jul 2026 21:48:19 +0300 (EEST)
Newsgroups org.kernel.vger.lvs-devel,org.kernel.vger.netfilter-devel
Message-ID <[email protected]>
	Hello,

On Tue, 28 Jul 2026, Ren Wei wrote:

> 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))

	Checking it in loop should not be needed. It is determined
only once before the loop. Perhaps, we can detect the problem early:

diff --git a/net/netfilter/ipvs/ip_vs_est.c b/net/netfilter/ipvs/ip_vs_est.c
index ab09f5182951..86d20f19fe31 100644
--- a/net/netfilter/ipvs/ip_vs_est.c
+++ b/net/netfilter/ipvs/ip_vs_est.c
@@ -191,8 +191,11 @@ static int ip_vs_estimation_kthread(void *data)
 		}
 
 		/* kthread 0 will handle the calc phase */
-		if (ipvs->est_calc_phase)
+		if (ipvs->est_calc_phase) {
 			ip_vs_est_calc_phase(ipvs);
+			if (kthread_should_stop() || !READ_ONCE(ipvs->enable))
+				return 0;
+		}
 	}
 
 	while (1) {

	Or even to check ipvs->est_chain_max too but it should not be
needed.


>  			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
> 

Regards

--
Julian Anastasov <[email protected]>