mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Julian Anastasov <ja@ssi.bg>
To: Zihan Xi <zihanx@nebusec.ai>
Cc: horms@verge.net.au, pablo@netfilter.org, fw@strlen.de,
	davem@davemloft.net, edumazet@google.com, kuba@kernel.org,
	pabeni@redhat.com, phil@nwl.cc, netdev@vger.kernel.org,
	lvs-devel@vger.kernel.org, netfilter-devel@vger.kernel.org,
	coreteam@netfilter.org, linux-kernel@vger.kernel.org,
	stable@vger.kernel.org
Subject: Re: [PATCH nf 0/1] ipvs: avoid stack overflow from recursive connection expiration
Date: Sat, 12 Sep 2026 20:50:29 +0300 (EEST)	[thread overview]
Message-ID: <d99cb205-24fc-7446-55ad-4ec9d79aac1d@ssi.bg> (raw)
In-Reply-To: <cover.1789110326.git.zihanx@nebusec.ai>


	Hello,

On Sat, 12 Sep 2026, Zihan Xi wrote:

> Hi Linux kernel maintainers,
> 
> We found and validated an issue triggered through
> net/netfilter/ipvs/ip_vs_ftp.c. An unprivileged user can trigger it by
> creating a user namespace and a network namespace. We tested the fix with
> the same trigger. The change applies to the generic
> ip_vs_conn_expire() control-chain cleanup path. For the reported trigger,
> it only defers recursive controller expiration; testing showed no change to
> other IPVS behavior.
> 
> We will provide detailed information about the bug
> in this email, along with a PoC to trigger it.
> 
> ---- details below ----
> 
> Bug details:
> 
> The trigger entry point is ip_vs_ftp_out() in
> net/netfilter/ipvs/ip_vs_ftp.c. It parses an EPSV reply and creates a
> wildcard data connection using the advertised port. If that port is 21,
> ip_vs_conn_new() binds the new connection to the FTP helper a second time,
> because 21 is the helper's control port. The connection has
> IP_VS_CONN_F_NO_CPORT, so the next connection from the same client to the
> VIP on port 21 matches the wildcard entry instead of creating a new
> top-level entry. Repeating EPSV builds a chain of controlled connections.

	Hm, may be we should also avoid such long chains.
Probably in separate patch, for example:

diff --git a/net/netfilter/ipvs/ip_vs_ftp.c b/net/netfilter/ipvs/ip_vs_ftp.c
index 9e3e005a8263..0622169b5970 100644
--- a/net/netfilter/ipvs/ip_vs_ftp.c
+++ b/net/netfilter/ipvs/ip_vs_ftp.c
@@ -237,6 +237,17 @@ static int ip_vs_ftp_get_addrport(char *data, char *data_limit,
 	return 1;
 }
 
+static bool is_control_port(u16 port)
+{
+	int i;
+
+	for (i = 0; i < ports_count; i++) {
+		if (ports[i] == port)
+			return true;
+	}
+	return false;
+}
+
 /* Look at outgoing ftp packets to catch the response to a PASV/EPSV command
  * from the server (inside-to-outside).
  * When we see one, we build a connection entry with the client address,
@@ -293,6 +304,9 @@ static int ip_vs_ftp_out(struct ip_vs_app *app, struct ip_vs_conn *cp,
 
 		IP_VS_DBG(7, "PASV response (%pI4:%u) -> %pI4:%u detected\n",
 			  &from.ip, ntohs(port), &cp->caddr.ip, 0);
+		/* Do not redirect data to control ports */
+		if (!port || is_control_port(ntohs(port)))
+			return 0;
 	} else if (cp->app_data == (void *) IP_VS_FTP_EPSV) {
 		data = ip_vs_ftp_data_ptr(skb, ipvsh);
 		data_limit = skb_tail_pointer(skb);
@@ -529,6 +543,9 @@ static int ip_vs_ftp_in(struct ip_vs_app *app, struct ip_vs_conn *cp,
 		return 1;
 	}
 
+	if (!port)
+		return 0;
+
 	/* Passive mode off */
 	cp->app_data = (void *) IP_VS_FTP_ACTIVE;

	The only problem I see with your proposed change is
that we may need 2-3 timer ticks to expire a DATA->CTL->TPL
chain. Or it expires on the same tick?

	Alternative would be to jump to the beginnig of the
function after successful timer_delete() for our cp->control,
i.e. to use loop instead of recursion. I.e. ip_vs_conn_del_put()
can be converted to function that returns bool instead of
calling ip_vs_conn_expire(), so that we can know if to loop.

	If ip_vs_conn_flush() demands faster expiring, a
loop will work faster. What do you think?

Regards

--
Julian Anastasov <ja@ssi.bg>


  parent reply	other threads:[~2026-09-12 17:50 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-12  3:56 Zihan Xi
2026-09-12  3:56 ` [PATCH nf 1/1] " Zihan Xi
2026-09-12 17:50 ` Julian Anastasov [this message]
2026-09-13  7:13   ` [PATCH nf 0/1] " zihan xi

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=d99cb205-24fc-7446-55ad-4ec9d79aac1d@ssi.bg \
    --to=ja@ssi.bg \
    --cc=coreteam@netfilter.org \
    --cc=davem@davemloft.net \
    --cc=edumazet@google.com \
    --cc=fw@strlen.de \
    --cc=horms@verge.net.au \
    --cc=kuba@kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=lvs-devel@vger.kernel.org \
    --cc=netdev@vger.kernel.org \
    --cc=netfilter-devel@vger.kernel.org \
    --cc=pabeni@redhat.com \
    --cc=pablo@netfilter.org \
    --cc=phil@nwl.cc \
    --cc=stable@vger.kernel.org \
    --cc=zihanx@nebusec.ai \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®