DORMANTno replies

[PATCH nf] netfilter: conntrack: prevent helper extension relocation

From: Jaeyeong Lee <hidden>
Date: 2026-07-15 14:48:36
Also in: netfilter-devel
Subsystem: netfilter, networking [general], the rest · Maintainers: Pablo Neira Ayuso, Florian Westphal, "David S. Miller", Eric Dumazet, Jakub Kicinski, Paolo Abeni, Linus Torvalds

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 <redacted>
---
 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

Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help