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>
next prev 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 [PATCH nf 0/1] ipvs: avoid stack overflow from recursive connection expiration 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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.