From nobody Thu Sep 24 12:52:59 2026 Received: from mail-pj2-f13.google.com (mail-pj2-f13.google.com [74.125.227.141]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A457747DD6A for ; Wed, 23 Sep 2026 09:54:32 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.227.141 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790157275; cv=none; b=ba2rITZPi4wKijzI06Ug1njdxUKo3EqfF0WHWdO0Ki+GV+ijprHaH4lrqP4auobtWA9yF8zYifu/ZRVnmn3X/TyMdVXCRdebzLP5iocUTT1iTIjD8CKVBLiHCsGvobngoJ2AxAUumUiChd0VXYHPBAzwrqWan5MPnus41yT7JWs= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790157275; c=relaxed/simple; bh=QP2zeCcSlOzjBL2aqTVUS2P4u++MGbpIG9oq+g1c7lQ=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=KlvSd3E46Hlyiie08n4XhH2S4ZoGwo2FGFonFhMKiulOA7wpXGKOc8JOZO7Fi74so4kqlAzECh9+wjmXQTefYNM6etB0Nd4AJAPasvTiSd5XmyRe8ZxNnA4mOtfdV35TzociUUH6OxuO9zI338vLGbg+TOIGEBSGu+VJEkD57lE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=nebusec.ai; spf=pass smtp.mailfrom=nebusec.ai; dkim=pass (2048-bit key) header.d=nebusec.ai header.i=@nebusec.ai header.b=YK0zlITG; arc=none smtp.client-ip=74.125.227.141 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=nebusec.ai Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=nebusec.ai Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=nebusec.ai header.i=@nebusec.ai header.b="YK0zlITG" Received: by mail-pj2-f13.google.com with SMTP id 98e67ed59e1d1-39b350c69b4so496317a91.2 for ; Wed, 23 Sep 2026 02:54:32 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=nebusec.ai; s=google; t=1790157271; x=1790762071; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=QR9bfHIsQ04W27fHUOP8MJLUlZC8gKLrUSCQoHM5E/g=; b=YK0zlITGRivyVw8kvh9uo8E3kdcGFbjx6uH1k5rhAuvGF436fdqBL//z1I2r5Dwd/P MutqWptlu2xsUoGIaU6t5zc5T93Jq7CsBcwV7+9cCHwel5qGL8Yk/i1+Q+SqhHs8wbWF 7SJetVz0w1Zvy6KLMkAbSK6Ytn34a9rVjkDfNkS099sjwAzNMZE2AgBV63iFMmsPFlcB oEA6uKqbfPDJA4XXVz4Ri0TMhYSXy+dQ7d7+7pvPu1k0KH0hlsAC5IyWZDWq8SiQMihm 4dOMlX6zqT5T38FPaXm5W5k1ozHliIUGxw8QWwPEczwZMXlPPVHq1iAYqpZtzOajNuoc Jgaw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1790157271; x=1790762071; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=QR9bfHIsQ04W27fHUOP8MJLUlZC8gKLrUSCQoHM5E/g=; b=pEkKNXBIgCKvZHQrKzJgBPQzx6sRewLf3gbBGlZTtcE+igaQh/GU2OQQaSG6Ic57ss bQSKBudyDMTXICB4mUArO7Re3SHqV9xGTD4xONnhuN0O9FxxCAgcJ2QMTK+UQQX6RkG3 4WKk4ri7fOKz8BgbIyhsOgPW7Wrf4/wWZi3ymlxszEjr7fO7W1r3a6K1Df9Su4PcIyQ1 RNV+GUp+5CRs6hXLEuq7mq+8wYzcUNOMJQgloiZzev0qB8tGAlFRAENIlB3B5Hz23S/q On3f9W+mwbnFtH6Jb2TD/tSbc2XWNXJJBOldP5qnTlNeLrEZ3tOAow3H6wz93mSK9LN7 pWug== X-Forwarded-Encrypted: i=1; AKwUvBxjxtz8aM5h+M0+fs/BwmXCY9AAjTQ9eQBaQNNlgHuWEYMviVn0g2l6Sb7emxMZVAtuWzsTu5PLS4qEt6o=@vger.kernel.org X-Gm-Message-State: AFuF++lvYWpMRjuYKTmAoiF+87a0OXgvqZVtwFGILsrqgFh973KP0eN4 DPwUAqQjOnCsWLjXOxjJ1HJJQZbpB0vt5drjqraTOOy2afWZ4mOjfH4+eGwZeNMwuSQ5 X-Gm-Gg: AYBFou02nXCtdmO8ElhOxTGOP+aEP4JQyfxYuKFbftpSNNTilnVrG4jKIUxY3tKI0IX UN1yP7UMzmfzD73PY+3NPyZ+kO6DIdyQPospXJmGXIlest/za2IY4eRxIbmiPXCn1FQ0fJ5yNTx 4ZFw8WiJHNqbq7s5AeaR0KDghwfB20udQIPe4iooDHNxQ1SK+69pixq1sD6TKlTrpAE7ZJJmGll y3ipIruYTKqg3Lp5E4R6ICUgkPJrV9N2AOkUmV9pKUDi30ywXt0W/G9ZrKmteypgO0cGJabAZwE upKLN6Odzy25Vfwq4XFG6w+WMZuovmMBghyp2IssydzbnBvCBdvnkA4x2sczxdXwgf07t451cuI 7wWgB11AmTSXsJzHqLNQ/IGZ7cAEDGGaVQgVYSHORfWJdITCFKEZCe9SGqfoA+mA8+pCkcGLnzv RHg8J0N8tWo0LlJ5xkeYd3gjut36pJAy8PzFlp6bEKUuerUQEkQIpXXJR037Idgm+hEwG0Wgm20 qDtfC3a4KvkPonagIAjXNceec5rOQ== X-Received: by 2002:a17:90b:51c8:b0:39e:4c80:44bd with SMTP id 98e67ed59e1d1-3a07e5ea39dmr1698414a91.32.1790157271227; Wed, 23 Sep 2026 02:54:31 -0700 (PDT) Received: from 954df21a5119.. ([122.51.212.64]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-3a07dc5f3d7sm4793862a91.16.2026.09.23.02.54.26 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 23 Sep 2026 02:54:30 -0700 (PDT) From: Zihan Xi To: netfilter-devel@vger.kernel.org Cc: netdev@vger.kernel.org, lvs-devel@vger.kernel.org, coreteam@netfilter.org, linux-kernel@vger.kernel.org, horms@verge.net.au, ja@ssi.bg, zihanx@nebusec.ai, pablo@netfilter.org, fw@strlen.de, phil@nwl.cc, davem@davemloft.net, edumazet@google.com, kuba@kernel.org, pabeni@redhat.com Subject: [PATCH nf v4 1/3] ipvs: wait the running timer cb on conn deletion Date: Wed, 23 Sep 2026 09:54:13 +0000 Message-ID: X-Mailer: git-send-email 2.47.3 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Julian Anastasov Sashiko reports for problem when deleting connections. If connection timer expires, its callback can not be concurrently running but if connection is deleted the callback can be running on another CPU even after all references are released. Before now we continued with the connection freeing, risking the callback to access the deleted connection after it is freed. As ip_vs_conn_del*() run under RCU lock there is no risk accessing a freed connection by concurrent timer callback as Sashiko warns, may be only if our timer expires and we try to delete the cp->control chain. Fix that by failing the ip_vs_conn_unlink() call after refcnt is restored to 1 allowing the timer callback to be scheduled for new execution which should happen after the detected running callback finishes. One of two things can happen when we detect the running callback: 1. the concurrent timer callback can see refcnt 0 and do nothing, so we will schedule new timer callback to expire the connection after the running one finishes 2. the concurrent timer callback can see refcnt 1 and to expire the connection as usually, in this case we will see refcnt 0 and will do nothing Add explicit rcu_read_lock() while deleting the cp->control chain to protect from concurrent timer callback for ct to expire it before us. During such races, try to keep 0 in cp->timeout as it is a request for deleting our cp->control chain immediately. Link: https://sashiko.dev/#/patchset/cover.1789435989.git.zihanx%40nebusec.= ai Fixes: f9200a52eedf ("ipvs: avoid expiring many connections from timer") Signed-off-by: Julian Anastasov --- net/netfilter/ipvs/ip_vs_conn.c | 103 +++++++++++++++++--------------- 1 file changed, 54 insertions(+), 49 deletions(-) diff --git a/net/netfilter/ipvs/ip_vs_conn.c b/net/netfilter/ipvs/ip_vs_con= n.c index 6fa3e1dc534c3..32cfc02aa2912 100644 --- a/net/netfilter/ipvs/ip_vs_conn.c +++ b/net/netfilter/ipvs/ip_vs_conn.c @@ -313,17 +313,34 @@ static inline int ip_vs_conn_hash(struct ip_vs_conn *= cp) /* Try to unlink ip_vs_conn from conn_tab. * returns bool success. */ -static inline bool ip_vs_conn_unlink(struct ip_vs_conn *cp) +static inline bool ip_vs_conn_unlink(struct ip_vs_conn *cp, bool my_cb) { struct netns_ipvs *ipvs =3D cp->ipvs; struct hlist_bl_head *head, *head2; u32 hash_key, hash_key2; struct ip_vs_rht *t; - bool ret =3D false; bool use2; =20 + if (!refcount_dec_if_one(&cp->refcnt)) + return false; + if (cp->flags & IP_VS_CONN_F_ONE_PACKET) - return refcount_dec_if_one(&cp->refcnt); + return true; + + /* Revalidate after conn is excluded from traffic: + * - not controlling other conns + * - no pending/running timer callback + * + * And the winner is ... + */ + if (atomic_read(&cp->n_control) || + (!timer_delete(&cp->timer) && !my_cb)) { + /* Not me? Give the timer callback another chance, even + * if one is concurrently running during the conn deletion. + */ + refcount_inc(&cp->refcnt); + return false; + } =20 rcu_read_lock(); local_bh_disable(); @@ -337,15 +354,11 @@ static inline bool ip_vs_conn_unlink(struct ip_vs_con= n *cp) false /* new_hash2 */, &head, &head2); =20 if (cp->flags & IP_VS_CONN_F_HASHED) { - /* Decrease refcnt and unlink conn only if we are last user */ - if (use2 =3D=3D ip_vs_conn_use_hash2(cp) && - refcount_dec_if_one(&cp->refcnt)) { - hlist_bl_del_rcu(&cp->hn0.node); - if (use2) - hlist_bl_del_rcu(&cp->hn1.node); - cp->flags &=3D ~IP_VS_CONN_F_HASHED; - ret =3D true; - } + /* Unlink conn as we are the last user */ + hlist_bl_del_rcu(&cp->hn0.node); + if (use2) + hlist_bl_del_rcu(&cp->hn1.node); + cp->flags &=3D ~IP_VS_CONN_F_HASHED; } =20 conn_tab_unlock(head, head2); @@ -353,7 +366,7 @@ static inline bool ip_vs_conn_unlink(struct ip_vs_conn = *cp) local_bh_enable(); rcu_read_unlock(); =20 - return ret; + return true; } =20 =20 @@ -1319,34 +1332,29 @@ static void ip_vs_conn_rcu_free(struct rcu_head *he= ad) kmem_cache_free(ip_vs_conn_cachep, cp); } =20 -/* Try to delete connection while not holding reference */ +/* Try to delete connection while not holding reference. + * It can be called concurrently and always under RCU lock. + */ static void ip_vs_conn_del(struct ip_vs_conn *cp) { - if (timer_delete(&cp->timer)) { - /* Drop cp->control chain too */ - if (cp->control) - cp->timeout =3D 0; - ip_vs_conn_expire(&cp->timer); - } -} + struct timer_list *t =3D (void *)((unsigned long)(&cp->timer) | 1UL); =20 -/* Try to delete connection while holding reference */ -static void ip_vs_conn_del_put(struct ip_vs_conn *cp) -{ - if (timer_delete(&cp->timer)) { - /* Drop cp->control chain too */ - if (cp->control) - cp->timeout =3D 0; - __ip_vs_conn_put(cp); - ip_vs_conn_expire(&cp->timer); - } else { - __ip_vs_conn_put(cp); - } + /* Drop cp->control chain too */ + if (cp->control) + cp->timeout =3D 0; + ip_vs_conn_expire(t); } =20 +/* Connection is removed in the following steps: + * - timer expires or connection is deleted + * - there should be no more references (n_control>0 and refcnt>1) + * - there should be no pending timer or a running timer callback (on dele= tion) + */ static void ip_vs_conn_expire(struct timer_list *t) { - struct ip_vs_conn *cp =3D timer_container_of(cp, t, timer); + bool my_cb =3D !((unsigned long)t & 1); + struct timer_list *t2 =3D (void *)((unsigned long)t & ~1UL); + struct ip_vs_conn *cp =3D timer_container_of(cp, t2, timer); struct netns_ipvs *ipvs =3D cp->ipvs; =20 /* @@ -1356,26 +1364,21 @@ static void ip_vs_conn_expire(struct timer_list *t) goto expire_later; =20 /* Unlink conn if not referenced anymore */ - if (likely(ip_vs_conn_unlink(cp))) { + if (likely(ip_vs_conn_unlink(cp, my_cb))) { struct ip_vs_conn *ct =3D cp->control; =20 - /* delete the timer if it is activated by other users */ - timer_delete(&cp->timer); - /* does anybody control me? */ if (ct) { - bool has_ref =3D !cp->timeout && __ip_vs_conn_get(ct); - + rcu_read_lock(); ip_vs_control_del(cp); /* Drop CTL or non-assured TPL if not used anymore */ - if (has_ref && !atomic_read(&ct->n_control) && + if (!cp->timeout && !atomic_read(&ct->n_control) && (!(ct->flags & IP_VS_CONN_F_TEMPLATE) || !(ct->state & IP_VS_CTPL_S_ASSURED))) { IP_VS_DBG(4, "drop controlling connection\n"); - ip_vs_conn_del_put(ct); - } else if (has_ref) { - __ip_vs_conn_put(ct); + ip_vs_conn_del(ct); } + rcu_read_unlock(); } =20 if ((cp->flags & IP_VS_CONN_F_NFCT) && @@ -1410,13 +1413,15 @@ static void ip_vs_conn_expire(struct timer_list *t) refcount_read(&cp->refcnt), atomic_read(&cp->n_control)); =20 - refcount_inc(&cp->refcnt); - cp->timeout =3D 60*HZ; + if (__ip_vs_conn_get(cp)) { + if (cp->timeout || atomic_read(&cp->n_control)) + cp->timeout =3D 60 * HZ; =20 - if (ipvs->sync_state & IP_VS_STATE_MASTER) - ip_vs_sync_conn(ipvs, cp, sysctl_sync_threshold(ipvs)); + if (ipvs->sync_state & IP_VS_STATE_MASTER) + ip_vs_sync_conn(ipvs, cp, sysctl_sync_threshold(ipvs)); =20 - __ip_vs_conn_put_timer(cp); + __ip_vs_conn_put_timer(cp); + } } =20 /* Modify timer, so that it expires as soon as possible. --=20 2.43.0 From nobody Thu Sep 24 12:52:59 2026 Received: from mail-pz2-f40.google.com (mail-pz2-f40.google.com [74.125.228.40]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 8A58D47F3C9 for ; Wed, 23 Sep 2026 09:54:40 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.228.40 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790157283; cv=none; b=sdAEvRXZF/7R6vu4IVF6kKZqqgs6kVkmPk/FxauSJ5vja4Q+85dqXB8rpDe/BFN4S8z6sEcRRIJ0/Fu8uPllf6+4I5gYe5huzWmt9LFawiwfMvATP4UvB8mAsa8NinVwEO09X4jX9a+wNbZkRl9z+Fwk1JM9oh5Hqm6w5JMoxxA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790157283; c=relaxed/simple; bh=+HYS3dvKzoXKXK40VmtMXbgr0IQRMY8SAmPFPgXydIc=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=k3ai08m4tPV2vDMw377kzFpwpRQWi1R4zt/VA5lVy+MGUGOVRhexjsHxiQBuJAJrKMwRoeNfJv5n3OWefgi99LV0DZt3pDrtiDWjHePipEOpQa5Ajt0/c7k/VjJRn/kiNUHrAbd3CsNoSe5z6IEJuC6XuvgXDJJqf53bTz5POUQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=nebusec.ai; spf=pass smtp.mailfrom=nebusec.ai; dkim=pass (2048-bit key) header.d=nebusec.ai header.i=@nebusec.ai header.b=WUBJPao1; arc=none smtp.client-ip=74.125.228.40 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=nebusec.ai Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=nebusec.ai Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=nebusec.ai header.i=@nebusec.ai header.b="WUBJPao1" Received: by mail-pz2-f40.google.com with SMTP id 41be03b00d2f7-cc5256c2a4bso351254a12.1 for ; Wed, 23 Sep 2026 02:54:40 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=nebusec.ai; s=google; t=1790157280; x=1790762080; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=NcjbznsDNqQjucWemj8GXAQz8O/NVaZRIfNms8EzTJY=; b=WUBJPao1nKCo6+v6kyemAzTI9rLczPqkO1XFflfuRQBEx37DSl7CId9ytkFjc5pMei sdUvomRgPXuj/sIQT4AkMD/O+4N6GrkqvMVQ2EDlj57PVMGE4CD8Jn4pk0FA7DGFTb9j fTCnlsdJiXzTkSlB6vpdAjbCypz/5RuHvoEJH/zVZJUtgmsP3/t4Y6MbZdHSZmLRvSFF W0Gylz09fUihZkUk6EP5OlAz6HEc8LJ8meMHrLocHSTdIPWUtDXQGKrtkK1WFHVyxBfW ZwJA1ekPG8q1kdzcyzxbKK/nR//MxJFPw28isSuKObJAFVR0ofL1fsZXhoDkUc29FI7n 7I2Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1790157280; x=1790762080; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=NcjbznsDNqQjucWemj8GXAQz8O/NVaZRIfNms8EzTJY=; b=lxvioY+xmYm9/ya9/HYagjelCjUj4ToqpZUQ9sN4qWk9SS2k9VQ6qxGe20vZ24JYFX SvwFxYZxVy+I3j9+gZa4S5G4Pb3VKdsF403LfsMPbPl99sSoUmxK+9qCCGjLtV1JoAPL A4l6WGAnu7lMX8vtbII3rgrLCGlBJtv7q5ANFgv1wFXncKwxw88rFajbpCpD4jhhtkn8 Sme4fHrNOc2wrjra1KbNvd0vch1CeTmM/brgk+ReK73EEzzQUdcPftKMhyJB7TkKWfRj vF867itguroLmHSfYevx4RgVgvUqNY8n6uW9tWWzXNxqsSUSrE2JbN0yCAnF6lNDsN8V 6udA== X-Forwarded-Encrypted: i=1; AKwUvByKEsGxl4Aja2hYWM+zj+6Xx8R/VBR5zuFU2OMI+MgnpP4j5nGBgUSTxumCAUvq6EWlmsLMhUo1frXGVng=@vger.kernel.org X-Gm-Message-State: AFuF++ndOnJwc5+Am7y/+W6YennIlXi3IZayE38Pn5ck3+sDHi9/wRb+ 8BsMYsttJE7E96zZkjhga1Rj1eSuX+3nAKaqYhEmw4nytfa8BtK6GWUF+MAKf01XXhrJ X-Gm-Gg: AYBFou3GK+IXXFNGkzQNpfttOXKStIqEnOWwfkUdebC9eN1V0DVR6o1a0eBOEkV6wwT dS2boOmhT4/KaUp5NzUByVToDZ0UD6nbqmkON/Yutcqqs+yxh4/IsIQ7iKsJ0gE+D9riWDI7pUg dc6+YoEqxeK3NBoMsQ440j1ivx3lQrAJNJpAYxd2LO+s8YIgH+139SKOc14ETfpg1qlNqCTfFN2 1aZdpl9NMrtvk2pu5cFKkyzDtJPRfY8tyHZNS9UCwsBSmsSvFhyv7ZAr1C+ZwArbV6BOHL9d0tk ajdc4bP6s66x8bYB1jGohVAGj8Qf1m327fcaJNOuFflkPq9KDRpzKdfdK2scZ1X2G/Po/xV/4wn f0DpDl1/FCxQTzGWK5fhsmp6KuOg/kuDMqEfbRCEMh834DMTwPRuu7a5F1ZZaUuOxQAgwuF8LVt X0NVn4U/mQLNyyUlKbVKiGv3rCX1frpico6WQ0Rddo9zB4xG4Z+mxg33tkIVFwWQxjeylxBK2pi /EsRMFreoC8HRjzGUdRdiIpIW5SAg== X-Received: by 2002:a17:90b:164a:b0:39e:35a0:9c0f with SMTP id 98e67ed59e1d1-3a07e568a6fmr1766161a91.14.1790157279865; Wed, 23 Sep 2026 02:54:39 -0700 (PDT) Received: from 954df21a5119.. ([122.51.212.64]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-3a07dc5f3d7sm4793862a91.16.2026.09.23.02.54.34 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 23 Sep 2026 02:54:39 -0700 (PDT) From: Zihan Xi To: netfilter-devel@vger.kernel.org Cc: netdev@vger.kernel.org, lvs-devel@vger.kernel.org, coreteam@netfilter.org, linux-kernel@vger.kernel.org, horms@verge.net.au, ja@ssi.bg, zihanx@nebusec.ai, pablo@netfilter.org, fw@strlen.de, phil@nwl.cc, davem@davemloft.net, edumazet@google.com, kuba@kernel.org, pabeni@redhat.com, stable@vger.kernel.org, Vega , Luxing Yin Subject: [PATCH nf v4 2/3] ipvs: avoid stack overflow from recursive connection expiration Date: Wed, 23 Sep 2026 09:54:14 +0000 Message-ID: <8d33e2a83fa6f2fb6419b3f34cd3ff8677684a31.1790146910.git.zihanx@nebusec.ai> X-Mailer: git-send-email 2.47.3 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" When a controlled IPVS connection expires, its controller may be expired synchronously if it has no remaining controlled connections. A chain of controlled connections can then recurse through ip_vs_conn_expire() and exhaust the kernel stack during namespace cleanup. Continue expiration with the controller after the current connection has been fully cleaned up instead of calling ip_vs_conn_del() recursively. Keep the expiration walk under RCU, preserve the immediate-drop timeout for a controller with its own controller, and switch to deletion mode before the next iteration. This keeps controlled-connection cleanup synchronous while using one stack frame for the whole chain. The timer callback race during connection deletion is handled by the preceding refcount fix. Fixes: f9200a52eedf ("ipvs: avoid expiring many connections from timer") Cc: stable@vger.kernel.org Reported-by: Vega Assisted-by: LLM Co-developed-by: Luxing Yin Signed-off-by: Luxing Yin Signed-off-by: Zihan Xi --- changes in v4: - Rebase the iterative controller cleanup on the timer-callback fix. - Keep the controller walk synchronous and switch to deletion mode for the next iteration. - v3 Link: https://lore.kernel.org/all/cover.1789877273.git.zihanx@nebusec.ai/ changes in v3: - Handle the timer-callback race while keeping controller cleanup iterative and synchronous. - v2 Link: https://lore.kernel.org/all/cover.1789435989.git.zihanx@nebusec.ai/ changes in v2: - Replace recursive controller expiration with an iterative repeat path. - v1 Link: https://lore.kernel.org/all/cover.1789110326.git.zihanx@nebusec.ai/ net/netfilter/ipvs/ip_vs_conn.c | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/net/netfilter/ipvs/ip_vs_conn.c b/net/netfilter/ipvs/ip_vs_con= n.c index 32cfc02aa2912..f85752e79ed92 100644 --- a/net/netfilter/ipvs/ip_vs_conn.c +++ b/net/netfilter/ipvs/ip_vs_conn.c @@ -1357,6 +1357,9 @@ static void ip_vs_conn_expire(struct timer_list *t) struct ip_vs_conn *cp =3D timer_container_of(cp, t2, timer); struct netns_ipvs *ipvs =3D cp->ipvs; =20 + rcu_read_lock(); + +repeat: /* * do I control anybody? */ @@ -1366,19 +1369,20 @@ static void ip_vs_conn_expire(struct timer_list *t) /* Unlink conn if not referenced anymore */ if (likely(ip_vs_conn_unlink(cp, my_cb))) { struct ip_vs_conn *ct =3D cp->control; + bool next =3D false; =20 /* does anybody control me? */ if (ct) { - rcu_read_lock(); ip_vs_control_del(cp); /* Drop CTL or non-assured TPL if not used anymore */ if (!cp->timeout && !atomic_read(&ct->n_control) && (!(ct->flags & IP_VS_CONN_F_TEMPLATE) || !(ct->state & IP_VS_CTPL_S_ASSURED))) { IP_VS_DBG(4, "drop controlling connection\n"); - ip_vs_conn_del(ct); + if (ct->control) + ct->timeout =3D 0; + next =3D true; } - rcu_read_unlock(); } =20 if ((cp->flags & IP_VS_CONN_F_NFCT) && @@ -1405,7 +1409,12 @@ static void ip_vs_conn_expire(struct timer_list *t) else call_rcu(&cp->rcu_head, ip_vs_conn_rcu_free); atomic_dec(&ipvs->conn_count); - return; + if (next) { + cp =3D ct; + my_cb =3D false; + goto repeat; + } + goto out; } =20 expire_later: @@ -1422,6 +1431,9 @@ static void ip_vs_conn_expire(struct timer_list *t) =20 __ip_vs_conn_put_timer(cp); } + +out: + rcu_read_unlock(); } =20 /* Modify timer, so that it expires as soon as possible. --=20 2.43.0 From nobody Thu Sep 24 12:52:59 2026 Received: from mail-pj2-f13.google.com (mail-pj2-f13.google.com [74.125.227.141]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 448A247F3B5 for ; Wed, 23 Sep 2026 09:54:49 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.227.141 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790157292; cv=none; b=iOjzDAlp/Wc5dJJ/vpMZgEf92S7rpPV+3IByP7sOHLb6N+9eOCAEvXtQ0TFMQuoaTh4gE5r2iK1fn75wpyyeBoWvGH9eAVoME1vqTkS4KmS4HOpNoLEMWXoo+UmfsHqsQBbcKotxy3m1SGAOLYOiaenZ26CTYCBFDBL6LMrARvM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790157292; c=relaxed/simple; bh=IUticTq4foak3yxsGQl2vQDiakNmvntsdMGpOFGsCD8=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=glHwU7GgnnvUZ4x1EuGizLDMfpC4n3/eMirBqIEcncIK17eyPgEFPLljptZL7XYlaKzld89qYbvtsnMmhfEQ6YbyAv41EZ42C9jFG3WxgUmueuOPL9L1Fkv28FjT+rW7gjSkcAG4WCVVxEndbpT/dR+JUOY6lWs+GfGZ7lDE0sg= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=nebusec.ai; spf=pass smtp.mailfrom=nebusec.ai; dkim=pass (2048-bit key) header.d=nebusec.ai header.i=@nebusec.ai header.b=iisnueaK; arc=none smtp.client-ip=74.125.227.141 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=nebusec.ai Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=nebusec.ai Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=nebusec.ai header.i=@nebusec.ai header.b="iisnueaK" Received: by mail-pj2-f13.google.com with SMTP id d9443c01a7336-2d747eb79f7so3133285ad.1 for ; Wed, 23 Sep 2026 02:54:48 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=nebusec.ai; s=google; t=1790157288; x=1790762088; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=aQDxycOIV80BPgrqxrzM7NANBApnebD30BToWezPxAI=; b=iisnueaKSaCz7CXaV8b7t8il/3RUHIYzy6lPx6TVpmG9za1rfF1/0Je4dT+5Ut8icn MDCQDiJ3nd2CFnN3rea8FYDjR42/I00+ZXLB+I0amPG2W87scf2m96JZ9n1w5+nFzJ0b toCVyg4+DOWleDkqmhccJAFWDdnsdfz1Lwl+eCsdJcFDzyLOoMDOeuuczfH/WNdxmsif j1PjijPDrKyO1I6DvULKYCDjVQF8BEhsvhXikiIkTqyGKxKYIdb1FaxQUZNBe3cahPGd N6n0b+rajCoH3tcNnrdxv4+9hju/Tz760jp7p/gGY1E++LswVcYcsYZEcIVyL4cIgvwY Myag== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1790157288; x=1790762088; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=aQDxycOIV80BPgrqxrzM7NANBApnebD30BToWezPxAI=; b=1C8wcNw9uXsQvhJySgYUUQ+hHcfUyFzfTzS6nKdD4z+mBcr/NZYpI2Ac3MyjcAlUGk rZy4ZYA9JPPRW3QRW3Ilfh4bmyNUPOL2u4eKT0OQs/8j5FHxX/EWZuJKBfEQ2OUP498R urVABdxXZrAr3x3wtV8kvBp51z9a9ycCfUDnSbIPkgNk8nclzD6UVMTj63YoZ4yMDORS eFGBhpiZLmv1NPjYT4pNimI3IdVq86tODnuIzBC6bjJm7gBphSBd7dwiKa7RxLHhirc5 93d4L8wx4aKVvMsm2uBO031n1EvB4qw65/mLdpBNEKLiW4X6EIFBFs0WYWWqv5m/1kcm 637g== X-Forwarded-Encrypted: i=1; AKwUvBwJ8DwFOibO9Bg7c2/ypM3mXVZ5Gn9vo2BDVsmXMspWavmCZty3XA2EcdTWCfdpoh1hXHXyhjqYHdXOb7o=@vger.kernel.org X-Gm-Message-State: AFuF++lcCY/G79CLdtykWEd9mQSqCqiJVINdZyVAscpQ8aAjDuTddZSR IeI8YhWJvjAXcUJr30o3vBTgbeK+6ju7azkXzGaexpUICpu4akzOjdZ51/TdDPpTfe70 X-Gm-Gg: AYBFou3bFHQ4xnich4ez5cudlQuKibi2jfcXk5miYN71EA7/+I0k7Jrt5CJtxDw3Vhh FdTYPT20dqtP/RuTPSRlTs5UHXvktbfH8B20bQpBco8iOsrjWnhayut3ys1CwpiyeOjFP5QwptR Whmuo1oP4YsQN4A7GctgjLILqO990G/raHTcJfTHy9zWonZdahv3KTEkSlwG4t3VmHUyWGlIvMn SWY2yW7PiwS7XhV0BJs1qsGY2iSRUG0qZql8TIy2hD1n0UOKmNjnbofHrnfpUObmYvAJ4BDE/j5 F9PNSqjwLvdxdUGNUU9ohIarlRQGxFS9ctBcLi/x4wd0zvEQ489Pyjd2UmQNuD7ck9oQPTM/fA+ RBuPDDOBLmdOy5lw83UHIDdUVtKb6NTRAcLNSM8S+NUzAe44d6GEGnTPcTsLoZcEWxJ+ofLpYq+ pbqa/u4Qq9GS2ieMTK+oeHTigx2VdezyFe15otOx7FBFJE9vnXXLGB68g7L0towSHsTprZKXMOP cR/AH2UxEAAE1UmrOTYdNAaGitbvw== X-Received: by 2002:a17:903:234e:b0:2dd:ad73:c983 with SMTP id d9443c01a7336-2df69d595f0mr17906435ad.27.1790157287897; Wed, 23 Sep 2026 02:54:47 -0700 (PDT) Received: from 954df21a5119.. ([122.51.212.64]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-3a07dc5f3d7sm4793862a91.16.2026.09.23.02.54.42 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 23 Sep 2026 02:54:47 -0700 (PDT) From: Zihan Xi To: netfilter-devel@vger.kernel.org Cc: netdev@vger.kernel.org, lvs-devel@vger.kernel.org, coreteam@netfilter.org, linux-kernel@vger.kernel.org, horms@verge.net.au, ja@ssi.bg, zihanx@nebusec.ai, pablo@netfilter.org, fw@strlen.de, phil@nwl.cc, davem@davemloft.net, edumazet@google.com, kuba@kernel.org, pabeni@redhat.com, stable@vger.kernel.org, Vega , Luxing Yin Subject: [PATCH nf v4 3/3] ipvs: reject FTP control ports as data ports Date: Wed, 23 Sep 2026 09:54:15 +0000 Message-ID: X-Mailer: git-send-email 2.47.3 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" ip_vs_ftp_out() creates a wildcard data connection from the server-advertised passive port. If that port is one of the configured FTP control ports, ip_vs_conn_new() binds the FTP helper to the new connection again. A subsequent wildcard lookup can then extend a controlled-connection chain. Reject zero and configured control ports before creating passive connections. For active mode, reject a zero client port and a data port derived from a configured control port. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Cc: stable@vger.kernel.org Reported-by: Vega Assisted-by: LLM Co-developed-by: Luxing Yin Signed-off-by: Luxing Yin Signed-off-by: Zihan Xi --- changes in v4: - Resend the FTP helper fix as patch 3/3 with the generic cleanup fixes. - v3 Link: https://lore.kernel.org/all/cover.1789877273.git.zihanx@nebusec.ai/ changes in v3: - Keep the active-mode guard for configured FTP control ports. - v2 Link: https://lore.kernel.org/all/cover.1789435989.git.zihanx@nebusec.ai/ changes in v2: - Add the active-mode check for a data port derived from a configured control port. - v1 Link: https://lore.kernel.org/all/cover.1789110326.git.zihanx@nebusec.ai/ net/netfilter/ipvs/ip_vs_ftp.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/net/netfilter/ipvs/ip_vs_ftp.c b/net/netfilter/ipvs/ip_vs_ftp.c index 9e3e005a82635..4822a1a75212d 100644 --- a/net/netfilter/ipvs/ip_vs_ftp.c +++ b/net/netfilter/ipvs/ip_vs_ftp.c @@ -62,6 +62,17 @@ static unsigned short ports[IP_VS_APP_MAX_PORTS] =3D {21= , 0}; module_param_array(ports, ushort, &ports_count, 0444); MODULE_PARM_DESC(ports, "Ports to monitor for FTP control commands"); =20 +static bool is_control_port(u16 port) +{ + unsigned int i; + + for (i =3D 0; i < ports_count; i++) { + if (ports[i] =3D=3D port) + return true; + } + return false; +} + =20 static char *ip_vs_ftp_data_ptr(struct sk_buff *skb, struct ip_vs_iphdr *i= pvsh) { @@ -319,6 +330,10 @@ static int ip_vs_ftp_out(struct ip_vs_app *app, struct= ip_vs_conn *cp, return 1; } =20 + /* Do not redirect data to control ports */ + if (!port || is_control_port(ntohs(port))) + return 0; + /* Now update or create a connection entry for it */ { struct ip_vs_conn_param p; @@ -529,6 +544,9 @@ static int ip_vs_ftp_in(struct ip_vs_app *app, struct i= p_vs_conn *cp, return 1; } =20 + if (!port || is_control_port(ntohs(cp->vport) - 1)) + return 0; + /* Passive mode off */ cp->app_data =3D (void *) IP_VS_FTP_ACTIVE; =20 --=20 2.43.0