* [PATCH 6.12.y] netfilter: nf_conntrack_expect: add and use nf_ct_expect_related_pair()
@ 2026-11-01 15:27 Joas Antonio dos Santos via B4 Relay
2026-11-01 15:48 ` Greg Kroah-Hartman
0 siblings, 1 reply; 4+ messages in thread
From: Joas Antonio dos Santos via B4 Relay @ 2026-11-01 15:27 UTC (permalink / raw)
To: stable
Cc: Pablo Neira Ayuso, Florian Westphal, netfilter-devel, Sasha Levin,
Greg Kroah-Hartman, Jaeyeong Lee, Joas Antonio dos Santos
From: Pablo Neira Ayuso <pablo@netfilter.org>
commit 4aa63842fc92de1bce59d4709a0d32e718890bb2 upstream.
Add a new function to insert a pair of expectations, this is required by
the SIP and H323 NAT helpers. The spinlock is held to check if there is
a slot for both expectations, in such case, insert them.
This removes the need for nf_ct_unexpect_related() inside the loop to
find a pair of consecutive ports, otherwise inserting expectations whose
dead flag is already set on can happen.
Bump master_help->expecting for the expectation class after checking if
the expectation fits in the master expectation list, which is needed for
this new _pair() function variant to run the eviction routine including
the preallocated slot for the first expectation in the pair.
[ Joas: nf_ct_expect_insert() and __nf_ct_expect_check() do not take
a master_help argument in this tree, so keep their existing
signatures; nf_ct_expect_related_report() gets master_help from
nfct_help(expect->master) for the moved expecting[] increment. ]
Fixes: b8b09dc2bf35 ("netfilter: nf_conntrack_expect: use conntrack GC to reap expectations")
Reported-by: Jaeyeong Lee <iostreampy@proton.me>
Link: https://patch.msgid.link/178377968720.33756.12204817361601593230@proton.me/
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Joas Antonio dos Santos <joasantonio108@gmail.com>
---
b8b09dc2bf35 ("netfilter: nf_conntrack_expect: use conntrack GC to reap
expectations") is in 6.6.160+, 6.12.114+ and 6.1.191, but its follow-up
4aa63842fc92 is not, and it is not in the stable queue either. Sasha
noted the missing dependency on 2026-09-20.
Without it, nf_ct_unexpect_related() only sets NF_CT_EXPECT_DEAD and
leaves the expectation linked, while the SIP and H.323 NAT port loops
still call nf_ct_unexpect_related(rtp_exp) on -EBUSY and re-insert the
same, still-linked rtp_exp. That corrupts the master expectation list
and the expect hash. It is reachable from an unprivileged user and
network namespace (sip helper + NAT, plus one ctnetlink expectation
occupying the RTCP port).
The affected code is the same in v6.12.115. Runtime-tested on v6.6.161
arm64 with KASAN and DEBUG_LIST, as uid 1000 in a new user+net
namespace; on v6.12.115 the patch is build-tested. On v6.6.161 without it:
KASAN: maybe wild-memory-access in range [0xdead000000000120-0xdead000000000127]
pc : nf_ct_unlink_expect_report+0xfc/0x418
nf_ct_unlink_expect_report
flush_expectations
process_invite_request
process_sip_msg
sip_help_udp
nf_confirm
Kernel panic - not syncing: Oops: Fatal exception in interrupt
With it, the same reproducer runs clean. The same backport applies to
6.6.y and 6.1.y (sent separately); I can share the reproducer
privately if useful.
Tool-use note: the analysis, backport and testing were done with help
from an LLM-based assistant; I reviewed the change and ran the tests
myself.
---
include/net/netfilter/nf_conntrack_expect.h | 3 +++
net/ipv4/netfilter/nf_nat_h323.c | 22 ++++++------------
net/netfilter/nf_conntrack_expect.c | 35 ++++++++++++++++++++++++++++-
net/netfilter/nf_nat_sip.c | 20 +++++------------
4 files changed, 50 insertions(+), 30 deletions(-)
diff --git a/include/net/netfilter/nf_conntrack_expect.h b/include/net/netfilter/nf_conntrack_expect.h
index be4a120d549e..8efc1ed0925a 100644
--- a/include/net/netfilter/nf_conntrack_expect.h
+++ b/include/net/netfilter/nf_conntrack_expect.h
@@ -160,6 +160,9 @@ static inline int nf_ct_expect_related(struct nf_conntrack_expect *expect,
return nf_ct_expect_related_report(expect, 0, 0, flags);
}
+int nf_ct_expect_related_pair(struct nf_conntrack_expect *expect[],
+ unsigned int flag);
+
struct nf_conn_help;
void nf_ct_expectation_gc(struct nf_conn_help *master_help);
diff --git a/net/ipv4/netfilter/nf_nat_h323.c b/net/ipv4/netfilter/nf_nat_h323.c
index 10e1b0837731..d55994fccf5d 100644
--- a/net/ipv4/netfilter/nf_nat_h323.c
+++ b/net/ipv4/netfilter/nf_nat_h323.c
@@ -179,6 +179,7 @@ static int nat_rtp_rtcp(struct sk_buff *skb, struct nf_conn *ct,
struct nf_conntrack_expect *rtp_exp,
struct nf_conntrack_expect *rtcp_exp)
{
+ struct nf_conntrack_expect *rtp_pair[2] = { rtp_exp, rtcp_exp };
struct nf_ct_h323_master *info = nfct_help_data(ct);
int dir = CTINFO2DIR(ctinfo);
int i;
@@ -221,22 +222,13 @@ static int nat_rtp_rtcp(struct sk_buff *skb, struct nf_conn *ct,
int ret;
rtp_exp->tuple.dst.u.udp.port = htons(nated_port);
- ret = nf_ct_expect_related(rtp_exp, 0);
+ rtcp_exp->tuple.dst.u.udp.port = htons(nated_port + 1);
+ ret = nf_ct_expect_related_pair(rtp_pair, 0);
if (ret == 0) {
- rtcp_exp->tuple.dst.u.udp.port =
- htons(nated_port + 1);
- ret = nf_ct_expect_related(rtcp_exp, 0);
- if (ret == 0)
- break;
- else if (ret == -EBUSY) {
- nf_ct_unexpect_related(rtp_exp);
- continue;
- } else if (ret < 0) {
- nf_ct_unexpect_related(rtp_exp);
- nated_port = 0;
- break;
- }
- } else if (ret != -EBUSY) {
+ break;
+ } else if (ret == -EBUSY) {
+ continue;
+ } else if (ret < 0) {
nated_port = 0;
break;
}
diff --git a/net/netfilter/nf_conntrack_expect.c b/net/netfilter/nf_conntrack_expect.c
index f9352dada84e..1e1e3a4a0d1c 100644
--- a/net/netfilter/nf_conntrack_expect.c
+++ b/net/netfilter/nf_conntrack_expect.c
@@ -425,7 +425,6 @@ static void nf_ct_expect_insert(struct nf_conntrack_expect *exp)
exp->timeout += helper->expect_policy[exp->class].timeout * HZ;
hlist_add_head_rcu(&exp->lnode, &master_help->expectations);
- master_help->expecting[exp->class]++;
hlist_add_head_rcu(&exp->hnode, &nf_ct_expect_hash[h]);
cnet = nf_ct_pernet(net);
@@ -524,6 +523,7 @@ int nf_ct_expect_related_report(struct nf_conntrack_expect *expect,
if (ret < 0)
goto out;
+ nfct_help(expect->master)->expecting[expect->class]++;
nf_ct_expect_insert(expect);
nf_ct_expect_event_report(IPEXP_NEW, expect, portid, report);
@@ -536,6 +536,39 @@ int nf_ct_expect_related_report(struct nf_conntrack_expect *expect,
}
EXPORT_SYMBOL_GPL(nf_ct_expect_related_report);
+int nf_ct_expect_related_pair(struct nf_conntrack_expect *expect[],
+ unsigned int flags)
+{
+ struct nf_conn_help *master_help;
+ int i, ret;
+
+ spin_lock_bh(&nf_conntrack_expect_lock);
+ master_help = nfct_help(expect[0]->master);
+ if (!master_help || master_help != nfct_help(expect[1]->master)) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ for (i = 0; i < 2; i++) {
+ ret = __nf_ct_expect_check(expect[i], flags);
+ if (ret < 0) {
+ if (i == 1)
+ master_help->expecting[expect[0]->class]--;
+ goto out;
+ }
+ master_help->expecting[expect[i]->class]++;
+ }
+
+ for (i = 0; i < 2; i++) {
+ nf_ct_expect_insert(expect[i]);
+ nf_ct_expect_event_report(IPEXP_NEW, expect[i], 0, 0);
+ }
+out:
+ spin_unlock_bh(&nf_conntrack_expect_lock);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(nf_ct_expect_related_pair);
+
void nf_ct_expect_iterate_destroy(bool (*iter)(struct nf_conntrack_expect *e, void *data),
void *data)
{
diff --git a/net/netfilter/nf_nat_sip.c b/net/netfilter/nf_nat_sip.c
index 6b00c81084fe..115d955bfaef 100644
--- a/net/netfilter/nf_nat_sip.c
+++ b/net/netfilter/nf_nat_sip.c
@@ -585,6 +585,7 @@ static unsigned int nf_nat_sdp_media(struct sk_buff *skb, unsigned int protoff,
unsigned int medialen,
union nf_inet_addr *rtp_addr)
{
+ struct nf_conntrack_expect *rtp_pair[2] = { rtp_exp, rtcp_exp };
enum ip_conntrack_info ctinfo;
struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
@@ -615,24 +616,15 @@ static unsigned int nf_nat_sdp_media(struct sk_buff *skb, unsigned int protoff,
int ret;
rtp_exp->tuple.dst.u.udp.port = htons(port);
- ret = nf_ct_expect_related(rtp_exp,
- NF_CT_EXP_F_SKIP_MASTER);
- if (ret == -EBUSY)
- continue;
- else if (ret < 0) {
- port = 0;
- break;
- }
rtcp_exp->tuple.dst.u.udp.port = htons(port + 1);
- ret = nf_ct_expect_related(rtcp_exp,
- NF_CT_EXP_F_SKIP_MASTER);
+
+ ret = nf_ct_expect_related_pair(rtp_pair,
+ NF_CT_EXP_F_SKIP_MASTER);
if (ret == 0)
break;
- else if (ret == -EBUSY) {
- nf_ct_unexpect_related(rtp_exp);
+ else if (ret == -EBUSY)
continue;
- } else if (ret < 0) {
- nf_ct_unexpect_related(rtp_exp);
+ else if (ret < 0) {
port = 0;
break;
}
---
base-commit: 03bae623ec326c2738198739625832aab0eb120a
change-id: 20261101-nfexp-pair-6-12-y-a2cb15426d3f
Best regards,
--
Joas Antonio dos Santos <joasantonio108@gmail.com>
^ permalink raw reply related [flat|nested] 4+ messages in thread
* Re: [PATCH 6.12.y] netfilter: nf_conntrack_expect: add and use nf_ct_expect_related_pair()
2026-11-01 15:27 [PATCH 6.12.y] netfilter: nf_conntrack_expect: add and use nf_ct_expect_related_pair() Joas Antonio dos Santos via B4 Relay
@ 2026-11-01 15:48 ` Greg Kroah-Hartman
2026-11-01 15:54 ` Joas Antonio
0 siblings, 1 reply; 4+ messages in thread
From: Greg Kroah-Hartman @ 2026-11-01 15:48 UTC (permalink / raw)
To: joasantonio108
Cc: stable, Pablo Neira Ayuso, Florian Westphal, netfilter-devel,
Sasha Levin, Jaeyeong Lee
On Sun, Nov 01, 2026 at 03:27:30PM +0000, Joas Antonio dos Santos via B4 Relay wrote:
> From: Pablo Neira Ayuso <pablo@netfilter.org>
>
> commit 4aa63842fc92de1bce59d4709a0d32e718890bb2 upstream.
<snip>
What about a backport for 6.18.y first?
thanks,
greg k-h
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH 6.12.y] netfilter: nf_conntrack_expect: add and use nf_ct_expect_related_pair()
2026-11-01 15:48 ` Greg Kroah-Hartman
@ 2026-11-01 15:54 ` Joas Antonio
2026-11-02 15:33 ` Sasha Levin
0 siblings, 1 reply; 4+ messages in thread
From: Joas Antonio @ 2026-11-01 15:54 UTC (permalink / raw)
To: Greg Kroah-Hartman
Cc: stable, Pablo Neira Ayuso, Florian Westphal, netfilter-devel,
Sasha Levin, Jaeyeong Lee
Hi Greg,
6.18.y does not need it: it does not have b8b09dc2bf35 ("netfilter:
nf_conntrack_expect: use conntrack GC to reap expectations"), which
is what makes this follow-up necessary. In v6.18.60
nf_ct_unexpect_related() still unlinks the expectation through
nf_ct_remove_expect() (net/netfilter/nf_conntrack_expect.c:292-297),
so the SIP/H.323 loops there are fine. 7.3.y has both commits.
b8b09dc2bf35 is only in 6.12.y, 6.6.y and 6.1.y (since 6.12.114,
6.6.160 and 6.1.191), and those are the trees that need this fix.
If b8b09dc2bf35 is queued for 6.18.y later, 4aa63842fc92 should go
with it; I can send both for 6.18.y if you want that.
thanks,
Joas
Em dom., 1 de nov. de 2026 às 12:48, Greg Kroah-Hartman
<gregkh@linuxfoundation.org> escreveu:
>
> On Sun, Nov 01, 2026 at 03:27:30PM +0000, Joas Antonio dos Santos via B4 Relay wrote:
> > From: Pablo Neira Ayuso <pablo@netfilter.org>
> >
> > commit 4aa63842fc92de1bce59d4709a0d32e718890bb2 upstream.
>
> <snip>
>
> What about a backport for 6.18.y first?
>
> thanks,
>
> greg k-h
--
Joas Antonio dos Santos
Cyber Security Specialist and Ethical Hacker
joasantonio108@gmail.com
LinkedIn
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH 6.12.y] netfilter: nf_conntrack_expect: add and use nf_ct_expect_related_pair()
2026-11-01 15:54 ` Joas Antonio
@ 2026-11-02 15:33 ` Sasha Levin
0 siblings, 0 replies; 4+ messages in thread
From: Sasha Levin @ 2026-11-02 15:33 UTC (permalink / raw)
To: Greg Kroah-Hartman
Cc: Sasha Levin, stable, Pablo Neira Ayuso, Florian Westphal,
netfilter-devel, Jaeyeong Lee, Joas Antonio
> 6.18.y does not need it: it does not have b8b09dc2bf35 ("netfilter:
> nf_conntrack_expect: use conntrack GC to reap expectations"), which
> is what makes this follow-up necessary.
6.18 not having b8b09dc2bf35 ("netfilter: nf_conntrack_expect: use conntrack GC
to reap expectations") is the gap, not a reason to skip it. It fixes the stale
exp->master race that bffcaad9afdf ("netfilter: ctnetlink: ensure safe access
to master conntrack") left behind, and 6.18 has that commit.
So 6.18 gets it together with 4aa63842fc92 ("netfilter: nf_conntrack_expect:
add and use nf_ct_expect_related_pair()") and 322371b09058 ("netfilter:
ctnetlink: do not expose expectation DEAD flag") as one chain. Nothing more to
send for 6.18.
Queued for 6.12, thanks.
--
Thanks,
Sasha
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2026-11-02 15:33 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-11-01 15:27 [PATCH 6.12.y] netfilter: nf_conntrack_expect: add and use nf_ct_expect_related_pair() Joas Antonio dos Santos via B4 Relay
2026-11-01 15:48 ` Greg Kroah-Hartman
2026-11-01 15:54 ` Joas Antonio
2026-11-02 15:33 ` Sasha Levin
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox