From: Zhiling Zou 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: stable@vger.kernel.org Reported-by: Vega Assisted-by: Codex:gpt-5.4 Signed-off-by: Zhiling Zou Signed-off-by: Ren Wei --- 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