net/netfilter/nf_conntrack_extend.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-)
struct nf_conn_help contains the head of the per-master expectation
list. hlist_add_head_rcu() makes the first expectation node point back to
that head, but nf_ct_ext_add() can later move the extension buffer with
krealloc(). This leaves the node backpointer aimed at freed memory, so
unlinking the expectation writes through a stale pointer.
Reserve the full u8-addressable extension space when adding the helper
extension and reuse the existing buffer once the helper is present. This
keeps the expectation list head stable while allowing later extensions
to be added.
Fixes: 857b46027d6f ("netfilter: nft_ct: add ct expectations support")
Bug found and triaged by OpenAI Security Research and
validated by Trail of Bits.
Assisted-by: Codex:gpt-5.6-sol gpt-5.5-cyber
Signed-off-by: Kyle Zeng <kylebot@openai.com>
---
Trail of Bits has a reproducer for this bug that triggers a
KASAN use-after-free and can share if needed.
net/netfilter/nf_conntrack_extend.c | 18 +++++++++++++++---
1 file changed, 15 insertions(+), 3 deletions(-)
diff --git a/net/netfilter/nf_conntrack_extend.c b/net/netfilter/nf_conntrack_extend.c
index 0da105e1d..1421944a3 100644
--- a/net/netfilter/nf_conntrack_extend.c
+++ b/net/netfilter/nf_conntrack_extend.c
@@ -112,9 +112,21 @@ 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);
- new = krealloc(ct->ext, alloc, gfp);
- if (!new)
- return NULL;
+ /*
+ * Once an expectation is linked, its list node points back to the
+ * hlist head in the helper extension. Reserve all available extension
+ * space for the helper and do not move it afterward.
+ */
+ if (ct->ext &&
+ __nf_ct_ext_exist(ct->ext, NF_CT_EXT_HELPER)) {
+ new = ct->ext;
+ } else {
+ if (id == NF_CT_EXT_HELPER)
+ alloc = U8_MAX;
+ new = krealloc(ct->ext, alloc, gfp);
+ if (!new)
+ return NULL;
+ }
if (!ct->ext)
memset(new->offset, 0, sizeof(new->offset));
--
2.53.0
On Fri, Jul 31, 2026 at 02:08:10PM +0000, David Lee wrote:
> struct nf_conn_help contains the head of the per-master expectation
> list. hlist_add_head_rcu() makes the first expectation node point back to
> that head, but nf_ct_ext_add() can later move the extension buffer with
> krealloc(). This leaves the node backpointer aimed at freed memory, so
> unlinking the expectation writes through a stale pointer.
>
> Reserve the full u8-addressable extension space when adding the helper
> extension and reuse the existing buffer once the helper is present. This
> keeps the expectation list head stable while allowing later extensions
> to be added.
>
> Fixes: 857b46027d6f ("netfilter: nft_ct: add ct expectations support")
> Bug found and triaged by OpenAI Security Research and
> validated by Trail of Bits.
Thanks for your patch.
This is a duplicate of:
https://patchwork.ozlabs.org/project/netfilter-devel/patch/20260715144755.00ea7dfcd9f@proton.me/
proposed fix tackles the problem from the ct expectation feature angle:
https://patchwork.ozlabs.org/project/netfilter-devel/patch/20260730150305.617144-1-pablo@netfilter.org/
© 2016 - 2026 Red Hat, Inc.