Netdev List
 help / color / mirror / Atom feed
From: Jaeyeong Lee <iostreampy@proton.me>
To: netfilter-devel@vger.kernel.org
Cc: pablo@netfilter.org, fw@strlen.de, phil@nwl.cc,
	sveyret@gmail.com, coreteam@netfilter.org,
	netdev@vger.kernel.org
Subject: [PATCH nf] netfilter: conntrack: prevent helper extension relocation
Date: Wed, 15 Jul 2026 14:48:23 +0000	[thread overview]
Message-ID: <20260715144755.00ea7dfcd9f@proton.me> (raw)

nf_ct_ext_add() may relocate an unconfirmed conntrack's extension blob
when the current allocation is too small. struct nf_conn_help embeds the
hlist head for the master's expectations. The first expectation's
lnode.pprev therefore points into that extension blob.

An nftables CT expectation object can link an expectation before a later
NAT expression adds extensions to the same unconfirmed conntrack. If an
addition relocates the blob, the copied hlist head still points to the
expectation, but the expectation's pprev continues to point into the
freed old blob. Removing the expectation later executes hlist_del_rcu()
and writes through this stale pointer.

Reserve enough storage for every extension whenever the helper extension
is added. Account for the alignment padding that may be required between
extensions so the reservation is independent of their addition order.
No expectation can be linked before its master has a helper extension,
so subsequent additions fit in the existing allocation and cannot
invalidate expectation list pointers. Conntracks without helpers are
unaffected.

Fixes: 857b46027d6f ("netfilter: nft_ct: add ct expectations support")
Cc: stable@vger.kernel.org
Signed-off-by: Jaeyeong Lee <iostreampy@proton.me>
---
 net/netfilter/nf_conntrack_extend.c | 31 +++++++++++++++++++----------
 1 file changed, 21 insertions(+), 10 deletions(-)

diff --git a/net/netfilter/nf_conntrack_extend.c b/net/netfilter/nf_conntrack_extend.c
index 0da105e1ded..e5e04e0ec40 100644
--- a/net/netfilter/nf_conntrack_extend.c
+++ b/net/netfilter/nf_conntrack_extend.c
@@ -54,35 +54,38 @@ static const u8 nf_ct_ext_type_len[NF_CT_EXT_NUM] = {
 #endif
 };
 
+#define NF_CT_EXT_TYPE_SIZE(type) \
+	ALIGN(sizeof(type), __alignof__(struct nf_ct_ext))
+
 static __always_inline unsigned int total_extension_size(void)
 {
 	/* remember to add new extensions below */
 	BUILD_BUG_ON(NF_CT_EXT_NUM > 10);
 
 	return sizeof(struct nf_ct_ext) +
-	       sizeof(struct nf_conn_help)
+	       NF_CT_EXT_TYPE_SIZE(struct nf_conn_help)
 #if IS_ENABLED(CONFIG_NF_NAT)
-		+ sizeof(struct nf_conn_nat)
+		+ NF_CT_EXT_TYPE_SIZE(struct nf_conn_nat)
 #endif
-		+ sizeof(struct nf_conn_seqadj)
-		+ sizeof(struct nf_conn_acct)
+		+ NF_CT_EXT_TYPE_SIZE(struct nf_conn_seqadj)
+		+ NF_CT_EXT_TYPE_SIZE(struct nf_conn_acct)
 #ifdef CONFIG_NF_CONNTRACK_EVENTS
-		+ sizeof(struct nf_conntrack_ecache)
+		+ NF_CT_EXT_TYPE_SIZE(struct nf_conntrack_ecache)
 #endif
 #ifdef CONFIG_NF_CONNTRACK_TIMESTAMP
-		+ sizeof(struct nf_conn_tstamp)
+		+ NF_CT_EXT_TYPE_SIZE(struct nf_conn_tstamp)
 #endif
 #ifdef CONFIG_NF_CONNTRACK_TIMEOUT
-		+ sizeof(struct nf_conn_timeout)
+		+ NF_CT_EXT_TYPE_SIZE(struct nf_conn_timeout)
 #endif
 #ifdef CONFIG_NF_CONNTRACK_LABELS
-		+ sizeof(struct nf_conn_labels)
+		+ NF_CT_EXT_TYPE_SIZE(struct nf_conn_labels)
 #endif
 #if IS_ENABLED(CONFIG_NETFILTER_SYNPROXY)
-		+ sizeof(struct nf_conn_synproxy)
+		+ NF_CT_EXT_TYPE_SIZE(struct nf_conn_synproxy)
 #endif
 #if IS_ENABLED(CONFIG_NET_ACT_CT)
-		+ sizeof(struct nf_conn_act_ct_ext)
+		+ NF_CT_EXT_TYPE_SIZE(struct nf_conn_act_ct_ext)
 #endif
 	;
 }
@@ -112,6 +115,14 @@ void *nf_ct_ext_add(struct nf_conn *ct, enum nf_ct_ext_id id, gfp_t gfp)
 	newlen = newoff + nf_ct_ext_type_len[id];
 
 	alloc = max(newlen, NF_CT_EXT_PREALLOC);
+	/*
+	 * nf_conn_help contains the expectation list head.  Once an
+	 * expectation is linked, its lnode.pprev points into this allocation,
+	 * so later extension additions must not be allowed to relocate it.
+	 */
+	if (id == NF_CT_EXT_HELPER)
+		alloc = max(alloc, total_extension_size());
+
 	new = krealloc(ct->ext, alloc, gfp);
 	if (!new)
 		return NULL;
-- 
2.43.0



                 reply	other threads:[~2026-07-15 14:48 UTC|newest]

Thread overview: [no followups] expand[flat|nested]  mbox.gz  Atom feed

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=20260715144755.00ea7dfcd9f@proton.me \
    --to=iostreampy@proton.me \
    --cc=coreteam@netfilter.org \
    --cc=fw@strlen.de \
    --cc=netdev@vger.kernel.org \
    --cc=netfilter-devel@vger.kernel.org \
    --cc=pablo@netfilter.org \
    --cc=phil@nwl.cc \
    --cc=sveyret@gmail.com \
    /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