Recreate RHEL kernel-6.12.0-211.47.1.el10_2 from CS10/upstream backports
This commit is contained in:
parent
a15dbea96c
commit
df706a6846
@ -0,0 +1,214 @@
|
||||
From 4655e3eac77838a9e585416ef176e1d1799287c0 Mon Sep 17 00:00:00 2001
|
||||
From: Florian Westphal <fwestpha@redhat.com>
|
||||
Date: Wed, 13 May 2026 17:15:52 +0200
|
||||
Subject: [PATCH] netfilter: nft_set_pipapo: split gc into unlink and reclaim
|
||||
phase
|
||||
|
||||
JIRA: https://redhat.atlassian.net/browse/RHEL-168848
|
||||
Upstream Status: commit 9df95785d3d8
|
||||
|
||||
commit 9df95785d3d8302f7c066050117b04cd3c2048c2
|
||||
Author: Florian Westphal <fw@strlen.de>
|
||||
Date: Tue Mar 3 16:31:32 2026 +0100
|
||||
|
||||
netfilter: nft_set_pipapo: split gc into unlink and reclaim phase
|
||||
|
||||
Yiming Qian reports Use-after-free in the pipapo set type:
|
||||
Under a large number of expired elements, commit-time GC can run for a very
|
||||
long time in a non-preemptible context, triggering soft lockup warnings and
|
||||
RCU stall reports (local denial of service).
|
||||
|
||||
We must split GC in an unlink and a reclaim phase.
|
||||
|
||||
We cannot queue elements for freeing until pointers have been swapped.
|
||||
Expired elements are still exposed to both the packet path and userspace
|
||||
dumpers via the live copy of the data structure.
|
||||
|
||||
call_rcu() does not protect us: dump operations or element lookups starting
|
||||
after call_rcu has fired can still observe the free'd element, unless the
|
||||
commit phase has made enough progress to swap the clone and live pointers
|
||||
before any new reader has picked up the old version.
|
||||
|
||||
This a similar approach as done recently for the rbtree backend in commit
|
||||
35f83a75529a ("netfilter: nft_set_rbtree: don't gc elements on insert").
|
||||
|
||||
Fixes: 3c4287f62044 ("nf_tables: Add set type for arbitrary concatenation of ranges")
|
||||
Reported-by: Yiming Qian <yimingqian591@gmail.com>
|
||||
Signed-off-by: Florian Westphal <fw@strlen.de>
|
||||
|
||||
Signed-off-by: Florian Westphal <fwestpha@redhat.com>
|
||||
|
||||
diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h
|
||||
index 38b3be3..81b52cd 100644
|
||||
--- a/include/net/netfilter/nf_tables.h
|
||||
+++ b/include/net/netfilter/nf_tables.h
|
||||
@@ -1856,6 +1856,11 @@ struct nft_trans_gc {
|
||||
struct rcu_head rcu;
|
||||
};
|
||||
|
||||
+static inline int nft_trans_gc_space(const struct nft_trans_gc *trans)
|
||||
+{
|
||||
+ return NFT_TRANS_GC_BATCHCOUNT - trans->count;
|
||||
+}
|
||||
+
|
||||
static inline void nft_ctx_update(struct nft_ctx *ctx,
|
||||
const struct nft_trans *trans)
|
||||
{
|
||||
diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c
|
||||
index 6411001..5d244d3 100644
|
||||
--- a/net/netfilter/nf_tables_api.c
|
||||
+++ b/net/netfilter/nf_tables_api.c
|
||||
@@ -10623,11 +10623,6 @@ static void nft_trans_gc_queue_work(struct nft_trans_gc *trans)
|
||||
schedule_work(&trans_gc_work);
|
||||
}
|
||||
|
||||
-static int nft_trans_gc_space(struct nft_trans_gc *trans)
|
||||
-{
|
||||
- return NFT_TRANS_GC_BATCHCOUNT - trans->count;
|
||||
-}
|
||||
-
|
||||
struct nft_trans_gc *nft_trans_gc_queue_async(struct nft_trans_gc *gc,
|
||||
unsigned int gc_seq, gfp_t gfp)
|
||||
{
|
||||
diff --git a/net/netfilter/nft_set_pipapo.c b/net/netfilter/nft_set_pipapo.c
|
||||
index 2824944..308af52 100644
|
||||
--- a/net/netfilter/nft_set_pipapo.c
|
||||
+++ b/net/netfilter/nft_set_pipapo.c
|
||||
@@ -1666,11 +1666,11 @@ static void nft_pipapo_gc_deactivate(struct net *net, struct nft_set *set,
|
||||
}
|
||||
|
||||
/**
|
||||
- * pipapo_gc() - Drop expired entries from set, destroy start and end elements
|
||||
+ * pipapo_gc_scan() - Drop expired entries from set and link them to gc list
|
||||
* @set: nftables API set representation
|
||||
* @m: Matching data
|
||||
*/
|
||||
-static void pipapo_gc(struct nft_set *set, struct nft_pipapo_match *m)
|
||||
+static void pipapo_gc_scan(struct nft_set *set, struct nft_pipapo_match *m)
|
||||
{
|
||||
struct nft_pipapo *priv = nft_set_priv(set);
|
||||
struct net *net = read_pnet(&set->net);
|
||||
@@ -1683,6 +1683,8 @@ static void pipapo_gc(struct nft_set *set, struct nft_pipapo_match *m)
|
||||
if (!gc)
|
||||
return;
|
||||
|
||||
+ list_add(&gc->list, &priv->gc_head);
|
||||
+
|
||||
while ((rules_f0 = pipapo_rules_same_key(m->f, first_rule))) {
|
||||
union nft_pipapo_map_bucket rulemap[NFT_PIPAPO_MAX_FIELDS];
|
||||
const struct nft_pipapo_field *f;
|
||||
@@ -1710,9 +1712,13 @@ static void pipapo_gc(struct nft_set *set, struct nft_pipapo_match *m)
|
||||
* NFT_SET_ELEM_DEAD_BIT.
|
||||
*/
|
||||
if (__nft_set_elem_expired(&e->ext, tstamp)) {
|
||||
- gc = nft_trans_gc_queue_sync(gc, GFP_KERNEL);
|
||||
- if (!gc)
|
||||
- return;
|
||||
+ if (!nft_trans_gc_space(gc)) {
|
||||
+ gc = nft_trans_gc_alloc(set, 0, GFP_KERNEL);
|
||||
+ if (!gc)
|
||||
+ return;
|
||||
+
|
||||
+ list_add(&gc->list, &priv->gc_head);
|
||||
+ }
|
||||
|
||||
nft_pipapo_gc_deactivate(net, set, e);
|
||||
pipapo_drop(m, rulemap);
|
||||
@@ -1726,10 +1732,30 @@ static void pipapo_gc(struct nft_set *set, struct nft_pipapo_match *m)
|
||||
}
|
||||
}
|
||||
|
||||
- gc = nft_trans_gc_catchall_sync(gc);
|
||||
+ priv->last_gc = jiffies;
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * pipapo_gc_queue() - Free expired elements
|
||||
+ * @set: nftables API set representation
|
||||
+ */
|
||||
+static void pipapo_gc_queue(struct nft_set *set)
|
||||
+{
|
||||
+ struct nft_pipapo *priv = nft_set_priv(set);
|
||||
+ struct nft_trans_gc *gc, *next;
|
||||
+
|
||||
+ /* always do a catchall cycle: */
|
||||
+ gc = nft_trans_gc_alloc(set, 0, GFP_KERNEL);
|
||||
if (gc) {
|
||||
+ gc = nft_trans_gc_catchall_sync(gc);
|
||||
+ if (gc)
|
||||
+ nft_trans_gc_queue_sync_done(gc);
|
||||
+ }
|
||||
+
|
||||
+ /* always purge queued gc elements. */
|
||||
+ list_for_each_entry_safe(gc, next, &priv->gc_head, list) {
|
||||
+ list_del(&gc->list);
|
||||
nft_trans_gc_queue_sync_done(gc);
|
||||
- priv->last_gc = jiffies;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1783,6 +1809,10 @@ static void pipapo_reclaim_match(struct rcu_head *rcu)
|
||||
*
|
||||
* We also need to create a new working copy for subsequent insertions and
|
||||
* deletions.
|
||||
+ *
|
||||
+ * After the live copy has been replaced by the clone, we can safely queue
|
||||
+ * expired elements that have been collected by pipapo_gc_scan() for
|
||||
+ * memory reclaim.
|
||||
*/
|
||||
static void nft_pipapo_commit(struct nft_set *set)
|
||||
{
|
||||
@@ -1793,7 +1823,7 @@ static void nft_pipapo_commit(struct nft_set *set)
|
||||
return;
|
||||
|
||||
if (time_after_eq(jiffies, priv->last_gc + nft_set_gc_interval(set)))
|
||||
- pipapo_gc(set, priv->clone);
|
||||
+ pipapo_gc_scan(set, priv->clone);
|
||||
|
||||
old = rcu_replace_pointer(priv->match, priv->clone,
|
||||
nft_pipapo_transaction_mutex_held(set));
|
||||
@@ -1801,6 +1831,8 @@ static void nft_pipapo_commit(struct nft_set *set)
|
||||
|
||||
if (old)
|
||||
call_rcu(&old->rcu, pipapo_reclaim_match);
|
||||
+
|
||||
+ pipapo_gc_queue(set);
|
||||
}
|
||||
|
||||
static void nft_pipapo_abort(const struct nft_set *set)
|
||||
@@ -2258,6 +2290,7 @@ static int nft_pipapo_init(const struct nft_set *set,
|
||||
f->mt = NULL;
|
||||
}
|
||||
|
||||
+ INIT_LIST_HEAD(&priv->gc_head);
|
||||
rcu_assign_pointer(priv->match, m);
|
||||
|
||||
return 0;
|
||||
@@ -2307,6 +2340,8 @@ static void nft_pipapo_destroy(const struct nft_ctx *ctx,
|
||||
struct nft_pipapo *priv = nft_set_priv(set);
|
||||
struct nft_pipapo_match *m;
|
||||
|
||||
+ WARN_ON_ONCE(!list_empty(&priv->gc_head));
|
||||
+
|
||||
m = rcu_dereference_protected(priv->match, true);
|
||||
|
||||
if (priv->clone) {
|
||||
diff --git a/net/netfilter/nft_set_pipapo.h b/net/netfilter/nft_set_pipapo.h
|
||||
index 4a2ff85..49000f5 100644
|
||||
--- a/net/netfilter/nft_set_pipapo.h
|
||||
+++ b/net/netfilter/nft_set_pipapo.h
|
||||
@@ -156,12 +156,14 @@ struct nft_pipapo_match {
|
||||
* @clone: Copy where pending insertions and deletions are kept
|
||||
* @width: Total bytes to be matched for one packet, including padding
|
||||
* @last_gc: Timestamp of last garbage collection run, jiffies
|
||||
+ * @gc_head: list of nft_trans_gc to queue up for mem reclaim
|
||||
*/
|
||||
struct nft_pipapo {
|
||||
struct nft_pipapo_match __rcu *match;
|
||||
struct nft_pipapo_match *clone;
|
||||
int width;
|
||||
unsigned long last_gc;
|
||||
+ struct list_head gc_head;
|
||||
};
|
||||
|
||||
struct nft_pipapo_elem;
|
||||
@ -0,0 +1,58 @@
|
||||
From ee1d70170aaa417e064d9f98e029e5aef0dd4397 Mon Sep 17 00:00:00 2001
|
||||
From: Florian Westphal <fwestpha@redhat.com>
|
||||
Date: Wed, 13 May 2026 17:15:53 +0200
|
||||
Subject: [PATCH] netfilter: nf_tables: always walk all pending catchall
|
||||
elements
|
||||
|
||||
JIRA: https://redhat.atlassian.net/browse/RHEL-168848
|
||||
Upstream Status: commit 7cb9a23d7ae4
|
||||
|
||||
commit 7cb9a23d7ae40a702577d3d8bacb7026f04ac2a9
|
||||
Author: Florian Westphal <fw@strlen.de>
|
||||
Date: Thu Mar 5 21:32:00 2026 +0100
|
||||
|
||||
netfilter: nf_tables: always walk all pending catchall elements
|
||||
|
||||
During transaction processing we might have more than one catchall element:
|
||||
1 live catchall element and 1 pending element that is coming as part of the
|
||||
new batch.
|
||||
|
||||
If the map holding the catchall elements is also going away, its
|
||||
required to toggle all catchall elements and not just the first viable
|
||||
candidate.
|
||||
|
||||
Otherwise, we get:
|
||||
WARNING: ./include/net/netfilter/nf_tables.h:1281 at nft_data_release+0xb7/0xe0 [nf_tables], CPU#2: nft/1404
|
||||
RIP: 0010:nft_data_release+0xb7/0xe0 [nf_tables]
|
||||
[..]
|
||||
__nft_set_elem_destroy+0x106/0x380 [nf_tables]
|
||||
nf_tables_abort_release+0x348/0x8d0 [nf_tables]
|
||||
nf_tables_abort+0xcf2/0x3ac0 [nf_tables]
|
||||
nfnetlink_rcv_batch+0x9c9/0x20e0 [..]
|
||||
|
||||
Fixes: 628bd3e49cba ("netfilter: nf_tables: drop map element references from preparation phase")
|
||||
Reported-by: Yiming Qian <yimingqian591@gmail.com>
|
||||
Signed-off-by: Florian Westphal <fw@strlen.de>
|
||||
|
||||
Signed-off-by: Florian Westphal <fwestpha@redhat.com>
|
||||
|
||||
diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c
|
||||
index 5d244d3..55434ea 100644
|
||||
--- a/net/netfilter/nf_tables_api.c
|
||||
+++ b/net/netfilter/nf_tables_api.c
|
||||
@@ -828,7 +828,6 @@ static void nft_map_catchall_deactivate(const struct nft_ctx *ctx,
|
||||
|
||||
nft_set_elem_change_active(ctx->net, set, ext);
|
||||
nft_setelem_data_deactivate(ctx->net, set, catchall->elem);
|
||||
- break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5918,7 +5917,6 @@ static void nft_map_catchall_activate(const struct nft_ctx *ctx,
|
||||
|
||||
nft_clear(ctx->net, ext);
|
||||
nft_setelem_data_activate(ctx->net, set, catchall->elem);
|
||||
- break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,57 @@
|
||||
From bb3bbb9f38362d2f5f47f38b354cc935cadea8ec Mon Sep 17 00:00:00 2001
|
||||
From: Florian Westphal <fwestpha@redhat.com>
|
||||
Date: Wed, 13 May 2026 17:10:08 +0200
|
||||
Subject: [PATCH] netfilter: nft_set_hash: fix get operation on big endian
|
||||
|
||||
JIRA: https://redhat.atlassian.net/browse/RHEL-168848
|
||||
Upstream Status: commit 2f635adbe264
|
||||
|
||||
commit 2f635adbe2642d398a0be3ab245accd2987be0c3
|
||||
Author: Florian Westphal <fw@strlen.de>
|
||||
Date: Tue Jan 27 20:13:45 2026 +0100
|
||||
|
||||
netfilter: nft_set_hash: fix get operation on big endian
|
||||
|
||||
tests/shell/testcases/packetpath/set_match_nomatch_hash_fast
|
||||
fails on big endian with:
|
||||
|
||||
Error: Could not process rule: No such file or directory
|
||||
reset element ip test s { 244.147.90.126 }
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
Fatal: Cannot fetch element "244.147.90.126"
|
||||
|
||||
... because the wrong bucket is searched, jhash() and jhash1_word are
|
||||
not interchangeable on big endian.
|
||||
|
||||
Fixes: 3b02b0adc242 ("netfilter: nft_set_hash: fix lookups with fixed size hash on big endian")
|
||||
Signed-off-by: Florian Westphal <fw@strlen.de>
|
||||
|
||||
Signed-off-by: Florian Westphal <fwestpha@redhat.com>
|
||||
|
||||
diff --git a/net/netfilter/nft_set_hash.c b/net/netfilter/nft_set_hash.c
|
||||
index ba01ce7..739b992 100644
|
||||
--- a/net/netfilter/nft_set_hash.c
|
||||
+++ b/net/netfilter/nft_set_hash.c
|
||||
@@ -619,15 +619,20 @@ static struct nft_elem_priv *
|
||||
nft_hash_get(const struct net *net, const struct nft_set *set,
|
||||
const struct nft_set_elem *elem, unsigned int flags)
|
||||
{
|
||||
+ const u32 *key = (const u32 *)&elem->key.val;
|
||||
struct nft_hash *priv = nft_set_priv(set);
|
||||
u8 genmask = nft_genmask_cur(net);
|
||||
struct nft_hash_elem *he;
|
||||
u32 hash;
|
||||
|
||||
- hash = jhash(elem->key.val.data, set->klen, priv->seed);
|
||||
+ if (set->klen == 4)
|
||||
+ hash = jhash_1word(*key, priv->seed);
|
||||
+ else
|
||||
+ hash = jhash(key, set->klen, priv->seed);
|
||||
+
|
||||
hash = reciprocal_scale(hash, priv->buckets);
|
||||
hlist_for_each_entry_rcu(he, &priv->table[hash], node) {
|
||||
- if (!memcmp(nft_set_ext_key(&he->ext), elem->key.val.data, set->klen) &&
|
||||
+ if (!memcmp(nft_set_ext_key(&he->ext), key, set->klen) &&
|
||||
nft_set_elem_active(&he->ext, genmask))
|
||||
return &he->priv;
|
||||
}
|
||||
@ -0,0 +1,65 @@
|
||||
From 812cb15e9cc9b8002c24e507a9004edbccb000c3 Mon Sep 17 00:00:00 2001
|
||||
From: Florian Westphal <fwestpha@redhat.com>
|
||||
Date: Wed, 13 May 2026 17:15:50 +0200
|
||||
Subject: [PATCH] netfilter: nf_conntrack_h323: fix OOB read in decode_choice()
|
||||
|
||||
JIRA: https://redhat.atlassian.net/browse/RHEL-168848
|
||||
Upstream Status: commit baed0d9ba91d
|
||||
|
||||
commit baed0d9ba91d4f390da12d5039128ee897253d60
|
||||
Author: Vahagn Vardanian <vahagn@redrays.io>
|
||||
Date: Wed Feb 25 14:06:18 2026 +0100
|
||||
|
||||
netfilter: nf_conntrack_h323: fix OOB read in decode_choice()
|
||||
|
||||
In decode_choice(), the boundary check before get_len() uses the
|
||||
variable `len`, which is still 0 from its initialization at the top of
|
||||
the function:
|
||||
|
||||
unsigned int type, ext, len = 0;
|
||||
...
|
||||
if (ext || (son->attr & OPEN)) {
|
||||
BYTE_ALIGN(bs);
|
||||
if (nf_h323_error_boundary(bs, len, 0)) /* len is 0 here */
|
||||
return H323_ERROR_BOUND;
|
||||
len = get_len(bs); /* OOB read */
|
||||
|
||||
When the bitstream is exactly consumed (bs->cur == bs->end), the check
|
||||
nf_h323_error_boundary(bs, 0, 0) evaluates to (bs->cur + 0 > bs->end),
|
||||
which is false. The subsequent get_len() call then dereferences
|
||||
*bs->cur++, reading 1 byte past the end of the buffer. If that byte
|
||||
has bit 7 set, get_len() reads a second byte as well.
|
||||
|
||||
This can be triggered remotely by sending a crafted Q.931 SETUP message
|
||||
with a User-User Information Element containing exactly 2 bytes of
|
||||
PER-encoded data ({0x08, 0x00}) to port 1720 through a firewall with
|
||||
the nf_conntrack_h323 helper active. The decoder fully consumes the
|
||||
PER buffer before reaching this code path, resulting in a 1-2 byte
|
||||
heap-buffer-overflow read confirmed by AddressSanitizer.
|
||||
|
||||
Fix this by checking for 2 bytes (the maximum that get_len() may read)
|
||||
instead of the uninitialized `len`. This matches the pattern used at
|
||||
every other get_len() call site in the same file, where the caller
|
||||
checks for 2 bytes of available data before calling get_len().
|
||||
|
||||
Fixes: ec8a8f3c31dd ("netfilter: nf_ct_h323: Extend nf_h323_error_boundary to work on bits as well")
|
||||
Signed-off-by: Vahagn Vardanian <vahagn@redrays.io>
|
||||
Signed-off-by: Florian Westphal <fw@strlen.de>
|
||||
Link: https://patch.msgid.link/20260225130619.1248-2-fw@strlen.de
|
||||
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
|
||||
|
||||
Signed-off-by: Florian Westphal <fwestpha@redhat.com>
|
||||
|
||||
diff --git a/net/netfilter/nf_conntrack_h323_asn1.c b/net/netfilter/nf_conntrack_h323_asn1.c
|
||||
index ca103c9..456bf3a 100644
|
||||
--- a/net/netfilter/nf_conntrack_h323_asn1.c
|
||||
+++ b/net/netfilter/nf_conntrack_h323_asn1.c
|
||||
@@ -796,7 +796,7 @@ static int decode_choice(struct bitstr *bs, const struct field_t *f,
|
||||
|
||||
if (ext || (son->attr & OPEN)) {
|
||||
BYTE_ALIGN(bs);
|
||||
- if (nf_h323_error_boundary(bs, len, 0))
|
||||
+ if (nf_h323_error_boundary(bs, 2, 0))
|
||||
return H323_ERROR_BOUND;
|
||||
len = get_len(bs);
|
||||
if (nf_h323_error_boundary(bs, len, 0))
|
||||
@ -0,0 +1,106 @@
|
||||
From 573d852abb5341047873d1257065ea6ad9174da4 Mon Sep 17 00:00:00 2001
|
||||
From: Florian Westphal <fwestpha@redhat.com>
|
||||
Date: Wed, 13 May 2026 17:15:52 +0200
|
||||
Subject: [PATCH] netfilter: nf_tables: unconditionally bump set->nelems before
|
||||
insertion
|
||||
|
||||
JIRA: https://redhat.atlassian.net/browse/RHEL-168848
|
||||
Upstream Status: commit def602e498a4
|
||||
|
||||
commit def602e498a4f951da95c95b1b8ce8ae68aa733a
|
||||
Author: Pablo Neira Ayuso <pablo@netfilter.org>
|
||||
Date: Mon Mar 2 23:12:37 2026 +0100
|
||||
|
||||
netfilter: nf_tables: unconditionally bump set->nelems before insertion
|
||||
|
||||
In case that the set is full, a new element gets published then removed
|
||||
without waiting for the RCU grace period, while RCU reader can be
|
||||
walking over it already.
|
||||
|
||||
To address this issue, add the element transaction even if set is full,
|
||||
but toggle the set_full flag to report -ENFILE so the abort path safely
|
||||
unwinds the set to its previous state.
|
||||
|
||||
As for element updates, decrement set->nelems to restore it.
|
||||
|
||||
A simpler fix is to call synchronize_rcu() in the error path.
|
||||
However, with a large batch adding elements to already maxed-out set,
|
||||
this could cause noticeable slowdown of such batches.
|
||||
|
||||
Fixes: 35d0ac9070ef ("netfilter: nf_tables: fix set->nelems counting with no NLM_F_EXCL")
|
||||
Reported-by: Inseo An <y0un9sa@gmail.com>
|
||||
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
|
||||
Signed-off-by: Florian Westphal <fw@strlen.de>
|
||||
|
||||
Signed-off-by: Florian Westphal <fwestpha@redhat.com>
|
||||
|
||||
diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c
|
||||
index 55434ea..f2d6ac3 100644
|
||||
--- a/net/netfilter/nf_tables_api.c
|
||||
+++ b/net/netfilter/nf_tables_api.c
|
||||
@@ -7281,6 +7281,7 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
|
||||
struct nft_data_desc desc;
|
||||
enum nft_registers dreg;
|
||||
struct nft_trans *trans;
|
||||
+ bool set_full = false;
|
||||
u64 expiration;
|
||||
u64 timeout;
|
||||
int err, i;
|
||||
@@ -7567,10 +7568,18 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
|
||||
if (err < 0)
|
||||
goto err_elem_free;
|
||||
|
||||
+ if (!(flags & NFT_SET_ELEM_CATCHALL)) {
|
||||
+ unsigned int max = nft_set_maxsize(set), nelems;
|
||||
+
|
||||
+ nelems = atomic_inc_return(&set->nelems);
|
||||
+ if (nelems > max)
|
||||
+ set_full = true;
|
||||
+ }
|
||||
+
|
||||
trans = nft_trans_elem_alloc(ctx, NFT_MSG_NEWSETELEM, set);
|
||||
if (trans == NULL) {
|
||||
err = -ENOMEM;
|
||||
- goto err_elem_free;
|
||||
+ goto err_set_size;
|
||||
}
|
||||
|
||||
ext->genmask = nft_genmask_cur(ctx->net);
|
||||
@@ -7622,7 +7631,7 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
|
||||
|
||||
ue->priv = elem_priv;
|
||||
nft_trans_commit_list_add_elem(ctx->net, trans);
|
||||
- goto err_elem_free;
|
||||
+ goto err_set_size;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7635,23 +7644,16 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
|
||||
goto err_element_clash;
|
||||
}
|
||||
|
||||
- if (!(flags & NFT_SET_ELEM_CATCHALL)) {
|
||||
- unsigned int max = nft_set_maxsize(set);
|
||||
-
|
||||
- if (!atomic_add_unless(&set->nelems, 1, max)) {
|
||||
- err = -ENFILE;
|
||||
- goto err_set_full;
|
||||
- }
|
||||
- }
|
||||
-
|
||||
nft_trans_container_elem(trans)->elems[0].priv = elem.priv;
|
||||
nft_trans_commit_list_add_elem(ctx->net, trans);
|
||||
- return 0;
|
||||
|
||||
-err_set_full:
|
||||
- nft_setelem_remove(ctx->net, set, elem.priv);
|
||||
+ return set_full ? -ENFILE : 0;
|
||||
+
|
||||
err_element_clash:
|
||||
kfree(trans);
|
||||
+err_set_size:
|
||||
+ if (!(flags & NFT_SET_ELEM_CATCHALL))
|
||||
+ atomic_dec(&set->nelems);
|
||||
err_elem_free:
|
||||
nf_tables_set_elem_destroy(ctx, set, elem.priv);
|
||||
err_parse_data:
|
||||
@ -0,0 +1,65 @@
|
||||
From 24f89c19d096999b6911538f085cd7d45d5086fe Mon Sep 17 00:00:00 2001
|
||||
From: Florian Westphal <fwestpha@redhat.com>
|
||||
Date: Wed, 13 May 2026 17:15:54 +0200
|
||||
Subject: [PATCH] netfilter: nft_set_pipapo: fix stack out-of-bounds read in
|
||||
pipapo_drop()
|
||||
|
||||
JIRA: https://redhat.atlassian.net/browse/RHEL-168848
|
||||
Upstream Status: commit d6d8cd2db236
|
||||
|
||||
commit d6d8cd2db236a9dd13dbc2d05843b3445cc964b5
|
||||
Author: Jenny Guanni Qu <qguanni@gmail.com>
|
||||
Date: Fri Mar 6 19:12:38 2026 +0000
|
||||
|
||||
netfilter: nft_set_pipapo: fix stack out-of-bounds read in pipapo_drop()
|
||||
|
||||
pipapo_drop() passes rulemap[i + 1].n to pipapo_unmap() as the
|
||||
to_offset argument on every iteration, including the last one where
|
||||
i == m->field_count - 1. This reads one element past the end of the
|
||||
stack-allocated rulemap array (declared as rulemap[NFT_PIPAPO_MAX_FIELDS]
|
||||
with NFT_PIPAPO_MAX_FIELDS == 16).
|
||||
|
||||
Although pipapo_unmap() returns early when is_last is true without
|
||||
using the to_offset value, the argument is evaluated at the call site
|
||||
before the function body executes, making this a genuine out-of-bounds
|
||||
stack read confirmed by KASAN:
|
||||
|
||||
BUG: KASAN: stack-out-of-bounds in pipapo_drop+0x50c/0x57c [nf_tables]
|
||||
Read of size 4 at addr ffff8000810e71a4
|
||||
|
||||
This frame has 1 object:
|
||||
[32, 160) 'rulemap'
|
||||
|
||||
The buggy address is at offset 164 -- exactly 4 bytes past the end
|
||||
of the rulemap array.
|
||||
|
||||
Pass 0 instead of rulemap[i + 1].n on the last iteration to avoid
|
||||
the out-of-bounds read.
|
||||
|
||||
Fixes: 3c4287f62044 ("nf_tables: Add set type for arbitrary concatenation of ranges")
|
||||
Signed-off-by: Jenny Guanni Qu <qguanni@gmail.com>
|
||||
Signed-off-by: Florian Westphal <fw@strlen.de>
|
||||
|
||||
Signed-off-by: Florian Westphal <fwestpha@redhat.com>
|
||||
|
||||
diff --git a/net/netfilter/nft_set_pipapo.c b/net/netfilter/nft_set_pipapo.c
|
||||
index 308af52..f2d9442 100644
|
||||
--- a/net/netfilter/nft_set_pipapo.c
|
||||
+++ b/net/netfilter/nft_set_pipapo.c
|
||||
@@ -1626,6 +1626,7 @@ static void pipapo_drop(struct nft_pipapo_match *m,
|
||||
int i;
|
||||
|
||||
nft_pipapo_for_each_field(f, i, m) {
|
||||
+ bool last = i == m->field_count - 1;
|
||||
int g;
|
||||
|
||||
for (g = 0; g < f->groups; g++) {
|
||||
@@ -1645,7 +1646,7 @@ static void pipapo_drop(struct nft_pipapo_match *m,
|
||||
}
|
||||
|
||||
pipapo_unmap(f->mt, f->rules, rulemap[i].to, rulemap[i].n,
|
||||
- rulemap[i + 1].n, i == m->field_count - 1);
|
||||
+ last ? 0 : rulemap[i + 1].n, last);
|
||||
if (pipapo_resize(f, f->rules, f->rules - rulemap[i].n)) {
|
||||
/* We can ignore this, a failure to shrink tables down
|
||||
* doesn't make tables invalid.
|
||||
@ -0,0 +1,52 @@
|
||||
From 6a8f494794e402fec53e71745f5afb1c34c7621a Mon Sep 17 00:00:00 2001
|
||||
From: Florian Westphal <fwestpha@redhat.com>
|
||||
Date: Wed, 13 May 2026 17:15:55 +0200
|
||||
Subject: [PATCH] netfilter: nfnetlink_queue: fix entry leak in bridge verdict
|
||||
error path
|
||||
|
||||
JIRA: https://redhat.atlassian.net/browse/RHEL-168848
|
||||
Upstream Status: commit f1ba83755d81
|
||||
|
||||
commit f1ba83755d81c6fc66ac7acd723d238f974091e9
|
||||
Author: Hyunwoo Kim <imv4bel@gmail.com>
|
||||
Date: Sun Mar 8 02:24:06 2026 +0900
|
||||
|
||||
netfilter: nfnetlink_queue: fix entry leak in bridge verdict error path
|
||||
|
||||
nfqnl_recv_verdict() calls find_dequeue_entry() to remove the queue
|
||||
entry from the queue data structures, taking ownership of the entry.
|
||||
For PF_BRIDGE packets, it then calls nfqa_parse_bridge() to parse VLAN
|
||||
attributes. If nfqa_parse_bridge() returns an error (e.g. NFQA_VLAN
|
||||
present but NFQA_VLAN_TCI missing), the function returns immediately
|
||||
without freeing the dequeued entry or its sk_buff.
|
||||
|
||||
This leaks the nf_queue_entry, its associated sk_buff, and all held
|
||||
references (net_device refcounts, struct net refcount). Repeated
|
||||
triggering exhausts kernel memory.
|
||||
|
||||
Fix this by dropping the entry via nfqnl_reinject() with NF_DROP verdict
|
||||
on the error path, consistent with other error handling in this file.
|
||||
|
||||
Fixes: 8d45ff22f1b4 ("netfilter: bridge: nf queue verdict to use NFQA_VLAN and NFQA_L2HDR")
|
||||
Reviewed-by: David Dull <monderasdor@gmail.com>
|
||||
Signed-off-by: Hyunwoo Kim <imv4bel@gmail.com>
|
||||
Signed-off-by: Florian Westphal <fw@strlen.de>
|
||||
|
||||
Signed-off-by: Florian Westphal <fwestpha@redhat.com>
|
||||
|
||||
diff --git a/net/netfilter/nfnetlink_queue.c b/net/netfilter/nfnetlink_queue.c
|
||||
index d2773ce..0bb1656 100644
|
||||
--- a/net/netfilter/nfnetlink_queue.c
|
||||
+++ b/net/netfilter/nfnetlink_queue.c
|
||||
@@ -1443,8 +1443,10 @@ static int nfqnl_recv_verdict(struct sk_buff *skb, const struct nfnl_info *info,
|
||||
|
||||
if (entry->state.pf == PF_BRIDGE) {
|
||||
err = nfqa_parse_bridge(entry, nfqa);
|
||||
- if (err < 0)
|
||||
+ if (err < 0) {
|
||||
+ nfqnl_reinject(entry, NF_DROP);
|
||||
return err;
|
||||
+ }
|
||||
}
|
||||
|
||||
if (nfqa[NFQA_PAYLOAD]) {
|
||||
@ -0,0 +1,124 @@
|
||||
From 8d7ea526ff2d5ac74a722765b1eb02c37bea4d06 Mon Sep 17 00:00:00 2001
|
||||
From: Florian Westphal <fwestpha@redhat.com>
|
||||
Date: Wed, 13 May 2026 17:15:58 +0200
|
||||
Subject: [PATCH] netfilter: ctnetlink: fix use-after-free in
|
||||
ctnetlink_dump_exp_ct()
|
||||
|
||||
JIRA: https://redhat.atlassian.net/browse/RHEL-168848
|
||||
Upstream Status: commit 5cb81eeda909
|
||||
|
||||
commit 5cb81eeda909dbb2def209dd10636b51549a3f8a
|
||||
Author: Hyunwoo Kim <imv4bel@gmail.com>
|
||||
Date: Sun Mar 8 02:21:37 2026 +0900
|
||||
|
||||
netfilter: ctnetlink: fix use-after-free in ctnetlink_dump_exp_ct()
|
||||
|
||||
ctnetlink_dump_exp_ct() stores a conntrack pointer in cb->data for the
|
||||
netlink dump callback ctnetlink_exp_ct_dump_table(), but drops the
|
||||
conntrack reference immediately after netlink_dump_start(). When the
|
||||
dump spans multiple rounds, the second recvmsg() triggers the dump
|
||||
callback which dereferences the now-freed conntrack via nfct_help(ct),
|
||||
leading to a use-after-free on ct->ext.
|
||||
|
||||
The bug is that the netlink_dump_control has no .start or .done
|
||||
callbacks to manage the conntrack reference across dump rounds. Other
|
||||
dump functions in the same file (e.g. ctnetlink_get_conntrack) properly
|
||||
use .start/.done callbacks for this purpose.
|
||||
|
||||
Fix this by adding .start and .done callbacks that hold and release the
|
||||
conntrack reference for the duration of the dump, and move the
|
||||
nfct_help() call after the cb->args[0] early-return check in the dump
|
||||
callback to avoid dereferencing ct->ext unnecessarily.
|
||||
|
||||
BUG: KASAN: slab-use-after-free in ctnetlink_exp_ct_dump_table+0x4f/0x2e0
|
||||
Read of size 8 at addr ffff88810597ebf0 by task ctnetlink_poc/133
|
||||
|
||||
CPU: 1 UID: 0 PID: 133 Comm: ctnetlink_poc Not tainted 7.0.0-rc2+ #3 PREEMPTLAZY
|
||||
Call Trace:
|
||||
<TASK>
|
||||
ctnetlink_exp_ct_dump_table+0x4f/0x2e0
|
||||
netlink_dump+0x333/0x880
|
||||
netlink_recvmsg+0x3e2/0x4b0
|
||||
? aa_sk_perm+0x184/0x450
|
||||
sock_recvmsg+0xde/0xf0
|
||||
|
||||
Allocated by task 133:
|
||||
kmem_cache_alloc_noprof+0x134/0x440
|
||||
__nf_conntrack_alloc+0xa8/0x2b0
|
||||
ctnetlink_create_conntrack+0xa1/0x900
|
||||
ctnetlink_new_conntrack+0x3cf/0x7d0
|
||||
nfnetlink_rcv_msg+0x48e/0x510
|
||||
netlink_rcv_skb+0xc9/0x1f0
|
||||
nfnetlink_rcv+0xdb/0x220
|
||||
netlink_unicast+0x3ec/0x590
|
||||
netlink_sendmsg+0x397/0x690
|
||||
__sys_sendmsg+0xf4/0x180
|
||||
|
||||
Freed by task 0:
|
||||
slab_free_after_rcu_debug+0xad/0x1e0
|
||||
rcu_core+0x5c3/0x9c0
|
||||
|
||||
Fixes: e844a928431f ("netfilter: ctnetlink: allow to dump expectation per master conntrack")
|
||||
Signed-off-by: Hyunwoo Kim <imv4bel@gmail.com>
|
||||
Signed-off-by: Florian Westphal <fw@strlen.de>
|
||||
|
||||
Signed-off-by: Florian Westphal <fwestpha@redhat.com>
|
||||
|
||||
diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c
|
||||
index 633c57f..399227e 100644
|
||||
--- a/net/netfilter/nf_conntrack_netlink.c
|
||||
+++ b/net/netfilter/nf_conntrack_netlink.c
|
||||
@@ -3231,7 +3231,7 @@ ctnetlink_exp_ct_dump_table(struct sk_buff *skb, struct netlink_callback *cb)
|
||||
{
|
||||
struct nfgenmsg *nfmsg = nlmsg_data(cb->nlh);
|
||||
struct nf_conn *ct = cb->data;
|
||||
- struct nf_conn_help *help = nfct_help(ct);
|
||||
+ struct nf_conn_help *help;
|
||||
u_int8_t l3proto = nfmsg->nfgen_family;
|
||||
unsigned long last_id = cb->args[1];
|
||||
struct nf_conntrack_expect *exp;
|
||||
@@ -3239,6 +3239,10 @@ ctnetlink_exp_ct_dump_table(struct sk_buff *skb, struct netlink_callback *cb)
|
||||
if (cb->args[0])
|
||||
return 0;
|
||||
|
||||
+ help = nfct_help(ct);
|
||||
+ if (!help)
|
||||
+ return 0;
|
||||
+
|
||||
rcu_read_lock();
|
||||
|
||||
restart:
|
||||
@@ -3268,6 +3272,24 @@ ctnetlink_exp_ct_dump_table(struct sk_buff *skb, struct netlink_callback *cb)
|
||||
return skb->len;
|
||||
}
|
||||
|
||||
+static int ctnetlink_dump_exp_ct_start(struct netlink_callback *cb)
|
||||
+{
|
||||
+ struct nf_conn *ct = cb->data;
|
||||
+
|
||||
+ if (!refcount_inc_not_zero(&ct->ct_general.use))
|
||||
+ return -ENOENT;
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int ctnetlink_dump_exp_ct_done(struct netlink_callback *cb)
|
||||
+{
|
||||
+ struct nf_conn *ct = cb->data;
|
||||
+
|
||||
+ if (ct)
|
||||
+ nf_ct_put(ct);
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
static int ctnetlink_dump_exp_ct(struct net *net, struct sock *ctnl,
|
||||
struct sk_buff *skb,
|
||||
const struct nlmsghdr *nlh,
|
||||
@@ -3283,6 +3305,8 @@ static int ctnetlink_dump_exp_ct(struct net *net, struct sock *ctnl,
|
||||
struct nf_conntrack_zone zone;
|
||||
struct netlink_dump_control c = {
|
||||
.dump = ctnetlink_exp_ct_dump_table,
|
||||
+ .start = ctnetlink_dump_exp_ct_start,
|
||||
+ .done = ctnetlink_dump_exp_ct_done,
|
||||
};
|
||||
|
||||
err = ctnetlink_parse_tuple(cda, &tuple, CTA_EXPECT_MASTER,
|
||||
@ -0,0 +1,63 @@
|
||||
From 3579bfdd048b5391909e01eae2e7bfb5d742b97b Mon Sep 17 00:00:00 2001
|
||||
From: Florian Westphal <fwestpha@redhat.com>
|
||||
Date: Wed, 13 May 2026 17:15:58 +0200
|
||||
Subject: [PATCH] netfilter: conntrack: add missing netlink policy validations
|
||||
|
||||
JIRA: https://redhat.atlassian.net/browse/RHEL-168848
|
||||
Upstream Status: commit f900e1d77ee0
|
||||
|
||||
commit f900e1d77ee0ef87bfb5ab3fe60f0b3d8ad5ba05
|
||||
Author: Florian Westphal <fw@strlen.de>
|
||||
Date: Tue Mar 10 00:28:29 2026 +0100
|
||||
|
||||
netfilter: conntrack: add missing netlink policy validations
|
||||
|
||||
Hyunwoo Kim reports out-of-bounds access in sctp and ctnetlink.
|
||||
|
||||
These attributes are used by the kernel without any validation.
|
||||
Extend the netlink policies accordingly.
|
||||
|
||||
Quoting the reporter:
|
||||
nlattr_to_sctp() assigns the user-supplied CTA_PROTOINFO_SCTP_STATE
|
||||
value directly to ct->proto.sctp.state without checking that it is
|
||||
within the valid range. [..]
|
||||
|
||||
and: ... with exp->dir = 100, the access at
|
||||
ct->master->tuplehash[100] reads 5600 bytes past the start of a
|
||||
320-byte nf_conn object, causing a slab-out-of-bounds read confirmed by
|
||||
UBSAN.
|
||||
|
||||
Fixes: 076a0ca02644 ("netfilter: ctnetlink: add NAT support for expectations")
|
||||
Fixes: a258860e01b8 ("netfilter: ctnetlink: add full support for SCTP to ctnetlink")
|
||||
Reported-by: Hyunwoo Kim <imv4bel@gmail.com>
|
||||
Signed-off-by: Florian Westphal <fw@strlen.de>
|
||||
|
||||
Signed-off-by: Florian Westphal <fwestpha@redhat.com>
|
||||
|
||||
diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c
|
||||
index 399227e..527bf07 100644
|
||||
--- a/net/netfilter/nf_conntrack_netlink.c
|
||||
+++ b/net/netfilter/nf_conntrack_netlink.c
|
||||
@@ -3518,7 +3518,7 @@ ctnetlink_change_expect(struct nf_conntrack_expect *x,
|
||||
|
||||
#if IS_ENABLED(CONFIG_NF_NAT)
|
||||
static const struct nla_policy exp_nat_nla_policy[CTA_EXPECT_NAT_MAX+1] = {
|
||||
- [CTA_EXPECT_NAT_DIR] = { .type = NLA_U32 },
|
||||
+ [CTA_EXPECT_NAT_DIR] = NLA_POLICY_MAX(NLA_BE32, IP_CT_DIR_REPLY),
|
||||
[CTA_EXPECT_NAT_TUPLE] = { .type = NLA_NESTED },
|
||||
};
|
||||
#endif
|
||||
diff --git a/net/netfilter/nf_conntrack_proto_sctp.c b/net/netfilter/nf_conntrack_proto_sctp.c
|
||||
index 4cc97f9..fabb2c1 100644
|
||||
--- a/net/netfilter/nf_conntrack_proto_sctp.c
|
||||
+++ b/net/netfilter/nf_conntrack_proto_sctp.c
|
||||
@@ -587,7 +587,8 @@ static int sctp_to_nlattr(struct sk_buff *skb, struct nlattr *nla,
|
||||
}
|
||||
|
||||
static const struct nla_policy sctp_nla_policy[CTA_PROTOINFO_SCTP_MAX+1] = {
|
||||
- [CTA_PROTOINFO_SCTP_STATE] = { .type = NLA_U8 },
|
||||
+ [CTA_PROTOINFO_SCTP_STATE] = NLA_POLICY_MAX(NLA_U8,
|
||||
+ SCTP_CONNTRACK_HEARTBEAT_SENT),
|
||||
[CTA_PROTOINFO_SCTP_VTAG_ORIGINAL] = { .type = NLA_U32 },
|
||||
[CTA_PROTOINFO_SCTP_VTAG_REPLY] = { .type = NLA_U32 },
|
||||
};
|
||||
@ -0,0 +1,66 @@
|
||||
From 2f1682c1275edb5ac6139158d930d43be0717378 Mon Sep 17 00:00:00 2001
|
||||
From: Florian Westphal <fwestpha@redhat.com>
|
||||
Date: Wed, 13 May 2026 17:15:59 +0200
|
||||
Subject: [PATCH] netfilter: nf_conntrack_sip: fix Content-Length u32
|
||||
truncation in sip_help_tcp()
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
JIRA: https://redhat.atlassian.net/browse/RHEL-168848
|
||||
Upstream Status: commit fbce58e719a1
|
||||
|
||||
commit fbce58e719a17aa215c724473fd5baaa4a8dc57c
|
||||
Author: Lukas Johannes Möller <research@johannes-moeller.dev>
|
||||
Date: Tue Mar 10 21:49:01 2026 +0000
|
||||
|
||||
netfilter: nf_conntrack_sip: fix Content-Length u32 truncation in sip_help_tcp()
|
||||
|
||||
sip_help_tcp() parses the SIP Content-Length header with
|
||||
simple_strtoul(), which returns unsigned long, but stores the result in
|
||||
unsigned int clen. On 64-bit systems, values exceeding UINT_MAX are
|
||||
silently truncated before computing the SIP message boundary.
|
||||
|
||||
For example, Content-Length 4294967328 (2^32 + 32) is truncated to 32,
|
||||
causing the parser to miscalculate where the current message ends. The
|
||||
loop then treats trailing data in the TCP segment as a second SIP
|
||||
message and processes it through the SDP parser.
|
||||
|
||||
Fix this by changing clen to unsigned long to match the return type of
|
||||
simple_strtoul(), and reject Content-Length values that exceed the
|
||||
remaining TCP payload length.
|
||||
|
||||
Fixes: f5b321bd37fb ("netfilter: nf_conntrack_sip: add TCP support")
|
||||
Signed-off-by: Lukas Johannes Möller <research@johannes-moeller.dev>
|
||||
Signed-off-by: Florian Westphal <fw@strlen.de>
|
||||
|
||||
Signed-off-by: Florian Westphal <fwestpha@redhat.com>
|
||||
|
||||
diff --git a/net/netfilter/nf_conntrack_sip.c b/net/netfilter/nf_conntrack_sip.c
|
||||
index ca748f8..4ab5ef7 100644
|
||||
--- a/net/netfilter/nf_conntrack_sip.c
|
||||
+++ b/net/netfilter/nf_conntrack_sip.c
|
||||
@@ -1534,11 +1534,12 @@ static int sip_help_tcp(struct sk_buff *skb, unsigned int protoff,
|
||||
{
|
||||
struct tcphdr *th, _tcph;
|
||||
unsigned int dataoff, datalen;
|
||||
- unsigned int matchoff, matchlen, clen;
|
||||
+ unsigned int matchoff, matchlen;
|
||||
unsigned int msglen, origlen;
|
||||
const char *dptr, *end;
|
||||
s16 diff, tdiff = 0;
|
||||
int ret = NF_ACCEPT;
|
||||
+ unsigned long clen;
|
||||
bool term;
|
||||
|
||||
if (ctinfo != IP_CT_ESTABLISHED &&
|
||||
@@ -1573,6 +1574,9 @@ static int sip_help_tcp(struct sk_buff *skb, unsigned int protoff,
|
||||
if (dptr + matchoff == end)
|
||||
break;
|
||||
|
||||
+ if (clen > datalen)
|
||||
+ break;
|
||||
+
|
||||
term = false;
|
||||
for (; end + strlen("\r\n\r\n") <= dptr + datalen; end++) {
|
||||
if (end[0] == '\r' && end[1] == '\n' &&
|
||||
@ -0,0 +1,49 @@
|
||||
From 6ad52cd472dace258bf7e2970ece287cfa2668b1 Mon Sep 17 00:00:00 2001
|
||||
From: Florian Westphal <fwestpha@redhat.com>
|
||||
Date: Wed, 13 May 2026 17:16:02 +0200
|
||||
Subject: [PATCH] netfilter: nf_conntrack_h323: fix OOB read in decode_int()
|
||||
CONS case
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
JIRA: https://redhat.atlassian.net/browse/RHEL-168848
|
||||
Upstream Status: commit 1e3a3593162c
|
||||
|
||||
commit 1e3a3593162c96e8a8de48b1e14f60c3b57fca8a
|
||||
Author: Jenny Guanni Qu <qguanni@gmail.com>
|
||||
Date: Thu Mar 12 02:29:32 2026 +0000
|
||||
|
||||
netfilter: nf_conntrack_h323: fix OOB read in decode_int() CONS case
|
||||
|
||||
In decode_int(), the CONS case calls get_bits(bs, 2) to read a length
|
||||
value, then calls get_uint(bs, len) without checking that len bytes
|
||||
remain in the buffer. The existing boundary check only validates the
|
||||
2 bits for get_bits(), not the subsequent 1-4 bytes that get_uint()
|
||||
reads. This allows a malformed H.323/RAS packet to cause a 1-4 byte
|
||||
slab-out-of-bounds read.
|
||||
|
||||
Add a boundary check for len bytes after get_bits() and before
|
||||
get_uint().
|
||||
|
||||
Fixes: 5e35941d9901 ("[NETFILTER]: Add H.323 conntrack/NAT helper")
|
||||
Reported-by: Klaudia Kloc <klaudia@vidocsecurity.com>
|
||||
Reported-by: Dawid Moczadło <dawid@vidocsecurity.com>
|
||||
Signed-off-by: Jenny Guanni Qu <qguanni@gmail.com>
|
||||
Signed-off-by: Florian Westphal <fw@strlen.de>
|
||||
|
||||
Signed-off-by: Florian Westphal <fwestpha@redhat.com>
|
||||
|
||||
diff --git a/net/netfilter/nf_conntrack_h323_asn1.c b/net/netfilter/nf_conntrack_h323_asn1.c
|
||||
index 456bf3a..7b1497e 100644
|
||||
--- a/net/netfilter/nf_conntrack_h323_asn1.c
|
||||
+++ b/net/netfilter/nf_conntrack_h323_asn1.c
|
||||
@@ -331,6 +331,8 @@ static int decode_int(struct bitstr *bs, const struct field_t *f,
|
||||
if (nf_h323_error_boundary(bs, 0, 2))
|
||||
return H323_ERROR_BOUND;
|
||||
len = get_bits(bs, 2) + 1;
|
||||
+ if (nf_h323_error_boundary(bs, len, 0))
|
||||
+ return H323_ERROR_BOUND;
|
||||
BYTE_ALIGN(bs);
|
||||
if (base && (f->attr & DECODE)) { /* timeToLive */
|
||||
unsigned int v = get_uint(bs, len) + f->lb;
|
||||
@ -0,0 +1,105 @@
|
||||
From d5cb2ee377e40a8ab981e46c0f83ec60239785ba Mon Sep 17 00:00:00 2001
|
||||
From: Florian Westphal <fwestpha@redhat.com>
|
||||
Date: Wed, 13 May 2026 17:16:03 +0200
|
||||
Subject: [PATCH] nf_tables: nft_dynset: fix possible stateful expression
|
||||
memleak in error path
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
JIRA: https://redhat.atlassian.net/browse/RHEL-168848
|
||||
Upstream Status: commit 0548a13b5a14
|
||||
|
||||
commit 0548a13b5a145b16e4da0628b5936baf35f51b43
|
||||
Author: Pablo Neira Ayuso <pablo@netfilter.org>
|
||||
Date: Thu Mar 12 12:38:59 2026 +0100
|
||||
|
||||
nf_tables: nft_dynset: fix possible stateful expression memleak in error path
|
||||
|
||||
If cloning the second stateful expression in the element via GFP_ATOMIC
|
||||
fails, then the first stateful expression remains in place without being
|
||||
released.
|
||||
|
||||
unreferenced object (percpu) 0x607b97e9cab8 (size 16):
|
||||
comm "softirq", pid 0, jiffies 4294931867
|
||||
hex dump (first 16 bytes on cpu 3):
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
backtrace (crc 0):
|
||||
pcpu_alloc_noprof+0x453/0xd80
|
||||
nft_counter_clone+0x9c/0x190 [nf_tables]
|
||||
nft_expr_clone+0x8f/0x1b0 [nf_tables]
|
||||
nft_dynset_new+0x2cb/0x5f0 [nf_tables]
|
||||
nft_rhash_update+0x236/0x11c0 [nf_tables]
|
||||
nft_dynset_eval+0x11f/0x670 [nf_tables]
|
||||
nft_do_chain+0x253/0x1700 [nf_tables]
|
||||
nft_do_chain_ipv4+0x18d/0x270 [nf_tables]
|
||||
nf_hook_slow+0xaa/0x1e0
|
||||
ip_local_deliver+0x209/0x330
|
||||
|
||||
Fixes: 563125a73ac3 ("netfilter: nftables: generalize set extension to support for several expressions")
|
||||
Reported-by: Gurpreet Shergill <giki.shergill@proton.me>
|
||||
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
|
||||
Signed-off-by: Florian Westphal <fw@strlen.de>
|
||||
|
||||
Signed-off-by: Florian Westphal <fwestpha@redhat.com>
|
||||
|
||||
diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h
|
||||
index 81b52cd..5fe20cc 100644
|
||||
--- a/include/net/netfilter/nf_tables.h
|
||||
+++ b/include/net/netfilter/nf_tables.h
|
||||
@@ -871,6 +871,8 @@ struct nft_elem_priv *nft_set_elem_init(const struct nft_set *set,
|
||||
u64 timeout, u64 expiration, gfp_t gfp);
|
||||
int nft_set_elem_expr_clone(const struct nft_ctx *ctx, struct nft_set *set,
|
||||
struct nft_expr *expr_array[]);
|
||||
+void nft_set_elem_expr_destroy(const struct nft_ctx *ctx,
|
||||
+ struct nft_set_elem_expr *elem_expr);
|
||||
void nft_set_elem_destroy(const struct nft_set *set,
|
||||
const struct nft_elem_priv *elem_priv,
|
||||
bool destroy_expr);
|
||||
diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c
|
||||
index f2d6ac3..81164ad 100644
|
||||
--- a/net/netfilter/nf_tables_api.c
|
||||
+++ b/net/netfilter/nf_tables_api.c
|
||||
@@ -6853,8 +6853,8 @@ static void __nft_set_elem_expr_destroy(const struct nft_ctx *ctx,
|
||||
}
|
||||
}
|
||||
|
||||
-static void nft_set_elem_expr_destroy(const struct nft_ctx *ctx,
|
||||
- struct nft_set_elem_expr *elem_expr)
|
||||
+void nft_set_elem_expr_destroy(const struct nft_ctx *ctx,
|
||||
+ struct nft_set_elem_expr *elem_expr)
|
||||
{
|
||||
struct nft_expr *expr;
|
||||
u32 size;
|
||||
diff --git a/net/netfilter/nft_dynset.c b/net/netfilter/nft_dynset.c
|
||||
index 7807d81..9123277 100644
|
||||
--- a/net/netfilter/nft_dynset.c
|
||||
+++ b/net/netfilter/nft_dynset.c
|
||||
@@ -30,18 +30,26 @@ static int nft_dynset_expr_setup(const struct nft_dynset *priv,
|
||||
const struct nft_set_ext *ext)
|
||||
{
|
||||
struct nft_set_elem_expr *elem_expr = nft_set_ext_expr(ext);
|
||||
+ struct nft_ctx ctx = {
|
||||
+ .net = read_pnet(&priv->set->net),
|
||||
+ .family = priv->set->table->family,
|
||||
+ };
|
||||
struct nft_expr *expr;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < priv->num_exprs; i++) {
|
||||
expr = nft_setelem_expr_at(elem_expr, elem_expr->size);
|
||||
if (nft_expr_clone(expr, priv->expr_array[i], GFP_ATOMIC) < 0)
|
||||
- return -1;
|
||||
+ goto err_out;
|
||||
|
||||
elem_expr->size += priv->expr_array[i]->ops->size;
|
||||
}
|
||||
|
||||
return 0;
|
||||
+err_out:
|
||||
+ nft_set_elem_expr_destroy(&ctx, elem_expr);
|
||||
+
|
||||
+ return -1;
|
||||
}
|
||||
|
||||
struct nft_elem_priv *nft_dynset_new(struct nft_set *set,
|
||||
@ -0,0 +1,161 @@
|
||||
From 84e748a8ba9c8453579b6335a098f4efdcd8fd6d Mon Sep 17 00:00:00 2001
|
||||
From: Florian Westphal <fwestpha@redhat.com>
|
||||
Date: Wed, 13 May 2026 17:16:13 +0200
|
||||
Subject: [PATCH] netfilter: nft_set_pipapo_avx2: don't return non-matching
|
||||
entry on expiry
|
||||
|
||||
JIRA: https://redhat.atlassian.net/browse/RHEL-168848
|
||||
Upstream Status: commit d3c0037ffe12
|
||||
|
||||
commit d3c0037ffe1273fa1961e779ff6906234d6cf53c
|
||||
Author: Florian Westphal <fw@strlen.de>
|
||||
Date: Wed Mar 25 14:10:55 2026 +0100
|
||||
|
||||
netfilter: nft_set_pipapo_avx2: don't return non-matching entry on expiry
|
||||
|
||||
New test case fails unexpectedly when avx2 matching functions are used.
|
||||
|
||||
The test first loads a ranomly generated pipapo set
|
||||
with 'ipv4 . port' key, i.e. nft -f foo.
|
||||
|
||||
This works. Then, it reloads the set after a flush:
|
||||
(echo flush set t s; cat foo) | nft -f -
|
||||
|
||||
This is expected to work, because its the same set after all and it was
|
||||
already loaded once.
|
||||
|
||||
But with avx2, this fails: nft reports a clashing element.
|
||||
|
||||
The reported clash is of following form:
|
||||
|
||||
We successfully re-inserted
|
||||
a . b
|
||||
c . d
|
||||
|
||||
Then we try to insert a . d
|
||||
|
||||
avx2 finds the already existing a . d, which (due to 'flush set') is marked
|
||||
as invalid in the new generation. It skips the element and moves to next.
|
||||
|
||||
Due to incorrect masking, the skip-step finds the next matching
|
||||
element *only considering the first field*,
|
||||
|
||||
i.e. we return the already reinserted "a . b", even though the
|
||||
last field is different and the entry should not have been matched.
|
||||
|
||||
No such error is reported for the generic c implementation (no avx2) or when
|
||||
the last field has to use the 'nft_pipapo_avx2_lookup_slow' fallback.
|
||||
|
||||
Bisection points to
|
||||
7711f4bb4b36 ("netfilter: nft_set_pipapo: fix range overlap detection")
|
||||
but that fix merely uncovers this bug.
|
||||
|
||||
Before this commit, the wrong element is returned, but erronously
|
||||
reported as a full, identical duplicate.
|
||||
|
||||
The root-cause is too early return in the avx2 match functions.
|
||||
When we process the last field, we should continue to process data
|
||||
until the entire input size has been consumed to make sure no stale
|
||||
bits remain in the map.
|
||||
|
||||
Link: https://lore.kernel.org/netfilter-devel/20260321152506.037f68c0@elisabeth/
|
||||
Signed-off-by: Florian Westphal <fw@strlen.de>
|
||||
Reviewed-by: Stefano Brivio <sbrivio@redhat.com>
|
||||
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
|
||||
|
||||
Signed-off-by: Florian Westphal <fwestpha@redhat.com>
|
||||
|
||||
diff --git a/net/netfilter/nft_set_pipapo_avx2.c b/net/netfilter/nft_set_pipapo_avx2.c
|
||||
index bf7a0f6..bb0917e 100644
|
||||
--- a/net/netfilter/nft_set_pipapo_avx2.c
|
||||
+++ b/net/netfilter/nft_set_pipapo_avx2.c
|
||||
@@ -242,7 +242,7 @@ static int nft_pipapo_avx2_lookup_4b_2(unsigned long *map, unsigned long *fill,
|
||||
|
||||
b = nft_pipapo_avx2_refill(i_ul, &map[i_ul], fill, f->mt, last);
|
||||
if (last)
|
||||
- return b;
|
||||
+ ret = b;
|
||||
|
||||
if (unlikely(ret == -1))
|
||||
ret = b / XSAVE_YMM_SIZE;
|
||||
@@ -319,7 +319,7 @@ static int nft_pipapo_avx2_lookup_4b_4(unsigned long *map, unsigned long *fill,
|
||||
|
||||
b = nft_pipapo_avx2_refill(i_ul, &map[i_ul], fill, f->mt, last);
|
||||
if (last)
|
||||
- return b;
|
||||
+ ret = b;
|
||||
|
||||
if (unlikely(ret == -1))
|
||||
ret = b / XSAVE_YMM_SIZE;
|
||||
@@ -414,7 +414,7 @@ static int nft_pipapo_avx2_lookup_4b_8(unsigned long *map, unsigned long *fill,
|
||||
|
||||
b = nft_pipapo_avx2_refill(i_ul, &map[i_ul], fill, f->mt, last);
|
||||
if (last)
|
||||
- return b;
|
||||
+ ret = b;
|
||||
|
||||
if (unlikely(ret == -1))
|
||||
ret = b / XSAVE_YMM_SIZE;
|
||||
@@ -505,7 +505,7 @@ static int nft_pipapo_avx2_lookup_4b_12(unsigned long *map, unsigned long *fill,
|
||||
|
||||
b = nft_pipapo_avx2_refill(i_ul, &map[i_ul], fill, f->mt, last);
|
||||
if (last)
|
||||
- return b;
|
||||
+ ret = b;
|
||||
|
||||
if (unlikely(ret == -1))
|
||||
ret = b / XSAVE_YMM_SIZE;
|
||||
@@ -641,7 +641,7 @@ static int nft_pipapo_avx2_lookup_4b_32(unsigned long *map, unsigned long *fill,
|
||||
|
||||
b = nft_pipapo_avx2_refill(i_ul, &map[i_ul], fill, f->mt, last);
|
||||
if (last)
|
||||
- return b;
|
||||
+ ret = b;
|
||||
|
||||
if (unlikely(ret == -1))
|
||||
ret = b / XSAVE_YMM_SIZE;
|
||||
@@ -699,7 +699,7 @@ static int nft_pipapo_avx2_lookup_8b_1(unsigned long *map, unsigned long *fill,
|
||||
|
||||
b = nft_pipapo_avx2_refill(i_ul, &map[i_ul], fill, f->mt, last);
|
||||
if (last)
|
||||
- return b;
|
||||
+ ret = b;
|
||||
|
||||
if (unlikely(ret == -1))
|
||||
ret = b / XSAVE_YMM_SIZE;
|
||||
@@ -764,7 +764,7 @@ static int nft_pipapo_avx2_lookup_8b_2(unsigned long *map, unsigned long *fill,
|
||||
|
||||
b = nft_pipapo_avx2_refill(i_ul, &map[i_ul], fill, f->mt, last);
|
||||
if (last)
|
||||
- return b;
|
||||
+ ret = b;
|
||||
|
||||
if (unlikely(ret == -1))
|
||||
ret = b / XSAVE_YMM_SIZE;
|
||||
@@ -839,7 +839,7 @@ static int nft_pipapo_avx2_lookup_8b_4(unsigned long *map, unsigned long *fill,
|
||||
|
||||
b = nft_pipapo_avx2_refill(i_ul, &map[i_ul], fill, f->mt, last);
|
||||
if (last)
|
||||
- return b;
|
||||
+ ret = b;
|
||||
|
||||
if (unlikely(ret == -1))
|
||||
ret = b / XSAVE_YMM_SIZE;
|
||||
@@ -925,7 +925,7 @@ static int nft_pipapo_avx2_lookup_8b_6(unsigned long *map, unsigned long *fill,
|
||||
|
||||
b = nft_pipapo_avx2_refill(i_ul, &map[i_ul], fill, f->mt, last);
|
||||
if (last)
|
||||
- return b;
|
||||
+ ret = b;
|
||||
|
||||
if (unlikely(ret == -1))
|
||||
ret = b / XSAVE_YMM_SIZE;
|
||||
@@ -1019,7 +1019,7 @@ static int nft_pipapo_avx2_lookup_8b_16(unsigned long *map, unsigned long *fill,
|
||||
|
||||
b = nft_pipapo_avx2_refill(i_ul, &map[i_ul], fill, f->mt, last);
|
||||
if (last)
|
||||
- return b;
|
||||
+ ret = b;
|
||||
|
||||
if (unlikely(ret == -1))
|
||||
ret = b / XSAVE_YMM_SIZE;
|
||||
@ -0,0 +1,58 @@
|
||||
From 95bac469e4bcdb05093eec68ee002e8d5fd79bc2 Mon Sep 17 00:00:00 2001
|
||||
From: Florian Westphal <fwestpha@redhat.com>
|
||||
Date: Wed, 13 May 2026 17:16:15 +0200
|
||||
Subject: [PATCH] netfilter: nfnetlink_log: fix uninitialized padding leak in
|
||||
NFULA_PAYLOAD
|
||||
|
||||
JIRA: https://redhat.atlassian.net/browse/RHEL-168848
|
||||
Upstream Status: commit 52025ebaa29f
|
||||
|
||||
commit 52025ebaa29f4eb4ed8bf92ce83a68f24ab7fdf7
|
||||
Author: Weiming Shi <bestswngs@gmail.com>
|
||||
Date: Wed Mar 25 14:10:58 2026 +0100
|
||||
|
||||
netfilter: nfnetlink_log: fix uninitialized padding leak in NFULA_PAYLOAD
|
||||
|
||||
__build_packet_message() manually constructs the NFULA_PAYLOAD netlink
|
||||
attribute using skb_put() and skb_copy_bits(), bypassing the standard
|
||||
nla_reserve()/nla_put() helpers. While nla_total_size(data_len) bytes
|
||||
are allocated (including NLA alignment padding), only data_len bytes
|
||||
of actual packet data are copied. The trailing nla_padlen(data_len)
|
||||
bytes (1-3 when data_len is not 4-byte aligned) are never initialized,
|
||||
leaking stale heap contents to userspace via the NFLOG netlink socket.
|
||||
|
||||
Replace the manual attribute construction with nla_reserve(), which
|
||||
handles the tailroom check, header setup, and padding zeroing via
|
||||
__nla_reserve(). The subsequent skb_copy_bits() fills in the payload
|
||||
data on top of the properly initialized attribute.
|
||||
|
||||
Fixes: df6fb868d611 ("[NETFILTER]: nfnetlink: convert to generic netlink attribute functions")
|
||||
Reported-by: Xiang Mei <xmei5@asu.edu>
|
||||
Signed-off-by: Weiming Shi <bestswngs@gmail.com>
|
||||
Signed-off-by: Florian Westphal <fw@strlen.de>
|
||||
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
|
||||
|
||||
Signed-off-by: Florian Westphal <fwestpha@redhat.com>
|
||||
|
||||
diff --git a/net/netfilter/nfnetlink_log.c b/net/netfilter/nfnetlink_log.c
|
||||
index bfcb9cd..27dd352 100644
|
||||
--- a/net/netfilter/nfnetlink_log.c
|
||||
+++ b/net/netfilter/nfnetlink_log.c
|
||||
@@ -647,15 +647,11 @@ __build_packet_message(struct nfnl_log_net *log,
|
||||
|
||||
if (data_len) {
|
||||
struct nlattr *nla;
|
||||
- int size = nla_attr_size(data_len);
|
||||
|
||||
- if (skb_tailroom(inst->skb) < nla_total_size(data_len))
|
||||
+ nla = nla_reserve(inst->skb, NFULA_PAYLOAD, data_len);
|
||||
+ if (!nla)
|
||||
goto nla_put_failure;
|
||||
|
||||
- nla = skb_put(inst->skb, nla_total_size(data_len));
|
||||
- nla->nla_type = NFULA_PAYLOAD;
|
||||
- nla->nla_len = size;
|
||||
-
|
||||
if (skb_copy_bits(skb, 0, nla_data(nla), data_len))
|
||||
BUG();
|
||||
}
|
||||
@ -0,0 +1,206 @@
|
||||
From 258cc75af0f0db2881d8c1940733dfd2d57dc95e Mon Sep 17 00:00:00 2001
|
||||
From: Florian Westphal <fwestpha@redhat.com>
|
||||
Date: Wed, 13 May 2026 17:16:19 +0200
|
||||
Subject: [PATCH] netfilter: nf_conntrack_expect: honor expectation helper
|
||||
field
|
||||
|
||||
JIRA: https://redhat.atlassian.net/browse/RHEL-168848
|
||||
Upstream Status: commit 9c42bc9db90a
|
||||
|
||||
commit 9c42bc9db90a154bc61ae337a070465f3393485a
|
||||
Author: Pablo Neira Ayuso <pablo@netfilter.org>
|
||||
Date: Wed Mar 25 14:11:02 2026 +0100
|
||||
|
||||
netfilter: nf_conntrack_expect: honor expectation helper field
|
||||
|
||||
The expectation helper field is mostly unused. As a result, the
|
||||
netfilter codebase relies on accessing the helper through exp->master.
|
||||
|
||||
Always set on the expectation helper field so it can be used to reach
|
||||
the helper.
|
||||
|
||||
nf_ct_expect_init() is called from packet path where the skb owns
|
||||
the ct object, therefore accessing exp->master for the newly created
|
||||
expectation is safe. This saves a lot of updates in all callsites
|
||||
to pass the ct object as parameter to nf_ct_expect_init().
|
||||
|
||||
This is a preparation patches for follow up fixes.
|
||||
|
||||
Signed-off-by: Florian Westphal <fw@strlen.de>
|
||||
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
|
||||
|
||||
Signed-off-by: Florian Westphal <fwestpha@redhat.com>
|
||||
|
||||
diff --git a/include/net/netfilter/nf_conntrack_expect.h b/include/net/netfilter/nf_conntrack_expect.h
|
||||
index 165e7a0..1b01400 100644
|
||||
--- a/include/net/netfilter/nf_conntrack_expect.h
|
||||
+++ b/include/net/netfilter/nf_conntrack_expect.h
|
||||
@@ -40,7 +40,7 @@ struct nf_conntrack_expect {
|
||||
struct nf_conntrack_expect *this);
|
||||
|
||||
/* Helper to assign to new connection */
|
||||
- struct nf_conntrack_helper *helper;
|
||||
+ struct nf_conntrack_helper __rcu *helper;
|
||||
|
||||
/* The conntrack of the master connection */
|
||||
struct nf_conn *master;
|
||||
diff --git a/net/netfilter/nf_conntrack_broadcast.c b/net/netfilter/nf_conntrack_broadcast.c
|
||||
index a7552a4..1964c59 100644
|
||||
--- a/net/netfilter/nf_conntrack_broadcast.c
|
||||
+++ b/net/netfilter/nf_conntrack_broadcast.c
|
||||
@@ -70,7 +70,7 @@ int nf_conntrack_broadcast_help(struct sk_buff *skb,
|
||||
exp->expectfn = NULL;
|
||||
exp->flags = NF_CT_EXPECT_PERMANENT;
|
||||
exp->class = NF_CT_EXPECT_CLASS_DEFAULT;
|
||||
- exp->helper = NULL;
|
||||
+ rcu_assign_pointer(exp->helper, helper);
|
||||
|
||||
nf_ct_expect_related(exp, 0);
|
||||
nf_ct_expect_put(exp);
|
||||
diff --git a/net/netfilter/nf_conntrack_expect.c b/net/netfilter/nf_conntrack_expect.c
|
||||
index f9e65f0..de5e2ae 100644
|
||||
--- a/net/netfilter/nf_conntrack_expect.c
|
||||
+++ b/net/netfilter/nf_conntrack_expect.c
|
||||
@@ -314,12 +314,19 @@ struct nf_conntrack_expect *nf_ct_expect_alloc(struct nf_conn *me)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nf_ct_expect_alloc);
|
||||
|
||||
+/* This function can only be used from packet path, where accessing
|
||||
+ * master's helper is safe, because the packet holds a reference on
|
||||
+ * the conntrack object. Never use it from control plane.
|
||||
+ */
|
||||
void nf_ct_expect_init(struct nf_conntrack_expect *exp, unsigned int class,
|
||||
u_int8_t family,
|
||||
const union nf_inet_addr *saddr,
|
||||
const union nf_inet_addr *daddr,
|
||||
u_int8_t proto, const __be16 *src, const __be16 *dst)
|
||||
{
|
||||
+ struct nf_conntrack_helper *helper = NULL;
|
||||
+ struct nf_conn *ct = exp->master;
|
||||
+ struct nf_conn_help *help;
|
||||
int len;
|
||||
|
||||
if (family == AF_INET)
|
||||
@@ -330,7 +337,12 @@ void nf_ct_expect_init(struct nf_conntrack_expect *exp, unsigned int class,
|
||||
exp->flags = 0;
|
||||
exp->class = class;
|
||||
exp->expectfn = NULL;
|
||||
- exp->helper = NULL;
|
||||
+
|
||||
+ help = nfct_help(ct);
|
||||
+ if (help)
|
||||
+ helper = rcu_dereference(help->helper);
|
||||
+
|
||||
+ rcu_assign_pointer(exp->helper, helper);
|
||||
exp->tuple.src.l3num = family;
|
||||
exp->tuple.dst.protonum = proto;
|
||||
|
||||
diff --git a/net/netfilter/nf_conntrack_h323_main.c b/net/netfilter/nf_conntrack_h323_main.c
|
||||
index 14f7387..fbf69d4 100644
|
||||
--- a/net/netfilter/nf_conntrack_h323_main.c
|
||||
+++ b/net/netfilter/nf_conntrack_h323_main.c
|
||||
@@ -642,7 +642,7 @@ static int expect_h245(struct sk_buff *skb, struct nf_conn *ct,
|
||||
&ct->tuplehash[!dir].tuple.src.u3,
|
||||
&ct->tuplehash[!dir].tuple.dst.u3,
|
||||
IPPROTO_TCP, NULL, &port);
|
||||
- exp->helper = &nf_conntrack_helper_h245;
|
||||
+ rcu_assign_pointer(exp->helper, &nf_conntrack_helper_h245);
|
||||
|
||||
nathook = rcu_dereference(nfct_h323_nat_hook);
|
||||
if (memcmp(&ct->tuplehash[dir].tuple.src.u3,
|
||||
@@ -766,7 +766,7 @@ static int expect_callforwarding(struct sk_buff *skb,
|
||||
nf_ct_expect_init(exp, NF_CT_EXPECT_CLASS_DEFAULT, nf_ct_l3num(ct),
|
||||
&ct->tuplehash[!dir].tuple.src.u3, &addr,
|
||||
IPPROTO_TCP, NULL, &port);
|
||||
- exp->helper = nf_conntrack_helper_q931;
|
||||
+ rcu_assign_pointer(exp->helper, nf_conntrack_helper_q931);
|
||||
|
||||
nathook = rcu_dereference(nfct_h323_nat_hook);
|
||||
if (memcmp(&ct->tuplehash[dir].tuple.src.u3,
|
||||
@@ -1233,7 +1233,7 @@ static int expect_q931(struct sk_buff *skb, struct nf_conn *ct,
|
||||
&ct->tuplehash[!dir].tuple.src.u3 : NULL,
|
||||
&ct->tuplehash[!dir].tuple.dst.u3,
|
||||
IPPROTO_TCP, NULL, &port);
|
||||
- exp->helper = nf_conntrack_helper_q931;
|
||||
+ rcu_assign_pointer(exp->helper, nf_conntrack_helper_q931);
|
||||
exp->flags = NF_CT_EXPECT_PERMANENT; /* Accept multiple calls */
|
||||
|
||||
nathook = rcu_dereference(nfct_h323_nat_hook);
|
||||
@@ -1305,7 +1305,7 @@ static int process_gcf(struct sk_buff *skb, struct nf_conn *ct,
|
||||
nf_ct_expect_init(exp, NF_CT_EXPECT_CLASS_DEFAULT, nf_ct_l3num(ct),
|
||||
&ct->tuplehash[!dir].tuple.src.u3, &addr,
|
||||
IPPROTO_UDP, NULL, &port);
|
||||
- exp->helper = nf_conntrack_helper_ras;
|
||||
+ rcu_assign_pointer(exp->helper, nf_conntrack_helper_ras);
|
||||
|
||||
if (nf_ct_expect_related(exp, 0) == 0) {
|
||||
pr_debug("nf_ct_ras: expect RAS ");
|
||||
@@ -1522,7 +1522,7 @@ static int process_acf(struct sk_buff *skb, struct nf_conn *ct,
|
||||
&ct->tuplehash[!dir].tuple.src.u3, &addr,
|
||||
IPPROTO_TCP, NULL, &port);
|
||||
exp->flags = NF_CT_EXPECT_PERMANENT;
|
||||
- exp->helper = nf_conntrack_helper_q931;
|
||||
+ rcu_assign_pointer(exp->helper, nf_conntrack_helper_q931);
|
||||
|
||||
if (nf_ct_expect_related(exp, 0) == 0) {
|
||||
pr_debug("nf_ct_ras: expect Q.931 ");
|
||||
@@ -1576,7 +1576,7 @@ static int process_lcf(struct sk_buff *skb, struct nf_conn *ct,
|
||||
&ct->tuplehash[!dir].tuple.src.u3, &addr,
|
||||
IPPROTO_TCP, NULL, &port);
|
||||
exp->flags = NF_CT_EXPECT_PERMANENT;
|
||||
- exp->helper = nf_conntrack_helper_q931;
|
||||
+ rcu_assign_pointer(exp->helper, nf_conntrack_helper_q931);
|
||||
|
||||
if (nf_ct_expect_related(exp, 0) == 0) {
|
||||
pr_debug("nf_ct_ras: expect Q.931 ");
|
||||
diff --git a/net/netfilter/nf_conntrack_helper.c b/net/netfilter/nf_conntrack_helper.c
|
||||
index 9d7d36a..a21c976 100644
|
||||
--- a/net/netfilter/nf_conntrack_helper.c
|
||||
+++ b/net/netfilter/nf_conntrack_helper.c
|
||||
@@ -399,7 +399,7 @@ static bool expect_iter_me(struct nf_conntrack_expect *exp, void *data)
|
||||
const struct nf_conntrack_helper *me = data;
|
||||
const struct nf_conntrack_helper *this;
|
||||
|
||||
- if (exp->helper == me)
|
||||
+ if (rcu_access_pointer(exp->helper) == me)
|
||||
return true;
|
||||
|
||||
this = rcu_dereference_protected(help->helper,
|
||||
@@ -421,6 +421,11 @@ void nf_conntrack_helper_unregister(struct nf_conntrack_helper *me)
|
||||
|
||||
nf_ct_expect_iterate_destroy(expect_iter_me, me);
|
||||
nf_ct_iterate_destroy(unhelp, me);
|
||||
+
|
||||
+ /* nf_ct_iterate_destroy() does an unconditional synchronize_rcu() as
|
||||
+ * last step, this ensures rcu readers of exp->helper are done.
|
||||
+ * No need for another synchronize_rcu() here.
|
||||
+ */
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nf_conntrack_helper_unregister);
|
||||
|
||||
diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c
|
||||
index 527bf07..1da2757 100644
|
||||
--- a/net/netfilter/nf_conntrack_netlink.c
|
||||
+++ b/net/netfilter/nf_conntrack_netlink.c
|
||||
@@ -3602,7 +3602,7 @@ ctnetlink_alloc_expect(const struct nlattr * const cda[], struct nf_conn *ct,
|
||||
|
||||
exp->class = class;
|
||||
exp->master = ct;
|
||||
- exp->helper = helper;
|
||||
+ rcu_assign_pointer(exp->helper, helper);
|
||||
exp->tuple = *tuple;
|
||||
exp->mask.src.u3 = mask->src.u3;
|
||||
exp->mask.src.u.all = mask->src.u.all;
|
||||
diff --git a/net/netfilter/nf_conntrack_sip.c b/net/netfilter/nf_conntrack_sip.c
|
||||
index 4ab5ef7..106b2f4 100644
|
||||
--- a/net/netfilter/nf_conntrack_sip.c
|
||||
+++ b/net/netfilter/nf_conntrack_sip.c
|
||||
@@ -1297,7 +1297,7 @@ static int process_register_request(struct sk_buff *skb, unsigned int protoff,
|
||||
nf_ct_expect_init(exp, SIP_EXPECT_SIGNALLING, nf_ct_l3num(ct),
|
||||
saddr, &daddr, proto, NULL, &port);
|
||||
exp->timeout.expires = sip_timeout * HZ;
|
||||
- exp->helper = helper;
|
||||
+ rcu_assign_pointer(exp->helper, helper);
|
||||
exp->flags = NF_CT_EXPECT_PERMANENT | NF_CT_EXPECT_INACTIVE;
|
||||
|
||||
hooks = rcu_dereference(nf_nat_sip_hooks);
|
||||
144
1115-netfilter-nf-conntrack-expect-use-expect-helper.patch
Normal file
144
1115-netfilter-nf-conntrack-expect-use-expect-helper.patch
Normal file
@ -0,0 +1,144 @@
|
||||
From 7214a2db7b72aa16525b5583488ae15b790d20bc Mon Sep 17 00:00:00 2001
|
||||
From: Florian Westphal <fwestpha@redhat.com>
|
||||
Date: Wed, 13 May 2026 17:16:20 +0200
|
||||
Subject: [PATCH] netfilter: nf_conntrack_expect: use expect->helper
|
||||
|
||||
JIRA: https://redhat.atlassian.net/browse/RHEL-168848
|
||||
Upstream Status: commit f01794106042
|
||||
|
||||
commit f01794106042ee27e54af6fdf5b319a2fe3df94d
|
||||
Author: Pablo Neira Ayuso <pablo@netfilter.org>
|
||||
Date: Wed Mar 25 14:11:03 2026 +0100
|
||||
|
||||
netfilter: nf_conntrack_expect: use expect->helper
|
||||
|
||||
Use expect->helper in ctnetlink and /proc to dump the helper name.
|
||||
Using nfct_help() without holding a reference to the master conntrack
|
||||
is unsafe.
|
||||
|
||||
Use exp->master->helper in ctnetlink path if userspace does not provide
|
||||
an explicit helper when creating an expectation to retain the existing
|
||||
behaviour. The ctnetlink expectation path holds the reference on the
|
||||
master conntrack and nf_conntrack_expect lock and the nfnetlink glue
|
||||
path refers to the master ct that is attached to the skb.
|
||||
|
||||
Reported-by: Hyunwoo Kim <imv4bel@gmail.com>
|
||||
Signed-off-by: Florian Westphal <fw@strlen.de>
|
||||
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
|
||||
|
||||
Signed-off-by: Florian Westphal <fwestpha@redhat.com>
|
||||
|
||||
diff --git a/net/netfilter/nf_conntrack_expect.c b/net/netfilter/nf_conntrack_expect.c
|
||||
index de5e2ae..1cbe5f1 100644
|
||||
--- a/net/netfilter/nf_conntrack_expect.c
|
||||
+++ b/net/netfilter/nf_conntrack_expect.c
|
||||
@@ -674,7 +674,7 @@ static int exp_seq_show(struct seq_file *s, void *v)
|
||||
if (expect->flags & NF_CT_EXPECT_USERSPACE)
|
||||
seq_printf(s, "%sUSERSPACE", delim);
|
||||
|
||||
- helper = rcu_dereference(nfct_help(expect->master)->helper);
|
||||
+ helper = rcu_dereference(expect->helper);
|
||||
if (helper) {
|
||||
seq_printf(s, "%s%s", expect->flags ? " " : "", helper->name);
|
||||
if (helper->expect_policy[expect->class].name[0])
|
||||
diff --git a/net/netfilter/nf_conntrack_helper.c b/net/netfilter/nf_conntrack_helper.c
|
||||
index a21c976..a715304 100644
|
||||
--- a/net/netfilter/nf_conntrack_helper.c
|
||||
+++ b/net/netfilter/nf_conntrack_helper.c
|
||||
@@ -395,14 +395,10 @@ EXPORT_SYMBOL_GPL(nf_conntrack_helper_register);
|
||||
|
||||
static bool expect_iter_me(struct nf_conntrack_expect *exp, void *data)
|
||||
{
|
||||
- struct nf_conn_help *help = nfct_help(exp->master);
|
||||
const struct nf_conntrack_helper *me = data;
|
||||
const struct nf_conntrack_helper *this;
|
||||
|
||||
- if (rcu_access_pointer(exp->helper) == me)
|
||||
- return true;
|
||||
-
|
||||
- this = rcu_dereference_protected(help->helper,
|
||||
+ this = rcu_dereference_protected(exp->helper,
|
||||
lockdep_is_held(&nf_conntrack_expect_lock));
|
||||
return this == me;
|
||||
}
|
||||
diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c
|
||||
index 1da2757..479acad 100644
|
||||
--- a/net/netfilter/nf_conntrack_netlink.c
|
||||
+++ b/net/netfilter/nf_conntrack_netlink.c
|
||||
@@ -3031,7 +3031,7 @@ ctnetlink_exp_dump_expect(struct sk_buff *skb,
|
||||
{
|
||||
struct nf_conn *master = exp->master;
|
||||
long timeout = ((long)exp->timeout.expires - (long)jiffies) / HZ;
|
||||
- struct nf_conn_help *help;
|
||||
+ struct nf_conntrack_helper *helper;
|
||||
#if IS_ENABLED(CONFIG_NF_NAT)
|
||||
struct nlattr *nest_parms;
|
||||
struct nf_conntrack_tuple nat_tuple = {};
|
||||
@@ -3076,15 +3076,12 @@ ctnetlink_exp_dump_expect(struct sk_buff *skb,
|
||||
nla_put_be32(skb, CTA_EXPECT_FLAGS, htonl(exp->flags)) ||
|
||||
nla_put_be32(skb, CTA_EXPECT_CLASS, htonl(exp->class)))
|
||||
goto nla_put_failure;
|
||||
- help = nfct_help(master);
|
||||
- if (help) {
|
||||
- struct nf_conntrack_helper *helper;
|
||||
|
||||
- helper = rcu_dereference(help->helper);
|
||||
- if (helper &&
|
||||
- nla_put_string(skb, CTA_EXPECT_HELP_NAME, helper->name))
|
||||
- goto nla_put_failure;
|
||||
- }
|
||||
+ helper = rcu_dereference(exp->helper);
|
||||
+ if (helper &&
|
||||
+ nla_put_string(skb, CTA_EXPECT_HELP_NAME, helper->name))
|
||||
+ goto nla_put_failure;
|
||||
+
|
||||
expfn = nf_ct_helper_expectfn_find_by_symbol(exp->expectfn);
|
||||
if (expfn != NULL &&
|
||||
nla_put_string(skb, CTA_EXPECT_FN, expfn->name))
|
||||
@@ -3419,12 +3416,9 @@ static int ctnetlink_get_expect(struct sk_buff *skb,
|
||||
static bool expect_iter_name(struct nf_conntrack_expect *exp, void *data)
|
||||
{
|
||||
struct nf_conntrack_helper *helper;
|
||||
- const struct nf_conn_help *m_help;
|
||||
const char *name = data;
|
||||
|
||||
- m_help = nfct_help(exp->master);
|
||||
-
|
||||
- helper = rcu_dereference(m_help->helper);
|
||||
+ helper = rcu_dereference(exp->helper);
|
||||
if (!helper)
|
||||
return false;
|
||||
|
||||
@@ -3563,9 +3557,9 @@ ctnetlink_alloc_expect(const struct nlattr * const cda[], struct nf_conn *ct,
|
||||
struct nf_conntrack_tuple *tuple,
|
||||
struct nf_conntrack_tuple *mask)
|
||||
{
|
||||
- u_int32_t class = 0;
|
||||
struct nf_conntrack_expect *exp;
|
||||
struct nf_conn_help *help;
|
||||
+ u32 class = 0;
|
||||
int err;
|
||||
|
||||
help = nfct_help(ct);
|
||||
@@ -3602,6 +3596,8 @@ ctnetlink_alloc_expect(const struct nlattr * const cda[], struct nf_conn *ct,
|
||||
|
||||
exp->class = class;
|
||||
exp->master = ct;
|
||||
+ if (!helper)
|
||||
+ helper = rcu_dereference(help->helper);
|
||||
rcu_assign_pointer(exp->helper, helper);
|
||||
exp->tuple = *tuple;
|
||||
exp->mask.src.u3 = mask->src.u3;
|
||||
diff --git a/net/netfilter/nf_conntrack_sip.c b/net/netfilter/nf_conntrack_sip.c
|
||||
index 106b2f4..20e57cf 100644
|
||||
--- a/net/netfilter/nf_conntrack_sip.c
|
||||
+++ b/net/netfilter/nf_conntrack_sip.c
|
||||
@@ -924,7 +924,7 @@ static int set_expected_rtp_rtcp(struct sk_buff *skb, unsigned int protoff,
|
||||
exp = __nf_ct_expect_find(net, nf_ct_zone(ct), &tuple);
|
||||
|
||||
if (!exp || exp->master == ct ||
|
||||
- nfct_help(exp->master)->helper != nfct_help(ct)->helper ||
|
||||
+ exp->helper != nfct_help(ct)->helper ||
|
||||
exp->class != class)
|
||||
break;
|
||||
#if IS_ENABLED(CONFIG_NF_NAT)
|
||||
@ -0,0 +1,155 @@
|
||||
From 1ef310863ab448530889c2f720436ef846da997a Mon Sep 17 00:00:00 2001
|
||||
From: Florian Westphal <fwestpha@redhat.com>
|
||||
Date: Wed, 13 May 2026 17:16:23 +0200
|
||||
Subject: [PATCH] netfilter: nf_conntrack_expect: store netns and zone in
|
||||
expectation
|
||||
|
||||
JIRA: https://redhat.atlassian.net/browse/RHEL-168848
|
||||
Upstream Status: commit 02a3231b6d82
|
||||
|
||||
commit 02a3231b6d82efe750da6554ebf280e4a6f78756
|
||||
Author: Pablo Neira Ayuso <pablo@netfilter.org>
|
||||
Date: Wed Mar 25 22:39:55 2026 +0100
|
||||
|
||||
netfilter: nf_conntrack_expect: store netns and zone in expectation
|
||||
|
||||
__nf_ct_expect_find() and nf_ct_expect_find_get() are called under
|
||||
rcu_read_lock() but they dereference the master conntrack via
|
||||
exp->master.
|
||||
|
||||
Since the expectation does not hold a reference on the master conntrack,
|
||||
this could be dying conntrack or different recycled conntrack than the
|
||||
real master due to SLAB_TYPESAFE_RCU.
|
||||
|
||||
Store the netns, the master_tuple and the zone in struct
|
||||
nf_conntrack_expect as a safety measure.
|
||||
|
||||
This patch is required by the follow up fix not to dump expectations
|
||||
that do not belong to this netns.
|
||||
|
||||
Signed-off-by: Florian Westphal <fw@strlen.de>
|
||||
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
|
||||
|
||||
Signed-off-by: Florian Westphal <fwestpha@redhat.com>
|
||||
|
||||
diff --git a/include/net/netfilter/nf_conntrack_expect.h b/include/net/netfilter/nf_conntrack_expect.h
|
||||
index 1b01400..e9a8350 100644
|
||||
--- a/include/net/netfilter/nf_conntrack_expect.h
|
||||
+++ b/include/net/netfilter/nf_conntrack_expect.h
|
||||
@@ -22,10 +22,16 @@ struct nf_conntrack_expect {
|
||||
/* Hash member */
|
||||
struct hlist_node hnode;
|
||||
|
||||
+ /* Network namespace */
|
||||
+ possible_net_t net;
|
||||
+
|
||||
/* We expect this tuple, with the following mask */
|
||||
struct nf_conntrack_tuple tuple;
|
||||
struct nf_conntrack_tuple_mask mask;
|
||||
|
||||
+#ifdef CONFIG_NF_CONNTRACK_ZONES
|
||||
+ struct nf_conntrack_zone zone;
|
||||
+#endif
|
||||
/* Usage count. */
|
||||
refcount_t use;
|
||||
|
||||
@@ -62,7 +68,17 @@ struct nf_conntrack_expect {
|
||||
|
||||
static inline struct net *nf_ct_exp_net(struct nf_conntrack_expect *exp)
|
||||
{
|
||||
- return nf_ct_net(exp->master);
|
||||
+ return read_pnet(&exp->net);
|
||||
+}
|
||||
+
|
||||
+static inline bool nf_ct_exp_zone_equal_any(const struct nf_conntrack_expect *a,
|
||||
+ const struct nf_conntrack_zone *b)
|
||||
+{
|
||||
+#ifdef CONFIG_NF_CONNTRACK_ZONES
|
||||
+ return a->zone.id == b->id;
|
||||
+#else
|
||||
+ return true;
|
||||
+#endif
|
||||
}
|
||||
|
||||
#define NF_CT_EXP_POLICY_NAME_LEN 16
|
||||
diff --git a/net/netfilter/nf_conntrack_broadcast.c b/net/netfilter/nf_conntrack_broadcast.c
|
||||
index 1964c59..4f39bf7 100644
|
||||
--- a/net/netfilter/nf_conntrack_broadcast.c
|
||||
+++ b/net/netfilter/nf_conntrack_broadcast.c
|
||||
@@ -21,6 +21,7 @@ int nf_conntrack_broadcast_help(struct sk_buff *skb,
|
||||
unsigned int timeout)
|
||||
{
|
||||
const struct nf_conntrack_helper *helper;
|
||||
+ struct net *net = read_pnet(&ct->ct_net);
|
||||
struct nf_conntrack_expect *exp;
|
||||
struct iphdr *iph = ip_hdr(skb);
|
||||
struct rtable *rt = skb_rtable(skb);
|
||||
@@ -71,7 +72,10 @@ int nf_conntrack_broadcast_help(struct sk_buff *skb,
|
||||
exp->flags = NF_CT_EXPECT_PERMANENT;
|
||||
exp->class = NF_CT_EXPECT_CLASS_DEFAULT;
|
||||
rcu_assign_pointer(exp->helper, helper);
|
||||
-
|
||||
+ write_pnet(&exp->net, net);
|
||||
+#ifdef CONFIG_NF_CONNTRACK_ZONES
|
||||
+ exp->zone = ct->zone;
|
||||
+#endif
|
||||
nf_ct_expect_related(exp, 0);
|
||||
nf_ct_expect_put(exp);
|
||||
|
||||
diff --git a/net/netfilter/nf_conntrack_expect.c b/net/netfilter/nf_conntrack_expect.c
|
||||
index 1cbe5f1..db28801 100644
|
||||
--- a/net/netfilter/nf_conntrack_expect.c
|
||||
+++ b/net/netfilter/nf_conntrack_expect.c
|
||||
@@ -113,8 +113,8 @@ nf_ct_exp_equal(const struct nf_conntrack_tuple *tuple,
|
||||
const struct net *net)
|
||||
{
|
||||
return nf_ct_tuple_mask_cmp(tuple, &i->tuple, &i->mask) &&
|
||||
- net_eq(net, nf_ct_net(i->master)) &&
|
||||
- nf_ct_zone_equal_any(i->master, zone);
|
||||
+ net_eq(net, read_pnet(&i->net)) &&
|
||||
+ nf_ct_exp_zone_equal_any(i, zone);
|
||||
}
|
||||
|
||||
bool nf_ct_remove_expect(struct nf_conntrack_expect *exp)
|
||||
@@ -326,6 +326,7 @@ void nf_ct_expect_init(struct nf_conntrack_expect *exp, unsigned int class,
|
||||
{
|
||||
struct nf_conntrack_helper *helper = NULL;
|
||||
struct nf_conn *ct = exp->master;
|
||||
+ struct net *net = read_pnet(&ct->ct_net);
|
||||
struct nf_conn_help *help;
|
||||
int len;
|
||||
|
||||
@@ -343,6 +344,10 @@ void nf_ct_expect_init(struct nf_conntrack_expect *exp, unsigned int class,
|
||||
helper = rcu_dereference(help->helper);
|
||||
|
||||
rcu_assign_pointer(exp->helper, helper);
|
||||
+ write_pnet(&exp->net, net);
|
||||
+#ifdef CONFIG_NF_CONNTRACK_ZONES
|
||||
+ exp->zone = ct->zone;
|
||||
+#endif
|
||||
exp->tuple.src.l3num = family;
|
||||
exp->tuple.dst.protonum = proto;
|
||||
|
||||
diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c
|
||||
index 479acad..844236c 100644
|
||||
--- a/net/netfilter/nf_conntrack_netlink.c
|
||||
+++ b/net/netfilter/nf_conntrack_netlink.c
|
||||
@@ -3557,6 +3557,7 @@ ctnetlink_alloc_expect(const struct nlattr * const cda[], struct nf_conn *ct,
|
||||
struct nf_conntrack_tuple *tuple,
|
||||
struct nf_conntrack_tuple *mask)
|
||||
{
|
||||
+ struct net *net = read_pnet(&ct->ct_net);
|
||||
struct nf_conntrack_expect *exp;
|
||||
struct nf_conn_help *help;
|
||||
u32 class = 0;
|
||||
@@ -3596,6 +3597,10 @@ ctnetlink_alloc_expect(const struct nlattr * const cda[], struct nf_conn *ct,
|
||||
|
||||
exp->class = class;
|
||||
exp->master = ct;
|
||||
+ write_pnet(&exp->net, net);
|
||||
+#ifdef CONFIG_NF_CONNTRACK_ZONES
|
||||
+ exp->zone = ct->zone;
|
||||
+#endif
|
||||
if (!helper)
|
||||
helper = rcu_dereference(help->helper);
|
||||
rcu_assign_pointer(exp->helper, helper);
|
||||
@ -0,0 +1,48 @@
|
||||
From 7769732787489525396da7af990f9f8fe0d3edd3 Mon Sep 17 00:00:00 2001
|
||||
From: Florian Westphal <fwestpha@redhat.com>
|
||||
Date: Wed, 13 May 2026 17:16:16 +0200
|
||||
Subject: [PATCH] netfilter: ip6t_rt: reject oversized addrnr in rt_mt6_check()
|
||||
|
||||
JIRA: https://redhat.atlassian.net/browse/RHEL-168848
|
||||
Upstream Status: commit 9d3f027327c2
|
||||
|
||||
commit 9d3f027327c2fa265f7f85ead41294792c3296ed
|
||||
Author: Ren Wei <n05ec@lzu.edu.cn>
|
||||
Date: Wed Mar 25 14:11:00 2026 +0100
|
||||
|
||||
netfilter: ip6t_rt: reject oversized addrnr in rt_mt6_check()
|
||||
|
||||
Reject rt match rules whose addrnr exceeds IP6T_RT_HOPS.
|
||||
|
||||
rt_mt6() expects addrnr to stay within the bounds of rtinfo->addrs[].
|
||||
Validate addrnr during rule installation so malformed rules are rejected
|
||||
before the match logic can use an out-of-range value.
|
||||
|
||||
Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
|
||||
Reported-by: Yifan Wu <yifanwucs@gmail.com>
|
||||
Reported-by: Juefei Pu <tomapufckgml@gmail.com>
|
||||
Co-developed-by: Yuan Tan <yuantan098@gmail.com>
|
||||
Signed-off-by: Yuan Tan <yuantan098@gmail.com>
|
||||
Suggested-by: Xin Liu <bird@lzu.edu.cn>
|
||||
Tested-by: Yuhang Zheng <z1652074432@gmail.com>
|
||||
Signed-off-by: Ren Wei <n05ec@lzu.edu.cn>
|
||||
Signed-off-by: Florian Westphal <fw@strlen.de>
|
||||
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
|
||||
|
||||
Signed-off-by: Florian Westphal <fwestpha@redhat.com>
|
||||
|
||||
diff --git a/net/ipv6/netfilter/ip6t_rt.c b/net/ipv6/netfilter/ip6t_rt.c
|
||||
index 4ad8b20..5561bd9 100644
|
||||
--- a/net/ipv6/netfilter/ip6t_rt.c
|
||||
+++ b/net/ipv6/netfilter/ip6t_rt.c
|
||||
@@ -157,6 +157,10 @@ static int rt_mt6_check(const struct xt_mtchk_param *par)
|
||||
pr_debug("unknown flags %X\n", rtinfo->invflags);
|
||||
return -EINVAL;
|
||||
}
|
||||
+ if (rtinfo->addrnr > IP6T_RT_HOPS) {
|
||||
+ pr_debug("too many addresses specified\n");
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
if ((rtinfo->flags & (IP6T_RT_RES | IP6T_RT_FST_MASK)) &&
|
||||
(!(rtinfo->flags & IP6T_RT_TYP) ||
|
||||
(rtinfo->rt_type != 0) ||
|
||||
125
1118-netfilter-ctnetlink-use-netlink-policy-range-checks.patch
Normal file
125
1118-netfilter-ctnetlink-use-netlink-policy-range-checks.patch
Normal file
@ -0,0 +1,125 @@
|
||||
From bb977545d63209b172be316ac61e3787f769a085 Mon Sep 17 00:00:00 2001
|
||||
From: Florian Westphal <fwestpha@redhat.com>
|
||||
Date: Wed, 13 May 2026 17:16:27 +0200
|
||||
Subject: [PATCH] netfilter: ctnetlink: use netlink policy range checks
|
||||
|
||||
JIRA: https://redhat.atlassian.net/browse/RHEL-168848
|
||||
Upstream Status: commit 8f15b5071b45
|
||||
|
||||
commit 8f15b5071b4548b0aafc03b366eb45c9c6566704
|
||||
Author: David Carlier <devnexen@gmail.com>
|
||||
Date: Wed Mar 25 14:11:08 2026 +0100
|
||||
|
||||
netfilter: ctnetlink: use netlink policy range checks
|
||||
|
||||
Replace manual range and mask validations with netlink policy
|
||||
annotations in ctnetlink code paths, so that the netlink core rejects
|
||||
invalid values early and can generate extack errors.
|
||||
|
||||
- CTA_PROTOINFO_TCP_STATE: reject values > TCP_CONNTRACK_SYN_SENT2 at
|
||||
policy level, removing the manual >= TCP_CONNTRACK_MAX check.
|
||||
- CTA_PROTOINFO_TCP_WSCALE_ORIGINAL/REPLY: reject values > TCP_MAX_WSCALE
|
||||
(14). The normal TCP option parsing path already clamps to this value,
|
||||
but the ctnetlink path accepted 0-255, causing undefined behavior when
|
||||
used as a u32 shift count.
|
||||
- CTA_FILTER_ORIG_FLAGS/REPLY_FLAGS: use NLA_POLICY_MASK with
|
||||
CTA_FILTER_F_ALL, removing the manual mask checks.
|
||||
- CTA_EXPECT_FLAGS: use NLA_POLICY_MASK with NF_CT_EXPECT_MASK, adding
|
||||
a new mask define grouping all valid expect flags.
|
||||
|
||||
Extracted from a broader nf-next patch by Florian Westphal, scoped to
|
||||
ctnetlink for the fixes tree.
|
||||
|
||||
Fixes: c8e2078cfe41 ("[NETFILTER]: ctnetlink: add support for internal tcp connection tracking flags handling")
|
||||
Signed-off-by: David Carlier <devnexen@gmail.com>
|
||||
Co-developed-by: Florian Westphal <fw@strlen.de>
|
||||
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
|
||||
|
||||
Signed-off-by: Florian Westphal <fwestpha@redhat.com>
|
||||
|
||||
diff --git a/include/uapi/linux/netfilter/nf_conntrack_common.h b/include/uapi/linux/netfilter/nf_conntrack_common.h
|
||||
index 2607102..56b6b60 100644
|
||||
--- a/include/uapi/linux/netfilter/nf_conntrack_common.h
|
||||
+++ b/include/uapi/linux/netfilter/nf_conntrack_common.h
|
||||
@@ -159,5 +159,9 @@ enum ip_conntrack_expect_events {
|
||||
#define NF_CT_EXPECT_INACTIVE 0x2
|
||||
#define NF_CT_EXPECT_USERSPACE 0x4
|
||||
|
||||
+#ifdef __KERNEL__
|
||||
+#define NF_CT_EXPECT_MASK (NF_CT_EXPECT_PERMANENT | NF_CT_EXPECT_INACTIVE | \
|
||||
+ NF_CT_EXPECT_USERSPACE)
|
||||
+#endif
|
||||
|
||||
#endif /* _UAPI_NF_CONNTRACK_COMMON_H */
|
||||
diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c
|
||||
index 844236c..edc6045 100644
|
||||
--- a/net/netfilter/nf_conntrack_netlink.c
|
||||
+++ b/net/netfilter/nf_conntrack_netlink.c
|
||||
@@ -909,8 +909,8 @@ struct ctnetlink_filter {
|
||||
};
|
||||
|
||||
static const struct nla_policy cta_filter_nla_policy[CTA_FILTER_MAX + 1] = {
|
||||
- [CTA_FILTER_ORIG_FLAGS] = { .type = NLA_U32 },
|
||||
- [CTA_FILTER_REPLY_FLAGS] = { .type = NLA_U32 },
|
||||
+ [CTA_FILTER_ORIG_FLAGS] = NLA_POLICY_MASK(NLA_U32, CTA_FILTER_F_ALL),
|
||||
+ [CTA_FILTER_REPLY_FLAGS] = NLA_POLICY_MASK(NLA_U32, CTA_FILTER_F_ALL),
|
||||
};
|
||||
|
||||
static int ctnetlink_parse_filter(const struct nlattr *attr,
|
||||
@@ -924,17 +924,11 @@ static int ctnetlink_parse_filter(const struct nlattr *attr,
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
- if (tb[CTA_FILTER_ORIG_FLAGS]) {
|
||||
+ if (tb[CTA_FILTER_ORIG_FLAGS])
|
||||
filter->orig_flags = nla_get_u32(tb[CTA_FILTER_ORIG_FLAGS]);
|
||||
- if (filter->orig_flags & ~CTA_FILTER_F_ALL)
|
||||
- return -EOPNOTSUPP;
|
||||
- }
|
||||
|
||||
- if (tb[CTA_FILTER_REPLY_FLAGS]) {
|
||||
+ if (tb[CTA_FILTER_REPLY_FLAGS])
|
||||
filter->reply_flags = nla_get_u32(tb[CTA_FILTER_REPLY_FLAGS]);
|
||||
- if (filter->reply_flags & ~CTA_FILTER_F_ALL)
|
||||
- return -EOPNOTSUPP;
|
||||
- }
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -2653,7 +2647,7 @@ static const struct nla_policy exp_nla_policy[CTA_EXPECT_MAX+1] = {
|
||||
[CTA_EXPECT_HELP_NAME] = { .type = NLA_NUL_STRING,
|
||||
.len = NF_CT_HELPER_NAME_LEN - 1 },
|
||||
[CTA_EXPECT_ZONE] = { .type = NLA_U16 },
|
||||
- [CTA_EXPECT_FLAGS] = { .type = NLA_U32 },
|
||||
+ [CTA_EXPECT_FLAGS] = NLA_POLICY_MASK(NLA_BE32, NF_CT_EXPECT_MASK),
|
||||
[CTA_EXPECT_CLASS] = { .type = NLA_U32 },
|
||||
[CTA_EXPECT_NAT] = { .type = NLA_NESTED },
|
||||
[CTA_EXPECT_FN] = { .type = NLA_NUL_STRING },
|
||||
diff --git a/net/netfilter/nf_conntrack_proto_tcp.c b/net/netfilter/nf_conntrack_proto_tcp.c
|
||||
index 0c1d086..b67426c 100644
|
||||
--- a/net/netfilter/nf_conntrack_proto_tcp.c
|
||||
+++ b/net/netfilter/nf_conntrack_proto_tcp.c
|
||||
@@ -1385,9 +1385,9 @@ static int tcp_to_nlattr(struct sk_buff *skb, struct nlattr *nla,
|
||||
}
|
||||
|
||||
static const struct nla_policy tcp_nla_policy[CTA_PROTOINFO_TCP_MAX+1] = {
|
||||
- [CTA_PROTOINFO_TCP_STATE] = { .type = NLA_U8 },
|
||||
- [CTA_PROTOINFO_TCP_WSCALE_ORIGINAL] = { .type = NLA_U8 },
|
||||
- [CTA_PROTOINFO_TCP_WSCALE_REPLY] = { .type = NLA_U8 },
|
||||
+ [CTA_PROTOINFO_TCP_STATE] = NLA_POLICY_MAX(NLA_U8, TCP_CONNTRACK_SYN_SENT2),
|
||||
+ [CTA_PROTOINFO_TCP_WSCALE_ORIGINAL] = NLA_POLICY_MAX(NLA_U8, TCP_MAX_WSCALE),
|
||||
+ [CTA_PROTOINFO_TCP_WSCALE_REPLY] = NLA_POLICY_MAX(NLA_U8, TCP_MAX_WSCALE),
|
||||
[CTA_PROTOINFO_TCP_FLAGS_ORIGINAL] = { .len = sizeof(struct nf_ct_tcp_flags) },
|
||||
[CTA_PROTOINFO_TCP_FLAGS_REPLY] = { .len = sizeof(struct nf_ct_tcp_flags) },
|
||||
};
|
||||
@@ -1414,10 +1414,6 @@ static int nlattr_to_tcp(struct nlattr *cda[], struct nf_conn *ct)
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
- if (tb[CTA_PROTOINFO_TCP_STATE] &&
|
||||
- nla_get_u8(tb[CTA_PROTOINFO_TCP_STATE]) >= TCP_CONNTRACK_MAX)
|
||||
- return -EINVAL;
|
||||
-
|
||||
spin_lock_bh(&ct->lock);
|
||||
if (tb[CTA_PROTOINFO_TCP_STATE])
|
||||
ct->proto.tcp.state = nla_get_u8(tb[CTA_PROTOINFO_TCP_STATE]);
|
||||
@ -0,0 +1,40 @@
|
||||
From 216247489e80cd8001356ed24e13323803cce985 Mon Sep 17 00:00:00 2001
|
||||
From: Florian Westphal <fwestpha@redhat.com>
|
||||
Date: Wed, 13 May 2026 17:16:30 +0200
|
||||
Subject: [PATCH] netfilter: nfnetlink_log: account for netlink header size
|
||||
|
||||
JIRA: https://redhat.atlassian.net/browse/RHEL-168848
|
||||
Upstream Status: commit 6d52a4a0520a
|
||||
|
||||
commit 6d52a4a0520a6696bdde51caa11f2d6821cd0c01
|
||||
Author: Florian Westphal <fw@strlen.de>
|
||||
Date: Thu Mar 26 16:17:24 2026 +0100
|
||||
|
||||
netfilter: nfnetlink_log: account for netlink header size
|
||||
|
||||
This is a followup to an old bug fix: NLMSG_DONE needs to account
|
||||
for the netlink header size, not just the attribute size.
|
||||
|
||||
This can result in a WARN splat + drop of the netlink message,
|
||||
but other than this there are no ill effects.
|
||||
|
||||
Fixes: 9dfa1dfe4d5e ("netfilter: nf_log: account for size of NLMSG_DONE attribute")
|
||||
Reported-by: Yiming Qian <yimingqian591@gmail.com>
|
||||
Signed-off-by: Florian Westphal <fw@strlen.de>
|
||||
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
|
||||
|
||||
Signed-off-by: Florian Westphal <fwestpha@redhat.com>
|
||||
|
||||
diff --git a/net/netfilter/nfnetlink_log.c b/net/netfilter/nfnetlink_log.c
|
||||
index 27dd352..dcd2493 100644
|
||||
--- a/net/netfilter/nfnetlink_log.c
|
||||
+++ b/net/netfilter/nfnetlink_log.c
|
||||
@@ -726,7 +726,7 @@ nfulnl_log_packet(struct net *net,
|
||||
+ nla_total_size(plen) /* prefix */
|
||||
+ nla_total_size(sizeof(struct nfulnl_msg_packet_hw))
|
||||
+ nla_total_size(sizeof(struct nfulnl_msg_packet_timestamp))
|
||||
- + nla_total_size(sizeof(struct nfgenmsg)); /* NLMSG_DONE */
|
||||
+ + nlmsg_total_size(sizeof(struct nfgenmsg)); /* NLMSG_DONE */
|
||||
|
||||
if (in && skb_mac_header_was_set(skb)) {
|
||||
size += nla_total_size(skb->dev->hard_header_len)
|
||||
@ -0,0 +1,65 @@
|
||||
From fb3e41e38a172b76e67ec5c97f4db2d3a9407c40 Mon Sep 17 00:00:00 2001
|
||||
From: Florian Westphal <fwestpha@redhat.com>
|
||||
Date: Wed, 13 May 2026 17:16:31 +0200
|
||||
Subject: [PATCH] netfilter: x_tables: ensure names are nul-terminated
|
||||
|
||||
JIRA: https://redhat.atlassian.net/browse/RHEL-168848
|
||||
Upstream Status: commit a958a4f90ddd
|
||||
|
||||
commit a958a4f90ddd7de0800b33ca9d7b886b7d40f74e
|
||||
Author: Florian Westphal <fw@strlen.de>
|
||||
Date: Tue Mar 31 23:13:36 2026 +0200
|
||||
|
||||
netfilter: x_tables: ensure names are nul-terminated
|
||||
|
||||
Reject names that lack a \0 character before feeding them
|
||||
to functions that expect c-strings.
|
||||
|
||||
Fixes tag is the most recent commit that needs this change.
|
||||
|
||||
Fixes: c38c4597e4bf ("netfilter: implement xt_cgroup cgroup2 path match")
|
||||
Signed-off-by: Florian Westphal <fw@strlen.de>
|
||||
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
|
||||
|
||||
Signed-off-by: Florian Westphal <fwestpha@redhat.com>
|
||||
|
||||
diff --git a/net/netfilter/xt_cgroup.c b/net/netfilter/xt_cgroup.c
|
||||
index c0f5e9a..bfc9871 100644
|
||||
--- a/net/netfilter/xt_cgroup.c
|
||||
+++ b/net/netfilter/xt_cgroup.c
|
||||
@@ -53,6 +53,9 @@ static int cgroup_mt_check_v1(const struct xt_mtchk_param *par)
|
||||
|
||||
info->priv = NULL;
|
||||
if (info->has_path) {
|
||||
+ if (strnlen(info->path, sizeof(info->path)) >= sizeof(info->path))
|
||||
+ return -ENAMETOOLONG;
|
||||
+
|
||||
cgrp = cgroup_get_from_path(info->path);
|
||||
if (IS_ERR(cgrp)) {
|
||||
pr_info_ratelimited("invalid path, errno=%ld\n",
|
||||
@@ -85,6 +88,9 @@ static int cgroup_mt_check_v2(const struct xt_mtchk_param *par)
|
||||
|
||||
info->priv = NULL;
|
||||
if (info->has_path) {
|
||||
+ if (strnlen(info->path, sizeof(info->path)) >= sizeof(info->path))
|
||||
+ return -ENAMETOOLONG;
|
||||
+
|
||||
cgrp = cgroup_get_from_path(info->path);
|
||||
if (IS_ERR(cgrp)) {
|
||||
pr_info_ratelimited("invalid path, errno=%ld\n",
|
||||
diff --git a/net/netfilter/xt_rateest.c b/net/netfilter/xt_rateest.c
|
||||
index 72324bd..b1d736c 100644
|
||||
--- a/net/netfilter/xt_rateest.c
|
||||
+++ b/net/netfilter/xt_rateest.c
|
||||
@@ -91,6 +91,11 @@ static int xt_rateest_mt_checkentry(const struct xt_mtchk_param *par)
|
||||
goto err1;
|
||||
}
|
||||
|
||||
+ if (strnlen(info->name1, sizeof(info->name1)) >= sizeof(info->name1))
|
||||
+ return -ENAMETOOLONG;
|
||||
+ if (strnlen(info->name2, sizeof(info->name2)) >= sizeof(info->name2))
|
||||
+ return -ENAMETOOLONG;
|
||||
+
|
||||
ret = -ENOENT;
|
||||
est1 = xt_rateest_lookup(par->net, info->name1);
|
||||
if (!est1)
|
||||
@ -0,0 +1,83 @@
|
||||
From 7563a383e063cf6be846f150a25d85098adad32b Mon Sep 17 00:00:00 2001
|
||||
From: Florian Westphal <fwestpha@redhat.com>
|
||||
Date: Wed, 13 May 2026 17:16:33 +0200
|
||||
Subject: [PATCH] netfilter: ipset: use nla_strcmp for IPSET_ATTR_NAME attr
|
||||
|
||||
JIRA: https://redhat.atlassian.net/browse/RHEL-168848
|
||||
Upstream Status: commit b7e8590987aa
|
||||
|
||||
commit b7e8590987aa94c9dc51518fad0e58cb887b1db5
|
||||
Author: Florian Westphal <fw@strlen.de>
|
||||
Date: Mon Mar 30 14:16:34 2026 +0200
|
||||
|
||||
netfilter: ipset: use nla_strcmp for IPSET_ATTR_NAME attr
|
||||
|
||||
IPSET_ATTR_NAME and IPSET_ATTR_NAMEREF are of NLA_STRING type, they
|
||||
cannot be treated like a c-string.
|
||||
|
||||
They either have to be switched to NLA_NUL_STRING, or the compare
|
||||
operations need to use the nla functions.
|
||||
|
||||
Fixes: f830837f0eed ("netfilter: ipset: list:set set type support")
|
||||
Signed-off-by: Florian Westphal <fw@strlen.de>
|
||||
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
|
||||
|
||||
Signed-off-by: Florian Westphal <fwestpha@redhat.com>
|
||||
|
||||
diff --git a/include/linux/netfilter/ipset/ip_set.h b/include/linux/netfilter/ipset/ip_set.h
|
||||
index e9f4f84..b983315 100644
|
||||
--- a/include/linux/netfilter/ipset/ip_set.h
|
||||
+++ b/include/linux/netfilter/ipset/ip_set.h
|
||||
@@ -309,7 +309,7 @@ enum {
|
||||
|
||||
/* register and unregister set references */
|
||||
extern ip_set_id_t ip_set_get_byname(struct net *net,
|
||||
- const char *name, struct ip_set **set);
|
||||
+ const struct nlattr *name, struct ip_set **set);
|
||||
extern void ip_set_put_byindex(struct net *net, ip_set_id_t index);
|
||||
extern void ip_set_name_byindex(struct net *net, ip_set_id_t index, char *name);
|
||||
extern ip_set_id_t ip_set_nfnl_get_byindex(struct net *net, ip_set_id_t index);
|
||||
diff --git a/net/netfilter/ipset/ip_set_core.c b/net/netfilter/ipset/ip_set_core.c
|
||||
index cc20e6d..a4e1d79 100644
|
||||
--- a/net/netfilter/ipset/ip_set_core.c
|
||||
+++ b/net/netfilter/ipset/ip_set_core.c
|
||||
@@ -821,7 +821,7 @@ EXPORT_SYMBOL_GPL(ip_set_del);
|
||||
*
|
||||
*/
|
||||
ip_set_id_t
|
||||
-ip_set_get_byname(struct net *net, const char *name, struct ip_set **set)
|
||||
+ip_set_get_byname(struct net *net, const struct nlattr *name, struct ip_set **set)
|
||||
{
|
||||
ip_set_id_t i, index = IPSET_INVALID_ID;
|
||||
struct ip_set *s;
|
||||
@@ -830,7 +830,7 @@ ip_set_get_byname(struct net *net, const char *name, struct ip_set **set)
|
||||
rcu_read_lock();
|
||||
for (i = 0; i < inst->ip_set_max; i++) {
|
||||
s = rcu_dereference(inst->ip_set_list)[i];
|
||||
- if (s && STRNCMP(s->name, name)) {
|
||||
+ if (s && nla_strcmp(name, s->name) == 0) {
|
||||
__ip_set_get(s);
|
||||
index = i;
|
||||
*set = s;
|
||||
diff --git a/net/netfilter/ipset/ip_set_list_set.c b/net/netfilter/ipset/ip_set_list_set.c
|
||||
index 13c7a08..34bb84d 100644
|
||||
--- a/net/netfilter/ipset/ip_set_list_set.c
|
||||
+++ b/net/netfilter/ipset/ip_set_list_set.c
|
||||
@@ -367,7 +367,7 @@ list_set_uadt(struct ip_set *set, struct nlattr *tb[],
|
||||
ret = ip_set_get_extensions(set, tb, &ext);
|
||||
if (ret)
|
||||
return ret;
|
||||
- e.id = ip_set_get_byname(map->net, nla_data(tb[IPSET_ATTR_NAME]), &s);
|
||||
+ e.id = ip_set_get_byname(map->net, tb[IPSET_ATTR_NAME], &s);
|
||||
if (e.id == IPSET_INVALID_ID)
|
||||
return -IPSET_ERR_NAME;
|
||||
/* "Loop detection" */
|
||||
@@ -389,7 +389,7 @@ list_set_uadt(struct ip_set *set, struct nlattr *tb[],
|
||||
|
||||
if (tb[IPSET_ATTR_NAMEREF]) {
|
||||
e.refid = ip_set_get_byname(map->net,
|
||||
- nla_data(tb[IPSET_ATTR_NAMEREF]),
|
||||
+ tb[IPSET_ATTR_NAMEREF],
|
||||
&s);
|
||||
if (e.refid == IPSET_INVALID_ID) {
|
||||
ret = -IPSET_ERR_NAMEREF;
|
||||
@ -0,0 +1,58 @@
|
||||
From fc4bd898b84a9af433a40544389691829990d4b2 Mon Sep 17 00:00:00 2001
|
||||
From: Florian Westphal <fwestpha@redhat.com>
|
||||
Date: Wed, 13 May 2026 17:16:35 +0200
|
||||
Subject: [PATCH] netfilter: ctnetlink: zero expect NAT fields when
|
||||
CTA_EXPECT_NAT absent
|
||||
|
||||
JIRA: https://redhat.atlassian.net/browse/RHEL-168848
|
||||
Upstream Status: commit 35177c687713
|
||||
|
||||
commit 35177c6877134a21315f37d57a5577846225623e
|
||||
Author: Qi Tang <tpluszz77@gmail.com>
|
||||
Date: Tue Mar 31 14:17:12 2026 +0800
|
||||
|
||||
netfilter: ctnetlink: zero expect NAT fields when CTA_EXPECT_NAT absent
|
||||
|
||||
ctnetlink_alloc_expect() allocates expectations from a non-zeroing
|
||||
slab cache via nf_ct_expect_alloc(). When CTA_EXPECT_NAT is not
|
||||
present in the netlink message, saved_addr and saved_proto are
|
||||
never initialized. Stale data from a previous slab occupant can
|
||||
then be dumped to userspace by ctnetlink_exp_dump_expect(), which
|
||||
checks these fields to decide whether to emit CTA_EXPECT_NAT.
|
||||
|
||||
The safe sibling nf_ct_expect_init(), used by the packet path,
|
||||
explicitly zeroes these fields.
|
||||
|
||||
Zero saved_addr, saved_proto and dir in the else branch, guarded
|
||||
by IS_ENABLED(CONFIG_NF_NAT) since these fields only exist when
|
||||
NAT is enabled.
|
||||
|
||||
Confirmed by priming the expect slab with NAT-bearing expectations,
|
||||
freeing them, creating a new expectation without CTA_EXPECT_NAT,
|
||||
and observing that the ctnetlink dump emits a spurious
|
||||
CTA_EXPECT_NAT containing stale data from the prior allocation.
|
||||
|
||||
Fixes: 076a0ca02644 ("netfilter: ctnetlink: add NAT support for expectations")
|
||||
Reported-by: kernel test robot <lkp@intel.com>
|
||||
Signed-off-by: Qi Tang <tpluszz77@gmail.com>
|
||||
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
|
||||
|
||||
Signed-off-by: Florian Westphal <fwestpha@redhat.com>
|
||||
|
||||
diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c
|
||||
index edc6045..208e26a 100644
|
||||
--- a/net/netfilter/nf_conntrack_netlink.c
|
||||
+++ b/net/netfilter/nf_conntrack_netlink.c
|
||||
@@ -3607,6 +3607,12 @@ ctnetlink_alloc_expect(const struct nlattr * const cda[], struct nf_conn *ct,
|
||||
exp, nf_ct_l3num(ct));
|
||||
if (err < 0)
|
||||
goto err_out;
|
||||
+#if IS_ENABLED(CONFIG_NF_NAT)
|
||||
+ } else {
|
||||
+ memset(&exp->saved_addr, 0, sizeof(exp->saved_addr));
|
||||
+ memset(&exp->saved_proto, 0, sizeof(exp->saved_proto));
|
||||
+ exp->dir = 0;
|
||||
+#endif
|
||||
}
|
||||
return exp;
|
||||
err_out:
|
||||
@ -0,0 +1,169 @@
|
||||
From dbc7553b9ac766a071e3fdd0467041890bf514da Mon Sep 17 00:00:00 2001
|
||||
From: Florian Westphal <fwestpha@redhat.com>
|
||||
Date: Wed, 13 May 2026 17:16:37 +0200
|
||||
Subject: [PATCH] netfilter: ctnetlink: ignore explicit helper on new
|
||||
expectations
|
||||
|
||||
JIRA: https://redhat.atlassian.net/browse/RHEL-168848
|
||||
Upstream Status: commit 917b61fa2042
|
||||
|
||||
commit 917b61fa2042f11e2af4c428e43f08199586633a
|
||||
Author: Pablo Neira Ayuso <pablo@netfilter.org>
|
||||
Date: Mon Mar 30 11:26:22 2026 +0200
|
||||
|
||||
netfilter: ctnetlink: ignore explicit helper on new expectations
|
||||
|
||||
Use the existing master conntrack helper, anything else is not really
|
||||
supported and it just makes validation more complicated, so just ignore
|
||||
what helper userspace suggests for this expectation.
|
||||
|
||||
This was uncovered when validating CTA_EXPECT_CLASS via different helper
|
||||
provided by userspace than the existing master conntrack helper:
|
||||
|
||||
BUG: KASAN: slab-out-of-bounds in nf_ct_expect_related_report+0x2479/0x27c0
|
||||
Read of size 4 at addr ffff8880043fe408 by task poc/102
|
||||
Call Trace:
|
||||
nf_ct_expect_related_report+0x2479/0x27c0
|
||||
ctnetlink_create_expect+0x22b/0x3b0
|
||||
ctnetlink_new_expect+0x4bd/0x5c0
|
||||
nfnetlink_rcv_msg+0x67a/0x950
|
||||
netlink_rcv_skb+0x120/0x350
|
||||
|
||||
Allowing to read kernel memory bytes off the expectation boundary.
|
||||
|
||||
CTA_EXPECT_HELP_NAME is still used to offer the helper name to userspace
|
||||
via netlink dump.
|
||||
|
||||
Fixes: bd0779370588 ("netfilter: nfnetlink_queue: allow to attach expectations to conntracks")
|
||||
Reported-by: Qi Tang <tpluszz77@gmail.com>
|
||||
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
|
||||
|
||||
Signed-off-by: Florian Westphal <fwestpha@redhat.com>
|
||||
|
||||
diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c
|
||||
index 208e26a..dcef3ef 100644
|
||||
--- a/net/netfilter/nf_conntrack_netlink.c
|
||||
+++ b/net/netfilter/nf_conntrack_netlink.c
|
||||
@@ -2655,7 +2655,6 @@ static const struct nla_policy exp_nla_policy[CTA_EXPECT_MAX+1] = {
|
||||
|
||||
static struct nf_conntrack_expect *
|
||||
ctnetlink_alloc_expect(const struct nlattr *const cda[], struct nf_conn *ct,
|
||||
- struct nf_conntrack_helper *helper,
|
||||
struct nf_conntrack_tuple *tuple,
|
||||
struct nf_conntrack_tuple *mask);
|
||||
|
||||
@@ -2884,7 +2883,6 @@ ctnetlink_glue_attach_expect(const struct nlattr *attr, struct nf_conn *ct,
|
||||
{
|
||||
struct nlattr *cda[CTA_EXPECT_MAX+1];
|
||||
struct nf_conntrack_tuple tuple, mask;
|
||||
- struct nf_conntrack_helper *helper = NULL;
|
||||
struct nf_conntrack_expect *exp;
|
||||
int err;
|
||||
|
||||
@@ -2898,17 +2896,8 @@ ctnetlink_glue_attach_expect(const struct nlattr *attr, struct nf_conn *ct,
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
- if (cda[CTA_EXPECT_HELP_NAME]) {
|
||||
- const char *helpname = nla_data(cda[CTA_EXPECT_HELP_NAME]);
|
||||
-
|
||||
- helper = __nf_conntrack_helper_find(helpname, nf_ct_l3num(ct),
|
||||
- nf_ct_protonum(ct));
|
||||
- if (helper == NULL)
|
||||
- return -EOPNOTSUPP;
|
||||
- }
|
||||
-
|
||||
exp = ctnetlink_alloc_expect((const struct nlattr * const *)cda, ct,
|
||||
- helper, &tuple, &mask);
|
||||
+ &tuple, &mask);
|
||||
if (IS_ERR(exp))
|
||||
return PTR_ERR(exp);
|
||||
|
||||
@@ -3547,11 +3536,11 @@ ctnetlink_parse_expect_nat(const struct nlattr *attr,
|
||||
|
||||
static struct nf_conntrack_expect *
|
||||
ctnetlink_alloc_expect(const struct nlattr * const cda[], struct nf_conn *ct,
|
||||
- struct nf_conntrack_helper *helper,
|
||||
struct nf_conntrack_tuple *tuple,
|
||||
struct nf_conntrack_tuple *mask)
|
||||
{
|
||||
struct net *net = read_pnet(&ct->ct_net);
|
||||
+ struct nf_conntrack_helper *helper;
|
||||
struct nf_conntrack_expect *exp;
|
||||
struct nf_conn_help *help;
|
||||
u32 class = 0;
|
||||
@@ -3561,7 +3550,11 @@ ctnetlink_alloc_expect(const struct nlattr * const cda[], struct nf_conn *ct,
|
||||
if (!help)
|
||||
return ERR_PTR(-EOPNOTSUPP);
|
||||
|
||||
- if (cda[CTA_EXPECT_CLASS] && helper) {
|
||||
+ helper = rcu_dereference(help->helper);
|
||||
+ if (!helper)
|
||||
+ return ERR_PTR(-EOPNOTSUPP);
|
||||
+
|
||||
+ if (cda[CTA_EXPECT_CLASS]) {
|
||||
class = ntohl(nla_get_be32(cda[CTA_EXPECT_CLASS]));
|
||||
if (class > helper->expect_class_max)
|
||||
return ERR_PTR(-EINVAL);
|
||||
@@ -3595,8 +3588,6 @@ ctnetlink_alloc_expect(const struct nlattr * const cda[], struct nf_conn *ct,
|
||||
#ifdef CONFIG_NF_CONNTRACK_ZONES
|
||||
exp->zone = ct->zone;
|
||||
#endif
|
||||
- if (!helper)
|
||||
- helper = rcu_dereference(help->helper);
|
||||
rcu_assign_pointer(exp->helper, helper);
|
||||
exp->tuple = *tuple;
|
||||
exp->mask.src.u3 = mask->src.u3;
|
||||
@@ -3628,7 +3619,6 @@ ctnetlink_create_expect(struct net *net,
|
||||
{
|
||||
struct nf_conntrack_tuple tuple, mask, master_tuple;
|
||||
struct nf_conntrack_tuple_hash *h = NULL;
|
||||
- struct nf_conntrack_helper *helper = NULL;
|
||||
struct nf_conntrack_expect *exp;
|
||||
struct nf_conn *ct;
|
||||
int err;
|
||||
@@ -3654,33 +3644,7 @@ ctnetlink_create_expect(struct net *net,
|
||||
ct = nf_ct_tuplehash_to_ctrack(h);
|
||||
|
||||
rcu_read_lock();
|
||||
- if (cda[CTA_EXPECT_HELP_NAME]) {
|
||||
- const char *helpname = nla_data(cda[CTA_EXPECT_HELP_NAME]);
|
||||
-
|
||||
- helper = __nf_conntrack_helper_find(helpname, u3,
|
||||
- nf_ct_protonum(ct));
|
||||
- if (helper == NULL) {
|
||||
- rcu_read_unlock();
|
||||
-#ifdef CONFIG_MODULES
|
||||
- if (request_module("nfct-helper-%s", helpname) < 0) {
|
||||
- err = -EOPNOTSUPP;
|
||||
- goto err_ct;
|
||||
- }
|
||||
- rcu_read_lock();
|
||||
- helper = __nf_conntrack_helper_find(helpname, u3,
|
||||
- nf_ct_protonum(ct));
|
||||
- if (helper) {
|
||||
- err = -EAGAIN;
|
||||
- goto err_rcu;
|
||||
- }
|
||||
- rcu_read_unlock();
|
||||
-#endif
|
||||
- err = -EOPNOTSUPP;
|
||||
- goto err_ct;
|
||||
- }
|
||||
- }
|
||||
-
|
||||
- exp = ctnetlink_alloc_expect(cda, ct, helper, &tuple, &mask);
|
||||
+ exp = ctnetlink_alloc_expect(cda, ct, &tuple, &mask);
|
||||
if (IS_ERR(exp)) {
|
||||
err = PTR_ERR(exp);
|
||||
goto err_rcu;
|
||||
@@ -3690,8 +3654,8 @@ ctnetlink_create_expect(struct net *net,
|
||||
nf_ct_expect_put(exp);
|
||||
err_rcu:
|
||||
rcu_read_unlock();
|
||||
-err_ct:
|
||||
nf_ct_put(ct);
|
||||
+
|
||||
return err;
|
||||
}
|
||||
|
||||
@ -0,0 +1,101 @@
|
||||
From 55107f6cfe5236b5522a7b83bdc908ea36acf54c Mon Sep 17 00:00:00 2001
|
||||
From: Florian Westphal <fwestpha@redhat.com>
|
||||
Date: Wed, 13 May 2026 17:16:40 +0200
|
||||
Subject: [PATCH] netfilter: x_tables: restrict xt_check_match/xt_check_target
|
||||
extensions for NFPROTO_ARP
|
||||
|
||||
JIRA: https://redhat.atlassian.net/browse/RHEL-168848
|
||||
Upstream Status: commit 3d5d488f1177
|
||||
|
||||
commit 3d5d488f11776738deab9da336038add95d342d1
|
||||
Author: Pablo Neira Ayuso <pablo@netfilter.org>
|
||||
Date: Tue Mar 31 16:41:25 2026 +0200
|
||||
|
||||
netfilter: x_tables: restrict xt_check_match/xt_check_target extensions for NFPROTO_ARP
|
||||
|
||||
Weiming Shi says:
|
||||
|
||||
xt_match and xt_target structs registered with NFPROTO_UNSPEC can be
|
||||
loaded by any protocol family through nft_compat. When such a
|
||||
match/target sets .hooks to restrict which hooks it may run on, the
|
||||
bitmask uses NF_INET_* constants. This is only correct for families
|
||||
whose hook layout matches NF_INET_*: IPv4, IPv6, INET, and bridge
|
||||
all share the same five hooks (PRE_ROUTING ... POST_ROUTING).
|
||||
|
||||
ARP only has three hooks (IN=0, OUT=1, FORWARD=2) with different
|
||||
semantics. Because NF_ARP_OUT == 1 == NF_INET_LOCAL_IN, the .hooks
|
||||
validation silently passes for the wrong reasons, allowing matches to
|
||||
run on ARP chains where the hook assumptions (e.g. state->in being
|
||||
set on input hooks) do not hold. This leads to NULL pointer
|
||||
dereferences; xt_devgroup is one concrete example:
|
||||
|
||||
Oops: general protection fault, probably for non-canonical address 0xdffffc0000000044: 0000 [#1] SMP KASAN NOPTI
|
||||
KASAN: null-ptr-deref in range [0x0000000000000220-0x0000000000000227]
|
||||
RIP: 0010:devgroup_mt+0xff/0x350
|
||||
Call Trace:
|
||||
<TASK>
|
||||
nft_match_eval (net/netfilter/nft_compat.c:407)
|
||||
nft_do_chain (net/netfilter/nf_tables_core.c:285)
|
||||
nft_do_chain_arp (net/netfilter/nft_chain_filter.c:61)
|
||||
nf_hook_slow (net/netfilter/core.c:623)
|
||||
arp_xmit (net/ipv4/arp.c:666)
|
||||
</TASK>
|
||||
Kernel panic - not syncing: Fatal exception in interrupt
|
||||
|
||||
Fix it by restricting arptables to NFPROTO_ARP extensions only.
|
||||
Note that arptables-legacy only supports:
|
||||
|
||||
- arpt_CLASSIFY
|
||||
- arpt_mangle
|
||||
- arpt_MARK
|
||||
|
||||
that provide explicit NFPROTO_ARP match/target declarations.
|
||||
|
||||
Fixes: 9291747f118d ("netfilter: xtables: add device group match")
|
||||
Reported-by: Xiang Mei <xmei5@asu.edu>
|
||||
Signed-off-by: Florian Westphal <fw@strlen.de>
|
||||
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
|
||||
|
||||
Signed-off-by: Florian Westphal <fwestpha@redhat.com>
|
||||
|
||||
diff --git a/net/netfilter/x_tables.c b/net/netfilter/x_tables.c
|
||||
index 7098406..10530a0 100644
|
||||
--- a/net/netfilter/x_tables.c
|
||||
+++ b/net/netfilter/x_tables.c
|
||||
@@ -501,6 +501,17 @@ int xt_check_match(struct xt_mtchk_param *par,
|
||||
par->match->table, par->table);
|
||||
return -EINVAL;
|
||||
}
|
||||
+
|
||||
+ /* NFPROTO_UNSPEC implies NF_INET_* hooks which do not overlap with
|
||||
+ * NF_ARP_IN,OUT,FORWARD, allow explicit extensions with NFPROTO_ARP
|
||||
+ * support.
|
||||
+ */
|
||||
+ if (par->family == NFPROTO_ARP &&
|
||||
+ par->match->family != NFPROTO_ARP) {
|
||||
+ pr_info_ratelimited("%s_tables: %s match: not valid for this family\n",
|
||||
+ xt_prefix[par->family], par->match->name);
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
if (par->match->hooks && (par->hook_mask & ~par->match->hooks) != 0) {
|
||||
char used[64], allow[64];
|
||||
|
||||
@@ -1016,6 +1027,18 @@ int xt_check_target(struct xt_tgchk_param *par,
|
||||
par->target->table, par->table);
|
||||
return -EINVAL;
|
||||
}
|
||||
+
|
||||
+ /* NFPROTO_UNSPEC implies NF_INET_* hooks which do not overlap with
|
||||
+ * NF_ARP_IN,OUT,FORWARD, allow explicit extensions with NFPROTO_ARP
|
||||
+ * support.
|
||||
+ */
|
||||
+ if (par->family == NFPROTO_ARP &&
|
||||
+ par->target->family != NFPROTO_ARP) {
|
||||
+ pr_info_ratelimited("%s_tables: %s target: not valid for this family\n",
|
||||
+ xt_prefix[par->family], par->target->name);
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
if (par->target->hooks && (par->hook_mask & ~par->target->hooks) != 0) {
|
||||
char used[64], allow[64];
|
||||
|
||||
@ -0,0 +1,52 @@
|
||||
From 94a7ba900e7ea7da14a796edc10401a700d3b894 Mon Sep 17 00:00:00 2001
|
||||
From: Florian Westphal <fwestpha@redhat.com>
|
||||
Date: Wed, 13 May 2026 17:16:43 +0200
|
||||
Subject: [PATCH] netfilter: nf_tables: reject immediate NF_QUEUE verdict
|
||||
|
||||
JIRA: https://redhat.atlassian.net/browse/RHEL-168848
|
||||
Upstream Status: commit da107398cbd4
|
||||
|
||||
commit da107398cbd4bbdb6bffecb2ce86d5c9384f4cec
|
||||
Author: Pablo Neira Ayuso <pablo@netfilter.org>
|
||||
Date: Tue Mar 31 23:08:02 2026 +0200
|
||||
|
||||
netfilter: nf_tables: reject immediate NF_QUEUE verdict
|
||||
|
||||
nft_queue is always used from userspace nftables to deliver the NF_QUEUE
|
||||
verdict. Immediately emitting an NF_QUEUE verdict is never used by the
|
||||
userspace nft tools, so reject immediate NF_QUEUE verdicts.
|
||||
|
||||
The arp family does not provide queue support, but such an immediate
|
||||
verdict is still reachable. Globally reject NF_QUEUE immediate verdicts
|
||||
to address this issue.
|
||||
|
||||
Fixes: f342de4e2f33 ("netfilter: nf_tables: reject QUEUE/DROP verdict parameters")
|
||||
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
|
||||
|
||||
Signed-off-by: Florian Westphal <fwestpha@redhat.com>
|
||||
|
||||
diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c
|
||||
index 81164ad..7cc4163 100644
|
||||
--- a/net/netfilter/nf_tables_api.c
|
||||
+++ b/net/netfilter/nf_tables_api.c
|
||||
@@ -11804,8 +11804,6 @@ static int nft_verdict_init(const struct nft_ctx *ctx, struct nft_data *data,
|
||||
switch (data->verdict.code) {
|
||||
case NF_ACCEPT:
|
||||
case NF_DROP:
|
||||
- case NF_QUEUE:
|
||||
- break;
|
||||
case NFT_CONTINUE:
|
||||
case NFT_BREAK:
|
||||
case NFT_RETURN:
|
||||
@@ -11840,6 +11838,11 @@ static int nft_verdict_init(const struct nft_ctx *ctx, struct nft_data *data,
|
||||
|
||||
data->verdict.chain = chain;
|
||||
break;
|
||||
+ case NF_QUEUE:
|
||||
+ /* The nft_queue expression is used for this purpose, an
|
||||
+ * immediate NF_QUEUE verdict should not ever be seen here.
|
||||
+ */
|
||||
+ fallthrough;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
@ -0,0 +1,52 @@
|
||||
From a9469302e0f5583c2d4a0796bf8e723f2a90e01c Mon Sep 17 00:00:00 2001
|
||||
From: Florian Westphal <fwestpha@redhat.com>
|
||||
Date: Wed, 13 May 2026 17:20:51 +0200
|
||||
Subject: [PATCH] netfilter: nfnetlink_log: initialize nfgenmsg in NLMSG_DONE
|
||||
terminator
|
||||
|
||||
JIRA: https://redhat.atlassian.net/browse/RHEL-168848
|
||||
Upstream Status: commit 1f3083aec883
|
||||
|
||||
commit 1f3083aec8836213da441270cdb1ab612dd82cf4
|
||||
Author: Xiang Mei <xmei5@asu.edu>
|
||||
Date: Wed Apr 1 14:20:57 2026 -0700
|
||||
|
||||
netfilter: nfnetlink_log: initialize nfgenmsg in NLMSG_DONE terminator
|
||||
|
||||
When batching multiple NFLOG messages (inst->qlen > 1), __nfulnl_send()
|
||||
appends an NLMSG_DONE terminator with sizeof(struct nfgenmsg) payload via
|
||||
nlmsg_put(), but never initializes the nfgenmsg bytes. The nlmsg_put()
|
||||
helper only zeroes alignment padding after the payload, not the payload
|
||||
itself, so four bytes of stale kernel heap data are leaked to userspace
|
||||
in the NLMSG_DONE message body.
|
||||
|
||||
Use nfnl_msg_put() to build the NLMSG_DONE terminator, which initializes
|
||||
the nfgenmsg payload via nfnl_fill_hdr(), consistent with how
|
||||
__build_packet_message() already constructs NFULNL_MSG_PACKET headers.
|
||||
|
||||
Fixes: 29c5d4afba51 ("[NETFILTER]: nfnetlink_log: fix sending of multipart messages")
|
||||
Reported-by: Weiming Shi <bestswngs@gmail.com>
|
||||
Signed-off-by: Xiang Mei <xmei5@asu.edu>
|
||||
Signed-off-by: Florian Westphal <fw@strlen.de>
|
||||
|
||||
Signed-off-by: Florian Westphal <fwestpha@redhat.com>
|
||||
|
||||
diff --git a/net/netfilter/nfnetlink_log.c b/net/netfilter/nfnetlink_log.c
|
||||
index dcd2493..b1f3eda 100644
|
||||
--- a/net/netfilter/nfnetlink_log.c
|
||||
+++ b/net/netfilter/nfnetlink_log.c
|
||||
@@ -361,10 +361,10 @@ static void
|
||||
__nfulnl_send(struct nfulnl_instance *inst)
|
||||
{
|
||||
if (inst->qlen > 1) {
|
||||
- struct nlmsghdr *nlh = nlmsg_put(inst->skb, 0, 0,
|
||||
- NLMSG_DONE,
|
||||
- sizeof(struct nfgenmsg),
|
||||
- 0);
|
||||
+ struct nlmsghdr *nlh = nfnl_msg_put(inst->skb, 0, 0,
|
||||
+ NLMSG_DONE, 0,
|
||||
+ AF_UNSPEC, NFNETLINK_V0,
|
||||
+ htons(inst->group_num));
|
||||
if (WARN_ONCE(!nlh, "bad nlskb size: %u, tailroom %d\n",
|
||||
inst->skb->len, skb_tailroom(inst->skb))) {
|
||||
kfree_skb(inst->skb);
|
||||
@ -0,0 +1,100 @@
|
||||
From 8fa17e23a20d7981a2193301408f1f339ea2d057 Mon Sep 17 00:00:00 2001
|
||||
From: Florian Westphal <fwestpha@redhat.com>
|
||||
Date: Wed, 13 May 2026 17:20:52 +0200
|
||||
Subject: [PATCH] netfilter: xt_multiport: validate range encoding in
|
||||
checkentry
|
||||
|
||||
JIRA: https://redhat.atlassian.net/browse/RHEL-168848
|
||||
Upstream Status: commit ff64c5bfef12
|
||||
|
||||
commit ff64c5bfef12461df8450e0f50bb693b5269c720
|
||||
Author: Ren Wei <n05ec@lzu.edu.cn>
|
||||
Date: Fri Apr 3 23:52:52 2026 +0800
|
||||
|
||||
netfilter: xt_multiport: validate range encoding in checkentry
|
||||
|
||||
ports_match_v1() treats any non-zero pflags entry as the start of a
|
||||
port range and unconditionally consumes the next ports[] element as
|
||||
the range end.
|
||||
|
||||
The checkentry path currently validates protocol, flags and count, but
|
||||
it does not validate the range encoding itself. As a result, malformed
|
||||
rules can mark the last slot as a range start or place two range starts
|
||||
back to back, leaving ports_match_v1() to step past the last valid
|
||||
ports[] element while interpreting the rule.
|
||||
|
||||
Reject malformed multiport v1 rules in checkentry by validating that
|
||||
each range start has a following element and that the following element
|
||||
is not itself marked as another range start.
|
||||
|
||||
Fixes: a89ecb6a2ef7 ("[NETFILTER]: x_tables: unify IPv4/IPv6 multiport match")
|
||||
Reported-by: Yifan Wu <yifanwucs@gmail.com>
|
||||
Reported-by: Juefei Pu <tomapufckgml@gmail.com>
|
||||
Co-developed-by: Yuan Tan <yuantan098@gmail.com>
|
||||
Signed-off-by: Yuan Tan <yuantan098@gmail.com>
|
||||
Suggested-by: Xin Liu <bird@lzu.edu.cn>
|
||||
Tested-by: Yuhang Zheng <z1652074432@gmail.com>
|
||||
Signed-off-by: Ren Wei <n05ec@lzu.edu.cn>
|
||||
Signed-off-by: Florian Westphal <fw@strlen.de>
|
||||
|
||||
Signed-off-by: Florian Westphal <fwestpha@redhat.com>
|
||||
|
||||
diff --git a/net/netfilter/xt_multiport.c b/net/netfilter/xt_multiport.c
|
||||
index 44a00f5..a1691ff 100644
|
||||
--- a/net/netfilter/xt_multiport.c
|
||||
+++ b/net/netfilter/xt_multiport.c
|
||||
@@ -105,6 +105,28 @@ multiport_mt(const struct sk_buff *skb, struct xt_action_param *par)
|
||||
return ports_match_v1(multiinfo, ntohs(pptr[0]), ntohs(pptr[1]));
|
||||
}
|
||||
|
||||
+static bool
|
||||
+multiport_valid_ranges(const struct xt_multiport_v1 *multiinfo)
|
||||
+{
|
||||
+ unsigned int i;
|
||||
+
|
||||
+ for (i = 0; i < multiinfo->count; i++) {
|
||||
+ if (!multiinfo->pflags[i])
|
||||
+ continue;
|
||||
+
|
||||
+ if (++i >= multiinfo->count)
|
||||
+ return false;
|
||||
+
|
||||
+ if (multiinfo->pflags[i])
|
||||
+ return false;
|
||||
+
|
||||
+ if (multiinfo->ports[i - 1] > multiinfo->ports[i])
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
+ return true;
|
||||
+}
|
||||
+
|
||||
static inline bool
|
||||
check(u_int16_t proto,
|
||||
u_int8_t ip_invflags,
|
||||
@@ -127,8 +149,10 @@ static int multiport_mt_check(const struct xt_mtchk_param *par)
|
||||
const struct ipt_ip *ip = par->entryinfo;
|
||||
const struct xt_multiport_v1 *multiinfo = par->matchinfo;
|
||||
|
||||
- return check(ip->proto, ip->invflags, multiinfo->flags,
|
||||
- multiinfo->count) ? 0 : -EINVAL;
|
||||
+ if (!check(ip->proto, ip->invflags, multiinfo->flags, multiinfo->count))
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ return multiport_valid_ranges(multiinfo) ? 0 : -EINVAL;
|
||||
}
|
||||
|
||||
static int multiport_mt6_check(const struct xt_mtchk_param *par)
|
||||
@@ -136,8 +160,10 @@ static int multiport_mt6_check(const struct xt_mtchk_param *par)
|
||||
const struct ip6t_ip6 *ip = par->entryinfo;
|
||||
const struct xt_multiport_v1 *multiinfo = par->matchinfo;
|
||||
|
||||
- return check(ip->proto, ip->invflags, multiinfo->flags,
|
||||
- multiinfo->count) ? 0 : -EINVAL;
|
||||
+ if (!check(ip->proto, ip->invflags, multiinfo->flags, multiinfo->count))
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ return multiport_valid_ranges(multiinfo) ? 0 : -EINVAL;
|
||||
}
|
||||
|
||||
static struct xt_match multiport_mt_reg[] __read_mostly = {
|
||||
@ -0,0 +1,80 @@
|
||||
From b79e7ff0af28845109dc3fcd964751f0391ceebf Mon Sep 17 00:00:00 2001
|
||||
From: Florian Westphal <fwestpha@redhat.com>
|
||||
Date: Wed, 13 May 2026 17:20:53 +0200
|
||||
Subject: [PATCH] netfilter: nft_ct: fix use-after-free in timeout object
|
||||
destroy
|
||||
|
||||
JIRA: https://redhat.atlassian.net/browse/RHEL-168848
|
||||
Upstream Status: commit f8dca15a1b19
|
||||
|
||||
commit f8dca15a1b190787bbd03285304b569631160eda
|
||||
Author: Tuan Do <tuan@calif.io>
|
||||
Date: Fri Apr 3 00:33:17 2026 -0700
|
||||
|
||||
netfilter: nft_ct: fix use-after-free in timeout object destroy
|
||||
|
||||
nft_ct_timeout_obj_destroy() frees the timeout object with kfree()
|
||||
immediately after nf_ct_untimeout(), without waiting for an RCU grace
|
||||
period. Concurrent packet processing on other CPUs may still hold
|
||||
RCU-protected references to the timeout object obtained via
|
||||
rcu_dereference() in nf_ct_timeout_data().
|
||||
|
||||
Add an rcu_head to struct nf_ct_timeout and use kfree_rcu() to defer
|
||||
freeing until after an RCU grace period, matching the approach already
|
||||
used in nfnetlink_cttimeout.c.
|
||||
|
||||
KASAN report:
|
||||
BUG: KASAN: slab-use-after-free in nf_conntrack_tcp_packet+0x1381/0x29d0
|
||||
Read of size 4 at addr ffff8881035fe19c by task exploit/80
|
||||
|
||||
Call Trace:
|
||||
nf_conntrack_tcp_packet+0x1381/0x29d0
|
||||
nf_conntrack_in+0x612/0x8b0
|
||||
nf_hook_slow+0x70/0x100
|
||||
__ip_local_out+0x1b2/0x210
|
||||
tcp_sendmsg_locked+0x722/0x1580
|
||||
__sys_sendto+0x2d8/0x320
|
||||
|
||||
Allocated by task 75:
|
||||
nft_ct_timeout_obj_init+0xf6/0x290
|
||||
nft_obj_init+0x107/0x1b0
|
||||
nf_tables_newobj+0x680/0x9c0
|
||||
nfnetlink_rcv_batch+0xc29/0xe00
|
||||
|
||||
Freed by task 26:
|
||||
nft_obj_destroy+0x3f/0xa0
|
||||
nf_tables_trans_destroy_work+0x51c/0x5c0
|
||||
process_one_work+0x2c4/0x5a0
|
||||
|
||||
Fixes: 7e0b2b57f01d ("netfilter: nft_ct: add ct timeout support")
|
||||
Cc: stable@vger.kernel.org
|
||||
Signed-off-by: Tuan Do <tuan@calif.io>
|
||||
Signed-off-by: Florian Westphal <fw@strlen.de>
|
||||
|
||||
Signed-off-by: Florian Westphal <fwestpha@redhat.com>
|
||||
|
||||
diff --git a/include/net/netfilter/nf_conntrack_timeout.h b/include/net/netfilter/nf_conntrack_timeout.h
|
||||
index 9fdaba9..3a66d4a 100644
|
||||
--- a/include/net/netfilter/nf_conntrack_timeout.h
|
||||
+++ b/include/net/netfilter/nf_conntrack_timeout.h
|
||||
@@ -14,6 +14,7 @@
|
||||
struct nf_ct_timeout {
|
||||
__u16 l3num;
|
||||
const struct nf_conntrack_l4proto *l4proto;
|
||||
+ struct rcu_head rcu;
|
||||
char data[];
|
||||
};
|
||||
|
||||
diff --git a/net/netfilter/nft_ct.c b/net/netfilter/nft_ct.c
|
||||
index 6f2ae7c..d0090d0 100644
|
||||
--- a/net/netfilter/nft_ct.c
|
||||
+++ b/net/netfilter/nft_ct.c
|
||||
@@ -1018,7 +1018,7 @@ static void nft_ct_timeout_obj_destroy(const struct nft_ctx *ctx,
|
||||
|
||||
nf_ct_untimeout(ctx->net, timeout);
|
||||
nf_ct_netns_put(ctx->net, ctx->family);
|
||||
- kfree(priv->timeout);
|
||||
+ kfree_rcu(priv->timeout, rcu);
|
||||
}
|
||||
|
||||
static int nft_ct_timeout_obj_dump(struct sk_buff *skb,
|
||||
47
1129-netfilter-nft-osf-restrict-it-to-ipv4.patch
Normal file
47
1129-netfilter-nft-osf-restrict-it-to-ipv4.patch
Normal file
@ -0,0 +1,47 @@
|
||||
From dc881297140fa0d80136180c1e2e1d66c5adac44 Mon Sep 17 00:00:00 2001
|
||||
From: Florian Westphal <fwestpha@redhat.com>
|
||||
Date: Wed, 13 May 2026 17:22:15 +0200
|
||||
Subject: [PATCH] netfilter: nft_osf: restrict it to ipv4
|
||||
|
||||
JIRA: https://redhat.atlassian.net/browse/RHEL-168848
|
||||
Upstream Status: commit b336fdbb7103
|
||||
|
||||
commit b336fdbb7103fb1484e1dcb6741151d4b5a41e35
|
||||
Author: Pablo Neira Ayuso <pablo@netfilter.org>
|
||||
Date: Tue Apr 14 13:06:38 2026 +0200
|
||||
|
||||
netfilter: nft_osf: restrict it to ipv4
|
||||
|
||||
This expression only supports for ipv4, restrict it.
|
||||
|
||||
Fixes: b96af92d6eaf ("netfilter: nf_tables: implement Passive OS fingerprint module in nft_osf")
|
||||
Acked-by: Florian Westphal <fw@strlen.de>
|
||||
Reviewed-by: Fernando Fernandez Mancera <fmancera@suse.de>
|
||||
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
|
||||
|
||||
Signed-off-by: Florian Westphal <fwestpha@redhat.com>
|
||||
|
||||
diff --git a/net/netfilter/nft_osf.c b/net/netfilter/nft_osf.c
|
||||
index 1c0b493..bdc2f6c 100644
|
||||
--- a/net/netfilter/nft_osf.c
|
||||
+++ b/net/netfilter/nft_osf.c
|
||||
@@ -28,6 +28,11 @@ static void nft_osf_eval(const struct nft_expr *expr, struct nft_regs *regs,
|
||||
struct nf_osf_data data;
|
||||
struct tcphdr _tcph;
|
||||
|
||||
+ if (nft_pf(pkt) != NFPROTO_IPV4) {
|
||||
+ regs->verdict.code = NFT_BREAK;
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
if (pkt->tprot != IPPROTO_TCP) {
|
||||
regs->verdict.code = NFT_BREAK;
|
||||
return;
|
||||
@@ -114,7 +119,6 @@ static int nft_osf_validate(const struct nft_ctx *ctx,
|
||||
|
||||
switch (ctx->family) {
|
||||
case NFPROTO_IPV4:
|
||||
- case NFPROTO_IPV6:
|
||||
case NFPROTO_INET:
|
||||
hooks = (1 << NF_INET_LOCAL_IN) |
|
||||
(1 << NF_INET_PRE_ROUTING) |
|
||||
@ -0,0 +1,84 @@
|
||||
From 05577640875b9759b4511a3cbe9cbcd2f92e27f3 Mon Sep 17 00:00:00 2001
|
||||
From: Florian Westphal <fwestpha@redhat.com>
|
||||
Date: Wed, 13 May 2026 17:16:11 +0200
|
||||
Subject: [PATCH] nfnetlink_osf: validate individual option lengths in
|
||||
fingerprints
|
||||
|
||||
JIRA: https://redhat.atlassian.net/browse/RHEL-168848
|
||||
Upstream Status: commit dbdfaae96096
|
||||
|
||||
commit dbdfaae9609629a9569362e3b8f33d0a20fd783c
|
||||
Author: Weiming Shi <bestswngs@gmail.com>
|
||||
Date: Thu Mar 19 15:32:44 2026 +0800
|
||||
|
||||
nfnetlink_osf: validate individual option lengths in fingerprints
|
||||
|
||||
nfnl_osf_add_callback() validates opt_num bounds and string
|
||||
NUL-termination but does not check individual option length fields.
|
||||
A zero-length option causes nf_osf_match_one() to enter the option
|
||||
matching loop even when foptsize sums to zero, which matches packets
|
||||
with no TCP options where ctx->optp is NULL:
|
||||
|
||||
Oops: general protection fault
|
||||
KASAN: null-ptr-deref in range [0x0000000000000000-0x0000000000000007]
|
||||
RIP: 0010:nf_osf_match_one (net/netfilter/nfnetlink_osf.c:98)
|
||||
Call Trace:
|
||||
nf_osf_match (net/netfilter/nfnetlink_osf.c:227)
|
||||
xt_osf_match_packet (net/netfilter/xt_osf.c:32)
|
||||
ipt_do_table (net/ipv4/netfilter/ip_tables.c:293)
|
||||
nf_hook_slow (net/netfilter/core.c:623)
|
||||
ip_local_deliver (net/ipv4/ip_input.c:262)
|
||||
ip_rcv (net/ipv4/ip_input.c:573)
|
||||
|
||||
Additionally, an MSS option (kind=2) with length < 4 causes
|
||||
out-of-bounds reads when nf_osf_match_one() unconditionally accesses
|
||||
optp[2] and optp[3] for MSS value extraction. While RFC 9293
|
||||
section 3.2 specifies that the MSS option is always exactly 4
|
||||
bytes (Kind=2, Length=4), the check uses "< 4" rather than
|
||||
"!= 4" because lengths greater than 4 do not cause memory
|
||||
safety issues -- the buffer is guaranteed to be at least
|
||||
foptsize bytes by the ctx->optsize == foptsize check.
|
||||
|
||||
Reject fingerprints where any option has zero length, or where an MSS
|
||||
option has length less than 4, at add time rather than trusting these
|
||||
values in the packet matching hot path.
|
||||
|
||||
Fixes: 11eeef41d5f6 ("netfilter: passive OS fingerprint xtables match")
|
||||
Reported-by: Xiang Mei <xmei5@asu.edu>
|
||||
Signed-off-by: Weiming Shi <bestswngs@gmail.com>
|
||||
Signed-off-by: Florian Westphal <fw@strlen.de>
|
||||
|
||||
Signed-off-by: Florian Westphal <fwestpha@redhat.com>
|
||||
|
||||
diff --git a/net/netfilter/nfnetlink_osf.c b/net/netfilter/nfnetlink_osf.c
|
||||
index c0fc431..9fc9544 100644
|
||||
--- a/net/netfilter/nfnetlink_osf.c
|
||||
+++ b/net/netfilter/nfnetlink_osf.c
|
||||
@@ -302,7 +302,9 @@ static int nfnl_osf_add_callback(struct sk_buff *skb,
|
||||
{
|
||||
struct nf_osf_user_finger *f;
|
||||
struct nf_osf_finger *kf = NULL, *sf;
|
||||
+ unsigned int tot_opt_len = 0;
|
||||
int err = 0;
|
||||
+ int i;
|
||||
|
||||
if (!capable(CAP_NET_ADMIN))
|
||||
return -EPERM;
|
||||
@@ -318,6 +320,17 @@ static int nfnl_osf_add_callback(struct sk_buff *skb,
|
||||
if (f->opt_num > ARRAY_SIZE(f->opt))
|
||||
return -EINVAL;
|
||||
|
||||
+ for (i = 0; i < f->opt_num; i++) {
|
||||
+ if (!f->opt[i].length || f->opt[i].length > MAX_IPOPTLEN)
|
||||
+ return -EINVAL;
|
||||
+ if (f->opt[i].kind == OSFOPT_MSS && f->opt[i].length < 4)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ tot_opt_len += f->opt[i].length;
|
||||
+ if (tot_opt_len > MAX_IPOPTLEN)
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
if (!memchr(f->genre, 0, MAXGENRELEN) ||
|
||||
!memchr(f->subtype, 0, MAXGENRELEN) ||
|
||||
!memchr(f->version, 0, MAXGENRELEN))
|
||||
@ -0,0 +1,68 @@
|
||||
From aea9be3048d15f93e770970a6206c38bdaa11cb2 Mon Sep 17 00:00:00 2001
|
||||
From: Florian Westphal <fwestpha@redhat.com>
|
||||
Date: Wed, 13 May 2026 17:22:16 +0200
|
||||
Subject: [PATCH] netfilter: nfnetlink_osf: fix divide-by-zero in
|
||||
OSF_WSS_MODULO
|
||||
|
||||
JIRA: https://redhat.atlassian.net/browse/RHEL-168848
|
||||
Upstream Status: commit 2195574dc6d9
|
||||
|
||||
commit 2195574dc6d9017d32ac346987e12659f931d932
|
||||
Author: Xiang Mei <xmei5@asu.edu>
|
||||
Date: Tue Apr 14 15:14:01 2026 -0700
|
||||
|
||||
netfilter: nfnetlink_osf: fix divide-by-zero in OSF_WSS_MODULO
|
||||
|
||||
nf_osf_match_one() computes ctx->window % f->wss.val in the
|
||||
OSF_WSS_MODULO branch with no guard for f->wss.val == 0. A
|
||||
CAP_NET_ADMIN user can add such a fingerprint via nfnetlink; a
|
||||
subsequent matching TCP SYN divides by zero and panics the kernel.
|
||||
|
||||
Reject the bogus fingerprint in nfnl_osf_add_callback() above the
|
||||
per-option for-loop. f->wss is per-fingerprint, not per-option, so
|
||||
the check must run regardless of f->opt_num (including 0). Also
|
||||
reject wss.wc >= OSF_WSS_MAX; nf_osf_match_one() already treats that
|
||||
as "should not happen".
|
||||
|
||||
Crash:
|
||||
Oops: divide error: 0000 [#1] SMP KASAN NOPTI
|
||||
RIP: 0010:nf_osf_match_one (net/netfilter/nfnetlink_osf.c:98)
|
||||
Call Trace:
|
||||
<IRQ>
|
||||
nf_osf_match (net/netfilter/nfnetlink_osf.c:220)
|
||||
xt_osf_match_packet (net/netfilter/xt_osf.c:32)
|
||||
ipt_do_table (net/ipv4/netfilter/ip_tables.c:348)
|
||||
nf_hook_slow (net/netfilter/core.c:622)
|
||||
ip_local_deliver (net/ipv4/ip_input.c:265)
|
||||
ip_rcv (include/linux/skbuff.h:1162)
|
||||
__netif_receive_skb_one_core (net/core/dev.c:6181)
|
||||
process_backlog (net/core/dev.c:6642)
|
||||
__napi_poll (net/core/dev.c:7710)
|
||||
net_rx_action (net/core/dev.c:7945)
|
||||
handle_softirqs (kernel/softirq.c:622)
|
||||
|
||||
Fixes: 11eeef41d5f6 ("netfilter: passive OS fingerprint xtables match")
|
||||
Reported-by: Weiming Shi <bestswngs@gmail.com>
|
||||
Suggested-by: Florian Westphal <fw@strlen.de>
|
||||
Suggested-by: Pablo Neira Ayuso <pablo@netfilter.org>
|
||||
Signed-off-by: Xiang Mei <xmei5@asu.edu>
|
||||
Reviewed-by: Fernando Fernandez Mancera <fmancera@suse.de>
|
||||
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
|
||||
|
||||
Signed-off-by: Florian Westphal <fwestpha@redhat.com>
|
||||
|
||||
diff --git a/net/netfilter/nfnetlink_osf.c b/net/netfilter/nfnetlink_osf.c
|
||||
index 9fc9544..2305c7d 100644
|
||||
--- a/net/netfilter/nfnetlink_osf.c
|
||||
+++ b/net/netfilter/nfnetlink_osf.c
|
||||
@@ -320,6 +320,10 @@ static int nfnl_osf_add_callback(struct sk_buff *skb,
|
||||
if (f->opt_num > ARRAY_SIZE(f->opt))
|
||||
return -EINVAL;
|
||||
|
||||
+ if (f->wss.wc >= OSF_WSS_MAX ||
|
||||
+ (f->wss.wc == OSF_WSS_MODULO && f->wss.val == 0))
|
||||
+ return -EINVAL;
|
||||
+
|
||||
for (i = 0; i < f->opt_num; i++) {
|
||||
if (!f->opt[i].length || f->opt[i].length > MAX_IPOPTLEN)
|
||||
return -EINVAL;
|
||||
181
1132-netfilter-conntrack-remove-sprintf-usage.patch
Normal file
181
1132-netfilter-conntrack-remove-sprintf-usage.patch
Normal file
@ -0,0 +1,181 @@
|
||||
From 1fa34c650b5f6cf04b877fa6cb5337ee834c826d Mon Sep 17 00:00:00 2001
|
||||
From: Florian Westphal <fwestpha@redhat.com>
|
||||
Date: Wed, 13 May 2026 17:22:16 +0200
|
||||
Subject: [PATCH] netfilter: conntrack: remove sprintf usage
|
||||
|
||||
JIRA: https://redhat.atlassian.net/browse/RHEL-168848
|
||||
Upstream Status: commit 6e7066bdb481
|
||||
|
||||
commit 6e7066bdb481a87fe88c4fa563e348c03b2d373d
|
||||
Author: Florian Westphal <fw@strlen.de>
|
||||
Date: Tue Apr 14 19:13:46 2026 +0200
|
||||
|
||||
netfilter: conntrack: remove sprintf usage
|
||||
|
||||
Replace it with scnprintf, the buffer sizes are expected to be large enough
|
||||
to hold the result, no need for snprintf+overflow check.
|
||||
|
||||
Increase buffer size in mangle_content_len() while at it.
|
||||
|
||||
BUG: KASAN: stack-out-of-bounds in vsnprintf+0xea5/0x1270
|
||||
Write of size 1 at addr [..]
|
||||
vsnprintf+0xea5/0x1270
|
||||
sprintf+0xb1/0xe0
|
||||
mangle_content_len+0x1ac/0x280
|
||||
nf_nat_sdp_session+0x1cc/0x240
|
||||
process_sdp+0x8f8/0xb80
|
||||
process_invite_request+0x108/0x2b0
|
||||
process_sip_msg+0x5da/0xf50
|
||||
sip_help_tcp+0x45e/0x780
|
||||
nf_confirm+0x34d/0x990
|
||||
[..]
|
||||
|
||||
Fixes: 9fafcd7b2032 ("[NETFILTER]: nf_conntrack/nf_nat: add SIP helper port")
|
||||
Reported-by: Yiming Qian <yimingqian591@gmail.com>
|
||||
Signed-off-by: Florian Westphal <fw@strlen.de>
|
||||
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
|
||||
|
||||
Signed-off-by: Florian Westphal <fwestpha@redhat.com>
|
||||
|
||||
diff --git a/net/netfilter/nf_nat_amanda.c b/net/netfilter/nf_nat_amanda.c
|
||||
index 98deef6..8f10549 100644
|
||||
--- a/net/netfilter/nf_nat_amanda.c
|
||||
+++ b/net/netfilter/nf_nat_amanda.c
|
||||
@@ -50,7 +50,7 @@ static unsigned int help(struct sk_buff *skb,
|
||||
return NF_DROP;
|
||||
}
|
||||
|
||||
- sprintf(buffer, "%u", port);
|
||||
+ snprintf(buffer, sizeof(buffer), "%u", port);
|
||||
if (!nf_nat_mangle_udp_packet(skb, exp->master, ctinfo,
|
||||
protoff, matchoff, matchlen,
|
||||
buffer, strlen(buffer))) {
|
||||
diff --git a/net/netfilter/nf_nat_sip.c b/net/netfilter/nf_nat_sip.c
|
||||
index cf4aeb2..c845b6d 100644
|
||||
--- a/net/netfilter/nf_nat_sip.c
|
||||
+++ b/net/netfilter/nf_nat_sip.c
|
||||
@@ -68,25 +68,27 @@ static unsigned int mangle_packet(struct sk_buff *skb, unsigned int protoff,
|
||||
}
|
||||
|
||||
static int sip_sprintf_addr(const struct nf_conn *ct, char *buffer,
|
||||
+ size_t size,
|
||||
const union nf_inet_addr *addr, bool delim)
|
||||
{
|
||||
if (nf_ct_l3num(ct) == NFPROTO_IPV4)
|
||||
- return sprintf(buffer, "%pI4", &addr->ip);
|
||||
+ return scnprintf(buffer, size, "%pI4", &addr->ip);
|
||||
else {
|
||||
if (delim)
|
||||
- return sprintf(buffer, "[%pI6c]", &addr->ip6);
|
||||
+ return scnprintf(buffer, size, "[%pI6c]", &addr->ip6);
|
||||
else
|
||||
- return sprintf(buffer, "%pI6c", &addr->ip6);
|
||||
+ return scnprintf(buffer, size, "%pI6c", &addr->ip6);
|
||||
}
|
||||
}
|
||||
|
||||
static int sip_sprintf_addr_port(const struct nf_conn *ct, char *buffer,
|
||||
+ size_t size,
|
||||
const union nf_inet_addr *addr, u16 port)
|
||||
{
|
||||
if (nf_ct_l3num(ct) == NFPROTO_IPV4)
|
||||
- return sprintf(buffer, "%pI4:%u", &addr->ip, port);
|
||||
+ return scnprintf(buffer, size, "%pI4:%u", &addr->ip, port);
|
||||
else
|
||||
- return sprintf(buffer, "[%pI6c]:%u", &addr->ip6, port);
|
||||
+ return scnprintf(buffer, size, "[%pI6c]:%u", &addr->ip6, port);
|
||||
}
|
||||
|
||||
static int map_addr(struct sk_buff *skb, unsigned int protoff,
|
||||
@@ -119,7 +121,7 @@ static int map_addr(struct sk_buff *skb, unsigned int protoff,
|
||||
if (nf_inet_addr_cmp(&newaddr, addr) && newport == port)
|
||||
return 1;
|
||||
|
||||
- buflen = sip_sprintf_addr_port(ct, buffer, &newaddr, ntohs(newport));
|
||||
+ buflen = sip_sprintf_addr_port(ct, buffer, sizeof(buffer), &newaddr, ntohs(newport));
|
||||
return mangle_packet(skb, protoff, dataoff, dptr, datalen,
|
||||
matchoff, matchlen, buffer, buflen);
|
||||
}
|
||||
@@ -212,7 +214,7 @@ static unsigned int nf_nat_sip(struct sk_buff *skb, unsigned int protoff,
|
||||
&addr, true) > 0 &&
|
||||
nf_inet_addr_cmp(&addr, &ct->tuplehash[dir].tuple.src.u3) &&
|
||||
!nf_inet_addr_cmp(&addr, &ct->tuplehash[!dir].tuple.dst.u3)) {
|
||||
- buflen = sip_sprintf_addr(ct, buffer,
|
||||
+ buflen = sip_sprintf_addr(ct, buffer, sizeof(buffer),
|
||||
&ct->tuplehash[!dir].tuple.dst.u3,
|
||||
true);
|
||||
if (!mangle_packet(skb, protoff, dataoff, dptr, datalen,
|
||||
@@ -229,7 +231,7 @@ static unsigned int nf_nat_sip(struct sk_buff *skb, unsigned int protoff,
|
||||
&addr, false) > 0 &&
|
||||
nf_inet_addr_cmp(&addr, &ct->tuplehash[dir].tuple.dst.u3) &&
|
||||
!nf_inet_addr_cmp(&addr, &ct->tuplehash[!dir].tuple.src.u3)) {
|
||||
- buflen = sip_sprintf_addr(ct, buffer,
|
||||
+ buflen = sip_sprintf_addr(ct, buffer, sizeof(buffer),
|
||||
&ct->tuplehash[!dir].tuple.src.u3,
|
||||
false);
|
||||
if (!mangle_packet(skb, protoff, dataoff, dptr, datalen,
|
||||
@@ -247,7 +249,7 @@ static unsigned int nf_nat_sip(struct sk_buff *skb, unsigned int protoff,
|
||||
htons(n) == ct->tuplehash[dir].tuple.dst.u.udp.port &&
|
||||
htons(n) != ct->tuplehash[!dir].tuple.src.u.udp.port) {
|
||||
__be16 p = ct->tuplehash[!dir].tuple.src.u.udp.port;
|
||||
- buflen = sprintf(buffer, "%u", ntohs(p));
|
||||
+ buflen = scnprintf(buffer, sizeof(buffer), "%u", ntohs(p));
|
||||
if (!mangle_packet(skb, protoff, dataoff, dptr, datalen,
|
||||
poff, plen, buffer, buflen)) {
|
||||
nf_ct_helper_log(skb, ct, "cannot mangle rport");
|
||||
@@ -418,7 +420,8 @@ static unsigned int nf_nat_sip_expect(struct sk_buff *skb, unsigned int protoff,
|
||||
|
||||
if (!nf_inet_addr_cmp(&exp->tuple.dst.u3, &exp->saved_addr) ||
|
||||
exp->tuple.dst.u.udp.port != exp->saved_proto.udp.port) {
|
||||
- buflen = sip_sprintf_addr_port(ct, buffer, &newaddr, port);
|
||||
+ buflen = sip_sprintf_addr_port(ct, buffer, sizeof(buffer),
|
||||
+ &newaddr, port);
|
||||
if (!mangle_packet(skb, protoff, dataoff, dptr, datalen,
|
||||
matchoff, matchlen, buffer, buflen)) {
|
||||
nf_ct_helper_log(skb, ct, "cannot mangle packet");
|
||||
@@ -438,8 +441,8 @@ static int mangle_content_len(struct sk_buff *skb, unsigned int protoff,
|
||||
{
|
||||
enum ip_conntrack_info ctinfo;
|
||||
struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
|
||||
+ char buffer[sizeof("4294967295")];
|
||||
unsigned int matchoff, matchlen;
|
||||
- char buffer[sizeof("65536")];
|
||||
int buflen, c_len;
|
||||
|
||||
/* Get actual SDP length */
|
||||
@@ -454,7 +457,7 @@ static int mangle_content_len(struct sk_buff *skb, unsigned int protoff,
|
||||
&matchoff, &matchlen) <= 0)
|
||||
return 0;
|
||||
|
||||
- buflen = sprintf(buffer, "%u", c_len);
|
||||
+ buflen = scnprintf(buffer, sizeof(buffer), "%u", c_len);
|
||||
return mangle_packet(skb, protoff, dataoff, dptr, datalen,
|
||||
matchoff, matchlen, buffer, buflen);
|
||||
}
|
||||
@@ -491,7 +494,7 @@ static unsigned int nf_nat_sdp_addr(struct sk_buff *skb, unsigned int protoff,
|
||||
char buffer[INET6_ADDRSTRLEN];
|
||||
unsigned int buflen;
|
||||
|
||||
- buflen = sip_sprintf_addr(ct, buffer, addr, false);
|
||||
+ buflen = sip_sprintf_addr(ct, buffer, sizeof(buffer), addr, false);
|
||||
if (mangle_sdp_packet(skb, protoff, dataoff, dptr, datalen,
|
||||
sdpoff, type, term, buffer, buflen))
|
||||
return 0;
|
||||
@@ -509,7 +512,7 @@ static unsigned int nf_nat_sdp_port(struct sk_buff *skb, unsigned int protoff,
|
||||
char buffer[sizeof("nnnnn")];
|
||||
unsigned int buflen;
|
||||
|
||||
- buflen = sprintf(buffer, "%u", port);
|
||||
+ buflen = scnprintf(buffer, sizeof(buffer), "%u", port);
|
||||
if (!mangle_packet(skb, protoff, dataoff, dptr, datalen,
|
||||
matchoff, matchlen, buffer, buflen))
|
||||
return 0;
|
||||
@@ -529,7 +532,7 @@ static unsigned int nf_nat_sdp_session(struct sk_buff *skb, unsigned int protoff
|
||||
unsigned int buflen;
|
||||
|
||||
/* Mangle session description owner and contact addresses */
|
||||
- buflen = sip_sprintf_addr(ct, buffer, addr, false);
|
||||
+ buflen = sip_sprintf_addr(ct, buffer, sizeof(buffer), addr, false);
|
||||
if (mangle_sdp_packet(skb, protoff, dataoff, dptr, datalen, sdpoff,
|
||||
SDP_HDR_OWNER, SDP_HDR_MEDIA, buffer, buflen))
|
||||
return 0;
|
||||
112
1133-netfilter-nat-use-kfree-rcu-to-release-ops.patch
Normal file
112
1133-netfilter-nat-use-kfree-rcu-to-release-ops.patch
Normal file
@ -0,0 +1,112 @@
|
||||
From 827d950e20067e03f0bdc4dda3c48d1162e1077c Mon Sep 17 00:00:00 2001
|
||||
From: Florian Westphal <fwestpha@redhat.com>
|
||||
Date: Wed, 13 May 2026 17:22:17 +0200
|
||||
Subject: [PATCH] netfilter: nat: use kfree_rcu to release ops
|
||||
|
||||
JIRA: https://redhat.atlassian.net/browse/RHEL-168848
|
||||
Upstream Status: commit 6eda0d771f94
|
||||
|
||||
commit 6eda0d771f94267f73f57c94630aa47e90957915
|
||||
Author: Pablo Neira Ayuso <pablo@netfilter.org>
|
||||
Date: Wed Apr 15 17:29:45 2026 +0200
|
||||
|
||||
netfilter: nat: use kfree_rcu to release ops
|
||||
|
||||
Florian Westphal says:
|
||||
|
||||
"Historically this is not an issue, even for normal base hooks: the data
|
||||
path doesn't use the original nf_hook_ops that are used to register the
|
||||
callbacks.
|
||||
|
||||
However, in v5.14 I added the ability to dump the active netfilter
|
||||
hooks from userspace.
|
||||
|
||||
This code will peek back into the nf_hook_ops that are available
|
||||
at the tail of the pointer-array blob used by the datapath.
|
||||
|
||||
The nat hooks are special, because they are called indirectly from
|
||||
the central nat dispatcher hook. They are currently invisible to
|
||||
the nfnl hook dump subsystem though.
|
||||
|
||||
But once that changes the nat ops structures have to be deferred too."
|
||||
|
||||
Update nf_nat_register_fn() to deal with partial exposition of the hooks
|
||||
from error path which can be also an issue for nfnetlink_hook.
|
||||
|
||||
Fixes: e2cf17d3774c ("netfilter: add new hook nfnl subsystem")
|
||||
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
|
||||
|
||||
Signed-off-by: Florian Westphal <fwestpha@redhat.com>
|
||||
|
||||
diff --git a/net/ipv4/netfilter/iptable_nat.c b/net/ipv4/netfilter/iptable_nat.c
|
||||
index a5db7c6..625a1ca 100644
|
||||
--- a/net/ipv4/netfilter/iptable_nat.c
|
||||
+++ b/net/ipv4/netfilter/iptable_nat.c
|
||||
@@ -79,7 +79,7 @@ static int ipt_nat_register_lookups(struct net *net)
|
||||
while (i)
|
||||
nf_nat_ipv4_unregister_fn(net, &ops[--i]);
|
||||
|
||||
- kfree(ops);
|
||||
+ kfree_rcu(ops, rcu);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
@@ -100,7 +100,7 @@ static void ipt_nat_unregister_lookups(struct net *net)
|
||||
for (i = 0; i < ARRAY_SIZE(nf_nat_ipv4_ops); i++)
|
||||
nf_nat_ipv4_unregister_fn(net, &ops[i]);
|
||||
|
||||
- kfree(ops);
|
||||
+ kfree_rcu(ops, rcu);
|
||||
}
|
||||
|
||||
static int iptable_nat_table_init(struct net *net)
|
||||
diff --git a/net/ipv6/netfilter/ip6table_nat.c b/net/ipv6/netfilter/ip6table_nat.c
|
||||
index e119d4f..5be7232 100644
|
||||
--- a/net/ipv6/netfilter/ip6table_nat.c
|
||||
+++ b/net/ipv6/netfilter/ip6table_nat.c
|
||||
@@ -81,7 +81,7 @@ static int ip6t_nat_register_lookups(struct net *net)
|
||||
while (i)
|
||||
nf_nat_ipv6_unregister_fn(net, &ops[--i]);
|
||||
|
||||
- kfree(ops);
|
||||
+ kfree_rcu(ops, rcu);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
@@ -102,7 +102,7 @@ static void ip6t_nat_unregister_lookups(struct net *net)
|
||||
for (i = 0; i < ARRAY_SIZE(nf_nat_ipv6_ops); i++)
|
||||
nf_nat_ipv6_unregister_fn(net, &ops[i]);
|
||||
|
||||
- kfree(ops);
|
||||
+ kfree_rcu(ops, rcu);
|
||||
}
|
||||
|
||||
static int ip6table_nat_table_init(struct net *net)
|
||||
diff --git a/net/netfilter/nf_nat_core.c b/net/netfilter/nf_nat_core.c
|
||||
index 746acd1..d380e1a 100644
|
||||
--- a/net/netfilter/nf_nat_core.c
|
||||
+++ b/net/netfilter/nf_nat_core.c
|
||||
@@ -1236,9 +1236,11 @@ int nf_nat_register_fn(struct net *net, u8 pf, const struct nf_hook_ops *ops,
|
||||
ret = nf_register_net_hooks(net, nat_ops, ops_count);
|
||||
if (ret < 0) {
|
||||
mutex_unlock(&nf_nat_proto_mutex);
|
||||
- for (i = 0; i < ops_count; i++)
|
||||
- kfree(nat_ops[i].priv);
|
||||
- kfree(nat_ops);
|
||||
+ for (i = 0; i < ops_count; i++) {
|
||||
+ priv = nat_ops[i].priv;
|
||||
+ kfree_rcu(priv, rcu_head);
|
||||
+ }
|
||||
+ kfree_rcu(nat_ops, rcu);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -1302,7 +1304,7 @@ void nf_nat_unregister_fn(struct net *net, u8 pf, const struct nf_hook_ops *ops,
|
||||
}
|
||||
|
||||
nat_proto_net->nat_hook_ops = NULL;
|
||||
- kfree(nat_ops);
|
||||
+ kfree_rcu(nat_ops, rcu);
|
||||
}
|
||||
unlock:
|
||||
mutex_unlock(&nf_nat_proto_mutex);
|
||||
@ -0,0 +1,101 @@
|
||||
From 08043ad607850a39c83dec4e06a59532552a0c96 Mon Sep 17 00:00:00 2001
|
||||
From: Florian Westphal <fwestpha@redhat.com>
|
||||
Date: Wed, 13 May 2026 17:22:18 +0200
|
||||
Subject: [PATCH] netfilter: nfnetlink_osf: fix out-of-bounds read on option
|
||||
matching
|
||||
|
||||
JIRA: https://redhat.atlassian.net/browse/RHEL-168848
|
||||
Upstream Status: commit f5ca450087c3
|
||||
|
||||
commit f5ca450087c3baf3651055e7a6de92600f827af3
|
||||
Author: Fernando Fernandez Mancera <fmancera@suse.de>
|
||||
Date: Fri Apr 17 18:20:56 2026 +0200
|
||||
|
||||
netfilter: nfnetlink_osf: fix out-of-bounds read on option matching
|
||||
|
||||
In nf_osf_match(), the nf_osf_hdr_ctx structure is initialized once
|
||||
and passed by reference to nf_osf_match_one() for each fingerprint
|
||||
checked. During TCP option parsing, nf_osf_match_one() advances the
|
||||
shared ctx->optp pointer.
|
||||
|
||||
If a fingerprint perfectly matches, the function returns early without
|
||||
restoring ctx->optp to its initial state. If the user has configured
|
||||
NF_OSF_LOGLEVEL_ALL, the loop continues to the next fingerprint.
|
||||
However, because ctx->optp was not restored, the next call to
|
||||
nf_osf_match_one() starts parsing from the end of the options buffer.
|
||||
This causes subsequent matches to read garbage data and fail
|
||||
immediately, making it impossible to log more than one match or logging
|
||||
incorrect matches.
|
||||
|
||||
Instead of using a shared ctx->optp pointer, pass the context as a
|
||||
constant pointer and use a local pointer (optp) for TCP option
|
||||
traversal. This makes nf_osf_match_one() strictly stateless from the
|
||||
caller's perspective, ensuring every fingerprint check starts at the
|
||||
correct option offset.
|
||||
|
||||
Fixes: 1a6a0951fc00 ("netfilter: nfnetlink_osf: add missing fmatch check")
|
||||
Suggested-by: Florian Westphal <fw@strlen.de>
|
||||
Signed-off-by: Fernando Fernandez Mancera <fmancera@suse.de>
|
||||
Reviewed-by: Pablo Neira Ayuso <pablo@netfilter.org>
|
||||
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
|
||||
|
||||
Signed-off-by: Florian Westphal <fwestpha@redhat.com>
|
||||
|
||||
diff --git a/net/netfilter/nfnetlink_osf.c b/net/netfilter/nfnetlink_osf.c
|
||||
index 2305c7d..832a973 100644
|
||||
--- a/net/netfilter/nfnetlink_osf.c
|
||||
+++ b/net/netfilter/nfnetlink_osf.c
|
||||
@@ -64,9 +64,9 @@ struct nf_osf_hdr_ctx {
|
||||
static bool nf_osf_match_one(const struct sk_buff *skb,
|
||||
const struct nf_osf_user_finger *f,
|
||||
int ttl_check,
|
||||
- struct nf_osf_hdr_ctx *ctx)
|
||||
+ const struct nf_osf_hdr_ctx *ctx)
|
||||
{
|
||||
- const __u8 *optpinit = ctx->optp;
|
||||
+ const __u8 *optp = ctx->optp;
|
||||
unsigned int check_WSS = 0;
|
||||
int fmatch = FMATCH_WRONG;
|
||||
int foptsize, optnum;
|
||||
@@ -95,17 +95,17 @@ static bool nf_osf_match_one(const struct sk_buff *skb,
|
||||
check_WSS = f->wss.wc;
|
||||
|
||||
for (optnum = 0; optnum < f->opt_num; ++optnum) {
|
||||
- if (f->opt[optnum].kind == *ctx->optp) {
|
||||
+ if (f->opt[optnum].kind == *optp) {
|
||||
__u32 len = f->opt[optnum].length;
|
||||
- const __u8 *optend = ctx->optp + len;
|
||||
+ const __u8 *optend = optp + len;
|
||||
|
||||
fmatch = FMATCH_OK;
|
||||
|
||||
- switch (*ctx->optp) {
|
||||
+ switch (*optp) {
|
||||
case OSFOPT_MSS:
|
||||
- mss = ctx->optp[3];
|
||||
+ mss = optp[3];
|
||||
mss <<= 8;
|
||||
- mss |= ctx->optp[2];
|
||||
+ mss |= optp[2];
|
||||
|
||||
mss = ntohs((__force __be16)mss);
|
||||
break;
|
||||
@@ -113,7 +113,7 @@ static bool nf_osf_match_one(const struct sk_buff *skb,
|
||||
break;
|
||||
}
|
||||
|
||||
- ctx->optp = optend;
|
||||
+ optp = optend;
|
||||
} else
|
||||
fmatch = FMATCH_OPT_WRONG;
|
||||
|
||||
@@ -156,9 +156,6 @@ static bool nf_osf_match_one(const struct sk_buff *skb,
|
||||
}
|
||||
}
|
||||
|
||||
- if (fmatch != FMATCH_OK)
|
||||
- ctx->optp = optpinit;
|
||||
-
|
||||
return fmatch == FMATCH_OK;
|
||||
}
|
||||
|
||||
@ -0,0 +1,75 @@
|
||||
From 7fce69f7fa9f3555eec125a06c235b5664b059c8 Mon Sep 17 00:00:00 2001
|
||||
From: Florian Westphal <fwestpha@redhat.com>
|
||||
Date: Wed, 13 May 2026 17:22:18 +0200
|
||||
Subject: [PATCH] netfilter: nfnetlink_osf: fix potential NULL dereference in
|
||||
ttl check
|
||||
|
||||
JIRA: https://redhat.atlassian.net/browse/RHEL-168848
|
||||
Upstream Status: commit 711987ba281f
|
||||
|
||||
commit 711987ba281fd806322a7cd244e98e2a81903114
|
||||
Author: Fernando Fernandez Mancera <fmancera@suse.de>
|
||||
Date: Fri Apr 17 18:20:57 2026 +0200
|
||||
|
||||
netfilter: nfnetlink_osf: fix potential NULL dereference in ttl check
|
||||
|
||||
The nf_osf_ttl() function accessed skb->dev to perform a local interface
|
||||
address lookup without verifying that the device pointer was valid.
|
||||
|
||||
Additionally, the implementation utilized an in_dev_for_each_ifa_rcu
|
||||
loop to match the packet source address against local interface
|
||||
addresses. It assumed that packets from the same subnet should not see a
|
||||
decrement on the initial TTL. A packet might appear it is from the same
|
||||
subnet but it actually isn't especially in modern environments with
|
||||
containers and virtual switching.
|
||||
|
||||
Remove the device dereference and interface loop. Replace the logic with
|
||||
a switch statement that evaluates the TTL according to the ttl_check.
|
||||
|
||||
Fixes: 11eeef41d5f6 ("netfilter: passive OS fingerprint xtables match")
|
||||
Reported-by: Kito Xu (veritas501) <hxzene@gmail.com>
|
||||
Closes: https://lore.kernel.org/netfilter-devel/20260414074556.2512750-1-hxzene@gmail.com/
|
||||
Signed-off-by: Fernando Fernandez Mancera <fmancera@suse.de>
|
||||
Reviewed-by: Pablo Neira Ayuso <pablo@netfilter.org>
|
||||
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
|
||||
|
||||
Signed-off-by: Florian Westphal <fwestpha@redhat.com>
|
||||
|
||||
diff --git a/net/netfilter/nfnetlink_osf.c b/net/netfilter/nfnetlink_osf.c
|
||||
index 832a973..c89efb9 100644
|
||||
--- a/net/netfilter/nfnetlink_osf.c
|
||||
+++ b/net/netfilter/nfnetlink_osf.c
|
||||
@@ -31,26 +31,18 @@ EXPORT_SYMBOL_GPL(nf_osf_fingers);
|
||||
static inline int nf_osf_ttl(const struct sk_buff *skb,
|
||||
int ttl_check, unsigned char f_ttl)
|
||||
{
|
||||
- struct in_device *in_dev = __in_dev_get_rcu(skb->dev);
|
||||
const struct iphdr *ip = ip_hdr(skb);
|
||||
- const struct in_ifaddr *ifa;
|
||||
- int ret = 0;
|
||||
|
||||
- if (ttl_check == NF_OSF_TTL_TRUE)
|
||||
+ switch (ttl_check) {
|
||||
+ case NF_OSF_TTL_TRUE:
|
||||
return ip->ttl == f_ttl;
|
||||
- if (ttl_check == NF_OSF_TTL_NOCHECK)
|
||||
- return 1;
|
||||
- else if (ip->ttl <= f_ttl)
|
||||
+ break;
|
||||
+ case NF_OSF_TTL_NOCHECK:
|
||||
return 1;
|
||||
-
|
||||
- in_dev_for_each_ifa_rcu(ifa, in_dev) {
|
||||
- if (inet_ifa_match(ip->saddr, ifa)) {
|
||||
- ret = (ip->ttl == f_ttl);
|
||||
- break;
|
||||
- }
|
||||
+ case NF_OSF_TTL_LESS:
|
||||
+ default:
|
||||
+ return ip->ttl <= f_ttl;
|
||||
}
|
||||
-
|
||||
- return ret;
|
||||
}
|
||||
|
||||
struct nf_osf_hdr_ctx {
|
||||
@ -0,0 +1,137 @@
|
||||
From bedd721414832ff460290adf488d09deff50db05 Mon Sep 17 00:00:00 2001
|
||||
From: Florian Westphal <fwestpha@redhat.com>
|
||||
Date: Wed, 13 May 2026 17:22:19 +0200
|
||||
Subject: [PATCH] netfilter: nf_tables: use list_del_rcu for netlink hooks
|
||||
|
||||
JIRA: https://redhat.atlassian.net/browse/RHEL-168848
|
||||
Upstream Status: commit f3224ee463f8
|
||||
|
||||
commit f3224ee463f8f6f6ced7dcdf6081add4f8128527
|
||||
Author: Florian Westphal <fw@strlen.de>
|
||||
Date: Thu Apr 16 15:14:51 2026 +0200
|
||||
|
||||
netfilter: nf_tables: use list_del_rcu for netlink hooks
|
||||
|
||||
nft_netdev_unregister_hooks and __nft_unregister_flowtable_net_hooks need
|
||||
to use list_del_rcu(), this list can be walked by concurrent dumpers.
|
||||
|
||||
Add a new helper and use it consistently.
|
||||
|
||||
Fixes: f9a43007d3f7 ("netfilter: nf_tables: double hook unregistration in netns path")
|
||||
Signed-off-by: Florian Westphal <fw@strlen.de>
|
||||
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
|
||||
|
||||
Signed-off-by: Florian Westphal <fwestpha@redhat.com>
|
||||
|
||||
diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c
|
||||
index 7cc4163..f67b9b0 100644
|
||||
--- a/net/netfilter/nf_tables_api.c
|
||||
+++ b/net/netfilter/nf_tables_api.c
|
||||
@@ -373,6 +373,12 @@ static void nft_netdev_hook_free_rcu(struct nft_hook *hook)
|
||||
call_rcu(&hook->rcu, __nft_netdev_hook_free_rcu);
|
||||
}
|
||||
|
||||
+static void nft_netdev_hook_unlink_free_rcu(struct nft_hook *hook)
|
||||
+{
|
||||
+ list_del_rcu(&hook->list);
|
||||
+ nft_netdev_hook_free_rcu(hook);
|
||||
+}
|
||||
+
|
||||
static void nft_netdev_unregister_hooks(struct net *net,
|
||||
struct list_head *hook_list,
|
||||
bool release_netdev)
|
||||
@@ -383,10 +389,8 @@ static void nft_netdev_unregister_hooks(struct net *net,
|
||||
list_for_each_entry_safe(hook, next, hook_list, list) {
|
||||
list_for_each_entry(ops, &hook->ops_list, list)
|
||||
nf_unregister_net_hook(net, ops);
|
||||
- if (release_netdev) {
|
||||
- list_del(&hook->list);
|
||||
- nft_netdev_hook_free_rcu(hook);
|
||||
- }
|
||||
+ if (release_netdev)
|
||||
+ nft_netdev_hook_unlink_free_rcu(hook);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2314,10 +2318,8 @@ void nf_tables_chain_destroy(struct nft_chain *chain)
|
||||
|
||||
if (nft_base_chain_netdev(table->family, basechain->ops.hooknum)) {
|
||||
list_for_each_entry_safe(hook, next,
|
||||
- &basechain->hook_list, list) {
|
||||
- list_del_rcu(&hook->list);
|
||||
- nft_netdev_hook_free_rcu(hook);
|
||||
- }
|
||||
+ &basechain->hook_list, list)
|
||||
+ nft_netdev_hook_unlink_free_rcu(hook);
|
||||
}
|
||||
module_put(basechain->type->owner);
|
||||
if (rcu_access_pointer(basechain->stats)) {
|
||||
@@ -3017,6 +3019,7 @@ static int nf_tables_updchain(struct nft_ctx *ctx, u8 genmask, u8 policy,
|
||||
list_for_each_entry(ops, &h->ops_list, list)
|
||||
nf_unregister_net_hook(ctx->net, ops);
|
||||
}
|
||||
+ /* hook.list is on stack, no need for list_del_rcu() */
|
||||
list_del(&h->list);
|
||||
nft_netdev_hook_free_rcu(h);
|
||||
}
|
||||
@@ -9049,10 +9052,8 @@ static void __nft_unregister_flowtable_net_hooks(struct net *net,
|
||||
list_for_each_entry_safe(hook, next, hook_list, list) {
|
||||
list_for_each_entry(ops, &hook->ops_list, list)
|
||||
nft_unregister_flowtable_ops(net, flowtable, ops);
|
||||
- if (release_netdev) {
|
||||
- list_del(&hook->list);
|
||||
- nft_netdev_hook_free_rcu(hook);
|
||||
- }
|
||||
+ if (release_netdev)
|
||||
+ nft_netdev_hook_unlink_free_rcu(hook);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9123,8 +9124,7 @@ static int nft_register_flowtable_net_hooks(struct net *net,
|
||||
|
||||
nft_unregister_flowtable_ops(net, flowtable, ops);
|
||||
}
|
||||
- list_del_rcu(&hook->list);
|
||||
- nft_netdev_hook_free_rcu(hook);
|
||||
+ nft_netdev_hook_unlink_free_rcu(hook);
|
||||
}
|
||||
|
||||
return err;
|
||||
@@ -9134,10 +9134,8 @@ static void nft_hooks_destroy(struct list_head *hook_list)
|
||||
{
|
||||
struct nft_hook *hook, *next;
|
||||
|
||||
- list_for_each_entry_safe(hook, next, hook_list, list) {
|
||||
- list_del_rcu(&hook->list);
|
||||
- nft_netdev_hook_free_rcu(hook);
|
||||
- }
|
||||
+ list_for_each_entry_safe(hook, next, hook_list, list)
|
||||
+ nft_netdev_hook_unlink_free_rcu(hook);
|
||||
}
|
||||
|
||||
static int nft_flowtable_update(struct nft_ctx *ctx, const struct nlmsghdr *nlh,
|
||||
@@ -9225,8 +9223,7 @@ static int nft_flowtable_update(struct nft_ctx *ctx, const struct nlmsghdr *nlh,
|
||||
nft_unregister_flowtable_ops(ctx->net,
|
||||
flowtable, ops);
|
||||
}
|
||||
- list_del_rcu(&hook->list);
|
||||
- nft_netdev_hook_free_rcu(hook);
|
||||
+ nft_netdev_hook_unlink_free_rcu(hook);
|
||||
}
|
||||
|
||||
return err;
|
||||
@@ -9730,13 +9727,8 @@ static void nf_tables_flowtable_notify(struct nft_ctx *ctx,
|
||||
|
||||
static void nf_tables_flowtable_destroy(struct nft_flowtable *flowtable)
|
||||
{
|
||||
- struct nft_hook *hook, *next;
|
||||
-
|
||||
flowtable->data.type->free(&flowtable->data);
|
||||
- list_for_each_entry_safe(hook, next, &flowtable->hook_list, list) {
|
||||
- list_del_rcu(&hook->list);
|
||||
- nft_netdev_hook_free_rcu(hook);
|
||||
- }
|
||||
+ nft_hooks_destroy(&flowtable->hook_list);
|
||||
kfree(flowtable->name);
|
||||
module_put(flowtable->data.type->owner);
|
||||
kfree(flowtable);
|
||||
78
1137-rculist-add-list-splice-rcu-for-private-lists.patch
Normal file
78
1137-rculist-add-list-splice-rcu-for-private-lists.patch
Normal file
@ -0,0 +1,78 @@
|
||||
From 4f79f8ec95a853c69e776a91f3258f8309713123 Mon Sep 17 00:00:00 2001
|
||||
From: Florian Westphal <fwestpha@redhat.com>
|
||||
Date: Wed, 13 May 2026 17:22:20 +0200
|
||||
Subject: [PATCH] rculist: add list_splice_rcu() for private lists
|
||||
|
||||
JIRA: https://redhat.atlassian.net/browse/RHEL-168848
|
||||
Upstream Status: commit f902877b6355
|
||||
|
||||
commit f902877b635551513729bdf9a8d1422c4aab7741
|
||||
Author: Pablo Neira Ayuso <pablo@netfilter.org>
|
||||
Date: Wed Apr 15 17:56:02 2026 +0200
|
||||
|
||||
rculist: add list_splice_rcu() for private lists
|
||||
|
||||
This patch adds a helper function, list_splice_rcu(), to safely splice
|
||||
a private (non-RCU-protected) list into an RCU-protected list.
|
||||
|
||||
The function ensures that only the pointer visible to RCU readers
|
||||
(prev->next) is updated using rcu_assign_pointer(), while the rest of
|
||||
the list manipulations are performed with regular assignments, as the
|
||||
source list is private and not visible to concurrent RCU readers.
|
||||
|
||||
This is useful for moving elements from a private list into a global
|
||||
RCU-protected list, ensuring safe publication for RCU readers.
|
||||
Subsystems with some sort of batching mechanism from userspace can
|
||||
benefit from this new function.
|
||||
|
||||
The function __list_splice_rcu() has been added for clarity and to
|
||||
follow the same pattern as in the existing list_splice*() interfaces,
|
||||
where there is a check to ensure that the list to splice is not
|
||||
empty. Note that __list_splice_rcu() has no documentation for this
|
||||
reason.
|
||||
|
||||
Reviewed-by: Paul E. McKenney <paulmck@kernel.org>
|
||||
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
|
||||
|
||||
Signed-off-by: Florian Westphal <fwestpha@redhat.com>
|
||||
|
||||
diff --git a/include/linux/rculist.h b/include/linux/rculist.h
|
||||
index 1b11926..591272f 100644
|
||||
--- a/include/linux/rculist.h
|
||||
+++ b/include/linux/rculist.h
|
||||
@@ -251,6 +251,35 @@ static inline void list_replace_rcu(struct list_head *old,
|
||||
old->prev = LIST_POISON2;
|
||||
}
|
||||
|
||||
+static inline void __list_splice_rcu(struct list_head *list,
|
||||
+ struct list_head *prev,
|
||||
+ struct list_head *next)
|
||||
+{
|
||||
+ struct list_head *first = list->next;
|
||||
+ struct list_head *last = list->prev;
|
||||
+
|
||||
+ last->next = next;
|
||||
+ first->prev = prev;
|
||||
+ next->prev = last;
|
||||
+ rcu_assign_pointer(list_next_rcu(prev), first);
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * list_splice_rcu - splice a non-RCU list into an RCU-protected list,
|
||||
+ * designed for stacks.
|
||||
+ * @list: the non RCU-protected list to splice
|
||||
+ * @head: the place in the existing RCU-protected list to splice
|
||||
+ *
|
||||
+ * The list pointed to by @head can be RCU-read traversed concurrently with
|
||||
+ * this function.
|
||||
+ */
|
||||
+static inline void list_splice_rcu(struct list_head *list,
|
||||
+ struct list_head *head)
|
||||
+{
|
||||
+ if (!list_empty(list))
|
||||
+ __list_splice_rcu(list, head, head->next);
|
||||
+}
|
||||
+
|
||||
/**
|
||||
* __list_splice_init_rcu - join an RCU-protected list into an existing list.
|
||||
* @list: the RCU-protected list to splice
|
||||
@ -0,0 +1,51 @@
|
||||
From f5e3847dafb8aae3a40977b9a266e8c31cf0e018 Mon Sep 17 00:00:00 2001
|
||||
From: Florian Westphal <fwestpha@redhat.com>
|
||||
Date: Wed, 13 May 2026 17:22:21 +0200
|
||||
Subject: [PATCH] netfilter: nf_tables: join hook list via splice_list_rcu() in
|
||||
commit phase
|
||||
|
||||
JIRA: https://redhat.atlassian.net/browse/RHEL-168848
|
||||
Upstream Status: commit a6134e62dba2
|
||||
|
||||
commit a6134e62dba2ea4f760b29d5226907f447c92400
|
||||
Author: Pablo Neira Ayuso <pablo@netfilter.org>
|
||||
Date: Wed Apr 15 17:56:14 2026 +0200
|
||||
|
||||
netfilter: nf_tables: join hook list via splice_list_rcu() in commit phase
|
||||
|
||||
Publish new hooks in the list into the basechain/flowtable using
|
||||
splice_list_rcu() to ensure netlink dump list traversal via rcu is safe
|
||||
while concurrent ruleset update is going on.
|
||||
|
||||
Fixes: 78d9f48f7f44 ("netfilter: nf_tables: add devices to existing flowtable")
|
||||
Fixes: b9703ed44ffb ("netfilter: nf_tables: support for adding new devices to an existing netdev chain")
|
||||
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
|
||||
|
||||
Signed-off-by: Florian Westphal <fwestpha@redhat.com>
|
||||
|
||||
diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c
|
||||
index f67b9b0..32155f1 100644
|
||||
--- a/net/netfilter/nf_tables_api.c
|
||||
+++ b/net/netfilter/nf_tables_api.c
|
||||
@@ -11048,8 +11048,8 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb)
|
||||
nft_chain_commit_update(nft_trans_container_chain(trans));
|
||||
nf_tables_chain_notify(&ctx, NFT_MSG_NEWCHAIN,
|
||||
&nft_trans_chain_hooks(trans));
|
||||
- list_splice(&nft_trans_chain_hooks(trans),
|
||||
- &nft_trans_basechain(trans)->hook_list);
|
||||
+ list_splice_rcu(&nft_trans_chain_hooks(trans),
|
||||
+ &nft_trans_basechain(trans)->hook_list);
|
||||
/* trans destroyed after rcu grace period */
|
||||
} else {
|
||||
nft_chain_commit_drop_policy(nft_trans_container_chain(trans));
|
||||
@@ -11178,8 +11178,8 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb)
|
||||
nft_trans_flowtable(trans),
|
||||
&nft_trans_flowtable_hooks(trans),
|
||||
NFT_MSG_NEWFLOWTABLE);
|
||||
- list_splice(&nft_trans_flowtable_hooks(trans),
|
||||
- &nft_trans_flowtable(trans)->hook_list);
|
||||
+ list_splice_rcu(&nft_trans_flowtable_hooks(trans),
|
||||
+ &nft_trans_flowtable(trans)->hook_list);
|
||||
} else {
|
||||
nft_clear(net, nft_trans_flowtable(trans));
|
||||
nf_tables_flowtable_notify(&ctx,
|
||||
@ -0,0 +1,82 @@
|
||||
From 28986d167301cbf492736a86d0bd9138186d4bc4 Mon Sep 17 00:00:00 2001
|
||||
From: Florian Westphal <fwestpha@redhat.com>
|
||||
Date: Wed, 13 May 2026 17:22:21 +0200
|
||||
Subject: [PATCH] netfilter: nf_tables: add hook transactions for device
|
||||
deletions
|
||||
|
||||
JIRA: https://redhat.atlassian.net/browse/RHEL-168848
|
||||
Upstream Status: commit 10f79dbd7719
|
||||
|
||||
commit 10f79dbd7719d1da9f5884d13060322d8729f091
|
||||
Author: Pablo Neira Ayuso <pablo@netfilter.org>
|
||||
Date: Wed Apr 15 22:58:23 2026 +0200
|
||||
|
||||
netfilter: nf_tables: add hook transactions for device deletions
|
||||
|
||||
Restore the flag that indicates that the hook is going away, ie.
|
||||
NFT_HOOK_REMOVE, but add a new transaction object to track deletion
|
||||
of hooks without altering the basechain/flowtable hook_list during
|
||||
the preparation phase.
|
||||
|
||||
The existing approach that moves the hook from the basechain/flowtable
|
||||
hook_list to transaction hook_list breaks netlink dump path readers
|
||||
of this RCU-protected list.
|
||||
|
||||
It should be possible use an array for nft_trans_hook to store the
|
||||
deleted hooks to compact the representation but I am not expecting
|
||||
many hook object, specially now that wildcard support for devices
|
||||
is in place.
|
||||
|
||||
Note that the nft_trans_chain_hooks() list contains a list of struct
|
||||
nft_trans_hook objects for DELCHAIN and DELFLOWTABLE commands, while
|
||||
this list stores struct nft_hook objects for NEWCHAIN and NEWFLOWTABLE.
|
||||
Note that new commands can be updated to use nft_trans_hook for
|
||||
consistency.
|
||||
|
||||
This patch also adapts the event notification path to deal with the list
|
||||
of hook transactions.
|
||||
|
||||
Fixes: 7d937b107108 ("netfilter: nf_tables: support for deleting devices in an existing netdev chain")
|
||||
Fixes: b6d9014a3335 ("netfilter: nf_tables: delete flowtable hooks via transaction list")
|
||||
Reported-by: Xiang Mei <xmei5@asu.edu>
|
||||
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
|
||||
|
||||
Signed-off-by: Florian Westphal <fwestpha@redhat.com>
|
||||
|
||||
diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h
|
||||
index 5fe20cc..a704616 100644
|
||||
--- a/include/net/netfilter/nf_tables.h
|
||||
+++ b/include/net/netfilter/nf_tables.h
|
||||
@@ -1213,12 +1213,15 @@ struct nft_stats {
|
||||
struct u64_stats_sync syncp;
|
||||
};
|
||||
|
||||
+#define NFT_HOOK_REMOVE (1 << 0)
|
||||
+
|
||||
struct nft_hook {
|
||||
struct list_head list;
|
||||
struct list_head ops_list;
|
||||
struct rcu_head rcu;
|
||||
char ifname[IFNAMSIZ];
|
||||
u8 ifnamelen;
|
||||
+ u8 flags;
|
||||
};
|
||||
|
||||
struct nf_hook_ops *nft_hook_find_ops(const struct nft_hook *hook,
|
||||
@@ -1673,6 +1676,16 @@ struct nft_trans {
|
||||
u8 put_net:1;
|
||||
};
|
||||
|
||||
+/**
|
||||
+ * struct nft_trans_hook - nf_tables hook update in transaction
|
||||
+ * @list: used internally
|
||||
+ * @hook: struct nft_hook with the device hook
|
||||
+ */
|
||||
+struct nft_trans_hook {
|
||||
+ struct list_head list;
|
||||
+ struct nft_hook *hook;
|
||||
+};
|
||||
+
|
||||
/**
|
||||
* struct nft_trans_binding - nf_tables object with binding support in transaction
|
||||
* @nft_trans: base structure, MUST be first member
|
||||
@ -0,0 +1,47 @@
|
||||
From 8356d8b52032a2e39e5e3ac6f8d4c8c69247064f Mon Sep 17 00:00:00 2001
|
||||
From: Florian Westphal <fwestpha@redhat.com>
|
||||
Date: Wed, 13 May 2026 17:22:52 +0200
|
||||
Subject: [PATCH] netfilter: xt_policy: fix strict mode inbound policy matching
|
||||
|
||||
JIRA: https://redhat.atlassian.net/browse/RHEL-168848
|
||||
Upstream Status: commit 4b2b4d7d4e20
|
||||
|
||||
commit 4b2b4d7d4e203c92db8966b163edfacb1f0e1e29
|
||||
Author: Jiexun Wang <wangjiexun2025@gmail.com>
|
||||
Date: Fri Apr 17 20:25:06 2026 +0800
|
||||
|
||||
netfilter: xt_policy: fix strict mode inbound policy matching
|
||||
|
||||
match_policy_in() walks sec_path entries from the last transform to the
|
||||
first one, but strict policy matching needs to consume info->pol[] in
|
||||
the same forward order as the rule layout.
|
||||
|
||||
Derive the strict-match policy position from the number of transforms
|
||||
already consumed so that multi-element inbound rules are matched
|
||||
consistently.
|
||||
|
||||
Fixes: c4b885139203 ("[NETFILTER]: x_tables: replace IPv4/IPv6 policy match by address family independant version")
|
||||
Reported-by: Yuan Tan <yuantan098@gmail.com>
|
||||
Reported-by: Yifan Wu <yifanwucs@gmail.com>
|
||||
Reported-by: Juefei Pu <tomapufckgml@gmail.com>
|
||||
Reported-by: Xin Liu <bird@lzu.edu.cn>
|
||||
Signed-off-by: Jiexun Wang <wangjiexun2025@gmail.com>
|
||||
Signed-off-by: Ren Wei <n05ec@lzu.edu.cn>
|
||||
Acked-by: Florian Westphal <fw@strlen.de>
|
||||
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
|
||||
|
||||
Signed-off-by: Florian Westphal <fwestpha@redhat.com>
|
||||
|
||||
diff --git a/net/netfilter/xt_policy.c b/net/netfilter/xt_policy.c
|
||||
index cb6e827..b5fa655 100644
|
||||
--- a/net/netfilter/xt_policy.c
|
||||
+++ b/net/netfilter/xt_policy.c
|
||||
@@ -63,7 +63,7 @@ match_policy_in(const struct sk_buff *skb, const struct xt_policy_info *info,
|
||||
return 0;
|
||||
|
||||
for (i = sp->len - 1; i >= 0; i--) {
|
||||
- pos = strict ? i - sp->len + 1 : 0;
|
||||
+ pos = strict ? sp->len - i - 1 : 0;
|
||||
if (pos >= info->len)
|
||||
return 0;
|
||||
e = &info->pol[pos];
|
||||
351
1141-netfilter-nf-conntrack-sip-don-t-use-simple-strtoul.patch
Normal file
351
1141-netfilter-nf-conntrack-sip-don-t-use-simple-strtoul.patch
Normal file
@ -0,0 +1,351 @@
|
||||
From cfeec2dad64d5c91326707d6a443086a260f5c22 Mon Sep 17 00:00:00 2001
|
||||
From: Florian Westphal <fwestpha@redhat.com>
|
||||
Date: Wed, 13 May 2026 17:22:53 +0200
|
||||
Subject: [PATCH] netfilter: nf_conntrack_sip: don't use simple_strtoul
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
JIRA: https://redhat.atlassian.net/browse/RHEL-168848
|
||||
Upstream Status: commit 8cf6809cddcb
|
||||
|
||||
commit 8cf6809cddcbe301aedfc6b51bcd4944d45795f6
|
||||
Author: Florian Westphal <fw@strlen.de>
|
||||
Date: Thu Apr 23 02:19:11 2026 +0200
|
||||
|
||||
netfilter: nf_conntrack_sip: don't use simple_strtoul
|
||||
|
||||
Replace unsafe port parsing in epaddr_len(), ct_sip_parse_header_uri(),
|
||||
and ct_sip_parse_request() with a new sip_parse_port() helper that
|
||||
validates each digit against the buffer limit, eliminating the use of
|
||||
simple_strtoul() which assumes NUL-terminated strings.
|
||||
|
||||
The previous code dereferenced pointers without bounds checks after
|
||||
sip_parse_addr() and relied on simple_strtoul() on non-NUL-terminated
|
||||
skb data. A port that reaches the buffer limit without a trailing
|
||||
character is also rejected as malformed.
|
||||
|
||||
Also get rid of all simple_strtoul() usage in conntrack, prefer a
|
||||
stricter version instead. There are intentional changes:
|
||||
|
||||
- Bail out if number is > UINT_MAX and indicate a failure, same for
|
||||
too long sequences.
|
||||
While we do accept 05535 as port 5535, we will not accept e.g.
|
||||
'sip:10.0.0.1:005060'. While its syntactically valid under RFC 3261,
|
||||
we should restrict this to not waste cycles when presented with
|
||||
malformed packets with 64k '0' characters.
|
||||
|
||||
- Force base 10 in ct_sip_parse_numerical_param(). This is used to fetch
|
||||
'expire=' and 'rports='; both are expected to use base-10.
|
||||
|
||||
- In nf_nat_sip.c, only accept the parsed value if its within the 1k-64k
|
||||
range.
|
||||
|
||||
- epaddr_len now returns 0 if the port is invalid, as it already does
|
||||
for invalid ip addresses. This is intentional. nf_conntrack_sip
|
||||
performs lots of guesswork to find the right parts of the message
|
||||
to parse. Being stricter could break existing setups.
|
||||
Connection tracking helpers are designed to allow traffic to
|
||||
pass, not to block it.
|
||||
|
||||
Based on an earlier patch from Jenny Guanni Qu <qguanni@gmail.com>.
|
||||
|
||||
Fixes: 05e3ced297fe ("[NETFILTER]: nf_conntrack_sip: introduce SIP-URI parsing helper")
|
||||
Reported-by: Klaudia Kloc <klaudia@vidocsecurity.com>
|
||||
Reported-by: Dawid Moczadło <dawid@vidocsecurity.com>
|
||||
Reported-by: Jenny Guanni Qu <qguanni@gmail.com>.
|
||||
Signed-off-by: Florian Westphal <fw@strlen.de>
|
||||
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
|
||||
|
||||
Signed-off-by: Florian Westphal <fwestpha@redhat.com>
|
||||
|
||||
diff --git a/net/netfilter/nf_conntrack_sip.c b/net/netfilter/nf_conntrack_sip.c
|
||||
index 20e57cf..b31f31e 100644
|
||||
--- a/net/netfilter/nf_conntrack_sip.c
|
||||
+++ b/net/netfilter/nf_conntrack_sip.c
|
||||
@@ -181,6 +181,57 @@ static int sip_parse_addr(const struct nf_conn *ct, const char *cp,
|
||||
return 1;
|
||||
}
|
||||
|
||||
+/* Parse optional port number after IP address.
|
||||
+ * Returns false on malformed input, true otherwise.
|
||||
+ * If port is non-NULL, stores parsed port in network byte order.
|
||||
+ * If no port is present, sets *port to default SIP port.
|
||||
+ */
|
||||
+static bool sip_parse_port(const char *dptr, const char **endp,
|
||||
+ const char *limit, __be16 *port)
|
||||
+{
|
||||
+ unsigned int p = 0;
|
||||
+ int len = 0;
|
||||
+
|
||||
+ if (dptr >= limit)
|
||||
+ return false;
|
||||
+
|
||||
+ if (*dptr != ':') {
|
||||
+ if (port)
|
||||
+ *port = htons(SIP_PORT);
|
||||
+ if (endp)
|
||||
+ *endp = dptr;
|
||||
+ return true;
|
||||
+ }
|
||||
+
|
||||
+ dptr++; /* skip ':' */
|
||||
+
|
||||
+ while (dptr < limit && isdigit(*dptr)) {
|
||||
+ p = p * 10 + (*dptr - '0');
|
||||
+ dptr++;
|
||||
+ len++;
|
||||
+ if (len > 5) /* max "65535" */
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
+ if (len == 0)
|
||||
+ return false;
|
||||
+
|
||||
+ /* reached limit while parsing port */
|
||||
+ if (dptr >= limit)
|
||||
+ return false;
|
||||
+
|
||||
+ if (p < 1024 || p > 65535)
|
||||
+ return false;
|
||||
+
|
||||
+ if (port)
|
||||
+ *port = htons(p);
|
||||
+
|
||||
+ if (endp)
|
||||
+ *endp = dptr;
|
||||
+
|
||||
+ return true;
|
||||
+}
|
||||
+
|
||||
/* skip ip address. returns its length. */
|
||||
static int epaddr_len(const struct nf_conn *ct, const char *dptr,
|
||||
const char *limit, int *shift)
|
||||
@@ -193,11 +244,8 @@ static int epaddr_len(const struct nf_conn *ct, const char *dptr,
|
||||
return 0;
|
||||
}
|
||||
|
||||
- /* Port number */
|
||||
- if (*dptr == ':') {
|
||||
- dptr++;
|
||||
- dptr += digits_len(ct, dptr, limit, shift);
|
||||
- }
|
||||
+ if (!sip_parse_port(dptr, &dptr, limit, NULL))
|
||||
+ return 0;
|
||||
return dptr - aux;
|
||||
}
|
||||
|
||||
@@ -228,6 +276,51 @@ static int skp_epaddr_len(const struct nf_conn *ct, const char *dptr,
|
||||
return epaddr_len(ct, dptr, limit, shift);
|
||||
}
|
||||
|
||||
+/* simple_strtoul stops after first non-number character.
|
||||
+ * But as we're not dealing with c-strings, we can't rely on
|
||||
+ * hitting \r,\n,\0 etc. before moving past end of buffer.
|
||||
+ *
|
||||
+ * This is a variant of simple_strtoul, but doesn't require
|
||||
+ * a c-string.
|
||||
+ *
|
||||
+ * If value exceeds UINT_MAX, 0 is returned.
|
||||
+ */
|
||||
+static unsigned int sip_strtouint(const char *cp, unsigned int len, char **endp)
|
||||
+{
|
||||
+ const unsigned int max = sizeof("4294967295");
|
||||
+ unsigned int olen = len;
|
||||
+ const char *s = cp;
|
||||
+ u64 result = 0;
|
||||
+
|
||||
+ if (len > max)
|
||||
+ len = max;
|
||||
+
|
||||
+ while (olen > 0 && isdigit(*s)) {
|
||||
+ unsigned int value;
|
||||
+
|
||||
+ if (len == 0)
|
||||
+ goto err;
|
||||
+
|
||||
+ value = *s - '0';
|
||||
+ result = result * 10 + value;
|
||||
+
|
||||
+ if (result > UINT_MAX)
|
||||
+ goto err;
|
||||
+ s++;
|
||||
+ len--;
|
||||
+ olen--;
|
||||
+ }
|
||||
+
|
||||
+ if (endp)
|
||||
+ *endp = (char *)s;
|
||||
+
|
||||
+ return result;
|
||||
+err:
|
||||
+ if (endp)
|
||||
+ *endp = (char *)cp;
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
/* Parse a SIP request line of the form:
|
||||
*
|
||||
* Request-Line = Method SP Request-URI SP SIP-Version CRLF
|
||||
@@ -241,7 +334,6 @@ int ct_sip_parse_request(const struct nf_conn *ct,
|
||||
{
|
||||
const char *start = dptr, *limit = dptr + datalen, *end;
|
||||
unsigned int mlen;
|
||||
- unsigned int p;
|
||||
int shift = 0;
|
||||
|
||||
/* Skip method and following whitespace */
|
||||
@@ -267,14 +359,8 @@ int ct_sip_parse_request(const struct nf_conn *ct,
|
||||
|
||||
if (!sip_parse_addr(ct, dptr, &end, addr, limit, true))
|
||||
return -1;
|
||||
- if (end < limit && *end == ':') {
|
||||
- end++;
|
||||
- p = simple_strtoul(end, (char **)&end, 10);
|
||||
- if (p < 1024 || p > 65535)
|
||||
- return -1;
|
||||
- *port = htons(p);
|
||||
- } else
|
||||
- *port = htons(SIP_PORT);
|
||||
+ if (!sip_parse_port(end, &end, limit, port))
|
||||
+ return -1;
|
||||
|
||||
if (end == dptr)
|
||||
return 0;
|
||||
@@ -509,7 +595,6 @@ int ct_sip_parse_header_uri(const struct nf_conn *ct, const char *dptr,
|
||||
union nf_inet_addr *addr, __be16 *port)
|
||||
{
|
||||
const char *c, *limit = dptr + datalen;
|
||||
- unsigned int p;
|
||||
int ret;
|
||||
|
||||
ret = ct_sip_walk_headers(ct, dptr, dataoff ? *dataoff : 0, datalen,
|
||||
@@ -520,14 +605,8 @@ int ct_sip_parse_header_uri(const struct nf_conn *ct, const char *dptr,
|
||||
|
||||
if (!sip_parse_addr(ct, dptr + *matchoff, &c, addr, limit, true))
|
||||
return -1;
|
||||
- if (*c == ':') {
|
||||
- c++;
|
||||
- p = simple_strtoul(c, (char **)&c, 10);
|
||||
- if (p < 1024 || p > 65535)
|
||||
- return -1;
|
||||
- *port = htons(p);
|
||||
- } else
|
||||
- *port = htons(SIP_PORT);
|
||||
+ if (!sip_parse_port(c, &c, limit, port))
|
||||
+ return -1;
|
||||
|
||||
if (dataoff)
|
||||
*dataoff = c - dptr;
|
||||
@@ -609,7 +688,7 @@ int ct_sip_parse_numerical_param(const struct nf_conn *ct, const char *dptr,
|
||||
return 0;
|
||||
|
||||
start += strlen(name);
|
||||
- *val = simple_strtoul(start, &end, 0);
|
||||
+ *val = sip_strtouint(start, limit - start, (char **)&end);
|
||||
if (start == end)
|
||||
return -1;
|
||||
if (matchoff && matchlen) {
|
||||
@@ -1061,6 +1140,8 @@ static int process_sdp(struct sk_buff *skb, unsigned int protoff,
|
||||
|
||||
mediaoff = sdpoff;
|
||||
for (i = 0; i < ARRAY_SIZE(sdp_media_types); ) {
|
||||
+ char *end;
|
||||
+
|
||||
if (ct_sip_get_sdp_header(ct, *dptr, mediaoff, *datalen,
|
||||
SDP_HDR_MEDIA, SDP_HDR_UNSPEC,
|
||||
&mediaoff, &medialen) <= 0)
|
||||
@@ -1076,8 +1157,8 @@ static int process_sdp(struct sk_buff *skb, unsigned int protoff,
|
||||
mediaoff += t->len;
|
||||
medialen -= t->len;
|
||||
|
||||
- port = simple_strtoul(*dptr + mediaoff, NULL, 10);
|
||||
- if (port == 0)
|
||||
+ port = sip_strtouint(*dptr + mediaoff, *datalen - mediaoff, (char **)&end);
|
||||
+ if (port == 0 || *dptr + mediaoff == end)
|
||||
continue;
|
||||
if (port < 1024 || port > 65535) {
|
||||
nf_ct_helper_log(skb, ct, "wrong port %u", port);
|
||||
@@ -1249,7 +1330,7 @@ static int process_register_request(struct sk_buff *skb, unsigned int protoff,
|
||||
*/
|
||||
if (ct_sip_get_header(ct, *dptr, 0, *datalen, SIP_HDR_EXPIRES,
|
||||
&matchoff, &matchlen) > 0)
|
||||
- expires = simple_strtoul(*dptr + matchoff, NULL, 10);
|
||||
+ expires = sip_strtouint(*dptr + matchoff, *datalen - matchoff, NULL);
|
||||
|
||||
ret = ct_sip_parse_header_uri(ct, *dptr, NULL, *datalen,
|
||||
SIP_HDR_CONTACT, NULL,
|
||||
@@ -1353,7 +1434,7 @@ static int process_register_response(struct sk_buff *skb, unsigned int protoff,
|
||||
|
||||
if (ct_sip_get_header(ct, *dptr, 0, *datalen, SIP_HDR_EXPIRES,
|
||||
&matchoff, &matchlen) > 0)
|
||||
- expires = simple_strtoul(*dptr + matchoff, NULL, 10);
|
||||
+ expires = sip_strtouint(*dptr + matchoff, *datalen - matchoff, NULL);
|
||||
|
||||
while (1) {
|
||||
unsigned int c_expires = expires;
|
||||
@@ -1413,10 +1494,12 @@ static int process_sip_response(struct sk_buff *skb, unsigned int protoff,
|
||||
struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
|
||||
unsigned int matchoff, matchlen, matchend;
|
||||
unsigned int code, cseq, i;
|
||||
+ char *end;
|
||||
|
||||
if (*datalen < strlen("SIP/2.0 200"))
|
||||
return NF_ACCEPT;
|
||||
- code = simple_strtoul(*dptr + strlen("SIP/2.0 "), NULL, 10);
|
||||
+ code = sip_strtouint(*dptr + strlen("SIP/2.0 "),
|
||||
+ *datalen - strlen("SIP/2.0 "), NULL);
|
||||
if (!code) {
|
||||
nf_ct_helper_log(skb, ct, "cannot get code");
|
||||
return NF_DROP;
|
||||
@@ -1427,8 +1510,8 @@ static int process_sip_response(struct sk_buff *skb, unsigned int protoff,
|
||||
nf_ct_helper_log(skb, ct, "cannot parse cseq");
|
||||
return NF_DROP;
|
||||
}
|
||||
- cseq = simple_strtoul(*dptr + matchoff, NULL, 10);
|
||||
- if (!cseq && *(*dptr + matchoff) != '0') {
|
||||
+ cseq = sip_strtouint(*dptr + matchoff, *datalen - matchoff, (char **)&end);
|
||||
+ if (*dptr + matchoff == end) {
|
||||
nf_ct_helper_log(skb, ct, "cannot get cseq");
|
||||
return NF_DROP;
|
||||
}
|
||||
@@ -1477,6 +1560,7 @@ static int process_sip_request(struct sk_buff *skb, unsigned int protoff,
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(sip_handlers); i++) {
|
||||
const struct sip_handler *handler;
|
||||
+ char *end;
|
||||
|
||||
handler = &sip_handlers[i];
|
||||
if (handler->request == NULL)
|
||||
@@ -1493,8 +1577,8 @@ static int process_sip_request(struct sk_buff *skb, unsigned int protoff,
|
||||
nf_ct_helper_log(skb, ct, "cannot parse cseq");
|
||||
return NF_DROP;
|
||||
}
|
||||
- cseq = simple_strtoul(*dptr + matchoff, NULL, 10);
|
||||
- if (!cseq && *(*dptr + matchoff) != '0') {
|
||||
+ cseq = sip_strtouint(*dptr + matchoff, *datalen - matchoff, (char **)&end);
|
||||
+ if (*dptr + matchoff == end) {
|
||||
nf_ct_helper_log(skb, ct, "cannot get cseq");
|
||||
return NF_DROP;
|
||||
}
|
||||
@@ -1570,7 +1654,7 @@ static int sip_help_tcp(struct sk_buff *skb, unsigned int protoff,
|
||||
&matchoff, &matchlen) <= 0)
|
||||
break;
|
||||
|
||||
- clen = simple_strtoul(dptr + matchoff, (char **)&end, 10);
|
||||
+ clen = sip_strtouint(dptr + matchoff, datalen - matchoff, (char **)&end);
|
||||
if (dptr + matchoff == end)
|
||||
break;
|
||||
|
||||
diff --git a/net/netfilter/nf_nat_sip.c b/net/netfilter/nf_nat_sip.c
|
||||
index c845b6d..9fbfc6b 100644
|
||||
--- a/net/netfilter/nf_nat_sip.c
|
||||
+++ b/net/netfilter/nf_nat_sip.c
|
||||
@@ -246,6 +246,7 @@ static unsigned int nf_nat_sip(struct sk_buff *skb, unsigned int protoff,
|
||||
if (ct_sip_parse_numerical_param(ct, *dptr, matchend, *datalen,
|
||||
"rport=", &poff, &plen,
|
||||
&n) > 0 &&
|
||||
+ n >= 1024 && n <= 65535 &&
|
||||
htons(n) == ct->tuplehash[dir].tuple.dst.u.udp.port &&
|
||||
htons(n) != ct->tuplehash[!dir].tuple.src.u.udp.port) {
|
||||
__be16 p = ct->tuplehash[!dir].tuple.src.u.udp.port;
|
||||
@ -0,0 +1,81 @@
|
||||
From a3f61eef6943577933d43f3546d57ae78f5b44f3 Mon Sep 17 00:00:00 2001
|
||||
From: Florian Westphal <fwestpha@redhat.com>
|
||||
Date: Wed, 13 May 2026 17:22:55 +0200
|
||||
Subject: [PATCH] netfilter: replace skb_try_make_writable() by
|
||||
skb_ensure_writable()
|
||||
|
||||
JIRA: https://redhat.atlassian.net/browse/RHEL-168848
|
||||
Upstream Status: commit 1049970d7583
|
||||
|
||||
commit 1049970d7583194eedc30e45a3c898b2cb1c30ba
|
||||
Author: Pablo Neira Ayuso <pablo@netfilter.org>
|
||||
Date: Mon Apr 27 14:34:45 2026 +0200
|
||||
|
||||
netfilter: replace skb_try_make_writable() by skb_ensure_writable()
|
||||
|
||||
skb_try_make_writable() only works on clones and uncloned packets might
|
||||
have their network header in paged fragments.
|
||||
|
||||
nft_fwd needs to work for the ingress and egress hooks, but the egress
|
||||
hook where skb->data points to the mac header, use skb_network_offset()
|
||||
to include the mac header. The flowtable is fine since it already uses
|
||||
the transport offset.
|
||||
|
||||
Fixes: d32de98ea70f ("netfilter: nft_fwd_netdev: allow to forward packets via neighbour layer")
|
||||
Fixes: 7d2086871762 ("netfilter: nf_flow_table: move ipv4 offload hook code to nf_flow_table")
|
||||
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
|
||||
|
||||
Signed-off-by: Florian Westphal <fwestpha@redhat.com>
|
||||
|
||||
diff --git a/net/netfilter/nf_flow_table_ip.c b/net/netfilter/nf_flow_table_ip.c
|
||||
index 8cd4cf7..43266ab 100644
|
||||
--- a/net/netfilter/nf_flow_table_ip.c
|
||||
+++ b/net/netfilter/nf_flow_table_ip.c
|
||||
@@ -394,7 +394,7 @@ static int nf_flow_offload_forward(struct nf_flowtable_ctx *ctx,
|
||||
return 0;
|
||||
}
|
||||
|
||||
- if (skb_try_make_writable(skb, thoff + ctx->hdrsize))
|
||||
+ if (skb_ensure_writable(skb, thoff + ctx->hdrsize))
|
||||
return -1;
|
||||
|
||||
flow_offload_refresh(flow_table, flow, false);
|
||||
@@ -673,7 +673,7 @@ static int nf_flow_offload_ipv6_forward(struct nf_flowtable_ctx *ctx,
|
||||
return 0;
|
||||
}
|
||||
|
||||
- if (skb_try_make_writable(skb, thoff + ctx->hdrsize))
|
||||
+ if (skb_ensure_writable(skb, thoff + ctx->hdrsize))
|
||||
return -1;
|
||||
|
||||
flow_offload_refresh(flow_table, flow, false);
|
||||
diff --git a/net/netfilter/nft_fwd_netdev.c b/net/netfilter/nft_fwd_netdev.c
|
||||
index 152a9fb..c49da00 100644
|
||||
--- a/net/netfilter/nft_fwd_netdev.c
|
||||
+++ b/net/netfilter/nft_fwd_netdev.c
|
||||
@@ -100,6 +100,7 @@ static void nft_fwd_neigh_eval(const struct nft_expr *expr,
|
||||
int oif = regs->data[priv->sreg_dev];
|
||||
unsigned int verdict = NF_STOLEN;
|
||||
struct sk_buff *skb = pkt->skb;
|
||||
+ int nhoff = skb_network_offset(skb);
|
||||
struct net_device *dev;
|
||||
int neigh_table;
|
||||
|
||||
@@ -111,7 +112,7 @@ static void nft_fwd_neigh_eval(const struct nft_expr *expr,
|
||||
verdict = NFT_BREAK;
|
||||
goto out;
|
||||
}
|
||||
- if (skb_try_make_writable(skb, sizeof(*iph))) {
|
||||
+ if (skb_ensure_writable(skb, nhoff + sizeof(*iph))) {
|
||||
verdict = NF_DROP;
|
||||
goto out;
|
||||
}
|
||||
@@ -127,7 +128,7 @@ static void nft_fwd_neigh_eval(const struct nft_expr *expr,
|
||||
verdict = NFT_BREAK;
|
||||
goto out;
|
||||
}
|
||||
- if (skb_try_make_writable(skb, sizeof(*ip6h))) {
|
||||
+ if (skb_ensure_writable(skb, nhoff + sizeof(*ip6h))) {
|
||||
verdict = NF_DROP;
|
||||
goto out;
|
||||
}
|
||||
@ -0,0 +1,63 @@
|
||||
From 2073f1cc95a6d0cfea7e966c7307730ba7dca41c Mon Sep 17 00:00:00 2001
|
||||
From: Florian Westphal <fwestpha@redhat.com>
|
||||
Date: Wed, 13 May 2026 17:22:57 +0200
|
||||
Subject: [PATCH] netfilter: nft_fwd_netdev: add device and headroom validate
|
||||
with neigh forwarding
|
||||
|
||||
JIRA: https://redhat.atlassian.net/browse/RHEL-168848
|
||||
Upstream Status: commit 0a0b35f0bf10
|
||||
|
||||
commit 0a0b35f0bf10b4c2be607465f5c9c12c8681305b
|
||||
Author: Pablo Neira Ayuso <pablo@netfilter.org>
|
||||
Date: Mon Apr 27 14:34:48 2026 +0200
|
||||
|
||||
netfilter: nft_fwd_netdev: add device and headroom validate with neigh forwarding
|
||||
|
||||
The ttl field has been decremented already and evaluation of this rule
|
||||
would proceed, just drop this packet instead if there is no destination
|
||||
device to forwards this packet. This is exactly what nf_dup already does
|
||||
in this case.
|
||||
|
||||
Moreover, check for headroom and call skb_expand_head() like in the IP
|
||||
output path to ensure there is sufficient headroom when forwarding this
|
||||
via neigh_xmit().
|
||||
|
||||
Fixes: d32de98ea70f ("netfilter: nft_fwd_netdev: allow to forward packets via neighbour layer")
|
||||
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
|
||||
|
||||
Signed-off-by: Florian Westphal <fwestpha@redhat.com>
|
||||
|
||||
diff --git a/net/netfilter/nft_fwd_netdev.c b/net/netfilter/nft_fwd_netdev.c
|
||||
index c49da00..08246ef 100644
|
||||
--- a/net/netfilter/nft_fwd_netdev.c
|
||||
+++ b/net/netfilter/nft_fwd_netdev.c
|
||||
@@ -102,6 +102,7 @@ static void nft_fwd_neigh_eval(const struct nft_expr *expr,
|
||||
struct sk_buff *skb = pkt->skb;
|
||||
int nhoff = skb_network_offset(skb);
|
||||
struct net_device *dev;
|
||||
+ unsigned int hh_len;
|
||||
int neigh_table;
|
||||
|
||||
switch (priv->nfproto) {
|
||||
@@ -143,8 +144,19 @@ static void nft_fwd_neigh_eval(const struct nft_expr *expr,
|
||||
}
|
||||
|
||||
dev = dev_get_by_index_rcu(nft_net(pkt), oif);
|
||||
- if (dev == NULL)
|
||||
- return;
|
||||
+ if (dev == NULL) {
|
||||
+ verdict = NF_DROP;
|
||||
+ goto out;
|
||||
+ }
|
||||
+
|
||||
+ hh_len = LL_RESERVED_SPACE(dev);
|
||||
+ if (unlikely(skb_headroom(skb) < hh_len && dev->header_ops)) {
|
||||
+ skb = skb_expand_head(skb, hh_len);
|
||||
+ if (!skb) {
|
||||
+ verdict = NF_STOLEN;
|
||||
+ goto out;
|
||||
+ }
|
||||
+ }
|
||||
|
||||
skb->dev = dev;
|
||||
skb_clear_tstamp(skb);
|
||||
@ -0,0 +1,124 @@
|
||||
From 5c6290d3ed9c20d216c2e6fe34d529a1417264e5 Mon Sep 17 00:00:00 2001
|
||||
From: Florian Westphal <fwestpha@redhat.com>
|
||||
Date: Wed, 13 May 2026 17:22:58 +0200
|
||||
Subject: [PATCH] netfilter: nft_fwd_netdev: use recursion counter in neigh
|
||||
egress path
|
||||
|
||||
JIRA: https://redhat.atlassian.net/browse/RHEL-168848
|
||||
Upstream Status: commit 1d47b55b36d2
|
||||
|
||||
commit 1d47b55b36d2ec73fe6901212c8b28a593c3b27c
|
||||
Author: Weiming Shi <bestswngs@gmail.com>
|
||||
Date: Mon Apr 27 14:34:50 2026 +0200
|
||||
|
||||
netfilter: nft_fwd_netdev: use recursion counter in neigh egress path
|
||||
|
||||
nft_fwd_neigh can be used in egress chains (NF_NETDEV_EGRESS). When the
|
||||
forwarding rule targets the same device or two devices forward to each
|
||||
other, neigh_xmit() triggers dev_queue_xmit() which re-enters
|
||||
nf_hook_egress(), causing infinite recursion and stack overflow.
|
||||
|
||||
Move the nf_get_nf_dup_skb_recursion() accessor and NF_RECURSION_LIMIT
|
||||
to the shared header nf_dup_netdev.h as a static inline, so that
|
||||
nft_fwd_netdev can use the recursion counter directly without exported
|
||||
function call overhead. Guard neigh_xmit() with the same recursion
|
||||
limit already used in nf_do_netdev_egress().
|
||||
|
||||
[ Updated to cache the nf_get_nf_dup_skb_recursion pointer. --pablo ]
|
||||
|
||||
Fixes: f87b9464d152 ("netfilter: nft_fwd_netdev: Support egress hook")
|
||||
Reported-by: Xiang Mei <xmei5@asu.edu>
|
||||
Signed-off-by: Weiming Shi <bestswngs@gmail.com>
|
||||
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
|
||||
|
||||
Signed-off-by: Florian Westphal <fwestpha@redhat.com>
|
||||
|
||||
diff --git a/include/net/netfilter/nf_dup_netdev.h b/include/net/netfilter/nf_dup_netdev.h
|
||||
index b175d27..609bcf4 100644
|
||||
--- a/include/net/netfilter/nf_dup_netdev.h
|
||||
+++ b/include/net/netfilter/nf_dup_netdev.h
|
||||
@@ -3,10 +3,23 @@
|
||||
#define _NF_DUP_NETDEV_H_
|
||||
|
||||
#include <net/netfilter/nf_tables.h>
|
||||
+#include <linux/netdevice.h>
|
||||
+#include <linux/sched.h>
|
||||
|
||||
void nf_dup_netdev_egress(const struct nft_pktinfo *pkt, int oif);
|
||||
void nf_fwd_netdev_egress(const struct nft_pktinfo *pkt, int oif);
|
||||
|
||||
+#define NF_RECURSION_LIMIT 2
|
||||
+
|
||||
+static inline u8 *nf_get_nf_dup_skb_recursion(void)
|
||||
+{
|
||||
+#ifndef CONFIG_PREEMPT_RT
|
||||
+ return this_cpu_ptr(&softnet_data.xmit.nf_dup_skb_recursion);
|
||||
+#else
|
||||
+ return ¤t->net_xmit.nf_dup_skb_recursion;
|
||||
+#endif
|
||||
+}
|
||||
+
|
||||
struct nft_offload_ctx;
|
||||
struct nft_flow_rule;
|
||||
|
||||
diff --git a/net/netfilter/nf_dup_netdev.c b/net/netfilter/nf_dup_netdev.c
|
||||
index a8e2425..516bcf4 100644
|
||||
--- a/net/netfilter/nf_dup_netdev.c
|
||||
+++ b/net/netfilter/nf_dup_netdev.c
|
||||
@@ -17,6 +17,22 @@
|
||||
|
||||
static DEFINE_PER_CPU(u8, nf_dup_skb_recursion);
|
||||
|
||||
+#define NF_RECURSION_LIMIT 2
|
||||
+
|
||||
+#ifndef CONFIG_PREEMPT_RT
|
||||
+static u8 *nf_get_nf_dup_skb_recursion(void)
|
||||
+{
|
||||
+ return this_cpu_ptr(&softnet_data.xmit.nf_dup_skb_recursion);
|
||||
+}
|
||||
+#else
|
||||
+
|
||||
+static u8 *nf_get_nf_dup_skb_recursion(void)
|
||||
+{
|
||||
+ return ¤t->net_xmit.nf_dup_skb_recursion;
|
||||
+}
|
||||
+
|
||||
+#endif
|
||||
+
|
||||
static void nf_do_netdev_egress(struct sk_buff *skb, struct net_device *dev,
|
||||
enum nf_dev_hooks hook)
|
||||
{
|
||||
diff --git a/net/netfilter/nft_fwd_netdev.c b/net/netfilter/nft_fwd_netdev.c
|
||||
index 08246ef..a9743a1 100644
|
||||
--- a/net/netfilter/nft_fwd_netdev.c
|
||||
+++ b/net/netfilter/nft_fwd_netdev.c
|
||||
@@ -95,6 +95,7 @@ static void nft_fwd_neigh_eval(const struct nft_expr *expr,
|
||||
struct nft_regs *regs,
|
||||
const struct nft_pktinfo *pkt)
|
||||
{
|
||||
+ u8 *nf_dup_skb_recursion = nf_get_nf_dup_skb_recursion();
|
||||
struct nft_fwd_neigh *priv = nft_expr_priv(expr);
|
||||
void *addr = ®s->data[priv->sreg_addr];
|
||||
int oif = regs->data[priv->sreg_dev];
|
||||
@@ -143,6 +144,11 @@ static void nft_fwd_neigh_eval(const struct nft_expr *expr,
|
||||
goto out;
|
||||
}
|
||||
|
||||
+ if (*nf_dup_skb_recursion > NF_RECURSION_LIMIT) {
|
||||
+ verdict = NF_DROP;
|
||||
+ goto out;
|
||||
+ }
|
||||
+
|
||||
dev = dev_get_by_index_rcu(nft_net(pkt), oif);
|
||||
if (dev == NULL) {
|
||||
verdict = NF_DROP;
|
||||
@@ -160,7 +166,9 @@ static void nft_fwd_neigh_eval(const struct nft_expr *expr,
|
||||
|
||||
skb->dev = dev;
|
||||
skb_clear_tstamp(skb);
|
||||
+ (*nf_dup_skb_recursion)++;
|
||||
neigh_xmit(neigh_table, dev, addr, skb);
|
||||
+ (*nf_dup_skb_recursion)--;
|
||||
out:
|
||||
regs->verdict.code = verdict;
|
||||
}
|
||||
@ -0,0 +1,205 @@
|
||||
From 2dd0f2f9dede63706b7b211f6ae7c0b0db170e7b Mon Sep 17 00:00:00 2001
|
||||
From: Florian Westphal <fwestpha@redhat.com>
|
||||
Date: Wed, 13 May 2026 17:22:16 +0200
|
||||
Subject: [PATCH] netfilter: xtables: restrict several matches to inet family
|
||||
|
||||
JIRA: https://redhat.atlassian.net/browse/RHEL-168848
|
||||
Upstream Status: commit b6fe26f86a16
|
||||
|
||||
commit b6fe26f86a1649f84e057f3f15605b08eda15497
|
||||
Author: Pablo Neira Ayuso <pablo@netfilter.org>
|
||||
Date: Wed Apr 15 12:21:00 2026 +0200
|
||||
|
||||
netfilter: xtables: restrict several matches to inet family
|
||||
|
||||
This is a partial revert of:
|
||||
|
||||
commit ab4f21e6fb1c ("netfilter: xtables: use NFPROTO_UNSPEC in more extensions")
|
||||
|
||||
to allow ipv4 and ipv6 only.
|
||||
|
||||
- xt_mac
|
||||
- xt_owner
|
||||
- xt_physdev
|
||||
|
||||
These extensions are not used by ebtables in userspace.
|
||||
|
||||
Moreover, xt_realm is only for ipv4, since dst->tclassid is ipv4
|
||||
specific.
|
||||
|
||||
Fixes: ab4f21e6fb1c ("netfilter: xtables: use NFPROTO_UNSPEC in more extensions")
|
||||
Reported-by: "Kito Xu (veritas501)" <hxzene@gmail.com>
|
||||
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
|
||||
|
||||
Signed-off-by: Florian Westphal <fwestpha@redhat.com>
|
||||
|
||||
diff --git a/net/netfilter/xt_mac.c b/net/netfilter/xt_mac.c
|
||||
index 81649da..bd23547 100644
|
||||
--- a/net/netfilter/xt_mac.c
|
||||
+++ b/net/netfilter/xt_mac.c
|
||||
@@ -38,25 +38,37 @@ static bool mac_mt(const struct sk_buff *skb, struct xt_action_param *par)
|
||||
return ret;
|
||||
}
|
||||
|
||||
-static struct xt_match mac_mt_reg __read_mostly = {
|
||||
- .name = "mac",
|
||||
- .revision = 0,
|
||||
- .family = NFPROTO_UNSPEC,
|
||||
- .match = mac_mt,
|
||||
- .matchsize = sizeof(struct xt_mac_info),
|
||||
- .hooks = (1 << NF_INET_PRE_ROUTING) | (1 << NF_INET_LOCAL_IN) |
|
||||
- (1 << NF_INET_FORWARD),
|
||||
- .me = THIS_MODULE,
|
||||
+static struct xt_match mac_mt_reg[] __read_mostly = {
|
||||
+ {
|
||||
+ .name = "mac",
|
||||
+ .family = NFPROTO_IPV4,
|
||||
+ .match = mac_mt,
|
||||
+ .matchsize = sizeof(struct xt_mac_info),
|
||||
+ .hooks = (1 << NF_INET_PRE_ROUTING) |
|
||||
+ (1 << NF_INET_LOCAL_IN) |
|
||||
+ (1 << NF_INET_FORWARD),
|
||||
+ .me = THIS_MODULE,
|
||||
+ },
|
||||
+ {
|
||||
+ .name = "mac",
|
||||
+ .family = NFPROTO_IPV6,
|
||||
+ .match = mac_mt,
|
||||
+ .matchsize = sizeof(struct xt_mac_info),
|
||||
+ .hooks = (1 << NF_INET_PRE_ROUTING) |
|
||||
+ (1 << NF_INET_LOCAL_IN) |
|
||||
+ (1 << NF_INET_FORWARD),
|
||||
+ .me = THIS_MODULE,
|
||||
+ },
|
||||
};
|
||||
|
||||
static int __init mac_mt_init(void)
|
||||
{
|
||||
- return xt_register_match(&mac_mt_reg);
|
||||
+ return xt_register_matches(mac_mt_reg, ARRAY_SIZE(mac_mt_reg));
|
||||
}
|
||||
|
||||
static void __exit mac_mt_exit(void)
|
||||
{
|
||||
- xt_unregister_match(&mac_mt_reg);
|
||||
+ xt_unregister_matches(mac_mt_reg, ARRAY_SIZE(mac_mt_reg));
|
||||
}
|
||||
|
||||
module_init(mac_mt_init);
|
||||
diff --git a/net/netfilter/xt_owner.c b/net/netfilter/xt_owner.c
|
||||
index 5033288..7be2fe2 100644
|
||||
--- a/net/netfilter/xt_owner.c
|
||||
+++ b/net/netfilter/xt_owner.c
|
||||
@@ -127,26 +127,39 @@ owner_mt(const struct sk_buff *skb, struct xt_action_param *par)
|
||||
return true;
|
||||
}
|
||||
|
||||
-static struct xt_match owner_mt_reg __read_mostly = {
|
||||
- .name = "owner",
|
||||
- .revision = 1,
|
||||
- .family = NFPROTO_UNSPEC,
|
||||
- .checkentry = owner_check,
|
||||
- .match = owner_mt,
|
||||
- .matchsize = sizeof(struct xt_owner_match_info),
|
||||
- .hooks = (1 << NF_INET_LOCAL_OUT) |
|
||||
- (1 << NF_INET_POST_ROUTING),
|
||||
- .me = THIS_MODULE,
|
||||
+static struct xt_match owner_mt_reg[] __read_mostly = {
|
||||
+ {
|
||||
+ .name = "owner",
|
||||
+ .revision = 1,
|
||||
+ .family = NFPROTO_IPV4,
|
||||
+ .checkentry = owner_check,
|
||||
+ .match = owner_mt,
|
||||
+ .matchsize = sizeof(struct xt_owner_match_info),
|
||||
+ .hooks = (1 << NF_INET_LOCAL_OUT) |
|
||||
+ (1 << NF_INET_POST_ROUTING),
|
||||
+ .me = THIS_MODULE,
|
||||
+ },
|
||||
+ {
|
||||
+ .name = "owner",
|
||||
+ .revision = 1,
|
||||
+ .family = NFPROTO_IPV6,
|
||||
+ .checkentry = owner_check,
|
||||
+ .match = owner_mt,
|
||||
+ .matchsize = sizeof(struct xt_owner_match_info),
|
||||
+ .hooks = (1 << NF_INET_LOCAL_OUT) |
|
||||
+ (1 << NF_INET_POST_ROUTING),
|
||||
+ .me = THIS_MODULE,
|
||||
+ }
|
||||
};
|
||||
|
||||
static int __init owner_mt_init(void)
|
||||
{
|
||||
- return xt_register_match(&owner_mt_reg);
|
||||
+ return xt_register_matches(owner_mt_reg, ARRAY_SIZE(owner_mt_reg));
|
||||
}
|
||||
|
||||
static void __exit owner_mt_exit(void)
|
||||
{
|
||||
- xt_unregister_match(&owner_mt_reg);
|
||||
+ xt_unregister_matches(owner_mt_reg, ARRAY_SIZE(owner_mt_reg));
|
||||
}
|
||||
|
||||
module_init(owner_mt_init);
|
||||
diff --git a/net/netfilter/xt_physdev.c b/net/netfilter/xt_physdev.c
|
||||
index 343e65f..130842c 100644
|
||||
--- a/net/netfilter/xt_physdev.c
|
||||
+++ b/net/netfilter/xt_physdev.c
|
||||
@@ -115,24 +115,33 @@ static int physdev_mt_check(const struct xt_mtchk_param *par)
|
||||
return 0;
|
||||
}
|
||||
|
||||
-static struct xt_match physdev_mt_reg __read_mostly = {
|
||||
- .name = "physdev",
|
||||
- .revision = 0,
|
||||
- .family = NFPROTO_UNSPEC,
|
||||
- .checkentry = physdev_mt_check,
|
||||
- .match = physdev_mt,
|
||||
- .matchsize = sizeof(struct xt_physdev_info),
|
||||
- .me = THIS_MODULE,
|
||||
+static struct xt_match physdev_mt_reg[] __read_mostly = {
|
||||
+ {
|
||||
+ .name = "physdev",
|
||||
+ .family = NFPROTO_IPV4,
|
||||
+ .checkentry = physdev_mt_check,
|
||||
+ .match = physdev_mt,
|
||||
+ .matchsize = sizeof(struct xt_physdev_info),
|
||||
+ .me = THIS_MODULE,
|
||||
+ },
|
||||
+ {
|
||||
+ .name = "physdev",
|
||||
+ .family = NFPROTO_IPV6,
|
||||
+ .checkentry = physdev_mt_check,
|
||||
+ .match = physdev_mt,
|
||||
+ .matchsize = sizeof(struct xt_physdev_info),
|
||||
+ .me = THIS_MODULE,
|
||||
+ },
|
||||
};
|
||||
|
||||
static int __init physdev_mt_init(void)
|
||||
{
|
||||
- return xt_register_match(&physdev_mt_reg);
|
||||
+ return xt_register_matches(physdev_mt_reg, ARRAY_SIZE(physdev_mt_reg));
|
||||
}
|
||||
|
||||
static void __exit physdev_mt_exit(void)
|
||||
{
|
||||
- xt_unregister_match(&physdev_mt_reg);
|
||||
+ xt_unregister_matches(physdev_mt_reg, ARRAY_SIZE(physdev_mt_reg));
|
||||
}
|
||||
|
||||
module_init(physdev_mt_init);
|
||||
diff --git a/net/netfilter/xt_realm.c b/net/netfilter/xt_realm.c
|
||||
index 6df485f..61b2f1e 100644
|
||||
--- a/net/netfilter/xt_realm.c
|
||||
+++ b/net/netfilter/xt_realm.c
|
||||
@@ -33,7 +33,7 @@ static struct xt_match realm_mt_reg __read_mostly = {
|
||||
.matchsize = sizeof(struct xt_realm_info),
|
||||
.hooks = (1 << NF_INET_POST_ROUTING) | (1 << NF_INET_FORWARD) |
|
||||
(1 << NF_INET_LOCAL_OUT) | (1 << NF_INET_LOCAL_IN),
|
||||
- .family = NFPROTO_UNSPEC,
|
||||
+ .family = NFPROTO_IPV4,
|
||||
.me = THIS_MODULE
|
||||
};
|
||||
|
||||
@ -0,0 +1,487 @@
|
||||
From 65acf9af5571ee9fd69dd8d8e4e5b7a423905d3b Mon Sep 17 00:00:00 2001
|
||||
From: Florian Westphal <fwestpha@redhat.com>
|
||||
Date: Wed, 13 May 2026 17:22:59 +0200
|
||||
Subject: [PATCH] netfilter: x_tables: add .check_hooks to matches and targets
|
||||
|
||||
JIRA: https://redhat.atlassian.net/browse/RHEL-168848
|
||||
Upstream Status: commit 6813985ca456
|
||||
|
||||
commit 6813985ca456d1f5677ad9554f55805cbf27e16f
|
||||
Author: Pablo Neira Ayuso <pablo@netfilter.org>
|
||||
Date: Tue Apr 28 17:35:18 2026 +0200
|
||||
|
||||
netfilter: x_tables: add .check_hooks to matches and targets
|
||||
|
||||
Add a new .check_hooks interface for checking if the match/target is
|
||||
used from the validate hook according to its configuration.
|
||||
|
||||
Move existing conditional hook check based on the match/target
|
||||
configuration from .checkentry to .check_hooks for the following
|
||||
matches/targets:
|
||||
|
||||
- addrtype
|
||||
- devgroup
|
||||
- physdev
|
||||
- policy
|
||||
- set
|
||||
- TCPMSS
|
||||
- SET
|
||||
|
||||
This is a preparation patch to fix nft_compat, not functional changes
|
||||
are intended.
|
||||
|
||||
Based on patch from Florian Westphal.
|
||||
|
||||
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
|
||||
|
||||
Signed-off-by: Florian Westphal <fwestpha@redhat.com>
|
||||
|
||||
diff --git a/include/linux/netfilter/x_tables.h b/include/linux/netfilter/x_tables.h
|
||||
index 5897f3d..53b1f25 100644
|
||||
--- a/include/linux/netfilter/x_tables.h
|
||||
+++ b/include/linux/netfilter/x_tables.h
|
||||
@@ -156,6 +156,9 @@ struct xt_match {
|
||||
/* Called when user tries to insert an entry of this type. */
|
||||
int (*checkentry)(const struct xt_mtchk_param *);
|
||||
|
||||
+ /* Called to validate hooks based on the match configuration. */
|
||||
+ int (*check_hooks)(const struct xt_mtchk_param *);
|
||||
+
|
||||
/* Called when entry of this type deleted. */
|
||||
void (*destroy)(const struct xt_mtdtor_param *);
|
||||
#ifdef CONFIG_NETFILTER_XTABLES_COMPAT
|
||||
@@ -197,6 +200,9 @@ struct xt_target {
|
||||
/* Should return 0 on success or an error code otherwise (-Exxxx). */
|
||||
int (*checkentry)(const struct xt_tgchk_param *);
|
||||
|
||||
+ /* Called to validate hooks based on the target configuration. */
|
||||
+ int (*check_hooks)(const struct xt_tgchk_param *);
|
||||
+
|
||||
/* Called when entry of this type deleted. */
|
||||
void (*destroy)(const struct xt_tgdtor_param *);
|
||||
#ifdef CONFIG_NETFILTER_XTABLES_COMPAT
|
||||
@@ -289,8 +295,10 @@ bool xt_find_jump_offset(const unsigned int *offsets,
|
||||
|
||||
int xt_check_proc_name(const char *name, unsigned int size);
|
||||
|
||||
+int xt_check_hooks_match(struct xt_mtchk_param *par);
|
||||
int xt_check_match(struct xt_mtchk_param *, unsigned int size, u16 proto,
|
||||
bool inv_proto);
|
||||
+int xt_check_hooks_target(struct xt_tgchk_param *par);
|
||||
int xt_check_target(struct xt_tgchk_param *, unsigned int size, u16 proto,
|
||||
bool inv_proto);
|
||||
|
||||
diff --git a/net/netfilter/x_tables.c b/net/netfilter/x_tables.c
|
||||
index 10530a0..9be7832 100644
|
||||
--- a/net/netfilter/x_tables.c
|
||||
+++ b/net/netfilter/x_tables.c
|
||||
@@ -477,11 +477,9 @@ int xt_check_proc_name(const char *name, unsigned int size)
|
||||
}
|
||||
EXPORT_SYMBOL(xt_check_proc_name);
|
||||
|
||||
-int xt_check_match(struct xt_mtchk_param *par,
|
||||
- unsigned int size, u16 proto, bool inv_proto)
|
||||
+static int xt_check_match_common(struct xt_mtchk_param *par,
|
||||
+ unsigned int size, u16 proto, bool inv_proto)
|
||||
{
|
||||
- int ret;
|
||||
-
|
||||
if (XT_ALIGN(par->match->matchsize) != size &&
|
||||
par->match->matchsize != -1) {
|
||||
/*
|
||||
@@ -530,6 +528,14 @@ int xt_check_match(struct xt_mtchk_param *par,
|
||||
par->match->proto);
|
||||
return -EINVAL;
|
||||
}
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int xt_checkentry_match(struct xt_mtchk_param *par)
|
||||
+{
|
||||
+ int ret;
|
||||
+
|
||||
if (par->match->checkentry != NULL) {
|
||||
ret = par->match->checkentry(par);
|
||||
if (ret < 0)
|
||||
@@ -538,8 +544,34 @@ int xt_check_match(struct xt_mtchk_param *par,
|
||||
/* Flag up potential errors. */
|
||||
return -EIO;
|
||||
}
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+int xt_check_hooks_match(struct xt_mtchk_param *par)
|
||||
+{
|
||||
+ if (par->match->check_hooks != NULL)
|
||||
+ return par->match->check_hooks(par);
|
||||
+
|
||||
return 0;
|
||||
}
|
||||
+EXPORT_SYMBOL_GPL(xt_check_hooks_match);
|
||||
+
|
||||
+int xt_check_match(struct xt_mtchk_param *par,
|
||||
+ unsigned int size, u16 proto, bool inv_proto)
|
||||
+{
|
||||
+ int ret;
|
||||
+
|
||||
+ ret = xt_check_match_common(par, size, proto, inv_proto);
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
+
|
||||
+ ret = xt_check_hooks_match(par);
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
+
|
||||
+ return xt_checkentry_match(par);
|
||||
+}
|
||||
EXPORT_SYMBOL_GPL(xt_check_match);
|
||||
|
||||
/** xt_check_entry_match - check that matches end before start of target
|
||||
@@ -1008,11 +1040,9 @@ bool xt_find_jump_offset(const unsigned int *offsets,
|
||||
}
|
||||
EXPORT_SYMBOL(xt_find_jump_offset);
|
||||
|
||||
-int xt_check_target(struct xt_tgchk_param *par,
|
||||
- unsigned int size, u16 proto, bool inv_proto)
|
||||
+static int xt_check_target_common(struct xt_tgchk_param *par,
|
||||
+ unsigned int size, u16 proto, bool inv_proto)
|
||||
{
|
||||
- int ret;
|
||||
-
|
||||
if (XT_ALIGN(par->target->targetsize) != size) {
|
||||
pr_err_ratelimited("%s_tables: %s.%u target: invalid size %u (kernel) != (user) %u\n",
|
||||
xt_prefix[par->family], par->target->name,
|
||||
@@ -1057,6 +1087,23 @@ int xt_check_target(struct xt_tgchk_param *par,
|
||||
par->target->proto);
|
||||
return -EINVAL;
|
||||
}
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+int xt_check_hooks_target(struct xt_tgchk_param *par)
|
||||
+{
|
||||
+ if (par->target->check_hooks != NULL)
|
||||
+ return par->target->check_hooks(par);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+EXPORT_SYMBOL_GPL(xt_check_hooks_target);
|
||||
+
|
||||
+static int xt_checkentry_target(struct xt_tgchk_param *par)
|
||||
+{
|
||||
+ int ret;
|
||||
+
|
||||
if (par->target->checkentry != NULL) {
|
||||
ret = par->target->checkentry(par);
|
||||
if (ret < 0)
|
||||
@@ -1067,6 +1114,22 @@ int xt_check_target(struct xt_tgchk_param *par,
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
+
|
||||
+int xt_check_target(struct xt_tgchk_param *par,
|
||||
+ unsigned int size, u16 proto, bool inv_proto)
|
||||
+{
|
||||
+ int ret;
|
||||
+
|
||||
+ ret = xt_check_target_common(par, size, proto, inv_proto);
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
+
|
||||
+ ret = xt_check_hooks_target(par);
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
+
|
||||
+ return xt_checkentry_target(par);
|
||||
+}
|
||||
EXPORT_SYMBOL_GPL(xt_check_target);
|
||||
|
||||
/**
|
||||
diff --git a/net/netfilter/xt_addrtype.c b/net/netfilter/xt_addrtype.c
|
||||
index a770889..913dbe3 100644
|
||||
--- a/net/netfilter/xt_addrtype.c
|
||||
+++ b/net/netfilter/xt_addrtype.c
|
||||
@@ -153,14 +153,10 @@ addrtype_mt_v1(const struct sk_buff *skb, struct xt_action_param *par)
|
||||
return ret;
|
||||
}
|
||||
|
||||
-static int addrtype_mt_checkentry_v1(const struct xt_mtchk_param *par)
|
||||
+static int addrtype_mt_check_hooks(const struct xt_mtchk_param *par)
|
||||
{
|
||||
- const char *errmsg = "both incoming and outgoing interface limitation cannot be selected";
|
||||
struct xt_addrtype_info_v1 *info = par->matchinfo;
|
||||
-
|
||||
- if (info->flags & XT_ADDRTYPE_LIMIT_IFACE_IN &&
|
||||
- info->flags & XT_ADDRTYPE_LIMIT_IFACE_OUT)
|
||||
- goto err;
|
||||
+ const char *errmsg;
|
||||
|
||||
if (par->hook_mask & ((1 << NF_INET_PRE_ROUTING) |
|
||||
(1 << NF_INET_LOCAL_IN)) &&
|
||||
@@ -176,6 +172,21 @@ static int addrtype_mt_checkentry_v1(const struct xt_mtchk_param *par)
|
||||
goto err;
|
||||
}
|
||||
|
||||
+ return 0;
|
||||
+err:
|
||||
+ pr_info_ratelimited("%s\n", errmsg);
|
||||
+ return -EINVAL;
|
||||
+}
|
||||
+
|
||||
+static int addrtype_mt_checkentry_v1(const struct xt_mtchk_param *par)
|
||||
+{
|
||||
+ const char *errmsg = "both incoming and outgoing interface limitation cannot be selected";
|
||||
+ struct xt_addrtype_info_v1 *info = par->matchinfo;
|
||||
+
|
||||
+ if (info->flags & XT_ADDRTYPE_LIMIT_IFACE_IN &&
|
||||
+ info->flags & XT_ADDRTYPE_LIMIT_IFACE_OUT)
|
||||
+ goto err;
|
||||
+
|
||||
#if IS_ENABLED(CONFIG_IP6_NF_IPTABLES)
|
||||
if (par->family == NFPROTO_IPV6) {
|
||||
if ((info->source | info->dest) & XT_ADDRTYPE_BLACKHOLE) {
|
||||
@@ -211,6 +222,7 @@ static struct xt_match addrtype_mt_reg[] __read_mostly = {
|
||||
.family = NFPROTO_IPV4,
|
||||
.revision = 1,
|
||||
.match = addrtype_mt_v1,
|
||||
+ .check_hooks = addrtype_mt_check_hooks,
|
||||
.checkentry = addrtype_mt_checkentry_v1,
|
||||
.matchsize = sizeof(struct xt_addrtype_info_v1),
|
||||
.me = THIS_MODULE
|
||||
@@ -221,6 +233,7 @@ static struct xt_match addrtype_mt_reg[] __read_mostly = {
|
||||
.family = NFPROTO_IPV6,
|
||||
.revision = 1,
|
||||
.match = addrtype_mt_v1,
|
||||
+ .check_hooks = addrtype_mt_check_hooks,
|
||||
.checkentry = addrtype_mt_checkentry_v1,
|
||||
.matchsize = sizeof(struct xt_addrtype_info_v1),
|
||||
.me = THIS_MODULE
|
||||
diff --git a/net/netfilter/xt_devgroup.c b/net/netfilter/xt_devgroup.c
|
||||
index 9520dd0..6d1a44a 100644
|
||||
--- a/net/netfilter/xt_devgroup.c
|
||||
+++ b/net/netfilter/xt_devgroup.c
|
||||
@@ -33,14 +33,10 @@ static bool devgroup_mt(const struct sk_buff *skb, struct xt_action_param *par)
|
||||
return true;
|
||||
}
|
||||
|
||||
-static int devgroup_mt_checkentry(const struct xt_mtchk_param *par)
|
||||
+static int devgroup_mt_check_hooks(const struct xt_mtchk_param *par)
|
||||
{
|
||||
const struct xt_devgroup_info *info = par->matchinfo;
|
||||
|
||||
- if (info->flags & ~(XT_DEVGROUP_MATCH_SRC | XT_DEVGROUP_INVERT_SRC |
|
||||
- XT_DEVGROUP_MATCH_DST | XT_DEVGROUP_INVERT_DST))
|
||||
- return -EINVAL;
|
||||
-
|
||||
if (info->flags & XT_DEVGROUP_MATCH_SRC &&
|
||||
par->hook_mask & ~((1 << NF_INET_PRE_ROUTING) |
|
||||
(1 << NF_INET_LOCAL_IN) |
|
||||
@@ -56,9 +52,21 @@ static int devgroup_mt_checkentry(const struct xt_mtchk_param *par)
|
||||
return 0;
|
||||
}
|
||||
|
||||
+static int devgroup_mt_checkentry(const struct xt_mtchk_param *par)
|
||||
+{
|
||||
+ const struct xt_devgroup_info *info = par->matchinfo;
|
||||
+
|
||||
+ if (info->flags & ~(XT_DEVGROUP_MATCH_SRC | XT_DEVGROUP_INVERT_SRC |
|
||||
+ XT_DEVGROUP_MATCH_DST | XT_DEVGROUP_INVERT_DST))
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
static struct xt_match devgroup_mt_reg __read_mostly = {
|
||||
.name = "devgroup",
|
||||
.match = devgroup_mt,
|
||||
+ .check_hooks = devgroup_mt_check_hooks,
|
||||
.checkentry = devgroup_mt_checkentry,
|
||||
.matchsize = sizeof(struct xt_devgroup_info),
|
||||
.family = NFPROTO_UNSPEC,
|
||||
diff --git a/net/netfilter/xt_physdev.c b/net/netfilter/xt_physdev.c
|
||||
index 130842c..e6025d7 100644
|
||||
--- a/net/netfilter/xt_physdev.c
|
||||
+++ b/net/netfilter/xt_physdev.c
|
||||
@@ -91,14 +91,10 @@ physdev_mt(const struct sk_buff *skb, struct xt_action_param *par)
|
||||
return (!!ret ^ !(info->invert & XT_PHYSDEV_OP_OUT));
|
||||
}
|
||||
|
||||
-static int physdev_mt_check(const struct xt_mtchk_param *par)
|
||||
+static int physdev_mt_check_hooks(const struct xt_mtchk_param *par)
|
||||
{
|
||||
const struct xt_physdev_info *info = par->matchinfo;
|
||||
- static bool brnf_probed __read_mostly;
|
||||
|
||||
- if (!(info->bitmask & XT_PHYSDEV_OP_MASK) ||
|
||||
- info->bitmask & ~XT_PHYSDEV_OP_MASK)
|
||||
- return -EINVAL;
|
||||
if (info->bitmask & (XT_PHYSDEV_OP_OUT | XT_PHYSDEV_OP_ISOUT) &&
|
||||
(!(info->bitmask & XT_PHYSDEV_OP_BRIDGED) ||
|
||||
info->invert & XT_PHYSDEV_OP_BRIDGED) &&
|
||||
@@ -107,6 +103,18 @@ static int physdev_mt_check(const struct xt_mtchk_param *par)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int physdev_mt_check(const struct xt_mtchk_param *par)
|
||||
+{
|
||||
+ const struct xt_physdev_info *info = par->matchinfo;
|
||||
+ static bool brnf_probed __read_mostly;
|
||||
+
|
||||
+ if (!(info->bitmask & XT_PHYSDEV_OP_MASK) ||
|
||||
+ info->bitmask & ~XT_PHYSDEV_OP_MASK)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
if (!brnf_probed) {
|
||||
brnf_probed = true;
|
||||
request_module("br_netfilter");
|
||||
@@ -119,6 +127,7 @@ static struct xt_match physdev_mt_reg[] __read_mostly = {
|
||||
{
|
||||
.name = "physdev",
|
||||
.family = NFPROTO_IPV4,
|
||||
+ .check_hooks = physdev_mt_check_hooks,
|
||||
.checkentry = physdev_mt_check,
|
||||
.match = physdev_mt,
|
||||
.matchsize = sizeof(struct xt_physdev_info),
|
||||
@@ -127,6 +136,7 @@ static struct xt_match physdev_mt_reg[] __read_mostly = {
|
||||
{
|
||||
.name = "physdev",
|
||||
.family = NFPROTO_IPV6,
|
||||
+ .check_hooks = physdev_mt_check_hooks,
|
||||
.checkentry = physdev_mt_check,
|
||||
.match = physdev_mt,
|
||||
.matchsize = sizeof(struct xt_physdev_info),
|
||||
diff --git a/net/netfilter/xt_policy.c b/net/netfilter/xt_policy.c
|
||||
index b5fa655..ff54e3a 100644
|
||||
--- a/net/netfilter/xt_policy.c
|
||||
+++ b/net/netfilter/xt_policy.c
|
||||
@@ -126,13 +126,10 @@ policy_mt(const struct sk_buff *skb, struct xt_action_param *par)
|
||||
return ret;
|
||||
}
|
||||
|
||||
-static int policy_mt_check(const struct xt_mtchk_param *par)
|
||||
+static int policy_mt_check_hooks(const struct xt_mtchk_param *par)
|
||||
{
|
||||
const struct xt_policy_info *info = par->matchinfo;
|
||||
- const char *errmsg = "neither incoming nor outgoing policy selected";
|
||||
-
|
||||
- if (!(info->flags & (XT_POLICY_MATCH_IN|XT_POLICY_MATCH_OUT)))
|
||||
- goto err;
|
||||
+ const char *errmsg;
|
||||
|
||||
if (par->hook_mask & ((1 << NF_INET_PRE_ROUTING) |
|
||||
(1 << NF_INET_LOCAL_IN)) && info->flags & XT_POLICY_MATCH_OUT) {
|
||||
@@ -144,6 +141,21 @@ static int policy_mt_check(const struct xt_mtchk_param *par)
|
||||
errmsg = "input policy not valid in POSTROUTING and OUTPUT";
|
||||
goto err;
|
||||
}
|
||||
+
|
||||
+ return 0;
|
||||
+err:
|
||||
+ pr_info_ratelimited("%s\n", errmsg);
|
||||
+ return -EINVAL;
|
||||
+}
|
||||
+
|
||||
+static int policy_mt_check(const struct xt_mtchk_param *par)
|
||||
+{
|
||||
+ const struct xt_policy_info *info = par->matchinfo;
|
||||
+ const char *errmsg = "neither incoming nor outgoing policy selected";
|
||||
+
|
||||
+ if (!(info->flags & (XT_POLICY_MATCH_IN|XT_POLICY_MATCH_OUT)))
|
||||
+ goto err;
|
||||
+
|
||||
if (info->len > XT_POLICY_MAX_ELEM) {
|
||||
errmsg = "too many policy elements";
|
||||
goto err;
|
||||
@@ -158,6 +170,7 @@ static struct xt_match policy_mt_reg[] __read_mostly = {
|
||||
{
|
||||
.name = "policy",
|
||||
.family = NFPROTO_IPV4,
|
||||
+ .check_hooks = policy_mt_check_hooks,
|
||||
.checkentry = policy_mt_check,
|
||||
.match = policy_mt,
|
||||
.matchsize = sizeof(struct xt_policy_info),
|
||||
@@ -166,6 +179,7 @@ static struct xt_match policy_mt_reg[] __read_mostly = {
|
||||
{
|
||||
.name = "policy",
|
||||
.family = NFPROTO_IPV6,
|
||||
+ .check_hooks = policy_mt_check_hooks,
|
||||
.checkentry = policy_mt_check,
|
||||
.match = policy_mt,
|
||||
.matchsize = sizeof(struct xt_policy_info),
|
||||
diff --git a/net/netfilter/xt_set.c b/net/netfilter/xt_set.c
|
||||
index 731bc2c..4ae04bb 100644
|
||||
--- a/net/netfilter/xt_set.c
|
||||
+++ b/net/netfilter/xt_set.c
|
||||
@@ -430,6 +430,29 @@ set_target_v3(struct sk_buff *skb, const struct xt_action_param *par)
|
||||
return XT_CONTINUE;
|
||||
}
|
||||
|
||||
+static int
|
||||
+set_target_v3_check_hooks(const struct xt_tgchk_param *par)
|
||||
+{
|
||||
+ const struct xt_set_info_target_v3 *info = par->targinfo;
|
||||
+
|
||||
+ if (info->map_set.index != IPSET_INVALID_ID) {
|
||||
+ if (strncmp(par->table, "mangle", 7)) {
|
||||
+ pr_info_ratelimited("--map-set only usable from mangle table\n");
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+ if (((info->flags & IPSET_FLAG_MAP_SKBPRIO) |
|
||||
+ (info->flags & IPSET_FLAG_MAP_SKBQUEUE)) &&
|
||||
+ (par->hook_mask & ~(1 << NF_INET_FORWARD |
|
||||
+ 1 << NF_INET_LOCAL_OUT |
|
||||
+ 1 << NF_INET_POST_ROUTING))) {
|
||||
+ pr_info_ratelimited("mapping of prio or/and queue is allowed only from OUTPUT/FORWARD/POSTROUTING chains\n");
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
static int
|
||||
set_target_v3_checkentry(const struct xt_tgchk_param *par)
|
||||
{
|
||||
@@ -459,20 +482,6 @@ set_target_v3_checkentry(const struct xt_tgchk_param *par)
|
||||
}
|
||||
|
||||
if (info->map_set.index != IPSET_INVALID_ID) {
|
||||
- if (strncmp(par->table, "mangle", 7)) {
|
||||
- pr_info_ratelimited("--map-set only usable from mangle table\n");
|
||||
- ret = -EINVAL;
|
||||
- goto cleanup_del;
|
||||
- }
|
||||
- if (((info->flags & IPSET_FLAG_MAP_SKBPRIO) |
|
||||
- (info->flags & IPSET_FLAG_MAP_SKBQUEUE)) &&
|
||||
- (par->hook_mask & ~(1 << NF_INET_FORWARD |
|
||||
- 1 << NF_INET_LOCAL_OUT |
|
||||
- 1 << NF_INET_POST_ROUTING))) {
|
||||
- pr_info_ratelimited("mapping of prio or/and queue is allowed only from OUTPUT/FORWARD/POSTROUTING chains\n");
|
||||
- ret = -EINVAL;
|
||||
- goto cleanup_del;
|
||||
- }
|
||||
index = ip_set_nfnl_get_byindex(par->net,
|
||||
info->map_set.index);
|
||||
if (index == IPSET_INVALID_ID) {
|
||||
@@ -672,6 +681,7 @@ static struct xt_target set_targets[] __read_mostly = {
|
||||
.family = NFPROTO_IPV4,
|
||||
.target = set_target_v3,
|
||||
.targetsize = sizeof(struct xt_set_info_target_v3),
|
||||
+ .check_hooks = set_target_v3_check_hooks,
|
||||
.checkentry = set_target_v3_checkentry,
|
||||
.destroy = set_target_v3_destroy,
|
||||
.me = THIS_MODULE
|
||||
@@ -682,6 +692,7 @@ static struct xt_target set_targets[] __read_mostly = {
|
||||
.family = NFPROTO_IPV6,
|
||||
.target = set_target_v3,
|
||||
.targetsize = sizeof(struct xt_set_info_target_v3),
|
||||
+ .check_hooks = set_target_v3_check_hooks,
|
||||
.checkentry = set_target_v3_checkentry,
|
||||
.destroy = set_target_v3_destroy,
|
||||
.me = THIS_MODULE
|
||||
@ -0,0 +1,148 @@
|
||||
From 0a7bf7be14b96c957f43956b2cec03faeee6a6c7 Mon Sep 17 00:00:00 2001
|
||||
From: Florian Westphal <fwestpha@redhat.com>
|
||||
Date: Wed, 13 May 2026 17:23:00 +0200
|
||||
Subject: [PATCH] netfilter: nft_compat: run xt_check_hooks_{match,target}()
|
||||
from .validate
|
||||
|
||||
JIRA: https://redhat.atlassian.net/browse/RHEL-168848
|
||||
Upstream Status: commit 2f768d638d97
|
||||
|
||||
commit 2f768d638d977eff824f64dcc9639e3fea32da8f
|
||||
Author: Pablo Neira Ayuso <pablo@netfilter.org>
|
||||
Date: Tue Apr 28 19:04:07 2026 +0200
|
||||
|
||||
netfilter: nft_compat: run xt_check_hooks_{match,target}() from .validate
|
||||
|
||||
Several matches and one target check that the hook is correct from
|
||||
checkentry(), however, the basechain is only available from
|
||||
nft_table_validate().
|
||||
|
||||
This patch uses xt_check_hooks_{match,target}() from the nft_compat
|
||||
expression .validate path.
|
||||
|
||||
This patch sets the table in the nft_ctx struct in nft_table_validate()
|
||||
which is required by this patch.
|
||||
|
||||
Based on patch from Florian Westphal.
|
||||
|
||||
Fixes: 0ca743a55991 ("netfilter: nf_tables: add compatibility layer for x_tables")
|
||||
Reported-by: Xiang Mei <xmei5@asu.edu>
|
||||
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
|
||||
|
||||
Signed-off-by: Florian Westphal <fwestpha@redhat.com>
|
||||
|
||||
diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c
|
||||
index 32155f1..188a621 100644
|
||||
--- a/net/netfilter/nf_tables_api.c
|
||||
+++ b/net/netfilter/nf_tables_api.c
|
||||
@@ -4185,6 +4185,7 @@ static int nft_table_validate(struct net *net, const struct nft_table *table)
|
||||
struct nft_chain *chain;
|
||||
struct nft_ctx ctx = {
|
||||
.net = net,
|
||||
+ .table = (struct nft_table *)table,
|
||||
.family = table->family,
|
||||
};
|
||||
int err = 0;
|
||||
diff --git a/net/netfilter/nft_compat.c b/net/netfilter/nft_compat.c
|
||||
index 72711d6..c58a93d 100644
|
||||
--- a/net/netfilter/nft_compat.c
|
||||
+++ b/net/netfilter/nft_compat.c
|
||||
@@ -260,10 +260,10 @@ nft_target_init(const struct nft_ctx *ctx, const struct nft_expr *expr,
|
||||
return ret;
|
||||
}
|
||||
|
||||
- nft_target_set_tgchk_param(&par, ctx, target, info, &e, proto, inv);
|
||||
-
|
||||
nft_compat_wait_for_destructors(ctx->net);
|
||||
|
||||
+ nft_target_set_tgchk_param(&par, ctx, target, info, &e, proto, inv);
|
||||
+
|
||||
ret = xt_check_target(&par, size, proto, inv);
|
||||
if (ret < 0) {
|
||||
if (ret == -ENOENT) {
|
||||
@@ -352,8 +352,6 @@ static int nft_target_dump(struct sk_buff *skb,
|
||||
static int nft_target_validate(const struct nft_ctx *ctx,
|
||||
const struct nft_expr *expr)
|
||||
{
|
||||
- struct xt_target *target = expr->ops->data;
|
||||
- unsigned int hook_mask = 0;
|
||||
int ret;
|
||||
|
||||
if (ctx->family != NFPROTO_IPV4 &&
|
||||
@@ -376,11 +374,21 @@ static int nft_target_validate(const struct nft_ctx *ctx,
|
||||
const struct nft_base_chain *basechain =
|
||||
nft_base_chain(ctx->chain);
|
||||
const struct nf_hook_ops *ops = &basechain->ops;
|
||||
+ unsigned int hook_mask = 1 << ops->hooknum;
|
||||
+ struct xt_target *target = expr->ops->data;
|
||||
+ void *info = nft_expr_priv(expr);
|
||||
+ struct xt_tgchk_param par;
|
||||
+ union nft_entry e = {};
|
||||
|
||||
- hook_mask = 1 << ops->hooknum;
|
||||
if (target->hooks && !(hook_mask & target->hooks))
|
||||
return -EINVAL;
|
||||
|
||||
+ nft_target_set_tgchk_param(&par, ctx, target, info, &e, 0, false);
|
||||
+
|
||||
+ ret = xt_check_hooks_target(&par);
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
+
|
||||
ret = nft_compat_chain_validate_dependency(ctx, target->table);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
@@ -513,10 +521,10 @@ __nft_match_init(const struct nft_ctx *ctx, const struct nft_expr *expr,
|
||||
return ret;
|
||||
}
|
||||
|
||||
- nft_match_set_mtchk_param(&par, ctx, match, info, &e, proto, inv);
|
||||
-
|
||||
nft_compat_wait_for_destructors(ctx->net);
|
||||
|
||||
+ nft_match_set_mtchk_param(&par, ctx, match, info, &e, proto, inv);
|
||||
+
|
||||
return xt_check_match(&par, size, proto, inv);
|
||||
}
|
||||
|
||||
@@ -612,8 +620,6 @@ static int nft_match_large_dump(struct sk_buff *skb,
|
||||
static int nft_match_validate(const struct nft_ctx *ctx,
|
||||
const struct nft_expr *expr)
|
||||
{
|
||||
- struct xt_match *match = expr->ops->data;
|
||||
- unsigned int hook_mask = 0;
|
||||
int ret;
|
||||
|
||||
if (ctx->family != NFPROTO_IPV4 &&
|
||||
@@ -636,11 +642,30 @@ static int nft_match_validate(const struct nft_ctx *ctx,
|
||||
const struct nft_base_chain *basechain =
|
||||
nft_base_chain(ctx->chain);
|
||||
const struct nf_hook_ops *ops = &basechain->ops;
|
||||
+ unsigned int hook_mask = 1 << ops->hooknum;
|
||||
+ struct xt_match *match = expr->ops->data;
|
||||
+ size_t size = XT_ALIGN(match->matchsize);
|
||||
+ struct xt_mtchk_param par;
|
||||
+ union nft_entry e = {};
|
||||
+ void *info;
|
||||
|
||||
- hook_mask = 1 << ops->hooknum;
|
||||
if (match->hooks && !(hook_mask & match->hooks))
|
||||
return -EINVAL;
|
||||
|
||||
+ if (NFT_EXPR_SIZE(size) > NFT_MATCH_LARGE_THRESH) {
|
||||
+ struct nft_xt_match_priv *priv = nft_expr_priv(expr);
|
||||
+
|
||||
+ info = priv->info;
|
||||
+ } else {
|
||||
+ info = nft_expr_priv(expr);
|
||||
+ }
|
||||
+
|
||||
+ nft_match_set_mtchk_param(&par, ctx, match, info, &e, 0, false);
|
||||
+
|
||||
+ ret = xt_check_hooks_match(&par);
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
+
|
||||
ret = nft_compat_chain_validate_dependency(ctx, match->table);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
@ -0,0 +1,69 @@
|
||||
From 9b62200a0fb5be4d5f78d4911d534b2cda92d079 Mon Sep 17 00:00:00 2001
|
||||
From: Florian Westphal <fwestpha@redhat.com>
|
||||
Date: Wed, 13 May 2026 17:23:01 +0200
|
||||
Subject: [PATCH] netfilter: xt_CT: fix usersize for v1 and v2 revision
|
||||
|
||||
JIRA: https://redhat.atlassian.net/browse/RHEL-168848
|
||||
Upstream Status: commit 8bedb6c46945
|
||||
|
||||
commit 8bedb6c46945752a688d9b0cf2021e0e68b1876c
|
||||
Author: Florian Westphal <fw@strlen.de>
|
||||
Date: Tue Apr 28 19:37:57 2026 +0200
|
||||
|
||||
netfilter: xt_CT: fix usersize for v1 and v2 revision
|
||||
|
||||
While resurrecting the conntrack-tool test cases I found following bug:
|
||||
In:
|
||||
iptables -I OUTPUT -t raw -p 13 -j CT --timeout test-generic
|
||||
Out:
|
||||
[0:0] -A OUTPUT -p 13 -j CT --timeout test
|
||||
|
||||
Data after first four bytes of the timeout policy name is never
|
||||
copied to userspace because its treated as kernel-only.
|
||||
|
||||
Fixes: ec2318904965 ("xtables: extend matches and targets with .usersize")
|
||||
Signed-off-by: Florian Westphal <fw@strlen.de>
|
||||
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
|
||||
|
||||
Signed-off-by: Florian Westphal <fwestpha@redhat.com>
|
||||
|
||||
diff --git a/net/netfilter/xt_CT.c b/net/netfilter/xt_CT.c
|
||||
index 3ba94c3..23f46bc 100644
|
||||
--- a/net/netfilter/xt_CT.c
|
||||
+++ b/net/netfilter/xt_CT.c
|
||||
@@ -350,7 +350,7 @@ static struct xt_target xt_ct_tg_reg[] __read_mostly = {
|
||||
.family = NFPROTO_IPV4,
|
||||
.revision = 1,
|
||||
.targetsize = sizeof(struct xt_ct_target_info_v1),
|
||||
- .usersize = offsetof(struct xt_ct_target_info, ct),
|
||||
+ .usersize = offsetof(struct xt_ct_target_info_v1, ct),
|
||||
.checkentry = xt_ct_tg_check_v1,
|
||||
.destroy = xt_ct_tg_destroy_v1,
|
||||
.target = xt_ct_target_v1,
|
||||
@@ -362,7 +362,7 @@ static struct xt_target xt_ct_tg_reg[] __read_mostly = {
|
||||
.family = NFPROTO_IPV4,
|
||||
.revision = 2,
|
||||
.targetsize = sizeof(struct xt_ct_target_info_v1),
|
||||
- .usersize = offsetof(struct xt_ct_target_info, ct),
|
||||
+ .usersize = offsetof(struct xt_ct_target_info_v1, ct),
|
||||
.checkentry = xt_ct_tg_check_v2,
|
||||
.destroy = xt_ct_tg_destroy_v1,
|
||||
.target = xt_ct_target_v1,
|
||||
@@ -394,7 +394,7 @@ static struct xt_target xt_ct_tg_reg[] __read_mostly = {
|
||||
.family = NFPROTO_IPV6,
|
||||
.revision = 1,
|
||||
.targetsize = sizeof(struct xt_ct_target_info_v1),
|
||||
- .usersize = offsetof(struct xt_ct_target_info, ct),
|
||||
+ .usersize = offsetof(struct xt_ct_target_info_v1, ct),
|
||||
.checkentry = xt_ct_tg_check_v1,
|
||||
.destroy = xt_ct_tg_destroy_v1,
|
||||
.target = xt_ct_target_v1,
|
||||
@@ -406,7 +406,7 @@ static struct xt_target xt_ct_tg_reg[] __read_mostly = {
|
||||
.family = NFPROTO_IPV6,
|
||||
.revision = 2,
|
||||
.targetsize = sizeof(struct xt_ct_target_info_v1),
|
||||
- .usersize = offsetof(struct xt_ct_target_info, ct),
|
||||
+ .usersize = offsetof(struct xt_ct_target_info_v1, ct),
|
||||
.checkentry = xt_ct_tg_check_v2,
|
||||
.destroy = xt_ct_tg_destroy_v1,
|
||||
.target = xt_ct_target_v1,
|
||||
@ -0,0 +1,631 @@
|
||||
From 56d449ab32e8a5457a93885e2d7a3e8d141ad11c Mon Sep 17 00:00:00 2001
|
||||
From: Florian Westphal <fwestpha@redhat.com>
|
||||
Date: Wed, 13 May 2026 17:23:03 +0200
|
||||
Subject: [PATCH] netfilter: nf_tables: fix netdev hook allocation memleak with
|
||||
dormant tables
|
||||
|
||||
JIRA: https://redhat.atlassian.net/browse/RHEL-168848
|
||||
Upstream Status: commit 63bac0278603
|
||||
|
||||
commit 63bac027860308d1344f761cb47aabb3b30973fd
|
||||
Author: Florian Westphal <fw@strlen.de>
|
||||
Date: Wed Apr 29 08:21:35 2026 +0200
|
||||
|
||||
netfilter: nf_tables: fix netdev hook allocation memleak with dormant tables
|
||||
|
||||
sashiko says:
|
||||
could the related code in __nf_tables_abort() leak the struct nft_hook objects when the table is dormant?
|
||||
|
||||
In __nf_tables_abort(), when rolling back a NEWCHAIN transaction that
|
||||
updates hooks, the code conditionally unregisters and frees the hooks only
|
||||
if the table is not dormant [..]
|
||||
if (!(table->flags & NFT_TABLE_F_DORMANT)) {
|
||||
nft_netdev_unregister_hooks(net,
|
||||
&nft_trans_chain_hooks(trans),
|
||||
true);
|
||||
}
|
||||
...
|
||||
nft_trans_destroy(trans);
|
||||
|
||||
Unfortunately netdev family mixes hook registration and allocation.
|
||||
Push table struct down and only check for the flag to unregister.
|
||||
|
||||
Fixes: 216e7bf7402c ("netfilter: nf_tables: skip netdev hook unregistration if table is dormant")
|
||||
Signed-off-by: Florian Westphal <fw@strlen.de>
|
||||
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
|
||||
|
||||
Signed-off-by: Florian Westphal <fwestpha@redhat.com>
|
||||
|
||||
diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c
|
||||
index 188a621..9883e64 100644
|
||||
--- a/net/netfilter/nf_tables_api.c
|
||||
+++ b/net/netfilter/nf_tables_api.c
|
||||
@@ -379,7 +379,34 @@ static void nft_netdev_hook_unlink_free_rcu(struct nft_hook *hook)
|
||||
nft_netdev_hook_free_rcu(hook);
|
||||
}
|
||||
|
||||
+static void nft_trans_hook_destroy(struct nft_trans_hook *trans_hook)
|
||||
+{
|
||||
+ list_del(&trans_hook->list);
|
||||
+ kfree(trans_hook);
|
||||
+}
|
||||
+
|
||||
+static void nft_netdev_unregister_trans_hook(struct net *net,
|
||||
+ const struct nft_table *table,
|
||||
+ struct list_head *hook_list)
|
||||
+{
|
||||
+ struct nft_trans_hook *trans_hook, *next;
|
||||
+ struct nf_hook_ops *ops;
|
||||
+ struct nft_hook *hook;
|
||||
+
|
||||
+ list_for_each_entry_safe(trans_hook, next, hook_list, list) {
|
||||
+ hook = trans_hook->hook;
|
||||
+
|
||||
+ if (!(table->flags & NFT_TABLE_F_DORMANT)) {
|
||||
+ list_for_each_entry(ops, &hook->ops_list, list)
|
||||
+ nf_unregister_net_hook(net, ops);
|
||||
+ }
|
||||
+ nft_netdev_hook_unlink_free_rcu(hook);
|
||||
+ nft_trans_hook_destroy(trans_hook);
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
static void nft_netdev_unregister_hooks(struct net *net,
|
||||
+ const struct nft_table *table,
|
||||
struct list_head *hook_list,
|
||||
bool release_netdev)
|
||||
{
|
||||
@@ -387,8 +414,10 @@ static void nft_netdev_unregister_hooks(struct net *net,
|
||||
struct nf_hook_ops *ops;
|
||||
|
||||
list_for_each_entry_safe(hook, next, hook_list, list) {
|
||||
- list_for_each_entry(ops, &hook->ops_list, list)
|
||||
- nf_unregister_net_hook(net, ops);
|
||||
+ if (!(table->flags & NFT_TABLE_F_DORMANT)) {
|
||||
+ list_for_each_entry(ops, &hook->ops_list, list)
|
||||
+ nf_unregister_net_hook(net, ops);
|
||||
+ }
|
||||
if (release_netdev)
|
||||
nft_netdev_hook_unlink_free_rcu(hook);
|
||||
}
|
||||
@@ -425,20 +454,25 @@ static void __nf_tables_unregister_hook(struct net *net,
|
||||
struct nft_base_chain *basechain;
|
||||
const struct nf_hook_ops *ops;
|
||||
|
||||
- if (table->flags & NFT_TABLE_F_DORMANT ||
|
||||
- !nft_is_base_chain(chain))
|
||||
+ if (!nft_is_base_chain(chain))
|
||||
return;
|
||||
basechain = nft_base_chain(chain);
|
||||
ops = &basechain->ops;
|
||||
|
||||
+ /* must also be called for dormant tables */
|
||||
+ if (nft_base_chain_netdev(table->family, basechain->ops.hooknum)) {
|
||||
+ nft_netdev_unregister_hooks(net, table, &basechain->hook_list,
|
||||
+ release_netdev);
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ if (table->flags & NFT_TABLE_F_DORMANT)
|
||||
+ return;
|
||||
+
|
||||
if (basechain->type->ops_unregister)
|
||||
return basechain->type->ops_unregister(net, ops);
|
||||
|
||||
- if (nft_base_chain_netdev(table->family, basechain->ops.hooknum))
|
||||
- nft_netdev_unregister_hooks(net, &basechain->hook_list,
|
||||
- release_netdev);
|
||||
- else
|
||||
- nf_unregister_net_hook(net, &basechain->ops);
|
||||
+ nf_unregister_net_hook(net, &basechain->ops);
|
||||
}
|
||||
|
||||
static void nf_tables_unregister_hook(struct net *net,
|
||||
@@ -1991,15 +2025,69 @@ static int nft_nla_put_hook_dev(struct sk_buff *skb, struct nft_hook *hook)
|
||||
return nla_put_string(skb, attr, hook->ifname);
|
||||
}
|
||||
|
||||
+struct nft_hook_dump_ctx {
|
||||
+ struct nft_hook *first;
|
||||
+ int n;
|
||||
+};
|
||||
+
|
||||
+static int nft_dump_basechain_hook_one(struct sk_buff *skb,
|
||||
+ struct nft_hook *hook,
|
||||
+ struct nft_hook_dump_ctx *dump_ctx)
|
||||
+{
|
||||
+ if (!dump_ctx->first)
|
||||
+ dump_ctx->first = hook;
|
||||
+
|
||||
+ if (nft_nla_put_hook_dev(skb, hook))
|
||||
+ return -1;
|
||||
+
|
||||
+ dump_ctx->n++;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int nft_dump_basechain_hook_list(struct sk_buff *skb,
|
||||
+ const struct net *net,
|
||||
+ const struct list_head *hook_list,
|
||||
+ struct nft_hook_dump_ctx *dump_ctx)
|
||||
+{
|
||||
+ struct nft_hook *hook;
|
||||
+ int err;
|
||||
+
|
||||
+ list_for_each_entry_rcu(hook, hook_list, list,
|
||||
+ lockdep_commit_lock_is_held(net)) {
|
||||
+ err = nft_dump_basechain_hook_one(skb, hook, dump_ctx);
|
||||
+ if (err < 0)
|
||||
+ return err;
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int nft_dump_basechain_trans_hook_list(struct sk_buff *skb,
|
||||
+ const struct list_head *trans_hook_list,
|
||||
+ struct nft_hook_dump_ctx *dump_ctx)
|
||||
+{
|
||||
+ struct nft_trans_hook *trans_hook;
|
||||
+ int err;
|
||||
+
|
||||
+ list_for_each_entry(trans_hook, trans_hook_list, list) {
|
||||
+ err = nft_dump_basechain_hook_one(skb, trans_hook->hook, dump_ctx);
|
||||
+ if (err < 0)
|
||||
+ return err;
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
static int nft_dump_basechain_hook(struct sk_buff *skb,
|
||||
const struct net *net, int family,
|
||||
const struct nft_base_chain *basechain,
|
||||
- const struct list_head *hook_list)
|
||||
+ const struct list_head *hook_list,
|
||||
+ const struct list_head *trans_hook_list)
|
||||
{
|
||||
const struct nf_hook_ops *ops = &basechain->ops;
|
||||
- struct nft_hook *hook, *first = NULL;
|
||||
+ struct nft_hook_dump_ctx dump_hook_ctx = {};
|
||||
struct nlattr *nest, *nest_devs;
|
||||
- int n = 0;
|
||||
|
||||
nest = nla_nest_start_noflag(skb, NFTA_CHAIN_HOOK);
|
||||
if (nest == NULL)
|
||||
@@ -2014,23 +2102,23 @@ static int nft_dump_basechain_hook(struct sk_buff *skb,
|
||||
if (!nest_devs)
|
||||
goto nla_put_failure;
|
||||
|
||||
- if (!hook_list)
|
||||
+ if (!hook_list && !trans_hook_list)
|
||||
hook_list = &basechain->hook_list;
|
||||
|
||||
- list_for_each_entry_rcu(hook, hook_list, list,
|
||||
- lockdep_commit_lock_is_held(net)) {
|
||||
- if (!first)
|
||||
- first = hook;
|
||||
-
|
||||
- if (nft_nla_put_hook_dev(skb, hook))
|
||||
- goto nla_put_failure;
|
||||
- n++;
|
||||
+ if (hook_list &&
|
||||
+ nft_dump_basechain_hook_list(skb, net, hook_list, &dump_hook_ctx)) {
|
||||
+ goto nla_put_failure;
|
||||
+ } else if (trans_hook_list &&
|
||||
+ nft_dump_basechain_trans_hook_list(skb, trans_hook_list,
|
||||
+ &dump_hook_ctx)) {
|
||||
+ goto nla_put_failure;
|
||||
}
|
||||
+
|
||||
nla_nest_end(skb, nest_devs);
|
||||
|
||||
- if (n == 1 &&
|
||||
- !hook_is_prefix(first) &&
|
||||
- nla_put_string(skb, NFTA_HOOK_DEV, first->ifname))
|
||||
+ if (dump_hook_ctx.n == 1 &&
|
||||
+ !hook_is_prefix(dump_hook_ctx.first) &&
|
||||
+ nla_put_string(skb, NFTA_HOOK_DEV, dump_hook_ctx.first->ifname))
|
||||
goto nla_put_failure;
|
||||
}
|
||||
nla_nest_end(skb, nest);
|
||||
@@ -2044,7 +2132,8 @@ static int nf_tables_fill_chain_info(struct sk_buff *skb, struct net *net,
|
||||
u32 portid, u32 seq, int event, u32 flags,
|
||||
int family, const struct nft_table *table,
|
||||
const struct nft_chain *chain,
|
||||
- const struct list_head *hook_list)
|
||||
+ const struct list_head *hook_list,
|
||||
+ const struct list_head *trans_hook_list)
|
||||
{
|
||||
struct nlmsghdr *nlh;
|
||||
|
||||
@@ -2060,7 +2149,7 @@ static int nf_tables_fill_chain_info(struct sk_buff *skb, struct net *net,
|
||||
NFTA_CHAIN_PAD))
|
||||
goto nla_put_failure;
|
||||
|
||||
- if (event == NFT_MSG_DELCHAIN && !hook_list) {
|
||||
+ if (event == NFT_MSG_DELCHAIN && !hook_list && !trans_hook_list) {
|
||||
nlmsg_end(skb, nlh);
|
||||
return 0;
|
||||
}
|
||||
@@ -2069,7 +2158,8 @@ static int nf_tables_fill_chain_info(struct sk_buff *skb, struct net *net,
|
||||
const struct nft_base_chain *basechain = nft_base_chain(chain);
|
||||
struct nft_stats __percpu *stats;
|
||||
|
||||
- if (nft_dump_basechain_hook(skb, net, family, basechain, hook_list))
|
||||
+ if (nft_dump_basechain_hook(skb, net, family, basechain,
|
||||
+ hook_list, trans_hook_list))
|
||||
goto nla_put_failure;
|
||||
|
||||
if (nla_put_be32(skb, NFTA_CHAIN_POLICY,
|
||||
@@ -2105,7 +2195,8 @@ static int nf_tables_fill_chain_info(struct sk_buff *skb, struct net *net,
|
||||
}
|
||||
|
||||
static void nf_tables_chain_notify(const struct nft_ctx *ctx, int event,
|
||||
- const struct list_head *hook_list)
|
||||
+ const struct list_head *hook_list,
|
||||
+ const struct list_head *trans_hook_list)
|
||||
{
|
||||
struct nftables_pernet *nft_net;
|
||||
struct sk_buff *skb;
|
||||
@@ -2125,7 +2216,7 @@ static void nf_tables_chain_notify(const struct nft_ctx *ctx, int event,
|
||||
|
||||
err = nf_tables_fill_chain_info(skb, ctx->net, ctx->portid, ctx->seq,
|
||||
event, flags, ctx->family, ctx->table,
|
||||
- ctx->chain, hook_list);
|
||||
+ ctx->chain, hook_list, trans_hook_list);
|
||||
if (err < 0) {
|
||||
kfree_skb(skb);
|
||||
goto err;
|
||||
@@ -2171,7 +2262,7 @@ static int nf_tables_dump_chains(struct sk_buff *skb,
|
||||
NFT_MSG_NEWCHAIN,
|
||||
NLM_F_MULTI,
|
||||
table->family, table,
|
||||
- chain, NULL) < 0)
|
||||
+ chain, NULL, NULL) < 0)
|
||||
goto done;
|
||||
|
||||
nl_dump_check_consistent(cb, nlmsg_hdr(skb));
|
||||
@@ -2225,7 +2316,7 @@ static int nf_tables_getchain(struct sk_buff *skb, const struct nfnl_info *info,
|
||||
|
||||
err = nf_tables_fill_chain_info(skb2, net, NETLINK_CB(skb).portid,
|
||||
info->nlh->nlmsg_seq, NFT_MSG_NEWCHAIN,
|
||||
- 0, family, table, chain, NULL);
|
||||
+ 0, family, table, chain, NULL, NULL);
|
||||
if (err < 0)
|
||||
goto err_fill_chain_info;
|
||||
|
||||
@@ -2388,8 +2479,12 @@ static struct nft_hook *nft_hook_list_find(struct list_head *hook_list,
|
||||
|
||||
list_for_each_entry(hook, hook_list, list) {
|
||||
if (!strncmp(hook->ifname, this->ifname,
|
||||
- min(hook->ifnamelen, this->ifnamelen)))
|
||||
+ min(hook->ifnamelen, this->ifnamelen))) {
|
||||
+ if (hook->flags & NFT_HOOK_REMOVE)
|
||||
+ continue;
|
||||
+
|
||||
return hook;
|
||||
+ }
|
||||
}
|
||||
|
||||
return NULL;
|
||||
@@ -3148,6 +3243,32 @@ static int nf_tables_newchain(struct sk_buff *skb, const struct nfnl_info *info,
|
||||
return nf_tables_addchain(&ctx, family, policy, flags, extack);
|
||||
}
|
||||
|
||||
+static int nft_trans_delhook(struct nft_hook *hook,
|
||||
+ struct list_head *del_list)
|
||||
+{
|
||||
+ struct nft_trans_hook *trans_hook;
|
||||
+
|
||||
+ trans_hook = kmalloc(sizeof(*trans_hook), GFP_KERNEL);
|
||||
+ if (!trans_hook)
|
||||
+ return -ENOMEM;
|
||||
+
|
||||
+ trans_hook->hook = hook;
|
||||
+ list_add_tail(&trans_hook->list, del_list);
|
||||
+ hook->flags |= NFT_HOOK_REMOVE;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static void nft_trans_delhook_abort(struct list_head *del_list)
|
||||
+{
|
||||
+ struct nft_trans_hook *trans_hook, *next;
|
||||
+
|
||||
+ list_for_each_entry_safe(trans_hook, next, del_list, list) {
|
||||
+ trans_hook->hook->flags &= ~NFT_HOOK_REMOVE;
|
||||
+ nft_trans_hook_destroy(trans_hook);
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
static int nft_delchain_hook(struct nft_ctx *ctx,
|
||||
struct nft_base_chain *basechain,
|
||||
struct netlink_ext_ack *extack)
|
||||
@@ -3174,7 +3295,10 @@ static int nft_delchain_hook(struct nft_ctx *ctx,
|
||||
err = -ENOENT;
|
||||
goto err_chain_del_hook;
|
||||
}
|
||||
- list_move(&hook->list, &chain_del_list);
|
||||
+ if (nft_trans_delhook(hook, &chain_del_list) < 0) {
|
||||
+ err = -ENOMEM;
|
||||
+ goto err_chain_del_hook;
|
||||
+ }
|
||||
}
|
||||
|
||||
trans = nft_trans_alloc_chain(ctx, NFT_MSG_DELCHAIN);
|
||||
@@ -3194,7 +3318,7 @@ static int nft_delchain_hook(struct nft_ctx *ctx,
|
||||
return 0;
|
||||
|
||||
err_chain_del_hook:
|
||||
- list_splice(&chain_del_list, &basechain->hook_list);
|
||||
+ nft_trans_delhook_abort(&chain_del_list);
|
||||
nft_chain_release_hook(&chain_hook);
|
||||
|
||||
return err;
|
||||
@@ -9139,6 +9263,24 @@ static void nft_hooks_destroy(struct list_head *hook_list)
|
||||
nft_netdev_hook_unlink_free_rcu(hook);
|
||||
}
|
||||
|
||||
+static void nft_flowtable_unregister_trans_hook(struct net *net,
|
||||
+ struct nft_flowtable *flowtable,
|
||||
+ struct list_head *hook_list)
|
||||
+{
|
||||
+ struct nft_trans_hook *trans_hook, *next;
|
||||
+ struct nf_hook_ops *ops;
|
||||
+ struct nft_hook *hook;
|
||||
+
|
||||
+ list_for_each_entry_safe(trans_hook, next, hook_list, list) {
|
||||
+ hook = trans_hook->hook;
|
||||
+ list_for_each_entry(ops, &hook->ops_list, list)
|
||||
+ nft_unregister_flowtable_ops(net, flowtable, ops);
|
||||
+
|
||||
+ nft_netdev_hook_unlink_free_rcu(hook);
|
||||
+ nft_trans_hook_destroy(trans_hook);
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
static int nft_flowtable_update(struct nft_ctx *ctx, const struct nlmsghdr *nlh,
|
||||
struct nft_flowtable *flowtable,
|
||||
struct netlink_ext_ack *extack)
|
||||
@@ -9397,7 +9539,10 @@ static int nft_delflowtable_hook(struct nft_ctx *ctx,
|
||||
err = -ENOENT;
|
||||
goto err_flowtable_del_hook;
|
||||
}
|
||||
- list_move(&hook->list, &flowtable_del_list);
|
||||
+ if (nft_trans_delhook(hook, &flowtable_del_list) < 0) {
|
||||
+ err = -ENOMEM;
|
||||
+ goto err_flowtable_del_hook;
|
||||
+ }
|
||||
}
|
||||
|
||||
trans = nft_trans_alloc(ctx, NFT_MSG_DELFLOWTABLE,
|
||||
@@ -9418,7 +9563,7 @@ static int nft_delflowtable_hook(struct nft_ctx *ctx,
|
||||
return 0;
|
||||
|
||||
err_flowtable_del_hook:
|
||||
- list_splice(&flowtable_del_list, &flowtable->hook_list);
|
||||
+ nft_trans_delhook_abort(&flowtable_del_list);
|
||||
nft_flowtable_hook_release(&flowtable_hook);
|
||||
|
||||
return err;
|
||||
@@ -9483,8 +9628,10 @@ static int nf_tables_fill_flowtable_info(struct sk_buff *skb, struct net *net,
|
||||
u32 portid, u32 seq, int event,
|
||||
u32 flags, int family,
|
||||
struct nft_flowtable *flowtable,
|
||||
- struct list_head *hook_list)
|
||||
+ struct list_head *hook_list,
|
||||
+ struct list_head *trans_hook_list)
|
||||
{
|
||||
+ struct nft_trans_hook *trans_hook;
|
||||
struct nlattr *nest, *nest_devs;
|
||||
struct nft_hook *hook;
|
||||
struct nlmsghdr *nlh;
|
||||
@@ -9501,7 +9648,7 @@ static int nf_tables_fill_flowtable_info(struct sk_buff *skb, struct net *net,
|
||||
NFTA_FLOWTABLE_PAD))
|
||||
goto nla_put_failure;
|
||||
|
||||
- if (event == NFT_MSG_DELFLOWTABLE && !hook_list) {
|
||||
+ if (event == NFT_MSG_DELFLOWTABLE && !hook_list && !trans_hook_list) {
|
||||
nlmsg_end(skb, nlh);
|
||||
return 0;
|
||||
}
|
||||
@@ -9521,13 +9668,20 @@ static int nf_tables_fill_flowtable_info(struct sk_buff *skb, struct net *net,
|
||||
if (!nest_devs)
|
||||
goto nla_put_failure;
|
||||
|
||||
- if (!hook_list)
|
||||
+ if (!hook_list && !trans_hook_list)
|
||||
hook_list = &flowtable->hook_list;
|
||||
|
||||
- list_for_each_entry_rcu(hook, hook_list, list,
|
||||
- lockdep_commit_lock_is_held(net)) {
|
||||
- if (nft_nla_put_hook_dev(skb, hook))
|
||||
- goto nla_put_failure;
|
||||
+ if (hook_list) {
|
||||
+ list_for_each_entry_rcu(hook, hook_list, list,
|
||||
+ lockdep_commit_lock_is_held(net)) {
|
||||
+ if (nft_nla_put_hook_dev(skb, hook))
|
||||
+ goto nla_put_failure;
|
||||
+ }
|
||||
+ } else if (trans_hook_list) {
|
||||
+ list_for_each_entry(trans_hook, trans_hook_list, list) {
|
||||
+ if (nft_nla_put_hook_dev(skb, trans_hook->hook))
|
||||
+ goto nla_put_failure;
|
||||
+ }
|
||||
}
|
||||
nla_nest_end(skb, nest_devs);
|
||||
nla_nest_end(skb, nest);
|
||||
@@ -9581,7 +9735,7 @@ static int nf_tables_dump_flowtable(struct sk_buff *skb,
|
||||
NFT_MSG_NEWFLOWTABLE,
|
||||
NLM_F_MULTI | NLM_F_APPEND,
|
||||
table->family,
|
||||
- flowtable, NULL) < 0)
|
||||
+ flowtable, NULL, NULL) < 0)
|
||||
goto done;
|
||||
|
||||
nl_dump_check_consistent(cb, nlmsg_hdr(skb));
|
||||
@@ -9681,7 +9835,7 @@ static int nf_tables_getflowtable(struct sk_buff *skb,
|
||||
err = nf_tables_fill_flowtable_info(skb2, net, NETLINK_CB(skb).portid,
|
||||
info->nlh->nlmsg_seq,
|
||||
NFT_MSG_NEWFLOWTABLE, 0, family,
|
||||
- flowtable, NULL);
|
||||
+ flowtable, NULL, NULL);
|
||||
if (err < 0)
|
||||
goto err_fill_flowtable_info;
|
||||
|
||||
@@ -9694,7 +9848,9 @@ static int nf_tables_getflowtable(struct sk_buff *skb,
|
||||
|
||||
static void nf_tables_flowtable_notify(struct nft_ctx *ctx,
|
||||
struct nft_flowtable *flowtable,
|
||||
- struct list_head *hook_list, int event)
|
||||
+ struct list_head *hook_list,
|
||||
+ struct list_head *trans_hook_list,
|
||||
+ int event)
|
||||
{
|
||||
struct nftables_pernet *nft_net = nft_pernet(ctx->net);
|
||||
struct sk_buff *skb;
|
||||
@@ -9714,7 +9870,8 @@ static void nf_tables_flowtable_notify(struct nft_ctx *ctx,
|
||||
|
||||
err = nf_tables_fill_flowtable_info(skb, ctx->net, ctx->portid,
|
||||
ctx->seq, event, flags,
|
||||
- ctx->family, flowtable, hook_list);
|
||||
+ ctx->family, flowtable,
|
||||
+ hook_list, trans_hook_list);
|
||||
if (err < 0) {
|
||||
kfree_skb(skb);
|
||||
goto err;
|
||||
@@ -10248,9 +10405,7 @@ static void nft_commit_release(struct nft_trans *trans)
|
||||
break;
|
||||
case NFT_MSG_DELCHAIN:
|
||||
case NFT_MSG_DESTROYCHAIN:
|
||||
- if (nft_trans_chain_update(trans))
|
||||
- nft_hooks_destroy(&nft_trans_chain_hooks(trans));
|
||||
- else
|
||||
+ if (!nft_trans_chain_update(trans))
|
||||
nf_tables_chain_destroy(nft_trans_chain(trans));
|
||||
break;
|
||||
case NFT_MSG_DELRULE:
|
||||
@@ -10271,9 +10426,7 @@ static void nft_commit_release(struct nft_trans *trans)
|
||||
break;
|
||||
case NFT_MSG_DELFLOWTABLE:
|
||||
case NFT_MSG_DESTROYFLOWTABLE:
|
||||
- if (nft_trans_flowtable_update(trans))
|
||||
- nft_hooks_destroy(&nft_trans_flowtable_hooks(trans));
|
||||
- else
|
||||
+ if (!nft_trans_flowtable_update(trans))
|
||||
nf_tables_flowtable_destroy(nft_trans_flowtable(trans));
|
||||
break;
|
||||
}
|
||||
@@ -11048,31 +11201,28 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb)
|
||||
if (nft_trans_chain_update(trans)) {
|
||||
nft_chain_commit_update(nft_trans_container_chain(trans));
|
||||
nf_tables_chain_notify(&ctx, NFT_MSG_NEWCHAIN,
|
||||
- &nft_trans_chain_hooks(trans));
|
||||
+ &nft_trans_chain_hooks(trans), NULL);
|
||||
list_splice_rcu(&nft_trans_chain_hooks(trans),
|
||||
&nft_trans_basechain(trans)->hook_list);
|
||||
/* trans destroyed after rcu grace period */
|
||||
} else {
|
||||
nft_chain_commit_drop_policy(nft_trans_container_chain(trans));
|
||||
nft_clear(net, nft_trans_chain(trans));
|
||||
- nf_tables_chain_notify(&ctx, NFT_MSG_NEWCHAIN, NULL);
|
||||
+ nf_tables_chain_notify(&ctx, NFT_MSG_NEWCHAIN, NULL, NULL);
|
||||
nft_trans_destroy(trans);
|
||||
}
|
||||
break;
|
||||
case NFT_MSG_DELCHAIN:
|
||||
case NFT_MSG_DESTROYCHAIN:
|
||||
if (nft_trans_chain_update(trans)) {
|
||||
- nf_tables_chain_notify(&ctx, NFT_MSG_DELCHAIN,
|
||||
+ nf_tables_chain_notify(&ctx, NFT_MSG_DELCHAIN, NULL,
|
||||
&nft_trans_chain_hooks(trans));
|
||||
- if (!(table->flags & NFT_TABLE_F_DORMANT)) {
|
||||
- nft_netdev_unregister_hooks(net,
|
||||
- &nft_trans_chain_hooks(trans),
|
||||
- true);
|
||||
- }
|
||||
+ nft_netdev_unregister_trans_hook(net, table,
|
||||
+ &nft_trans_chain_hooks(trans));
|
||||
} else {
|
||||
nft_chain_del(nft_trans_chain(trans));
|
||||
nf_tables_chain_notify(&ctx, NFT_MSG_DELCHAIN,
|
||||
- NULL);
|
||||
+ NULL, NULL);
|
||||
nf_tables_unregister_hook(ctx.net, ctx.table,
|
||||
nft_trans_chain(trans));
|
||||
}
|
||||
@@ -11178,6 +11328,7 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb)
|
||||
nf_tables_flowtable_notify(&ctx,
|
||||
nft_trans_flowtable(trans),
|
||||
&nft_trans_flowtable_hooks(trans),
|
||||
+ NULL,
|
||||
NFT_MSG_NEWFLOWTABLE);
|
||||
list_splice_rcu(&nft_trans_flowtable_hooks(trans),
|
||||
&nft_trans_flowtable(trans)->hook_list);
|
||||
@@ -11186,6 +11337,7 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb)
|
||||
nf_tables_flowtable_notify(&ctx,
|
||||
nft_trans_flowtable(trans),
|
||||
NULL,
|
||||
+ NULL,
|
||||
NFT_MSG_NEWFLOWTABLE);
|
||||
}
|
||||
nft_trans_destroy(trans);
|
||||
@@ -11195,16 +11347,18 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb)
|
||||
if (nft_trans_flowtable_update(trans)) {
|
||||
nf_tables_flowtable_notify(&ctx,
|
||||
nft_trans_flowtable(trans),
|
||||
+ NULL,
|
||||
&nft_trans_flowtable_hooks(trans),
|
||||
trans->msg_type);
|
||||
- nft_unregister_flowtable_net_hooks(net,
|
||||
- nft_trans_flowtable(trans),
|
||||
- &nft_trans_flowtable_hooks(trans));
|
||||
+ nft_flowtable_unregister_trans_hook(net,
|
||||
+ nft_trans_flowtable(trans),
|
||||
+ &nft_trans_flowtable_hooks(trans));
|
||||
} else {
|
||||
list_del_rcu(&nft_trans_flowtable(trans)->list);
|
||||
nf_tables_flowtable_notify(&ctx,
|
||||
nft_trans_flowtable(trans),
|
||||
NULL,
|
||||
+ NULL,
|
||||
trans->msg_type);
|
||||
nft_unregister_flowtable_net_hooks(net,
|
||||
nft_trans_flowtable(trans),
|
||||
@@ -11346,11 +11500,9 @@ static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action)
|
||||
break;
|
||||
case NFT_MSG_NEWCHAIN:
|
||||
if (nft_trans_chain_update(trans)) {
|
||||
- if (!(table->flags & NFT_TABLE_F_DORMANT)) {
|
||||
- nft_netdev_unregister_hooks(net,
|
||||
- &nft_trans_chain_hooks(trans),
|
||||
- true);
|
||||
- }
|
||||
+ nft_netdev_unregister_hooks(net, table,
|
||||
+ &nft_trans_chain_hooks(trans),
|
||||
+ true);
|
||||
free_percpu(nft_trans_chain_stats(trans));
|
||||
kfree(nft_trans_chain_name(trans));
|
||||
nft_trans_destroy(trans);
|
||||
@@ -11368,8 +11520,7 @@ static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action)
|
||||
case NFT_MSG_DELCHAIN:
|
||||
case NFT_MSG_DESTROYCHAIN:
|
||||
if (nft_trans_chain_update(trans)) {
|
||||
- list_splice(&nft_trans_chain_hooks(trans),
|
||||
- &nft_trans_basechain(trans)->hook_list);
|
||||
+ nft_trans_delhook_abort(&nft_trans_chain_hooks(trans));
|
||||
} else {
|
||||
nft_use_inc_restore(&table->use);
|
||||
nft_clear(trans->net, nft_trans_chain(trans));
|
||||
@@ -11483,8 +11634,7 @@ static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action)
|
||||
case NFT_MSG_DELFLOWTABLE:
|
||||
case NFT_MSG_DESTROYFLOWTABLE:
|
||||
if (nft_trans_flowtable_update(trans)) {
|
||||
- list_splice(&nft_trans_flowtable_hooks(trans),
|
||||
- &nft_trans_flowtable(trans)->hook_list);
|
||||
+ nft_trans_delhook_abort(&nft_trans_flowtable_hooks(trans));
|
||||
} else {
|
||||
nft_use_inc_restore(&table->use);
|
||||
nft_clear(trans->net, nft_trans_flowtable(trans));
|
||||
@ -0,0 +1,260 @@
|
||||
From 5a3930625f5b94ea1c8906d5f8ec7a6c34133018 Mon Sep 17 00:00:00 2001
|
||||
From: Florian Westphal <fwestpha@redhat.com>
|
||||
Date: Wed, 13 May 2026 17:24:34 +0200
|
||||
Subject: [PATCH] netfilter: nf_conntrack_expect: restore helper propagation
|
||||
via expectation
|
||||
|
||||
JIRA: https://redhat.atlassian.net/browse/RHEL-168848
|
||||
Upstream Status: commit dcb0f9aefdd6
|
||||
|
||||
commit dcb0f9aefdd604d36710fda53c25bd7cf4a3e37a
|
||||
Author: Pablo Neira Ayuso <pablo@netfilter.org>
|
||||
Date: Thu May 7 13:00:28 2026 +0200
|
||||
|
||||
netfilter: nf_conntrack_expect: restore helper propagation via expectation
|
||||
|
||||
A recent series to fix expectations broke helper propagation via
|
||||
expectation, this mechanism is used by the sip and h323 helper. This
|
||||
also propagates the conntrack helper to expected connections. I changed
|
||||
semantics of exp->helper which now tells us the actual helper that
|
||||
created the expectation.
|
||||
|
||||
Add an explicit assign_helper field to expectations for this purpose
|
||||
and update helpers to use it.
|
||||
|
||||
Restore this feature for userspace conntrack helper via ctnetlink
|
||||
nfqueue integration so it is again possible to attach a helper to an
|
||||
expectation, where it makes sense. This is not restored via ctnetlink
|
||||
expectation creation as there is no client for such feature. Use the
|
||||
expectation layer 4 protocol number for the helper lookup for
|
||||
consistency.
|
||||
|
||||
Make sure the expectation using this helper propagation mechanism also
|
||||
go away when the helper is unregistered.
|
||||
|
||||
Fixes: 9c42bc9db90a ("netfilter: nf_conntrack_expect: honor expectation helper field")
|
||||
Fixes: 917b61fa2042 ("netfilter: ctnetlink: ignore explicit helper on new expectations")
|
||||
Reported-by: Ilya Maximets <i.maximets@ovn.org>
|
||||
Tested-by: Ilya Maximets <i.maximets@ovn.org>
|
||||
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
|
||||
|
||||
Signed-off-by: Florian Westphal <fwestpha@redhat.com>
|
||||
|
||||
diff --git a/include/net/netfilter/nf_conntrack_expect.h b/include/net/netfilter/nf_conntrack_expect.h
|
||||
index e9a8350..80f50fd 100644
|
||||
--- a/include/net/netfilter/nf_conntrack_expect.h
|
||||
+++ b/include/net/netfilter/nf_conntrack_expect.h
|
||||
@@ -45,9 +45,12 @@ struct nf_conntrack_expect {
|
||||
void (*expectfn)(struct nf_conn *new,
|
||||
struct nf_conntrack_expect *this);
|
||||
|
||||
- /* Helper to assign to new connection */
|
||||
+ /* Helper that created this expectation */
|
||||
struct nf_conntrack_helper __rcu *helper;
|
||||
|
||||
+ /* Helper to assign to new connection */
|
||||
+ struct nf_conntrack_helper __rcu *assign_helper;
|
||||
+
|
||||
/* The conntrack of the master connection */
|
||||
struct nf_conn *master;
|
||||
|
||||
diff --git a/net/netfilter/nf_conntrack_broadcast.c b/net/netfilter/nf_conntrack_broadcast.c
|
||||
index 4f39bf7..75e53fd 100644
|
||||
--- a/net/netfilter/nf_conntrack_broadcast.c
|
||||
+++ b/net/netfilter/nf_conntrack_broadcast.c
|
||||
@@ -72,6 +72,7 @@ int nf_conntrack_broadcast_help(struct sk_buff *skb,
|
||||
exp->flags = NF_CT_EXPECT_PERMANENT;
|
||||
exp->class = NF_CT_EXPECT_CLASS_DEFAULT;
|
||||
rcu_assign_pointer(exp->helper, helper);
|
||||
+ rcu_assign_pointer(exp->assign_helper, NULL);
|
||||
write_pnet(&exp->net, net);
|
||||
#ifdef CONFIG_NF_CONNTRACK_ZONES
|
||||
exp->zone = ct->zone;
|
||||
diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c
|
||||
index 963f9ac..02a73b2 100644
|
||||
--- a/net/netfilter/nf_conntrack_core.c
|
||||
+++ b/net/netfilter/nf_conntrack_core.c
|
||||
@@ -1819,14 +1819,17 @@ init_conntrack(struct net *net, struct nf_conn *tmpl,
|
||||
spin_lock_bh(&nf_conntrack_expect_lock);
|
||||
exp = nf_ct_find_expectation(net, zone, tuple, !tmpl || nf_ct_is_confirmed(tmpl));
|
||||
if (exp) {
|
||||
+ struct nf_conntrack_helper *assign_helper;
|
||||
+
|
||||
/* Welcome, Mr. Bond. We've been expecting you... */
|
||||
__set_bit(IPS_EXPECTED_BIT, &ct->status);
|
||||
/* exp->master safe, refcnt bumped in nf_ct_find_expectation */
|
||||
ct->master = exp->master;
|
||||
- if (exp->helper) {
|
||||
+ assign_helper = rcu_dereference(exp->assign_helper);
|
||||
+ if (assign_helper) {
|
||||
help = nf_ct_helper_ext_add(ct, GFP_ATOMIC);
|
||||
if (help)
|
||||
- rcu_assign_pointer(help->helper, exp->helper);
|
||||
+ rcu_assign_pointer(help->helper, assign_helper);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_NF_CONNTRACK_MARK
|
||||
diff --git a/net/netfilter/nf_conntrack_expect.c b/net/netfilter/nf_conntrack_expect.c
|
||||
index db28801..1bb5bf8 100644
|
||||
--- a/net/netfilter/nf_conntrack_expect.c
|
||||
+++ b/net/netfilter/nf_conntrack_expect.c
|
||||
@@ -344,6 +344,7 @@ void nf_ct_expect_init(struct nf_conntrack_expect *exp, unsigned int class,
|
||||
helper = rcu_dereference(help->helper);
|
||||
|
||||
rcu_assign_pointer(exp->helper, helper);
|
||||
+ rcu_assign_pointer(exp->assign_helper, NULL);
|
||||
write_pnet(&exp->net, net);
|
||||
#ifdef CONFIG_NF_CONNTRACK_ZONES
|
||||
exp->zone = ct->zone;
|
||||
diff --git a/net/netfilter/nf_conntrack_h323_main.c b/net/netfilter/nf_conntrack_h323_main.c
|
||||
index fbf69d4..8a14404 100644
|
||||
--- a/net/netfilter/nf_conntrack_h323_main.c
|
||||
+++ b/net/netfilter/nf_conntrack_h323_main.c
|
||||
@@ -642,7 +642,7 @@ static int expect_h245(struct sk_buff *skb, struct nf_conn *ct,
|
||||
&ct->tuplehash[!dir].tuple.src.u3,
|
||||
&ct->tuplehash[!dir].tuple.dst.u3,
|
||||
IPPROTO_TCP, NULL, &port);
|
||||
- rcu_assign_pointer(exp->helper, &nf_conntrack_helper_h245);
|
||||
+ rcu_assign_pointer(exp->assign_helper, &nf_conntrack_helper_h245);
|
||||
|
||||
nathook = rcu_dereference(nfct_h323_nat_hook);
|
||||
if (memcmp(&ct->tuplehash[dir].tuple.src.u3,
|
||||
@@ -766,7 +766,7 @@ static int expect_callforwarding(struct sk_buff *skb,
|
||||
nf_ct_expect_init(exp, NF_CT_EXPECT_CLASS_DEFAULT, nf_ct_l3num(ct),
|
||||
&ct->tuplehash[!dir].tuple.src.u3, &addr,
|
||||
IPPROTO_TCP, NULL, &port);
|
||||
- rcu_assign_pointer(exp->helper, nf_conntrack_helper_q931);
|
||||
+ rcu_assign_pointer(exp->assign_helper, nf_conntrack_helper_q931);
|
||||
|
||||
nathook = rcu_dereference(nfct_h323_nat_hook);
|
||||
if (memcmp(&ct->tuplehash[dir].tuple.src.u3,
|
||||
@@ -1233,7 +1233,7 @@ static int expect_q931(struct sk_buff *skb, struct nf_conn *ct,
|
||||
&ct->tuplehash[!dir].tuple.src.u3 : NULL,
|
||||
&ct->tuplehash[!dir].tuple.dst.u3,
|
||||
IPPROTO_TCP, NULL, &port);
|
||||
- rcu_assign_pointer(exp->helper, nf_conntrack_helper_q931);
|
||||
+ rcu_assign_pointer(exp->assign_helper, nf_conntrack_helper_q931);
|
||||
exp->flags = NF_CT_EXPECT_PERMANENT; /* Accept multiple calls */
|
||||
|
||||
nathook = rcu_dereference(nfct_h323_nat_hook);
|
||||
@@ -1305,7 +1305,7 @@ static int process_gcf(struct sk_buff *skb, struct nf_conn *ct,
|
||||
nf_ct_expect_init(exp, NF_CT_EXPECT_CLASS_DEFAULT, nf_ct_l3num(ct),
|
||||
&ct->tuplehash[!dir].tuple.src.u3, &addr,
|
||||
IPPROTO_UDP, NULL, &port);
|
||||
- rcu_assign_pointer(exp->helper, nf_conntrack_helper_ras);
|
||||
+ rcu_assign_pointer(exp->assign_helper, nf_conntrack_helper_ras);
|
||||
|
||||
if (nf_ct_expect_related(exp, 0) == 0) {
|
||||
pr_debug("nf_ct_ras: expect RAS ");
|
||||
@@ -1522,7 +1522,7 @@ static int process_acf(struct sk_buff *skb, struct nf_conn *ct,
|
||||
&ct->tuplehash[!dir].tuple.src.u3, &addr,
|
||||
IPPROTO_TCP, NULL, &port);
|
||||
exp->flags = NF_CT_EXPECT_PERMANENT;
|
||||
- rcu_assign_pointer(exp->helper, nf_conntrack_helper_q931);
|
||||
+ rcu_assign_pointer(exp->assign_helper, nf_conntrack_helper_q931);
|
||||
|
||||
if (nf_ct_expect_related(exp, 0) == 0) {
|
||||
pr_debug("nf_ct_ras: expect Q.931 ");
|
||||
@@ -1576,7 +1576,7 @@ static int process_lcf(struct sk_buff *skb, struct nf_conn *ct,
|
||||
&ct->tuplehash[!dir].tuple.src.u3, &addr,
|
||||
IPPROTO_TCP, NULL, &port);
|
||||
exp->flags = NF_CT_EXPECT_PERMANENT;
|
||||
- rcu_assign_pointer(exp->helper, nf_conntrack_helper_q931);
|
||||
+ rcu_assign_pointer(exp->assign_helper, nf_conntrack_helper_q931);
|
||||
|
||||
if (nf_ct_expect_related(exp, 0) == 0) {
|
||||
pr_debug("nf_ct_ras: expect Q.931 ");
|
||||
diff --git a/net/netfilter/nf_conntrack_helper.c b/net/netfilter/nf_conntrack_helper.c
|
||||
index a715304..b594cd2 100644
|
||||
--- a/net/netfilter/nf_conntrack_helper.c
|
||||
+++ b/net/netfilter/nf_conntrack_helper.c
|
||||
@@ -400,6 +400,11 @@ static bool expect_iter_me(struct nf_conntrack_expect *exp, void *data)
|
||||
|
||||
this = rcu_dereference_protected(exp->helper,
|
||||
lockdep_is_held(&nf_conntrack_expect_lock));
|
||||
+ if (this == me)
|
||||
+ return true;
|
||||
+
|
||||
+ this = rcu_dereference_protected(exp->assign_helper,
|
||||
+ lockdep_is_held(&nf_conntrack_expect_lock));
|
||||
return this == me;
|
||||
}
|
||||
|
||||
diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c
|
||||
index dcef3ef..7e71cbc 100644
|
||||
--- a/net/netfilter/nf_conntrack_netlink.c
|
||||
+++ b/net/netfilter/nf_conntrack_netlink.c
|
||||
@@ -2655,6 +2655,7 @@ static const struct nla_policy exp_nla_policy[CTA_EXPECT_MAX+1] = {
|
||||
|
||||
static struct nf_conntrack_expect *
|
||||
ctnetlink_alloc_expect(const struct nlattr *const cda[], struct nf_conn *ct,
|
||||
+ const struct nf_conntrack_helper *assign_helper,
|
||||
struct nf_conntrack_tuple *tuple,
|
||||
struct nf_conntrack_tuple *mask);
|
||||
|
||||
@@ -2881,6 +2882,7 @@ static int
|
||||
ctnetlink_glue_attach_expect(const struct nlattr *attr, struct nf_conn *ct,
|
||||
u32 portid, u32 report)
|
||||
{
|
||||
+ struct nf_conntrack_helper *assign_helper = NULL;
|
||||
struct nlattr *cda[CTA_EXPECT_MAX+1];
|
||||
struct nf_conntrack_tuple tuple, mask;
|
||||
struct nf_conntrack_expect *exp;
|
||||
@@ -2896,8 +2898,18 @@ ctnetlink_glue_attach_expect(const struct nlattr *attr, struct nf_conn *ct,
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
+ if (cda[CTA_EXPECT_HELP_NAME]) {
|
||||
+ const char *helpname = nla_data(cda[CTA_EXPECT_HELP_NAME]);
|
||||
+
|
||||
+ assign_helper = __nf_conntrack_helper_find(helpname,
|
||||
+ nf_ct_l3num(ct),
|
||||
+ tuple.dst.protonum);
|
||||
+ if (!assign_helper)
|
||||
+ return -EOPNOTSUPP;
|
||||
+ }
|
||||
+
|
||||
exp = ctnetlink_alloc_expect((const struct nlattr * const *)cda, ct,
|
||||
- &tuple, &mask);
|
||||
+ assign_helper, &tuple, &mask);
|
||||
if (IS_ERR(exp))
|
||||
return PTR_ERR(exp);
|
||||
|
||||
@@ -3536,6 +3548,7 @@ ctnetlink_parse_expect_nat(const struct nlattr *attr,
|
||||
|
||||
static struct nf_conntrack_expect *
|
||||
ctnetlink_alloc_expect(const struct nlattr * const cda[], struct nf_conn *ct,
|
||||
+ const struct nf_conntrack_helper *assign_helper,
|
||||
struct nf_conntrack_tuple *tuple,
|
||||
struct nf_conntrack_tuple *mask)
|
||||
{
|
||||
@@ -3589,6 +3602,7 @@ ctnetlink_alloc_expect(const struct nlattr * const cda[], struct nf_conn *ct,
|
||||
exp->zone = ct->zone;
|
||||
#endif
|
||||
rcu_assign_pointer(exp->helper, helper);
|
||||
+ rcu_assign_pointer(exp->assign_helper, assign_helper);
|
||||
exp->tuple = *tuple;
|
||||
exp->mask.src.u3 = mask->src.u3;
|
||||
exp->mask.src.u.all = mask->src.u.all;
|
||||
@@ -3644,7 +3658,7 @@ ctnetlink_create_expect(struct net *net,
|
||||
ct = nf_ct_tuplehash_to_ctrack(h);
|
||||
|
||||
rcu_read_lock();
|
||||
- exp = ctnetlink_alloc_expect(cda, ct, &tuple, &mask);
|
||||
+ exp = ctnetlink_alloc_expect(cda, ct, NULL, &tuple, &mask);
|
||||
if (IS_ERR(exp)) {
|
||||
err = PTR_ERR(exp);
|
||||
goto err_rcu;
|
||||
diff --git a/net/netfilter/nf_conntrack_sip.c b/net/netfilter/nf_conntrack_sip.c
|
||||
index b31f31e..25ab92e 100644
|
||||
--- a/net/netfilter/nf_conntrack_sip.c
|
||||
+++ b/net/netfilter/nf_conntrack_sip.c
|
||||
@@ -1378,7 +1378,7 @@ static int process_register_request(struct sk_buff *skb, unsigned int protoff,
|
||||
nf_ct_expect_init(exp, SIP_EXPECT_SIGNALLING, nf_ct_l3num(ct),
|
||||
saddr, &daddr, proto, NULL, &port);
|
||||
exp->timeout.expires = sip_timeout * HZ;
|
||||
- rcu_assign_pointer(exp->helper, helper);
|
||||
+ rcu_assign_pointer(exp->assign_helper, helper);
|
||||
exp->flags = NF_CT_EXPECT_PERMANENT | NF_CT_EXPECT_INACTIVE;
|
||||
|
||||
hooks = rcu_dereference(nf_nat_sip_hooks);
|
||||
@ -0,0 +1,37 @@
|
||||
From b837d5c40d7bd2c2bb19b0ac2bcf96a81a130009 Mon Sep 17 00:00:00 2001
|
||||
From: Florian Westphal <fwestpha@redhat.com>
|
||||
Date: Wed, 13 May 2026 17:24:36 +0200
|
||||
Subject: [PATCH] netfilter: ctnetlink: check tuple and mask in expectations
|
||||
created via nfqueue
|
||||
|
||||
JIRA: https://redhat.atlassian.net/browse/RHEL-168848
|
||||
Upstream Status: commit d8ef54c83ad7
|
||||
|
||||
commit d8ef54c83ad70b81735b506431affadd2f720aa1
|
||||
Author: Pablo Neira Ayuso <pablo@netfilter.org>
|
||||
Date: Thu May 7 23:57:55 2026 +0200
|
||||
|
||||
netfilter: ctnetlink: check tuple and mask in expectations created via nfqueue
|
||||
|
||||
Ensure the expectation tuple and mask attributes are present in netlink
|
||||
message, otherwise null-ptr-deref is possible.
|
||||
|
||||
Fixes: bd0779370588 ("netfilter: nfnetlink_queue: allow to attach expectations to conntracks")
|
||||
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
|
||||
|
||||
Signed-off-by: Florian Westphal <fwestpha@redhat.com>
|
||||
|
||||
diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c
|
||||
index 7e71cbc..caf0506 100644
|
||||
--- a/net/netfilter/nf_conntrack_netlink.c
|
||||
+++ b/net/netfilter/nf_conntrack_netlink.c
|
||||
@@ -2893,6 +2893,9 @@ ctnetlink_glue_attach_expect(const struct nlattr *attr, struct nf_conn *ct,
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
+ if (!cda[CTA_EXPECT_TUPLE] || !cda[CTA_EXPECT_MASK])
|
||||
+ return -EINVAL;
|
||||
+
|
||||
err = ctnetlink_glue_exp_parse((const struct nlattr * const *)cda,
|
||||
ct, &tuple, &mask);
|
||||
if (err < 0)
|
||||
@ -0,0 +1,56 @@
|
||||
From f2a8d75fdf87f1a3075b27c58f10f0ac902475bc Mon Sep 17 00:00:00 2001
|
||||
From: Florian Westphal <fwestpha@redhat.com>
|
||||
Date: Wed, 13 May 2026 17:24:37 +0200
|
||||
Subject: [PATCH] netfilter: nf_conntrack_sip: get helper before allocating
|
||||
expectation
|
||||
|
||||
JIRA: https://redhat.atlassian.net/browse/RHEL-168848
|
||||
Upstream Status: commit eb6317739b1e
|
||||
|
||||
commit eb6317739b1ea3ab28791e1f91b24781905fa815
|
||||
Author: Li Xiasong <lixiasong1@huawei.com>
|
||||
Date: Thu May 7 22:04:22 2026 +0800
|
||||
|
||||
netfilter: nf_conntrack_sip: get helper before allocating expectation
|
||||
|
||||
process_register_request() allocates an expectation and then checks
|
||||
whether a conntrack helper is available. If helper lookup fails, the
|
||||
function returns early and the allocated expectation is left behind.
|
||||
|
||||
Reorder the code to fetch and validate helper before calling
|
||||
nf_ct_expect_alloc(). This keeps the logic simpler and removes the leak
|
||||
path while preserving existing behavior.
|
||||
|
||||
Fixes: e14575fa7529 ("netfilter: nf_conntrack: use rcu accessors where needed")
|
||||
Cc: stable@vger.kernel.org
|
||||
Signed-off-by: Li Xiasong <lixiasong1@huawei.com>
|
||||
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
|
||||
|
||||
Signed-off-by: Florian Westphal <fwestpha@redhat.com>
|
||||
|
||||
diff --git a/net/netfilter/nf_conntrack_sip.c b/net/netfilter/nf_conntrack_sip.c
|
||||
index 25ab92e..d5bc99b 100644
|
||||
--- a/net/netfilter/nf_conntrack_sip.c
|
||||
+++ b/net/netfilter/nf_conntrack_sip.c
|
||||
@@ -1361,6 +1361,10 @@ static int process_register_request(struct sk_buff *skb, unsigned int protoff,
|
||||
goto store_cseq;
|
||||
}
|
||||
|
||||
+ helper = rcu_dereference(nfct_help(ct)->helper);
|
||||
+ if (!helper)
|
||||
+ return NF_DROP;
|
||||
+
|
||||
exp = nf_ct_expect_alloc(ct);
|
||||
if (!exp) {
|
||||
nf_ct_helper_log(skb, ct, "cannot alloc expectation");
|
||||
@@ -1371,10 +1375,6 @@ static int process_register_request(struct sk_buff *skb, unsigned int protoff,
|
||||
if (sip_direct_signalling)
|
||||
saddr = &ct->tuplehash[!dir].tuple.src.u3;
|
||||
|
||||
- helper = rcu_dereference(nfct_help(ct)->helper);
|
||||
- if (!helper)
|
||||
- return NF_DROP;
|
||||
-
|
||||
nf_ct_expect_init(exp, SIP_EXPECT_SIGNALLING, nf_ct_l3num(ct),
|
||||
saddr, &daddr, proto, NULL, &port);
|
||||
exp->timeout.expires = sip_timeout * HZ;
|
||||
@ -0,0 +1,39 @@
|
||||
From 3bbba78afad15b303372a701c571840395dea7a1 Mon Sep 17 00:00:00 2001
|
||||
From: Florian Westphal <fwestpha@redhat.com>
|
||||
Date: Wed, 13 May 2026 17:24:38 +0200
|
||||
Subject: [PATCH] netfilter: nft_ct: fix missing expect put in obj eval
|
||||
|
||||
JIRA: https://redhat.atlassian.net/browse/RHEL-168848
|
||||
Upstream Status: commit 19f94b6fee75
|
||||
|
||||
commit 19f94b6fee75b3ef7fbc06f3745b9a771a8a19a4
|
||||
Author: Li Xiasong <lixiasong1@huawei.com>
|
||||
Date: Thu May 7 22:04:23 2026 +0800
|
||||
|
||||
netfilter: nft_ct: fix missing expect put in obj eval
|
||||
|
||||
nft_ct_expect_obj_eval() allocates an expectation and may call
|
||||
nf_ct_expect_related(), but never drops its local reference.
|
||||
|
||||
Add nf_ct_expect_put(exp) before return to balance allocation.
|
||||
|
||||
Fixes: 857b46027d6f ("netfilter: nft_ct: add ct expectations support")
|
||||
Cc: stable@vger.kernel.org
|
||||
Signed-off-by: Li Xiasong <lixiasong1@huawei.com>
|
||||
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
|
||||
|
||||
Signed-off-by: Florian Westphal <fwestpha@redhat.com>
|
||||
|
||||
diff --git a/net/netfilter/nft_ct.c b/net/netfilter/nft_ct.c
|
||||
index d0090d0..d877767 100644
|
||||
--- a/net/netfilter/nft_ct.c
|
||||
+++ b/net/netfilter/nft_ct.c
|
||||
@@ -1378,6 +1378,8 @@ static void nft_ct_expect_obj_eval(struct nft_object *obj,
|
||||
|
||||
if (nf_ct_expect_related(exp, 0) != 0)
|
||||
regs->verdict.code = NF_DROP;
|
||||
+
|
||||
+ nf_ct_expect_put(exp);
|
||||
}
|
||||
|
||||
static const struct nla_policy nft_ct_expect_policy[NFTA_CT_EXPECT_MAX + 1] = {
|
||||
@ -0,0 +1,65 @@
|
||||
From 57807b90c78a36d8729fc8d8a04ce31bd4f3e4ff Mon Sep 17 00:00:00 2001
|
||||
From: Florian Westphal <fwestpha@redhat.com>
|
||||
Date: Wed, 20 May 2026 11:48:56 +0200
|
||||
Subject: [PATCH] netfilter: nf_conntrack_helper: fix possible null deref
|
||||
during error log
|
||||
|
||||
JIRA: https://redhat.atlassian.net/browse/RHEL-168848
|
||||
Upstream Status: commit 1afc25ae7528
|
||||
|
||||
commit 1afc25ae75288b3ce59e9e5a4b448bd354c9e565
|
||||
Author: Florian Westphal <fw@strlen.de>
|
||||
Date: Sat May 9 10:27:06 2026 +0200
|
||||
|
||||
netfilter: nf_conntrack_helper: fix possible null deref during error log
|
||||
|
||||
Reported by sashiko: there is a small race window.
|
||||
|
||||
If a helper module is unloaded or a userspace-defined helper is
|
||||
removed, nf_conntrack_helper_unregister() sets ->helper to NULL.
|
||||
|
||||
Handle this safely. This needs a second patch to close related
|
||||
race during nf_conntrack_helper_unregister().
|
||||
|
||||
Fixes: b20ab9cc63ca ("netfilter: nf_ct_helper: better logging for dropped packets")
|
||||
Signed-off-by: Florian Westphal <fw@strlen.de>
|
||||
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
|
||||
|
||||
Signed-off-by: Florian Westphal <fwestpha@redhat.com>
|
||||
|
||||
diff --git a/net/netfilter/nf_conntrack_helper.c b/net/netfilter/nf_conntrack_helper.c
|
||||
index b594cd2..17e971b 100644
|
||||
--- a/net/netfilter/nf_conntrack_helper.c
|
||||
+++ b/net/netfilter/nf_conntrack_helper.c
|
||||
@@ -321,8 +321,8 @@ __printf(3, 4)
|
||||
void nf_ct_helper_log(struct sk_buff *skb, const struct nf_conn *ct,
|
||||
const char *fmt, ...)
|
||||
{
|
||||
+ const char *helper_name = "(null)";
|
||||
const struct nf_conn_help *help;
|
||||
- const struct nf_conntrack_helper *helper;
|
||||
struct va_format vaf;
|
||||
va_list args;
|
||||
|
||||
@@ -331,14 +331,17 @@ void nf_ct_helper_log(struct sk_buff *skb, const struct nf_conn *ct,
|
||||
vaf.fmt = fmt;
|
||||
vaf.va = &args;
|
||||
|
||||
- /* Called from the helper function, this call never fails */
|
||||
help = nfct_help(ct);
|
||||
+ if (help) {
|
||||
+ const struct nf_conntrack_helper *helper;
|
||||
|
||||
- /* rcu_read_lock()ed by nf_hook_thresh */
|
||||
- helper = rcu_dereference(help->helper);
|
||||
+ helper = rcu_dereference(help->helper);
|
||||
+ if (helper)
|
||||
+ helper_name = helper->name;
|
||||
+ }
|
||||
|
||||
nf_log_packet(nf_ct_net(ct), nf_ct_l3num(ct), 0, skb, NULL, NULL, NULL,
|
||||
- "nf_ct_%s: dropping packet: %pV ", helper->name, &vaf);
|
||||
+ "helper %s dropping packet: %pV ", helper_name, &vaf);
|
||||
|
||||
va_end(args);
|
||||
}
|
||||
56
1155-netfilter-ip6t-hbh-reject-oversized-option-lists.patch
Normal file
56
1155-netfilter-ip6t-hbh-reject-oversized-option-lists.patch
Normal file
@ -0,0 +1,56 @@
|
||||
From c3badd18c01702498ec26b9c13396a7fc2e8fc11 Mon Sep 17 00:00:00 2001
|
||||
From: Florian Westphal <fwestpha@redhat.com>
|
||||
Date: Wed, 20 May 2026 11:48:58 +0200
|
||||
Subject: [PATCH] netfilter: ip6t_hbh: reject oversized option lists
|
||||
|
||||
JIRA: https://redhat.atlassian.net/browse/RHEL-168848
|
||||
Upstream Status: commit 4322dcde6b41
|
||||
|
||||
commit 4322dcde6b4173c2d8e8e6118ed290794263bcc8
|
||||
Author: Zhengchuan Liang <zcliangcn@gmail.com>
|
||||
Date: Wed May 13 15:57:17 2026 +0800
|
||||
|
||||
netfilter: ip6t_hbh: reject oversized option lists
|
||||
|
||||
struct ip6t_opts stores at most IP6T_OPTS_OPTSNR option descriptors,
|
||||
but hbh_mt6_check() does not reject larger optsnr values supplied from
|
||||
userspace.
|
||||
|
||||
Validate optsnr in the rule setup path so only match data that fits the
|
||||
fixed-size opts array can be installed. This follows the existing xtables
|
||||
pattern of rejecting invalid user-provided counts in checkentry() and
|
||||
keeps the packet matching path unchanged.
|
||||
|
||||
`struct ip6t_opts` has a fixed `opts[IP6T_OPTS_OPTSNR]` array,
|
||||
where `IP6T_OPTS_OPTSNR` is 16, then off-by-one array access is possible:
|
||||
|
||||
[ 137.924693][ T8692] UBSAN: array-index-out-of-bounds in ../net/ipv6/netfilter/ip6t_hbh.c:110:29
|
||||
[ 137.926167][ T8692] index 16 is out of range for type '__u16 [16]'
|
||||
|
||||
Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
|
||||
Cc: stable@kernel.org
|
||||
Reported-by: Yuan Tan <yuantan098@gmail.com>
|
||||
Reported-by: Yifan Wu <yifanwucs@gmail.com>
|
||||
Reported-by: Juefei Pu <tomapufckgml@gmail.com>
|
||||
Reported-by: Xin Liu <bird@lzu.edu.cn>
|
||||
Signed-off-by: Zhengchuan Liang <zcliangcn@gmail.com>
|
||||
Signed-off-by: Ren Wei <n05ec@lzu.edu.cn>
|
||||
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
|
||||
|
||||
Signed-off-by: Florian Westphal <fwestpha@redhat.com>
|
||||
|
||||
diff --git a/net/ipv6/netfilter/ip6t_hbh.c b/net/ipv6/netfilter/ip6t_hbh.c
|
||||
index e7a3fb9..450dd53 100644
|
||||
--- a/net/ipv6/netfilter/ip6t_hbh.c
|
||||
+++ b/net/ipv6/netfilter/ip6t_hbh.c
|
||||
@@ -168,6 +168,10 @@ static int hbh_mt6_check(const struct xt_mtchk_param *par)
|
||||
pr_debug("unknown flags %X\n", optsinfo->invflags);
|
||||
return -EINVAL;
|
||||
}
|
||||
+ if (optsinfo->optsnr > IP6T_OPTS_OPTSNR) {
|
||||
+ pr_debug("too many supported opts specified\n");
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
|
||||
if (optsinfo->flags & IP6T_OPTS_NSTRICT) {
|
||||
pr_debug("Not strict - not implemented");
|
||||
@ -0,0 +1,141 @@
|
||||
From 5c589ceb09725bf23a6c443837049ee5212c8bb2 Mon Sep 17 00:00:00 2001
|
||||
From: Florian Westphal <fwestpha@redhat.com>
|
||||
Date: Wed, 20 May 2026 11:48:59 +0200
|
||||
Subject: [PATCH] netfilter: br_netfilter: Reallocate headroom if necessary in
|
||||
neigh_hh_bridge()
|
||||
|
||||
JIRA: https://redhat.atlassian.net/browse/RHEL-168848
|
||||
Upstream Status: commit b2870fc21601
|
||||
|
||||
commit b2870fc21601db9133bc70c48c603b487614fa3b
|
||||
Author: Lorenzo Bianconi <lorenzo@kernel.org>
|
||||
Date: Thu May 14 16:46:38 2026 +0200
|
||||
|
||||
netfilter: br_netfilter: Reallocate headroom if necessary in neigh_hh_bridge()
|
||||
|
||||
neigh_hh_bridge() assumes the skb always has sufficient headroom to copy
|
||||
the aligned L2 header. This assumption can trigger the crash reported
|
||||
below using the following netfilter setup:
|
||||
|
||||
$modprobe br_netfilter
|
||||
$sysctl -w net.bridge.bridge-nf-call-iptables=1
|
||||
|
||||
$root@OpenWrt:~# nft list ruleset
|
||||
table ip nat {
|
||||
chain prerouting {
|
||||
type nat hook prerouting priority dstnat; policy accept;
|
||||
ip daddr 192.168.83.123 dnat to 192.168.83.120
|
||||
}
|
||||
}
|
||||
|
||||
- iperf3 client (192.168.83.119) --> bridge (192.168.83.118) --> iperf3 server (192.168.83.120)
|
||||
|
||||
the iperf3 client is sending packet for 192.168.83.123 to the bridge device.
|
||||
|
||||
[ 1579.036575] Unable to handle kernel write to read-only memory at virtual address ffffff8004d76ffe
|
||||
[ 1579.045482] Mem abort info:
|
||||
[ 1579.048273] ESR = 0x000000009600004f
|
||||
[ 1579.052024] EC = 0x25: DABT (current EL), IL = 32 bits
|
||||
[ 1579.057363] SET = 0, FnV = 0
|
||||
[ 1579.060417] EA = 0, S1PTW = 0
|
||||
[ 1579.063550] FSC = 0x0f: level 3 permission fault
|
||||
[ 1579.068345] Data abort info:
|
||||
[ 1579.071224] ISV = 0, ISS = 0x0000004f, ISS2 = 0x00000000
|
||||
[ 1579.076720] CM = 0, WnR = 1, TnD = 0, TagAccess = 0
|
||||
[ 1579.081770] GCS = 0, Overlay = 0, DirtyBit = 0, Xs = 0
|
||||
[ 1579.087092] swapper pgtable: 4k pages, 39-bit VAs, pgdp=0000000080dc4000
|
||||
[ 1579.093794] [ffffff8004d76ffe] pgd=180000009ffff003, p4d=180000009ffff003, pud=180000009ffff003, pmd=180000009ffe3003, pte=0060000084d76787
|
||||
[ 1579.106343] Internal error: Oops: 000000009600004f [#1] SMP
|
||||
[ 1579.193824] CPU: 0 UID: 0 PID: 235 Comm: napi/qdma_eth-3 Tainted: G O 6.12.57 #0
|
||||
[ 1579.202614] Tainted: [O]=OOT_MODULE
|
||||
[ 1579.206102] Hardware name: Airoha AN7581 Evaluation Board (DT)
|
||||
[ 1579.211929] pstate: 60400005 (nZCv daif +PAN -UAO -TCO -DIT -SSBS BTYPE=--)
|
||||
[ 1579.218889] pc : br_nf_pre_routing_finish_bridge+0x1ac/0xcc8 [br_netfilter]
|
||||
[ 1579.225859] lr : br_nf_pre_routing_finish_bridge+0x18c/0xcc8 [br_netfilter]
|
||||
[ 1579.232822] sp : ffffffc0817cba20
|
||||
[ 1579.236128] x29: ffffffc0817cba20 x28: 0000000000000000 x27: ffffff8002b89000
|
||||
[ 1579.243273] x26: ffffff8004d7700e x25: 0000000000000008 x24: 0000000000000000
|
||||
[ 1579.250416] x23: ffffffc08179d4c0 x22: 0000000000000000 x21: ffffffc08179d4c0
|
||||
[ 1579.257561] x20: ffffff8004d9b800 x19: ffffff8015010000 x18: 0000000000000014
|
||||
[ 1579.264704] x17: ffffffbf9e930000 x16: ffffffc0817c8000 x15: 0000000000000070
|
||||
[ 1579.271848] x14: 0000000000000080 x13: 0000000000000001 x12: 0000000000000000
|
||||
[ 1579.278993] x11: ffffffc0798caae0 x10: ffffff8014db6fd8 x9 : 0000000000000000
|
||||
[ 1579.286136] x8 : 0000000000000003 x7 : ffffffc08171f628 x6 : 000000001a3b83d3
|
||||
[ 1579.293281] x5 : 0000000000000000 x4 : 1beb76f22fee0000 x3 : ffffff8004d7700e
|
||||
[ 1579.300425] x2 : 0000000000000000 x1 : ffffff8004d9b8bc x0 : ffffff80026ed000
|
||||
[ 1579.307570] Call trace:
|
||||
[ 1579.310018] br_nf_pre_routing_finish_bridge+0x1ac/0xcc8 [br_netfilter]
|
||||
[ 1579.316632] br_nf_hook_thresh+0xd4/0x14bc [br_netfilter]
|
||||
[ 1579.322032] br_nf_hook_thresh+0x250/0x14bc [br_netfilter]
|
||||
[ 1579.327517] br_nf_hook_thresh+0x76c/0x14bc [br_netfilter]
|
||||
[ 1579.333003] br_handle_frame+0x180/0x480
|
||||
[ 1579.336935] __netif_receive_skb_core.constprop.0+0x540/0xf40
|
||||
[ 1579.342682] __netif_receive_skb_one_core+0x28/0x50
|
||||
[ 1579.347561] process_backlog+0x98/0x1e0
|
||||
[ 1579.351398] __napi_poll+0x34/0x1c4
|
||||
[ 1579.354887] net_rx_action+0x178/0x330
|
||||
[ 1579.358638] handle_softirqs+0x108/0x2d4
|
||||
[ 1579.362560] __do_softirq+0x10/0x18
|
||||
[ 1579.366051] ____do_softirq+0xc/0x20
|
||||
[ 1579.369627] call_on_irq_stack+0x30/0x4c
|
||||
[ 1579.373550] do_softirq_own_stack+0x18/0x20
|
||||
[ 1579.377734] do_softirq+0x4c/0x60
|
||||
[ 1579.381050] __local_bh_enable_ip+0x88/0x98
|
||||
[ 1579.385234] napi_threaded_poll_loop+0x188/0x21c
|
||||
[ 1579.389853] napi_threaded_poll+0x70/0x80
|
||||
[ 1579.393863] kthread+0xd8/0xdc
|
||||
[ 1579.396918] ret_from_fork+0x10/0x20
|
||||
[ 1579.400499] Code: 88dffc22 3707ffc2 f9406663 f9406684 (f81f0064)
|
||||
[ 1579.406589] ---[ end trace 0000000000000000 ]---
|
||||
[ 1579.411209] Kernel panic - not syncing: Oops: Fatal exception in interrupt
|
||||
[ 1579.418083] SMP: stopping secondary CPUs
|
||||
[ 1579.422012] Kernel Offset: disabled
|
||||
|
||||
Fix the issue reallocating the skb headroom if necessary in neigh_hh_bridge routine.
|
||||
|
||||
Fixes: e179e6322ac33 ("netfilter: bridge-netfilter: Fix MAC header handling with IP DNAT")
|
||||
Reviewed-by: Ido Schimmel <idosch@nvidia.com>
|
||||
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
|
||||
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
|
||||
|
||||
Signed-off-by: Florian Westphal <fwestpha@redhat.com>
|
||||
|
||||
diff --git a/include/net/neighbour.h b/include/net/neighbour.h
|
||||
index a44f262..81807ec 100644
|
||||
--- a/include/net/neighbour.h
|
||||
+++ b/include/net/neighbour.h
|
||||
@@ -475,11 +475,15 @@ static inline int neigh_event_send(struct neighbour *neigh, struct sk_buff *skb)
|
||||
#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
|
||||
static inline int neigh_hh_bridge(struct hh_cache *hh, struct sk_buff *skb)
|
||||
{
|
||||
- unsigned int seq, hh_alen;
|
||||
+ unsigned int seq, hh_alen = HH_DATA_ALIGN(ETH_HLEN);
|
||||
+ int err;
|
||||
+
|
||||
+ err = skb_cow_head(skb, hh_alen);
|
||||
+ if (err)
|
||||
+ return err;
|
||||
|
||||
do {
|
||||
seq = read_seqbegin(&hh->hh_lock);
|
||||
- hh_alen = HH_DATA_ALIGN(ETH_HLEN);
|
||||
memcpy(skb->data - hh_alen, hh->hh_data, ETH_ALEN + hh_alen - ETH_HLEN);
|
||||
} while (read_seqretry(&hh->hh_lock, seq));
|
||||
return 0;
|
||||
diff --git a/net/bridge/br_netfilter_hooks.c b/net/bridge/br_netfilter_hooks.c
|
||||
index 1ba0780..1e5ac85 100644
|
||||
--- a/net/bridge/br_netfilter_hooks.c
|
||||
+++ b/net/bridge/br_netfilter_hooks.c
|
||||
@@ -296,7 +296,11 @@ int br_nf_pre_routing_finish_bridge(struct net *net, struct sock *sk, struct sk_
|
||||
goto free_skb;
|
||||
}
|
||||
|
||||
- neigh_hh_bridge(&neigh->hh, skb);
|
||||
+ if (neigh_hh_bridge(&neigh->hh, skb)) {
|
||||
+ neigh_release(neigh);
|
||||
+ goto free_skb;
|
||||
+ }
|
||||
+
|
||||
skb->dev = br_indev;
|
||||
|
||||
ret = br_handle_frame_finish(net, sk, skb);
|
||||
@ -0,0 +1,97 @@
|
||||
From a5a3e9f4eb2479abc1e49647644becd5106e74ab Mon Sep 17 00:00:00 2001
|
||||
From: Florian Westphal <fwestpha@redhat.com>
|
||||
Date: Wed, 20 May 2026 11:49:00 +0200
|
||||
Subject: [PATCH] netfilter: nf_queue: hold bridge skb->dev while queued
|
||||
|
||||
JIRA: https://redhat.atlassian.net/browse/RHEL-168848
|
||||
Upstream Status: commit e196115ec330
|
||||
|
||||
commit e196115ec330a18de415bdb9f5071aa9f08e53ce
|
||||
Author: Haoze Xie <royenheart@gmail.com>
|
||||
Date: Fri May 15 11:19:02 2026 +0800
|
||||
|
||||
netfilter: nf_queue: hold bridge skb->dev while queued
|
||||
|
||||
br_pass_frame_up() rewrites skb->dev from the ingress port to the bridge
|
||||
master before queueing bridge LOCAL_IN packets. NFQUEUE only holds
|
||||
references on state.in/out and bridge physdevs, so a queued bridge
|
||||
packet can retain a freed bridge master in skb->dev until reinjection.
|
||||
|
||||
When the verdict is reinjected later, br_netif_receive_skb() re-enters
|
||||
the receive path with skb->dev still pointing at the freed bridge master,
|
||||
triggering a use-after-free.
|
||||
|
||||
Store skb->dev in the queue entry, hold a reference on it for the queue
|
||||
lifetime, and use the saved device when dropping queued packets during
|
||||
NETDEV_DOWN handling.
|
||||
|
||||
Fixes: ac2863445686 ("netfilter: bridge: add nf_afinfo to enable queuing to userspace")
|
||||
Cc: stable@kernel.org
|
||||
Reported-by: Yuan Tan <yuantan098@gmail.com>
|
||||
Reported-by: Yifan Wu <yifanwucs@gmail.com>
|
||||
Reported-by: Juefei Pu <tomapufckgml@gmail.com>
|
||||
Reported-by: Xin Liu <bird@lzu.edu.cn>
|
||||
Signed-off-by: Haoze Xie <royenheart@gmail.com>
|
||||
Signed-off-by: Ren Wei <n05ec@lzu.edu.cn>
|
||||
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
|
||||
|
||||
Signed-off-by: Florian Westphal <fwestpha@redhat.com>
|
||||
|
||||
diff --git a/include/net/netfilter/nf_queue.h b/include/net/netfilter/nf_queue.h
|
||||
index 4aeffdd..b880f18 100644
|
||||
--- a/include/net/netfilter/nf_queue.h
|
||||
+++ b/include/net/netfilter/nf_queue.h
|
||||
@@ -12,6 +12,7 @@
|
||||
struct nf_queue_entry {
|
||||
struct list_head list;
|
||||
struct sk_buff *skb;
|
||||
+ struct net_device *skb_dev;
|
||||
unsigned int id;
|
||||
unsigned int hook_index; /* index in hook_entries->hook[] */
|
||||
#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
|
||||
diff --git a/net/netfilter/nf_queue.c b/net/netfilter/nf_queue.c
|
||||
index 7f12e56..dd416c8 100644
|
||||
--- a/net/netfilter/nf_queue.c
|
||||
+++ b/net/netfilter/nf_queue.c
|
||||
@@ -60,6 +60,7 @@ static void nf_queue_entry_release_refs(struct nf_queue_entry *entry)
|
||||
struct nf_hook_state *state = &entry->state;
|
||||
|
||||
/* Release those devices we held, or Alexey will kill me. */
|
||||
+ dev_put(entry->skb_dev);
|
||||
dev_put(state->in);
|
||||
dev_put(state->out);
|
||||
if (state->sk)
|
||||
@@ -101,6 +102,7 @@ bool nf_queue_entry_get_refs(struct nf_queue_entry *entry)
|
||||
if (state->sk && !refcount_inc_not_zero(&state->sk->sk_refcnt))
|
||||
return false;
|
||||
|
||||
+ dev_hold(entry->skb_dev);
|
||||
dev_hold(state->in);
|
||||
dev_hold(state->out);
|
||||
|
||||
@@ -201,11 +203,11 @@ static int __nf_queue(struct sk_buff *skb, const struct nf_hook_state *state,
|
||||
|
||||
*entry = (struct nf_queue_entry) {
|
||||
.skb = skb,
|
||||
+ .skb_dev = skb->dev,
|
||||
.state = *state,
|
||||
.hook_index = index,
|
||||
.size = sizeof(*entry) + route_key_size,
|
||||
};
|
||||
-
|
||||
__nf_queue_entry_init_physdevs(entry);
|
||||
|
||||
if (!nf_queue_entry_get_refs(entry)) {
|
||||
diff --git a/net/netfilter/nfnetlink_queue.c b/net/netfilter/nfnetlink_queue.c
|
||||
index 0bb1656..64496e7 100644
|
||||
--- a/net/netfilter/nfnetlink_queue.c
|
||||
+++ b/net/netfilter/nfnetlink_queue.c
|
||||
@@ -1132,6 +1132,8 @@ dev_cmp(struct nf_queue_entry *entry, unsigned long ifindex)
|
||||
if (physinif == ifindex || physoutif == ifindex)
|
||||
return 1;
|
||||
#endif
|
||||
+ if (entry->skb_dev && entry->skb_dev->ifindex == ifindex)
|
||||
+ return 1;
|
||||
if (entry->state.in)
|
||||
if (entry->state.in->ifindex == ifindex)
|
||||
return 1;
|
||||
@ -0,0 +1,55 @@
|
||||
From 4a13cd8d6754b3c1c69f9c883caa7ee03674a53b Mon Sep 17 00:00:00 2001
|
||||
From: Florian Westphal <fwestpha@redhat.com>
|
||||
Date: Tue, 26 May 2026 13:22:20 +0200
|
||||
Subject: [PATCH] netfilter: conntrack: tcp: do not force CLOSE on invalid-seq
|
||||
RST without direction check
|
||||
|
||||
JIRA: https://redhat.atlassian.net/browse/RHEL-168848
|
||||
Upstream Status: commit bed6e04be8e6
|
||||
|
||||
commit bed6e04be8e6b9133d8b16d5a42d0e0ce674fa9a
|
||||
Author: Hamza Mahfooz <hamzamahfooz@linux.microsoft.com>
|
||||
Date: Mon May 11 10:43:14 2026 -0400
|
||||
|
||||
netfilter: conntrack: tcp: do not force CLOSE on invalid-seq RST without direction check
|
||||
|
||||
An unintended behavior in the TCP conntrack state machine allows a
|
||||
connection to be forced into the CLOSE state using an RST packet with an
|
||||
invalid sequence number.
|
||||
|
||||
Specifically, after a SYN packet is observed, an RST with an invalid SEQ
|
||||
can transition the conntrack entry to TCP_CONNTRACK_CLOSE, regardless of
|
||||
whether the RST corresponds to the expected reply direction. The relevant
|
||||
code path assumes the RST is a response to an outgoing SYN, but does not
|
||||
validate packet direction or ensure that a matching SYN was actually sent
|
||||
in the opposite direction.
|
||||
|
||||
As a result, a crafted packet sequence consisting of a SYN followed by an
|
||||
invalid-sequence RST can prematurely terminate an active NAT entry. This
|
||||
makes connection teardown easier than intended.
|
||||
|
||||
So, tighten the state transition logic to ensure that RST-triggered
|
||||
CLOSE transitions only occur when the RST is a valid response to a
|
||||
previously observed SYN in the correct direction.
|
||||
|
||||
Cc: stable@vger.kernel.org
|
||||
Fixes: 9fb9cbb1082d ("[NETFILTER]: Add nf_conntrack subsystem.")
|
||||
Signed-off-by: Hamza Mahfooz <hamzamahfooz@linux.microsoft.com>
|
||||
Signed-off-by: Florian Westphal <fw@strlen.de>
|
||||
|
||||
Signed-off-by: Florian Westphal <fwestpha@redhat.com>
|
||||
|
||||
diff --git a/net/netfilter/nf_conntrack_proto_tcp.c b/net/netfilter/nf_conntrack_proto_tcp.c
|
||||
index b67426c..e99ab1e 100644
|
||||
--- a/net/netfilter/nf_conntrack_proto_tcp.c
|
||||
+++ b/net/netfilter/nf_conntrack_proto_tcp.c
|
||||
@@ -1221,7 +1221,8 @@ int nf_conntrack_tcp_packet(struct nf_conn *ct,
|
||||
new_state = old_state;
|
||||
}
|
||||
if (((test_bit(IPS_SEEN_REPLY_BIT, &ct->status)
|
||||
- && ct->proto.tcp.last_index == TCP_SYN_SET)
|
||||
+ && ct->proto.tcp.last_index == TCP_SYN_SET
|
||||
+ && ct->proto.tcp.last_dir != dir)
|
||||
|| (!test_bit(IPS_ASSURED_BIT, &ct->status)
|
||||
&& ct->proto.tcp.last_index == TCP_ACK_SET))
|
||||
&& ntohl(th->ack_seq) == ct->proto.tcp.last_end) {
|
||||
@ -0,0 +1,68 @@
|
||||
From 35ea4ea680d6794f01d54eec2b1e507a45f422cf Mon Sep 17 00:00:00 2001
|
||||
From: Florian Westphal <fwestpha@redhat.com>
|
||||
Date: Tue, 26 May 2026 13:22:20 +0200
|
||||
Subject: [PATCH] netfilter: synproxy: refresh tcphdr after skb_ensure_writable
|
||||
|
||||
JIRA: https://redhat.atlassian.net/browse/RHEL-168848
|
||||
Upstream Status: commit 92170e6afe92
|
||||
|
||||
commit 92170e6afe927ab2792a3f71902845789c8e31b1
|
||||
Author: Chris Mason <clm@meta.com>
|
||||
Date: Tue May 19 12:36:14 2026 -0700
|
||||
|
||||
netfilter: synproxy: refresh tcphdr after skb_ensure_writable
|
||||
|
||||
synproxy_tstamp_adjust() rewrites the TCP timestamp option in place
|
||||
and then patches the TCP checksum via inet_proto_csum_replace4() on
|
||||
the caller-supplied tcphdr pointer. Both ipv4_synproxy_hook() and
|
||||
ipv6_synproxy_hook() obtain that pointer with skb_header_pointer()
|
||||
before calling in, so it may either alias skb->head directly or
|
||||
point at the caller's on-stack _tcph buffer.
|
||||
|
||||
Between obtaining the pointer and using it, the function calls
|
||||
skb_ensure_writable(skb, optend), which on a cloned or non-linear
|
||||
skb invokes pskb_expand_head() and frees the old skb->head. After
|
||||
that point the cached th is stale:
|
||||
|
||||
caller (ipv[46]_synproxy_hook)
|
||||
th = skb_header_pointer(skb, ..., &_tcph)
|
||||
synproxy_tstamp_adjust(skb, protoff, th, ...)
|
||||
skb_ensure_writable(skb, optend)
|
||||
pskb_expand_head() /* kfree(old skb->head) */
|
||||
...
|
||||
inet_proto_csum_replace4(&th->check, ...)
|
||||
/* writes into freed head, or
|
||||
into the caller's stack copy
|
||||
leaving the on-wire checksum
|
||||
stale */
|
||||
|
||||
The option bytes are written through skb->data and are fine; only
|
||||
the checksum update goes through th and so lands in the wrong
|
||||
place. The result is either a write into freed slab memory or a
|
||||
packet leaving with a checksum that does not match its payload.
|
||||
|
||||
Fix by re-deriving th from skb->data + protoff immediately after
|
||||
skb_ensure_writable() succeeds, so the subsequent checksum update
|
||||
targets the linear, writable header.
|
||||
|
||||
Fixes: 48b1de4c110a ("netfilter: add SYNPROXY core/target")
|
||||
Assisted-by: kres (claude-opus-4-7)
|
||||
Signed-off-by: Chris Mason <clm@meta.com>
|
||||
Reviewed-by: Fernando Fernandez Mancera <fmancera@suse.de>
|
||||
Signed-off-by: Florian Westphal <fw@strlen.de>
|
||||
|
||||
Signed-off-by: Florian Westphal <fwestpha@redhat.com>
|
||||
|
||||
diff --git a/net/netfilter/nf_synproxy_core.c b/net/netfilter/nf_synproxy_core.c
|
||||
index 3fa3f5d..6a851ac 100644
|
||||
--- a/net/netfilter/nf_synproxy_core.c
|
||||
+++ b/net/netfilter/nf_synproxy_core.c
|
||||
@@ -199,6 +199,8 @@ synproxy_tstamp_adjust(struct sk_buff *skb, unsigned int protoff,
|
||||
if (skb_ensure_writable(skb, optend))
|
||||
return 0;
|
||||
|
||||
+ th = (struct tcphdr *)(skb->data + protoff);
|
||||
+
|
||||
while (optoff < optend) {
|
||||
unsigned char *op = skb->data + optoff;
|
||||
|
||||
@ -0,0 +1,246 @@
|
||||
From 8fa68cde22f8c43d3f1bc0d4c7bf7c3fd8492c2e Mon Sep 17 00:00:00 2001
|
||||
From: Florian Westphal <fwestpha@redhat.com>
|
||||
Date: Tue, 26 May 2026 13:22:21 +0200
|
||||
Subject: [PATCH] netfilter: nf_conntrack_gre: fix gre keymap list corruption
|
||||
|
||||
JIRA: https://redhat.atlassian.net/browse/RHEL-168848
|
||||
Upstream Status: commit 47980b6dbf83
|
||||
|
||||
Conflict is due to missing kmalloc_obj() conversion in cs-10.
|
||||
|
||||
commit 47980b6dbf83961eec1c1363ea986e9c06ff8054
|
||||
Author: Florian Westphal <fw@strlen.de>
|
||||
Date: Thu May 14 14:21:57 2026 +0200
|
||||
|
||||
netfilter: nf_conntrack_gre: fix gre keymap list corruption
|
||||
|
||||
Quoting reporter:
|
||||
A race between GRE keymap insertion and destruction can corrupt the
|
||||
kernel list or use a freed object. `nf_ct_gre_keymap_add()` publishes a
|
||||
new keymap pointer before the embedded `list_head` is linked, while
|
||||
`nf_ct_gre_keymap_destroy()` can concurrently delete and free that
|
||||
same object. An unprivileged user can reach this through the PPTP
|
||||
conntrack helper by racing PPTP control messages or helper teardown,
|
||||
leading to KASAN-detectable list corruption/UAF in kernel context.
|
||||
|
||||
## Root Cause Analysis
|
||||
`exp_gre()` installs GRE expectations for a PPTP control flow and then
|
||||
adds two GRE keymap entries [..]
|
||||
|
||||
The add path publishes `ct_pptp_info->keymap[dir]` before linking the
|
||||
embedded list node [..]
|
||||
Concurrent teardown deletes that partially initialized object.
|
||||
|
||||
Make add/destroy symmetric: install both, destroy both while under lock.
|
||||
|
||||
Furthermore, we should refuse to publish a new mapping in case ct is going
|
||||
away, else we may leak the allocation.
|
||||
|
||||
The "retrans" detection is strange: existing mapping is checked for key
|
||||
equality with the new mapping, then for "is on the list" via list walk.
|
||||
|
||||
But I can't see how an existing keymap entry can be NOT on list.
|
||||
|
||||
Change this to only check if we're asked to map same tuple again -- if so,
|
||||
skip re-install, else signal failure.
|
||||
|
||||
Last, add a bug trap for the keymap list; it has to be empty when namespace
|
||||
is going away.
|
||||
|
||||
Reported-by: Leo Lin <leo@depthfirst.com>
|
||||
Signed-off-by: Florian Westphal <fw@strlen.de>
|
||||
|
||||
Assisted-by: Patchpal AI
|
||||
Signed-off-by: Florian Westphal <fwestpha@redhat.com>
|
||||
|
||||
diff --git a/include/linux/netfilter/nf_conntrack_proto_gre.h b/include/linux/netfilter/nf_conntrack_proto_gre.h
|
||||
index 34ce5d2..4014f78 100644
|
||||
--- a/include/linux/netfilter/nf_conntrack_proto_gre.h
|
||||
+++ b/include/linux/netfilter/nf_conntrack_proto_gre.h
|
||||
@@ -21,9 +21,10 @@ struct nf_ct_gre_keymap {
|
||||
struct rcu_head rcu;
|
||||
};
|
||||
|
||||
-/* add new tuple->key_reply pair to keymap */
|
||||
-int nf_ct_gre_keymap_add(struct nf_conn *ct, enum ip_conntrack_dir dir,
|
||||
- struct nf_conntrack_tuple *t);
|
||||
+/* add tuple->key_reply pairs to keymap */
|
||||
+bool nf_ct_gre_keymap_add(struct nf_conn *ct,
|
||||
+ const struct nf_conntrack_tuple *orig,
|
||||
+ const struct nf_conntrack_tuple *repl);
|
||||
|
||||
/* delete keymap entries */
|
||||
void nf_ct_gre_keymap_destroy(struct nf_conn *ct);
|
||||
diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c
|
||||
index 02a73b2..04f7a13 100644
|
||||
--- a/net/netfilter/nf_conntrack_core.c
|
||||
+++ b/net/netfilter/nf_conntrack_core.c
|
||||
@@ -576,6 +576,13 @@ static void destroy_gre_conntrack(struct nf_conn *ct)
|
||||
#endif
|
||||
}
|
||||
|
||||
+static void warn_on_keymap_list_leak(const struct net *net)
|
||||
+{
|
||||
+#ifdef CONFIG_NF_CT_PROTO_GRE
|
||||
+ WARN_ON_ONCE(!list_empty(&net->ct.nf_ct_proto.gre.keymap_list));
|
||||
+#endif
|
||||
+}
|
||||
+
|
||||
void nf_ct_destroy(struct nf_conntrack *nfct)
|
||||
{
|
||||
struct nf_conn *ct = (struct nf_conn *)nfct;
|
||||
@@ -2525,6 +2532,7 @@ void nf_conntrack_cleanup_net_list(struct list_head *net_exit_list)
|
||||
}
|
||||
|
||||
list_for_each_entry(net, net_exit_list, exit_list) {
|
||||
+ warn_on_keymap_list_leak(net);
|
||||
nf_conntrack_ecache_pernet_fini(net);
|
||||
nf_conntrack_expect_pernet_fini(net);
|
||||
free_percpu(net->ct.stat);
|
||||
diff --git a/net/netfilter/nf_conntrack_pptp.c b/net/netfilter/nf_conntrack_pptp.c
|
||||
index 4c67963..dc23e41 100644
|
||||
--- a/net/netfilter/nf_conntrack_pptp.c
|
||||
+++ b/net/netfilter/nf_conntrack_pptp.c
|
||||
@@ -225,13 +225,9 @@ static int exp_gre(struct nf_conn *ct, __be16 callid, __be16 peer_callid)
|
||||
if (nf_ct_expect_related(exp_reply, 0) != 0)
|
||||
goto out_unexpect_orig;
|
||||
|
||||
- /* Add GRE keymap entries */
|
||||
- if (nf_ct_gre_keymap_add(ct, IP_CT_DIR_ORIGINAL, &exp_orig->tuple) != 0)
|
||||
+ if (!nf_ct_gre_keymap_add(ct, &exp_orig->tuple,
|
||||
+ &exp_reply->tuple))
|
||||
goto out_unexpect_both;
|
||||
- if (nf_ct_gre_keymap_add(ct, IP_CT_DIR_REPLY, &exp_reply->tuple) != 0) {
|
||||
- nf_ct_gre_keymap_destroy(ct);
|
||||
- goto out_unexpect_both;
|
||||
- }
|
||||
ret = 0;
|
||||
|
||||
out_put_both:
|
||||
diff --git a/net/netfilter/nf_conntrack_proto_gre.c b/net/netfilter/nf_conntrack_proto_gre.c
|
||||
index af369e6..d637080 100644
|
||||
--- a/net/netfilter/nf_conntrack_proto_gre.c
|
||||
+++ b/net/netfilter/nf_conntrack_proto_gre.c
|
||||
@@ -85,41 +85,97 @@ static __be16 gre_keymap_lookup(struct net *net, struct nf_conntrack_tuple *t)
|
||||
return key;
|
||||
}
|
||||
|
||||
-/* add a single keymap entry, associate with specified master ct */
|
||||
-int nf_ct_gre_keymap_add(struct nf_conn *ct, enum ip_conntrack_dir dir,
|
||||
- struct nf_conntrack_tuple *t)
|
||||
+enum nf_ct_gre_km_act {
|
||||
+ NF_CT_GRE_KM_NEW,
|
||||
+ NF_CT_GRE_KM_BAD,
|
||||
+ NF_CT_GRE_KM_DUP
|
||||
+};
|
||||
+
|
||||
+static enum nf_ct_gre_km_act
|
||||
+nf_ct_gre_km_acceptable(const struct nf_ct_pptp_master *ct_pptp_info,
|
||||
+ const struct nf_conntrack_tuple *orig,
|
||||
+ const struct nf_conntrack_tuple *repl)
|
||||
+{
|
||||
+ struct nf_ct_gre_keymap *km_orig, *km_repl;
|
||||
+
|
||||
+ lockdep_assert_held(&keymap_lock);
|
||||
+
|
||||
+ km_orig = ct_pptp_info->keymap[IP_CT_DIR_ORIGINAL];
|
||||
+ km_repl = ct_pptp_info->keymap[IP_CT_DIR_REPLY];
|
||||
+
|
||||
+ if (km_orig && km_repl) {
|
||||
+ if (!gre_key_cmpfn(km_orig, orig))
|
||||
+ return NF_CT_GRE_KM_BAD;
|
||||
+
|
||||
+ if (!gre_key_cmpfn(km_repl, repl))
|
||||
+ return NF_CT_GRE_KM_BAD;
|
||||
+
|
||||
+ return NF_CT_GRE_KM_DUP;
|
||||
+ }
|
||||
+
|
||||
+ DEBUG_NET_WARN_ON_ONCE(km_orig);
|
||||
+ DEBUG_NET_WARN_ON_ONCE(km_repl);
|
||||
+ return NF_CT_GRE_KM_NEW;
|
||||
+}
|
||||
+
|
||||
+/* add keymap entries, associate with specified master ct */
|
||||
+bool nf_ct_gre_keymap_add(struct nf_conn *ct,
|
||||
+ const struct nf_conntrack_tuple *orig,
|
||||
+ const struct nf_conntrack_tuple *repl)
|
||||
{
|
||||
struct net *net = nf_ct_net(ct);
|
||||
struct nf_gre_net *net_gre = gre_pernet(net);
|
||||
struct nf_ct_pptp_master *ct_pptp_info = nfct_help_data(ct);
|
||||
- struct nf_ct_gre_keymap **kmp, *km;
|
||||
-
|
||||
- kmp = &ct_pptp_info->keymap[dir];
|
||||
- if (*kmp) {
|
||||
- /* check whether it's a retransmission */
|
||||
- list_for_each_entry_rcu(km, &net_gre->keymap_list, list) {
|
||||
- if (gre_key_cmpfn(km, t) && km == *kmp)
|
||||
- return 0;
|
||||
- }
|
||||
- pr_debug("trying to override keymap_%s for ct %p\n",
|
||||
- dir == IP_CT_DIR_REPLY ? "reply" : "orig", ct);
|
||||
- return -EEXIST;
|
||||
- }
|
||||
+ struct nf_ct_gre_keymap *km_orig, *km_repl;
|
||||
+ bool ret = false;
|
||||
|
||||
- km = kmalloc(sizeof(*km), GFP_ATOMIC);
|
||||
- if (!km)
|
||||
- return -ENOMEM;
|
||||
- memcpy(&km->tuple, t, sizeof(*t));
|
||||
- *kmp = km;
|
||||
+ km_orig = kmalloc(sizeof(*km_orig), GFP_ATOMIC);
|
||||
+ if (!km_orig)
|
||||
+ return false;
|
||||
+ km_repl = kmalloc(sizeof(*km_repl), GFP_ATOMIC);
|
||||
+ if (!km_repl)
|
||||
+ goto km_free;
|
||||
|
||||
- pr_debug("adding new entry %p: ", km);
|
||||
- nf_ct_dump_tuple(&km->tuple);
|
||||
+ memcpy(&km_orig->tuple, orig, sizeof(*orig));
|
||||
+ memcpy(&km_repl->tuple, repl, sizeof(*repl));
|
||||
|
||||
spin_lock_bh(&keymap_lock);
|
||||
- list_add_tail(&km->list, &net_gre->keymap_list);
|
||||
+ if (nf_ct_is_dying(ct))
|
||||
+ goto unlock_free;
|
||||
+
|
||||
+ switch (nf_ct_gre_km_acceptable(ct_pptp_info, orig, repl)) {
|
||||
+ case NF_CT_GRE_KM_NEW:
|
||||
+ break;
|
||||
+ case NF_CT_GRE_KM_DUP:
|
||||
+ ret = true;
|
||||
+ goto unlock_free;
|
||||
+ case NF_CT_GRE_KM_BAD:
|
||||
+ pr_debug("trying to override keymap for ct %p\n", ct);
|
||||
+ goto unlock_free;
|
||||
+ }
|
||||
+
|
||||
+ if (ct_pptp_info->keymap[IP_CT_DIR_ORIGINAL] ||
|
||||
+ ct_pptp_info->keymap[IP_CT_DIR_REPLY])
|
||||
+ goto unlock_free;
|
||||
+
|
||||
+ pr_debug("adding new entries %p,%p: ", km_orig, km_repl);
|
||||
+ nf_ct_dump_tuple(&km_orig->tuple);
|
||||
+ nf_ct_dump_tuple(&km_repl->tuple);
|
||||
+
|
||||
+ list_add_tail_rcu(&km_orig->list, &net_gre->keymap_list);
|
||||
+ list_add_tail_rcu(&km_repl->list, &net_gre->keymap_list);
|
||||
+ ct_pptp_info->keymap[IP_CT_DIR_ORIGINAL] = km_orig;
|
||||
+ ct_pptp_info->keymap[IP_CT_DIR_REPLY] = km_repl;
|
||||
spin_unlock_bh(&keymap_lock);
|
||||
|
||||
- return 0;
|
||||
+ return true;
|
||||
+
|
||||
+unlock_free:
|
||||
+ spin_unlock_bh(&keymap_lock);
|
||||
+km_free:
|
||||
+ kfree(km_orig);
|
||||
+ kfree(km_repl);
|
||||
+ return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nf_ct_gre_keymap_add);
|
||||
|
||||
69
1161-netfilter-disable-payload-mangling-in-userns.patch
Normal file
69
1161-netfilter-disable-payload-mangling-in-userns.patch
Normal file
@ -0,0 +1,69 @@
|
||||
From 968cc2c96390f06e56ed6a43f935bfebdefed28f Mon Sep 17 00:00:00 2001
|
||||
From: Florian Westphal <fw@strlen.de>
|
||||
Date: Sat, 16 May 2026 23:23:21 +0800
|
||||
Subject: [PATCH] netfilter: disable payload mangling in userns
|
||||
|
||||
Several parts of network stack rely on iph->ihl validation
|
||||
done by network stack before PRE_ROUTING.
|
||||
|
||||
Disable this feature for user namespaces for now.
|
||||
|
||||
tcp option handling is likely safe even for LOCAL_IN, so this
|
||||
this leaves tcp option mangling via nft_exthdr.c as-is.
|
||||
|
||||
I don't think these are the only means to alter packets, but these
|
||||
appear to be relatively prominent.
|
||||
|
||||
This could be relaxed later. Example:
|
||||
- allow userns for ingress hook.
|
||||
- allow userns if base is transport header.
|
||||
|
||||
Also, we should revalidate or restrict generally:
|
||||
- Don't allow linklayer writes to spill into network header
|
||||
- restrict ipv4 and ipv6 to 'known safe' writes, e.g.
|
||||
saddr/daddr/check/tos
|
||||
|
||||
Reported-by: Qi Tang <tpluszz77@gmail.com>
|
||||
Reported-by: Tong Liu <lyutoon@gmail.com>
|
||||
Tested-by: Qi Tang <tpluszz77@gmail.com>
|
||||
Link: https://lore.kernel.org/netfilter-devel/20260515100411.3141-1-fw@strlen.de/
|
||||
Signed-off-by: Florian Westphal <fw@strlen.de>
|
||||
|
||||
diff --git a/net/netfilter/nfnetlink_queue.c b/net/netfilter/nfnetlink_queue.c
|
||||
index 64496e7..8d4fa10 100644
|
||||
--- a/net/netfilter/nfnetlink_queue.c
|
||||
+++ b/net/netfilter/nfnetlink_queue.c
|
||||
@@ -1061,6 +1061,9 @@ nfqnl_mangle(void *data, unsigned int data_len, struct nf_queue_entry *e, int di
|
||||
{
|
||||
struct sk_buff *nskb;
|
||||
|
||||
+ if (e->state.net->user_ns != &init_user_ns)
|
||||
+ return -EPERM;
|
||||
+
|
||||
if (diff < 0) {
|
||||
unsigned int min_len = skb_transport_offset(e->skb);
|
||||
|
||||
@@ -1458,8 +1461,7 @@ static int nfqnl_recv_verdict(struct sk_buff *skb, const struct nfnl_info *info,
|
||||
if (nfqnl_mangle(nla_data(nfqa[NFQA_PAYLOAD]),
|
||||
payload_len, entry, diff) < 0)
|
||||
verdict = NF_DROP;
|
||||
-
|
||||
- if (ct && diff)
|
||||
+ else if (ct && diff)
|
||||
nfnl_ct->seq_adjust(entry->skb, ct, ctinfo, diff);
|
||||
}
|
||||
|
||||
diff --git a/net/netfilter/nft_payload.c b/net/netfilter/nft_payload.c
|
||||
index 7dfc534..0dba42e 100644
|
||||
--- a/net/netfilter/nft_payload.c
|
||||
+++ b/net/netfilter/nft_payload.c
|
||||
@@ -944,6 +944,9 @@ static int nft_payload_set_init(const struct nft_ctx *ctx,
|
||||
u32 csum_offset, csum_type = NFT_PAYLOAD_CSUM_NONE;
|
||||
int err;
|
||||
|
||||
+ if (ctx->net->user_ns != &init_user_ns)
|
||||
+ return -EPERM;
|
||||
+
|
||||
priv->base = ntohl(nla_get_be32(tb[NFTA_PAYLOAD_BASE]));
|
||||
priv->offset = ntohl(nla_get_be32(tb[NFTA_PAYLOAD_OFFSET]));
|
||||
priv->len = ntohl(nla_get_be32(tb[NFTA_PAYLOAD_LEN]));
|
||||
@ -0,0 +1,106 @@
|
||||
From 640441348258220e78daed40528b85b8afcedab6 Mon Sep 17 00:00:00 2001
|
||||
From: Fernando Fernandez Mancera <fmancera@suse.de>
|
||||
Date: Tue, 26 May 2026 23:58:31 +0200
|
||||
Subject: [PATCH] netfilter: synproxy: add mutex to guard hook reference
|
||||
counting
|
||||
|
||||
[ Upstream commit 2fcba19caaeb2a33017459d3430f057967bb91b6 ]
|
||||
|
||||
As the synproxy infrastructure register netfilter hooks on-demand when a
|
||||
user adds the first iptables target or nftables expression, if done
|
||||
concurrently they can race each other.
|
||||
|
||||
Introduce a mutex to serialize the refcount control blocks access from
|
||||
both frontends. While a per namespace mutex might be more efficient, it
|
||||
is not needed for target/expression like SYNPROXY.
|
||||
|
||||
Fixes: ad49d86e07a4 ("netfilter: nf_tables: Add synproxy support")
|
||||
Signed-off-by: Fernando Fernandez Mancera <fmancera@suse.de>
|
||||
Signed-off-by: Florian Westphal <fw@strlen.de>
|
||||
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
|
||||
Signed-off-by: Sasha Levin <sashal@kernel.org>
|
||||
|
||||
diff --git a/net/netfilter/nf_synproxy_core.c b/net/netfilter/nf_synproxy_core.c
|
||||
index 6a851ac..a277b2b 100644
|
||||
--- a/net/netfilter/nf_synproxy_core.c
|
||||
+++ b/net/netfilter/nf_synproxy_core.c
|
||||
@@ -21,6 +21,8 @@
|
||||
#include <net/netfilter/nf_conntrack_zones.h>
|
||||
#include <net/netfilter/nf_synproxy.h>
|
||||
|
||||
+static DEFINE_MUTEX(synproxy_mutex);
|
||||
+
|
||||
unsigned int synproxy_net_id;
|
||||
EXPORT_SYMBOL_GPL(synproxy_net_id);
|
||||
|
||||
@@ -768,26 +770,31 @@ static const struct nf_hook_ops ipv4_synproxy_ops[] = {
|
||||
|
||||
int nf_synproxy_ipv4_init(struct synproxy_net *snet, struct net *net)
|
||||
{
|
||||
- int err;
|
||||
+ int err = 0;
|
||||
|
||||
+ mutex_lock(&synproxy_mutex);
|
||||
if (snet->hook_ref4 == 0) {
|
||||
err = nf_register_net_hooks(net, ipv4_synproxy_ops,
|
||||
ARRAY_SIZE(ipv4_synproxy_ops));
|
||||
if (err)
|
||||
- return err;
|
||||
+ goto out;
|
||||
}
|
||||
|
||||
snet->hook_ref4++;
|
||||
- return 0;
|
||||
+out:
|
||||
+ mutex_unlock(&synproxy_mutex);
|
||||
+ return err;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nf_synproxy_ipv4_init);
|
||||
|
||||
void nf_synproxy_ipv4_fini(struct synproxy_net *snet, struct net *net)
|
||||
{
|
||||
+ mutex_lock(&synproxy_mutex);
|
||||
snet->hook_ref4--;
|
||||
if (snet->hook_ref4 == 0)
|
||||
nf_unregister_net_hooks(net, ipv4_synproxy_ops,
|
||||
ARRAY_SIZE(ipv4_synproxy_ops));
|
||||
+ mutex_unlock(&synproxy_mutex);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nf_synproxy_ipv4_fini);
|
||||
|
||||
@@ -1192,27 +1199,32 @@ static const struct nf_hook_ops ipv6_synproxy_ops[] = {
|
||||
int
|
||||
nf_synproxy_ipv6_init(struct synproxy_net *snet, struct net *net)
|
||||
{
|
||||
- int err;
|
||||
+ int err = 0;
|
||||
|
||||
+ mutex_lock(&synproxy_mutex);
|
||||
if (snet->hook_ref6 == 0) {
|
||||
err = nf_register_net_hooks(net, ipv6_synproxy_ops,
|
||||
ARRAY_SIZE(ipv6_synproxy_ops));
|
||||
if (err)
|
||||
- return err;
|
||||
+ goto out;
|
||||
}
|
||||
|
||||
snet->hook_ref6++;
|
||||
- return 0;
|
||||
+out:
|
||||
+ mutex_unlock(&synproxy_mutex);
|
||||
+ return err;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nf_synproxy_ipv6_init);
|
||||
|
||||
void
|
||||
nf_synproxy_ipv6_fini(struct synproxy_net *snet, struct net *net)
|
||||
{
|
||||
+ mutex_lock(&synproxy_mutex);
|
||||
snet->hook_ref6--;
|
||||
if (snet->hook_ref6 == 0)
|
||||
nf_unregister_net_hooks(net, ipv6_synproxy_ops,
|
||||
ARRAY_SIZE(ipv6_synproxy_ops));
|
||||
+ mutex_unlock(&synproxy_mutex);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nf_synproxy_ipv6_fini);
|
||||
#endif /* CONFIG_IPV6 */
|
||||
@ -0,0 +1,42 @@
|
||||
From ddddd8271359961e403d11c90c9ba9fc38914f7e Mon Sep 17 00:00:00 2001
|
||||
From: Florian Westphal <fw@strlen.de>
|
||||
Date: Wed, 27 May 2026 12:20:19 +0200
|
||||
Subject: [PATCH] netfilter: conntrack_irc: fix possible out-of-bounds read
|
||||
|
||||
[ Upstream commit 66eba0ffce3b7e11449946b4cbbef8ea36112f56 ]
|
||||
|
||||
When parsing fails after we've matched the command string we
|
||||
should bail out instead of trying to match a different command.
|
||||
|
||||
This helper should be deprecated, given prevalence of TLS I doubt it has
|
||||
any relevance in 2026.
|
||||
|
||||
Fixes: 869f37d8e48f ("[NETFILTER]: nf_conntrack/nf_nat: add IRC helper port")
|
||||
Closes: https://sashiko.dev/#/patchset/20260525182924.28456-1-fw%40strlen.de
|
||||
Signed-off-by: Florian Westphal <fw@strlen.de>
|
||||
Reviewed-by: Fernando Fernandez Mancera <fmancera@suse.de>
|
||||
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
|
||||
Signed-off-by: Sasha Levin <sashal@kernel.org>
|
||||
|
||||
diff --git a/net/netfilter/nf_conntrack_irc.c b/net/netfilter/nf_conntrack_irc.c
|
||||
index 5703846..0f50ea9 100644
|
||||
--- a/net/netfilter/nf_conntrack_irc.c
|
||||
+++ b/net/netfilter/nf_conntrack_irc.c
|
||||
@@ -208,7 +208,7 @@ static int help(struct sk_buff *skb, unsigned int protoff,
|
||||
if (parse_dcc(data, data_limit, &dcc_ip,
|
||||
&dcc_port, &addr_beg_p, &addr_end_p)) {
|
||||
pr_debug("unable to parse dcc command\n");
|
||||
- continue;
|
||||
+ goto out;
|
||||
}
|
||||
|
||||
pr_debug("DCC bound ip/port: %pI4:%u\n",
|
||||
@@ -222,7 +222,7 @@ static int help(struct sk_buff *skb, unsigned int protoff,
|
||||
net_warn_ratelimited("Forged DCC command from %pI4: %pI4:%u\n",
|
||||
&tuple->src.u3.ip,
|
||||
&dcc_ip, dcc_port);
|
||||
- continue;
|
||||
+ goto out;
|
||||
}
|
||||
|
||||
exp = nf_ct_expect_alloc(ct);
|
||||
@ -0,0 +1,40 @@
|
||||
From fda6573a46ad24f35348e024905ee5bdf729797e Mon Sep 17 00:00:00 2001
|
||||
From: Tristan Madani <tristan@talencesecurity.com>
|
||||
Date: Wed, 27 May 2026 13:57:50 +0000
|
||||
Subject: [PATCH] netfilter: nft_tunnel: fix use-after-free on object destroy
|
||||
|
||||
commit c32b26aaa2f9216520a38b3f4bfeec846eb3eb8a upstream.
|
||||
|
||||
nft_tunnel_obj_destroy() calls metadata_dst_free() which directly
|
||||
kfree()s the metadata_dst, ignoring the dst_entry refcount. Packets
|
||||
that took a reference via dst_hold() in nft_tunnel_obj_eval() and
|
||||
are still queued (e.g. in a netem qdisc) are left with a dangling
|
||||
pointer. When these packets are eventually dequeued, dst_release()
|
||||
operates on freed memory.
|
||||
|
||||
Replace metadata_dst_free() with dst_release() so the metadata_dst
|
||||
is freed only after all references are dropped. The dst subsystem
|
||||
already handles metadata_dst cleanup in dst_destroy() when
|
||||
DST_METADATA is set.
|
||||
|
||||
Fixes: af308b94a2a4 ("netfilter: nf_tables: add tunnel support")
|
||||
Cc: stable@vger.kernel.org
|
||||
Signed-off-by: Tristan Madani <tristan@talencesecurity.com>
|
||||
Reviewed-by: Fernando Fernandez Mancera <fmancera@suse.de>
|
||||
Signed-off-by: Florian Westphal <fw@strlen.de>
|
||||
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
|
||||
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
||||
|
||||
diff --git a/net/netfilter/nft_tunnel.c b/net/netfilter/nft_tunnel.c
|
||||
index e18d322..714b6a5 100644
|
||||
--- a/net/netfilter/nft_tunnel.c
|
||||
+++ b/net/netfilter/nft_tunnel.c
|
||||
@@ -705,7 +705,7 @@ static void nft_tunnel_obj_destroy(const struct nft_ctx *ctx,
|
||||
{
|
||||
struct nft_tunnel_obj *priv = nft_obj_data(obj);
|
||||
|
||||
- metadata_dst_free(priv->md);
|
||||
+ dst_release(&priv->md->dst);
|
||||
}
|
||||
|
||||
static struct nft_object_type nft_tunnel_obj_type;
|
||||
@ -0,0 +1,87 @@
|
||||
From 8470f676eadeab99132708acb1a85915664d6115 Mon Sep 17 00:00:00 2001
|
||||
From: Jiayuan Chen <jiayuan.chen@linux.dev>
|
||||
Date: Thu, 28 May 2026 19:09:19 +0800
|
||||
Subject: [PATCH] netfilter: nft_ct: bail out on template ct in get eval
|
||||
|
||||
[ Upstream commit 3027ecbdb5fdf9200251c21d4818e4c447ef78e1 ]
|
||||
|
||||
I noticed this issue while looking at a historic syzbot report [1].
|
||||
|
||||
A rule like the one below is enough to trigger the bug:
|
||||
|
||||
table ip t {
|
||||
chain pre {
|
||||
type filter hook prerouting priority raw;
|
||||
ct zone set 1
|
||||
ct original saddr 1.2.3.4 accept
|
||||
}
|
||||
}
|
||||
|
||||
The first expression attaches a per-cpu template ct via
|
||||
nft_ct_set_zone_eval() (nf_ct_tmpl_alloc -> kzalloc, tuple is all
|
||||
zero, nf_ct_l3num(ct) == 0). The next expression then calls
|
||||
nft_ct_get_eval() on the same skb, treats the template as a real ct
|
||||
and hits the 16-byte memcpy path. With dreg at NFT_REG32_15 this
|
||||
overflows past struct nft_regs on the kernel stack; with smaller
|
||||
dreg values it silently clobbers adjacent registers.
|
||||
|
||||
Reject template ct at the eval entry and in nft_ct_get_fast_eval(),
|
||||
mirroring the check nft_ct_set_eval() already has. Additionally,
|
||||
bound the address copy in NFT_CT_SRC / NFT_CT_DST by priv->len
|
||||
instead of by nf_ct_l3num(ct): nf_ct_get_tuple() zeroes the tuple
|
||||
before pkt_to_tuple() fills in only the protocol-relevant leading
|
||||
bytes, so the trailing bytes of tuple->{src,dst}.u3.all are
|
||||
well-defined zero. priv->len is validated at rule load, so the
|
||||
copy size is now bounded by the destination register rather than
|
||||
by an untrusted field on the conntrack.
|
||||
|
||||
[1]: https://syzkaller.appspot.com/bug?id=389cf09cb72926114fce90dc85a2c3231dcb647c
|
||||
|
||||
Fixes: 45d9bcda21f4 ("netfilter: nf_tables: validate len in nft_validate_data_load()")
|
||||
Suggested-by: Florian Westphal <fw@strlen.de>
|
||||
Signed-off-by: Jiayuan Chen <jiayuan.chen@linux.dev>
|
||||
Signed-off-by: Florian Westphal <fw@strlen.de>
|
||||
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
|
||||
Signed-off-by: Sasha Levin <sashal@kernel.org>
|
||||
|
||||
diff --git a/net/netfilter/nft_ct.c b/net/netfilter/nft_ct.c
|
||||
index d877767..d411971 100644
|
||||
--- a/net/netfilter/nft_ct.c
|
||||
+++ b/net/netfilter/nft_ct.c
|
||||
@@ -77,7 +77,7 @@ static void nft_ct_get_eval(const struct nft_expr *expr,
|
||||
break;
|
||||
}
|
||||
|
||||
- if (ct == NULL)
|
||||
+ if (!ct || nf_ct_is_template(ct))
|
||||
goto err;
|
||||
|
||||
switch (priv->key) {
|
||||
@@ -179,12 +179,10 @@ static void nft_ct_get_eval(const struct nft_expr *expr,
|
||||
tuple = &ct->tuplehash[priv->dir].tuple;
|
||||
switch (priv->key) {
|
||||
case NFT_CT_SRC:
|
||||
- memcpy(dest, tuple->src.u3.all,
|
||||
- nf_ct_l3num(ct) == NFPROTO_IPV4 ? 4 : 16);
|
||||
+ memcpy(dest, tuple->src.u3.all, priv->len);
|
||||
return;
|
||||
case NFT_CT_DST:
|
||||
- memcpy(dest, tuple->dst.u3.all,
|
||||
- nf_ct_l3num(ct) == NFPROTO_IPV4 ? 4 : 16);
|
||||
+ memcpy(dest, tuple->dst.u3.all, priv->len);
|
||||
return;
|
||||
case NFT_CT_PROTO_SRC:
|
||||
nft_reg_store16(dest, (__force u16)tuple->src.u.all);
|
||||
diff --git a/net/netfilter/nft_ct_fast.c b/net/netfilter/nft_ct_fast.c
|
||||
index e684c8a..ecf7b3a 100644
|
||||
--- a/net/netfilter/nft_ct_fast.c
|
||||
+++ b/net/netfilter/nft_ct_fast.c
|
||||
@@ -30,7 +30,7 @@ void nft_ct_get_fast_eval(const struct nft_expr *expr,
|
||||
break;
|
||||
}
|
||||
|
||||
- if (!ct) {
|
||||
+ if (!ct || nf_ct_is_template(ct)) {
|
||||
regs->verdict.code = NFT_BREAK;
|
||||
return;
|
||||
}
|
||||
228
1166-netfilter-revalidate-bridge-ports.patch
Normal file
228
1166-netfilter-revalidate-bridge-ports.patch
Normal file
@ -0,0 +1,228 @@
|
||||
From 43330a1e8aace6b5a8de9aba127e9e394ab49b0f Mon Sep 17 00:00:00 2001
|
||||
From: Florian Westphal <fw@strlen.de>
|
||||
Date: Tue, 2 Jun 2026 17:04:25 +0200
|
||||
Subject: [PATCH] netfilter: revalidate bridge ports
|
||||
|
||||
[ Upstream commit ccb9fd4b87538ccf19ccff78ee26700526d94867 ]
|
||||
|
||||
ebt_redirect_tg() dereferences br_port_get_rcu() return without a
|
||||
NULL check, causing a kernel panic when the bridge port has been
|
||||
removed between the original hook invocation and an NFQUEUE
|
||||
reinject.
|
||||
|
||||
A mere NULL check isn't sufficient, however. As sashiko review
|
||||
points out userspace can not only remove the port from the bridge,
|
||||
it could also place the device in a different virtual device, e.g.
|
||||
macvlan.
|
||||
|
||||
If this happens, we must drop the packet, there is no way for us to
|
||||
reinject it into the bridge path.
|
||||
|
||||
Switch to _upper API, we don't need the bridge port structure.
|
||||
Also, this fix keeps another bug intact:
|
||||
|
||||
Both nfnetlink_log and nfnetlink_queue use CONFIG_BRIDGE_NETFILTER
|
||||
too aggressive, which prevents certain logging features when queueing
|
||||
in bridge family: NETFILTER_FAMILY_BRIDGE can be enabled while the old
|
||||
CONFIG_BRIDGE_NETFILTER cruft is off.
|
||||
|
||||
Fixes tag is a common ancestor, this was always broken.
|
||||
|
||||
Fixes: f350a0a87374 ("bridge: use rx_handler_data pointer to store net_bridge_port pointer")
|
||||
Reported-by: Ji'an Zhou <eilaimemedsnaimel@gmail.com>
|
||||
Assisted-by: Claude:claude-sonnet-4-6
|
||||
Signed-off-by: Florian Westphal <fw@strlen.de>
|
||||
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
|
||||
Signed-off-by: Sasha Levin <sashal@kernel.org>
|
||||
|
||||
diff --git a/net/bridge/netfilter/ebt_dnat.c b/net/bridge/netfilter/ebt_dnat.c
|
||||
index 3fda71a..73f185c 100644
|
||||
--- a/net/bridge/netfilter/ebt_dnat.c
|
||||
+++ b/net/bridge/netfilter/ebt_dnat.c
|
||||
@@ -39,7 +39,9 @@ ebt_dnat_tg(struct sk_buff *skb, const struct xt_action_param *par)
|
||||
dev = xt_in(par);
|
||||
break;
|
||||
case NF_BR_PRE_ROUTING:
|
||||
- dev = br_port_get_rcu(xt_in(par))->br->dev;
|
||||
+ dev = netdev_master_upper_dev_get_rcu(xt_in(par));
|
||||
+ if (!dev) /* bridge port removed? */
|
||||
+ return EBT_DROP;
|
||||
break;
|
||||
default:
|
||||
dev = NULL;
|
||||
diff --git a/net/bridge/netfilter/ebt_redirect.c b/net/bridge/netfilter/ebt_redirect.c
|
||||
index 3077905..83486cd 100644
|
||||
--- a/net/bridge/netfilter/ebt_redirect.c
|
||||
+++ b/net/bridge/netfilter/ebt_redirect.c
|
||||
@@ -24,12 +24,18 @@ ebt_redirect_tg(struct sk_buff *skb, const struct xt_action_param *par)
|
||||
if (skb_ensure_writable(skb, 0))
|
||||
return EBT_DROP;
|
||||
|
||||
- if (xt_hooknum(par) != NF_BR_BROUTING)
|
||||
- /* rcu_read_lock()ed by nf_hook_thresh */
|
||||
- ether_addr_copy(eth_hdr(skb)->h_dest,
|
||||
- br_port_get_rcu(xt_in(par))->br->dev->dev_addr);
|
||||
- else
|
||||
+ if (xt_hooknum(par) != NF_BR_BROUTING) {
|
||||
+ const struct net_device *dev;
|
||||
+
|
||||
+ dev = netdev_master_upper_dev_get_rcu(xt_in(par));
|
||||
+ if (!dev)
|
||||
+ return EBT_DROP;
|
||||
+
|
||||
+ ether_addr_copy(eth_hdr(skb)->h_dest, dev->dev_addr);
|
||||
+ } else {
|
||||
ether_addr_copy(eth_hdr(skb)->h_dest, xt_in(par)->dev_addr);
|
||||
+ }
|
||||
+
|
||||
skb->pkt_type = PACKET_HOST;
|
||||
return info->target;
|
||||
}
|
||||
diff --git a/net/netfilter/nfnetlink_log.c b/net/netfilter/nfnetlink_log.c
|
||||
index b1f3eda..25a30bf 100644
|
||||
--- a/net/netfilter/nfnetlink_log.c
|
||||
+++ b/net/netfilter/nfnetlink_log.c
|
||||
@@ -450,6 +450,23 @@ static int nfulnl_put_bridge(struct nfulnl_instance *inst, const struct sk_buff
|
||||
return -1;
|
||||
}
|
||||
|
||||
+#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
|
||||
+static int nflog_put_master_ifindex(struct sk_buff *nlskb, int attr,
|
||||
+ const struct net_device *dev)
|
||||
+{
|
||||
+ const struct net_device *upper;
|
||||
+
|
||||
+ if (dev && !netif_is_bridge_port(dev))
|
||||
+ return 0;
|
||||
+
|
||||
+ upper = netdev_master_upper_dev_get_rcu((struct net_device *)dev);
|
||||
+ if (upper && nla_put_be32(nlskb, attr, htonl(upper->ifindex)))
|
||||
+ return -EMSGSIZE;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+#endif
|
||||
+
|
||||
/* This is an inline function, we don't really care about a long
|
||||
* list of arguments */
|
||||
static inline int
|
||||
@@ -504,8 +521,7 @@ __build_packet_message(struct nfnl_log_net *log,
|
||||
/* rcu_read_lock()ed by nf_hook_thresh or
|
||||
* nf_log_packet.
|
||||
*/
|
||||
- nla_put_be32(inst->skb, NFULA_IFINDEX_INDEV,
|
||||
- htonl(br_port_get_rcu(indev)->br->dev->ifindex)))
|
||||
+ nflog_put_master_ifindex(inst->skb, NFULA_IFINDEX_INDEV, indev))
|
||||
goto nla_put_failure;
|
||||
} else {
|
||||
int physinif;
|
||||
@@ -541,8 +557,7 @@ __build_packet_message(struct nfnl_log_net *log,
|
||||
/* rcu_read_lock()ed by nf_hook_thresh or
|
||||
* nf_log_packet.
|
||||
*/
|
||||
- nla_put_be32(inst->skb, NFULA_IFINDEX_OUTDEV,
|
||||
- htonl(br_port_get_rcu(outdev)->br->dev->ifindex)))
|
||||
+ nflog_put_master_ifindex(inst->skb, NFULA_IFINDEX_OUTDEV, outdev))
|
||||
goto nla_put_failure;
|
||||
} else {
|
||||
struct net_device *physoutdev;
|
||||
diff --git a/net/netfilter/nfnetlink_queue.c b/net/netfilter/nfnetlink_queue.c
|
||||
index 8d4fa10..87ac5c8 100644
|
||||
--- a/net/netfilter/nfnetlink_queue.c
|
||||
+++ b/net/netfilter/nfnetlink_queue.c
|
||||
@@ -369,10 +369,47 @@ static void nf_reinject(struct nf_queue_entry *entry, unsigned int verdict)
|
||||
nf_queue_entry_free(entry);
|
||||
}
|
||||
|
||||
+static bool nf_bridge_port_valid(const struct net_device *dev)
|
||||
+{
|
||||
+ if (!dev)
|
||||
+ return true;
|
||||
+
|
||||
+ return netif_is_bridge_port(dev);
|
||||
+}
|
||||
+
|
||||
+/* queued skbs leave rcu protection. We bump device refcount so that
|
||||
+ * the device cannot go away. However, while packet was out the port
|
||||
+ * could have been removed from the bridge.
|
||||
+ *
|
||||
+ * Ensure in+outdev are still part of a bridge at reinject time.
|
||||
+ *
|
||||
+ * The device rx_handler_data could even be pointing at data that is
|
||||
+ * not a net_bridge_port structure.
|
||||
+ */
|
||||
+static bool nf_bridge_ports_valid(const struct nf_queue_entry *entry)
|
||||
+{
|
||||
+#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
|
||||
+ if (!nf_bridge_port_valid(entry->physin) ||
|
||||
+ !nf_bridge_port_valid(entry->physout))
|
||||
+ return false;
|
||||
+#endif
|
||||
+ if (entry->state.pf != PF_BRIDGE)
|
||||
+ return true;
|
||||
+
|
||||
+ if (!nf_bridge_port_valid(entry->state.in) ||
|
||||
+ !nf_bridge_port_valid(entry->state.out))
|
||||
+ return false;
|
||||
+
|
||||
+ return true;
|
||||
+}
|
||||
+
|
||||
static void nfqnl_reinject(struct nf_queue_entry *entry, unsigned int verdict)
|
||||
{
|
||||
const struct nf_ct_hook *ct_hook;
|
||||
|
||||
+ if (!nf_bridge_ports_valid(entry))
|
||||
+ verdict = NF_DROP;
|
||||
+
|
||||
if (verdict == NF_ACCEPT ||
|
||||
verdict == NF_REPEAT ||
|
||||
verdict == NF_STOP) {
|
||||
@@ -548,6 +585,23 @@ static int nf_queue_checksum_help(struct sk_buff *entskb)
|
||||
return skb_checksum_help(entskb);
|
||||
}
|
||||
|
||||
+#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
|
||||
+static int nfqnl_put_master_ifindex(struct sk_buff *nlskb, int attr,
|
||||
+ const struct net_device *dev)
|
||||
+{
|
||||
+ const struct net_device *upper;
|
||||
+
|
||||
+ if (dev && !netif_is_bridge_port(dev))
|
||||
+ return 0;
|
||||
+
|
||||
+ upper = netdev_master_upper_dev_get_rcu((struct net_device *)dev);
|
||||
+ if (upper && nla_put_be32(nlskb, attr, htonl(upper->ifindex)))
|
||||
+ return -EMSGSIZE;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+#endif
|
||||
+
|
||||
static struct sk_buff *
|
||||
nfqnl_build_packet_message(struct net *net, struct nfqnl_instance *queue,
|
||||
struct nf_queue_entry *entry,
|
||||
@@ -681,10 +735,7 @@ nfqnl_build_packet_message(struct net *net, struct nfqnl_instance *queue,
|
||||
* netfilter_bridge) */
|
||||
if (nla_put_be32(skb, NFQA_IFINDEX_PHYSINDEV,
|
||||
htonl(indev->ifindex)) ||
|
||||
- /* this is the bridge group "brX" */
|
||||
- /* rcu_read_lock()ed by __nf_queue */
|
||||
- nla_put_be32(skb, NFQA_IFINDEX_INDEV,
|
||||
- htonl(br_port_get_rcu(indev)->br->dev->ifindex)))
|
||||
+ nfqnl_put_master_ifindex(skb, NFQA_IFINDEX_INDEV, indev))
|
||||
goto nla_put_failure;
|
||||
} else {
|
||||
int physinif;
|
||||
@@ -715,10 +766,7 @@ nfqnl_build_packet_message(struct net *net, struct nfqnl_instance *queue,
|
||||
* netfilter_bridge) */
|
||||
if (nla_put_be32(skb, NFQA_IFINDEX_PHYSOUTDEV,
|
||||
htonl(outdev->ifindex)) ||
|
||||
- /* this is the bridge group "brX" */
|
||||
- /* rcu_read_lock()ed by __nf_queue */
|
||||
- nla_put_be32(skb, NFQA_IFINDEX_OUTDEV,
|
||||
- htonl(br_port_get_rcu(outdev)->br->dev->ifindex)))
|
||||
+ nfqnl_put_master_ifindex(skb, NFQA_IFINDEX_OUTDEV, outdev))
|
||||
goto nla_put_failure;
|
||||
} else {
|
||||
int physoutif;
|
||||
@ -0,0 +1,138 @@
|
||||
From f92c90a2a3e6ff6f9f7fe88fde9004b4ca8f956d Mon Sep 17 00:00:00 2001
|
||||
From: Weiming Shi <bestswngs@gmail.com>
|
||||
Date: Wed, 3 Jun 2026 00:38:17 -0700
|
||||
Subject: [PATCH] netfilter: nf_conntrack: destroy stale expectfn expectations
|
||||
on unregister
|
||||
|
||||
[ Upstream commit c3009418f9fa1dcb3eb86f4d8c92583537b5faa3 ]
|
||||
|
||||
NAT helpers such as nf_nat_h323 store a raw pointer to module text in
|
||||
exp->expectfn (e.g. ip_nat_q931_expect). nf_ct_helper_expectfn_unregister()
|
||||
only unlinks the callback descriptor and never walks the expectation table,
|
||||
so an expectation pending at module removal survives with a dangling
|
||||
exp->expectfn into freed module text.
|
||||
|
||||
When the expected connection arrives, init_conntrack() invokes
|
||||
exp->expectfn(), now a stale pointer into the unloaded module. Reproduced
|
||||
on a KASAN build by loading the H.323 helpers, creating a Q.931
|
||||
expectation, unloading nf_nat_h323, then connecting to the expected port:
|
||||
|
||||
Oops: int3: 0000 [#1] SMP KASAN NOPTI
|
||||
RIP: 0010:0xffffffffa06102d1
|
||||
init_conntrack.isra.0 (net/netfilter/nf_conntrack_core.c:1862)
|
||||
nf_conntrack_in (net/netfilter/nf_conntrack_core.c:2049)
|
||||
ipv4_conntrack_local (net/netfilter/nf_conntrack_proto.c:223)
|
||||
nf_hook_slow (net/netfilter/core.c:619)
|
||||
__ip_local_out (net/ipv4/ip_output.c:120)
|
||||
__tcp_transmit_skb (net/ipv4/tcp_output.c:1715)
|
||||
tcp_connect (net/ipv4/tcp_output.c:4374)
|
||||
tcp_v4_connect (net/ipv4/tcp_ipv4.c:345)
|
||||
__sys_connect (net/socket.c:2167)
|
||||
Modules linked in: nf_conntrack_h323 [last unloaded: nf_nat_h323]
|
||||
|
||||
Reaching the dangling state requires CAP_SYS_MODULE in the initial user
|
||||
namespace to remove a NAT helper that still has live expectations, so this
|
||||
is a robustness fix; leaving an expectation pointing at freed text is wrong
|
||||
regardless.
|
||||
|
||||
Add nf_ct_helper_expectfn_destroy(), which walks the expectation table and
|
||||
drops every expectation whose ->expectfn matches the descriptor being torn
|
||||
down. Call it from each NAT helper's exit path after the existing RCU grace
|
||||
period, so no expectation outlives the code it points at and no extra
|
||||
synchronize_rcu() is introduced. With the fix, the same reproducer runs to
|
||||
completion without the Oops.
|
||||
|
||||
Fixes: f587de0e2feb ("[NETFILTER]: nf_conntrack/nf_nat: add H.323 helper port")
|
||||
Reported-by: Xiang Mei <xmei5@asu.edu>
|
||||
Assisted-by: Claude:claude-opus-4-8
|
||||
Signed-off-by: Weiming Shi <bestswngs@gmail.com>
|
||||
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
|
||||
Signed-off-by: Sasha Levin <sashal@kernel.org>
|
||||
|
||||
diff --git a/include/net/netfilter/nf_conntrack_helper.h b/include/net/netfilter/nf_conntrack_helper.h
|
||||
index de2f956..24cf3d2 100644
|
||||
--- a/include/net/netfilter/nf_conntrack_helper.h
|
||||
+++ b/include/net/netfilter/nf_conntrack_helper.h
|
||||
@@ -155,6 +155,7 @@ void nf_ct_helper_log(struct sk_buff *skb, const struct nf_conn *ct,
|
||||
|
||||
void nf_ct_helper_expectfn_register(struct nf_ct_helper_expectfn *n);
|
||||
void nf_ct_helper_expectfn_unregister(struct nf_ct_helper_expectfn *n);
|
||||
+void nf_ct_helper_expectfn_destroy(const struct nf_ct_helper_expectfn *n);
|
||||
struct nf_ct_helper_expectfn *
|
||||
nf_ct_helper_expectfn_find_by_name(const char *name);
|
||||
struct nf_ct_helper_expectfn *
|
||||
diff --git a/net/ipv4/netfilter/nf_nat_h323.c b/net/ipv4/netfilter/nf_nat_h323.c
|
||||
index faee20a..10e1b08 100644
|
||||
--- a/net/ipv4/netfilter/nf_nat_h323.c
|
||||
+++ b/net/ipv4/netfilter/nf_nat_h323.c
|
||||
@@ -555,6 +555,8 @@ static void __exit nf_nat_h323_fini(void)
|
||||
nf_ct_helper_expectfn_unregister(&q931_nat);
|
||||
nf_ct_helper_expectfn_unregister(&callforwarding_nat);
|
||||
synchronize_rcu();
|
||||
+ nf_ct_helper_expectfn_destroy(&q931_nat);
|
||||
+ nf_ct_helper_expectfn_destroy(&callforwarding_nat);
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
diff --git a/net/netfilter/nf_conntrack_helper.c b/net/netfilter/nf_conntrack_helper.c
|
||||
index 17e971b..2c5a717 100644
|
||||
--- a/net/netfilter/nf_conntrack_helper.c
|
||||
+++ b/net/netfilter/nf_conntrack_helper.c
|
||||
@@ -283,6 +283,25 @@ void nf_ct_helper_expectfn_unregister(struct nf_ct_helper_expectfn *n)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nf_ct_helper_expectfn_unregister);
|
||||
|
||||
+static bool expect_iter_expectfn(struct nf_conntrack_expect *exp, void *data)
|
||||
+{
|
||||
+ const struct nf_ct_helper_expectfn *n = data;
|
||||
+
|
||||
+ /* Relies on registered expectfn descriptors having unique ->expectfn
|
||||
+ * pointers, which holds for the in-tree NAT helpers.
|
||||
+ */
|
||||
+ return exp->expectfn == n->expectfn;
|
||||
+}
|
||||
+
|
||||
+/* Destroy expectations still pointing at @n->expectfn; call after the
|
||||
+ * caller's RCU grace period so none outlives the (often modular) callback.
|
||||
+ */
|
||||
+void nf_ct_helper_expectfn_destroy(const struct nf_ct_helper_expectfn *n)
|
||||
+{
|
||||
+ nf_ct_expect_iterate_destroy(expect_iter_expectfn, (void *)n);
|
||||
+}
|
||||
+EXPORT_SYMBOL_GPL(nf_ct_helper_expectfn_destroy);
|
||||
+
|
||||
/* Caller should hold the rcu lock */
|
||||
struct nf_ct_helper_expectfn *
|
||||
nf_ct_helper_expectfn_find_by_name(const char *name)
|
||||
diff --git a/net/netfilter/nf_nat_core.c b/net/netfilter/nf_nat_core.c
|
||||
index d380e1a..12bc8c9 100644
|
||||
--- a/net/netfilter/nf_nat_core.c
|
||||
+++ b/net/netfilter/nf_nat_core.c
|
||||
@@ -1355,6 +1355,7 @@ static int __init nf_nat_init(void)
|
||||
RCU_INIT_POINTER(nf_nat_hook, NULL);
|
||||
nf_ct_helper_expectfn_unregister(&follow_master_nat);
|
||||
synchronize_net();
|
||||
+ nf_ct_helper_expectfn_destroy(&follow_master_nat);
|
||||
unregister_pernet_subsys(&nat_net_ops);
|
||||
kvfree(nf_nat_bysource);
|
||||
}
|
||||
@@ -1372,6 +1373,7 @@ static void __exit nf_nat_cleanup(void)
|
||||
RCU_INIT_POINTER(nf_nat_hook, NULL);
|
||||
|
||||
synchronize_net();
|
||||
+ nf_ct_helper_expectfn_destroy(&follow_master_nat);
|
||||
kvfree(nf_nat_bysource);
|
||||
unregister_pernet_subsys(&nat_net_ops);
|
||||
}
|
||||
diff --git a/net/netfilter/nf_nat_sip.c b/net/netfilter/nf_nat_sip.c
|
||||
index 9fbfc6b..00838c0 100644
|
||||
--- a/net/netfilter/nf_nat_sip.c
|
||||
+++ b/net/netfilter/nf_nat_sip.c
|
||||
@@ -655,6 +655,7 @@ static void __exit nf_nat_sip_fini(void)
|
||||
RCU_INIT_POINTER(nf_nat_sip_hooks, NULL);
|
||||
nf_ct_helper_expectfn_unregister(&sip_nat);
|
||||
synchronize_rcu();
|
||||
+ nf_ct_helper_expectfn_destroy(&sip_nat);
|
||||
}
|
||||
|
||||
static const struct nf_nat_sip_hooks sip_hooks = {
|
||||
@ -0,0 +1,63 @@
|
||||
From c38d41134085193efd5b237cf513ad5b3421a60d Mon Sep 17 00:00:00 2001
|
||||
From: Xiang Mei <xmei5@asu.edu>
|
||||
Date: Tue, 9 Jun 2026 15:55:02 -0700
|
||||
Subject: [PATCH] netfilter: nf_log: validate MAC header was set before dumping
|
||||
it
|
||||
|
||||
[ Upstream commit a84b6fedbc97078788be78dbdd7517d143ad1a77 ]
|
||||
|
||||
The fallback path of dump_mac_header() guards the MAC header access
|
||||
only with "skb->mac_header != skb->network_header", without checking
|
||||
skb_mac_header_was_set(). When the MAC header is unset, mac_header is
|
||||
0xffff, so the test passes and skb_mac_header(skb) returns
|
||||
skb->head + 0xffff, ~64 KiB past the buffer; the loop then reads
|
||||
dev->hard_header_len bytes out of bounds into the kernel log.
|
||||
|
||||
This is reachable via the netdev logger: nf_log_unknown_packet() calls
|
||||
dump_mac_header() unconditionally, and an skb sent through AF_PACKET
|
||||
with PACKET_QDISC_BYPASS reaches the egress hook with mac_header still
|
||||
unset (__dev_queue_xmit(), which would reset it, is bypassed).
|
||||
|
||||
Add the skb_mac_header_was_set() check the ARPHRD_ETHER path already
|
||||
uses, and replace the open-coded MAC header length test with
|
||||
skb_mac_header_len(). Only skbs with an unset MAC header are affected;
|
||||
valid ones are dumped as before.
|
||||
|
||||
BUG: KASAN: slab-out-of-bounds in dump_mac_header (net/netfilter/nf_log_syslog.c:831)
|
||||
Read of size 1 at addr ffff88800ea49d3f by task exploit/148
|
||||
Call Trace:
|
||||
kasan_report (mm/kasan/report.c:595)
|
||||
dump_mac_header (net/netfilter/nf_log_syslog.c:831)
|
||||
nf_log_netdev_packet (net/netfilter/nf_log_syslog.c:938 net/netfilter/nf_log_syslog.c:963)
|
||||
nf_log_packet (net/netfilter/nf_log.c:260)
|
||||
nft_log_eval (net/netfilter/nft_log.c:60)
|
||||
nft_do_chain (net/netfilter/nf_tables_core.c:285)
|
||||
nft_do_chain_netdev (net/netfilter/nft_chain_filter.c:307)
|
||||
nf_hook_slow (net/netfilter/core.c:619)
|
||||
nf_hook_direct_egress (net/packet/af_packet.c:257)
|
||||
packet_xmit (net/packet/af_packet.c:280)
|
||||
packet_sendmsg (net/packet/af_packet.c:3114)
|
||||
__sys_sendto (net/socket.c:2265)
|
||||
|
||||
Fixes: 7eb9282cd0ef ("netfilter: ipt_LOG/ip6t_LOG: add option to print decoded MAC header")
|
||||
Reported-by: Weiming Shi <bestswngs@gmail.com>
|
||||
Assisted-by: Claude:claude-opus-4-8
|
||||
Signed-off-by: Xiang Mei <xmei5@asu.edu>
|
||||
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
|
||||
Signed-off-by: Sasha Levin <sashal@kernel.org>
|
||||
|
||||
diff --git a/net/netfilter/nf_log_syslog.c b/net/netfilter/nf_log_syslog.c
|
||||
index 5840222..09b9152 100644
|
||||
--- a/net/netfilter/nf_log_syslog.c
|
||||
+++ b/net/netfilter/nf_log_syslog.c
|
||||
@@ -799,8 +799,8 @@ static void dump_mac_header(struct nf_log_buf *m,
|
||||
|
||||
fallback:
|
||||
nf_log_buf_add(m, "MAC=");
|
||||
- if (dev->hard_header_len &&
|
||||
- skb->mac_header != skb->network_header) {
|
||||
+ if (dev->hard_header_len && skb_mac_header_was_set(skb) &&
|
||||
+ skb_mac_header_len(skb) != 0) {
|
||||
const unsigned char *p = skb_mac_header(skb);
|
||||
unsigned int i;
|
||||
|
||||
@ -0,0 +1,38 @@
|
||||
From 67b27434c43b68a97becda98c9f0c8cf6cba2134 Mon Sep 17 00:00:00 2001
|
||||
From: Florian Westphal <fw@strlen.de>
|
||||
Date: Tue, 9 Jun 2026 21:28:09 +0200
|
||||
Subject: [PATCH] netfilter: nft_exthdr: fix register tracking for F_PRESENT
|
||||
flag
|
||||
|
||||
[ Upstream commit 772cecf198da732faebb5dcfc46d66a505be8495 ]
|
||||
|
||||
nft_exthdr_init() passes user-controlled priv->len to
|
||||
nft_parse_register_store(), which marks that many bytes in the
|
||||
register bitmap as initialized. However, when NFT_EXTHDR_F_PRESENT
|
||||
is set, the eval paths write only 1 byte (nft_reg_store8) or
|
||||
4 bytes (*dest = 0 on TCP/DCCP error path). When len > 4,
|
||||
registers beyond the first are never written, retaining
|
||||
uninitialized stack data from nft_regs.
|
||||
|
||||
Bail out if userspace requests too much data when F_PRESENT is set.
|
||||
|
||||
Reported-by: Ji'an Zhou <eilaimemedsnaimel@gmail.com>
|
||||
Fixes: c078ca3b0c5b ("netfilter: nft_exthdr: Add support for existence check")
|
||||
Signed-off-by: Florian Westphal <fw@strlen.de>
|
||||
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
|
||||
Signed-off-by: Sasha Levin <sashal@kernel.org>
|
||||
|
||||
diff --git a/net/netfilter/nft_exthdr.c b/net/netfilter/nft_exthdr.c
|
||||
index c74012c..1fc2a94 100644
|
||||
--- a/net/netfilter/nft_exthdr.c
|
||||
+++ b/net/netfilter/nft_exthdr.c
|
||||
@@ -530,6 +530,9 @@ static int nft_exthdr_init(const struct nft_ctx *ctx,
|
||||
return err;
|
||||
}
|
||||
|
||||
+ if ((flags & NFT_EXTHDR_F_PRESENT) && len != 1)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
priv->type = nla_get_u8(tb[NFTA_EXTHDR_TYPE]);
|
||||
priv->offset = offset;
|
||||
priv->len = len;
|
||||
@ -0,0 +1,83 @@
|
||||
From 8c84885e9790823828bb8084736ea15769b1ac16 Mon Sep 17 00:00:00 2001
|
||||
From: Davide Ornaghi <d.ornaghi97@gmail.com>
|
||||
Date: Mon, 15 Jun 2026 09:34:53 -0400
|
||||
Subject: [PATCH] netfilter: nft_fib: fix stale stack leak via the OIFNAME
|
||||
register
|
||||
|
||||
[ Upstream commit ab185e0c4fb82dfba6fb86f8271e06f931d9c64c ]
|
||||
|
||||
For NFT_FIB_RESULT_OIFNAME the destination register is declared with
|
||||
len = IFNAMSIZ (four 32-bit registers), but on the lookup-fail,
|
||||
RTN_LOCAL and oif-mismatch paths nft_fib{4,6}_eval() only writes one
|
||||
register via "*dest = 0". The remaining three registers are left as
|
||||
whatever was on the stack in nft_do_chain()'s struct nft_regs, and a
|
||||
downstream expression that loads the register span can leak that
|
||||
uninitialised kernel stack to userspace.
|
||||
|
||||
The NFTA_FIB_F_PRESENT existence check has the same shape: it is only
|
||||
meaningful for NFT_FIB_RESULT_OIF, yet it was accepted for any result type
|
||||
while the eval stores a single byte via nft_reg_store8(), leaving the rest
|
||||
of the declared span stale.
|
||||
|
||||
Fix both:
|
||||
|
||||
- replace the bare "*dest = 0" in the eval with nft_fib_store_result(),
|
||||
which strscpy_pad()s the whole IFNAMSIZ for OIFNAME (and is already
|
||||
used on the other early-return path), and
|
||||
|
||||
- restrict NFTA_FIB_F_PRESENT to NFT_FIB_RESULT_OIF and declare its
|
||||
destination as a single u8, so the marked span matches the one byte
|
||||
the eval writes.
|
||||
|
||||
Fixes: f6d0cbcf09c5 ("netfilter: nf_tables: add fib expression")
|
||||
Suggested-by: Florian Westphal <fw@strlen.de>
|
||||
Cc: stable@vger.kernel.org
|
||||
Signed-off-by: Davide Ornaghi <d.ornaghi97@gmail.com>
|
||||
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
|
||||
[ kept the tree's older `ip6_route_lookup()`/`rt6_info` IPv6 context and changed only `*dest = 0;` to `nft_fib_store_result(dest, priv, NULL);` ]
|
||||
Signed-off-by: Sasha Levin <sashal@kernel.org>
|
||||
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
||||
|
||||
diff --git a/net/ipv4/netfilter/nft_fib_ipv4.c b/net/ipv4/netfilter/nft_fib_ipv4.c
|
||||
index f514eb5..1c22ee4 100644
|
||||
--- a/net/ipv4/netfilter/nft_fib_ipv4.c
|
||||
+++ b/net/ipv4/netfilter/nft_fib_ipv4.c
|
||||
@@ -127,7 +127,7 @@ void nft_fib4_eval(const struct nft_expr *expr, struct nft_regs *regs,
|
||||
fl4.saddr = get_saddr(iph->daddr);
|
||||
}
|
||||
|
||||
- *dest = 0;
|
||||
+ nft_fib_store_result(dest, priv, NULL);
|
||||
|
||||
if (fib_lookup(nft_net(pkt), &fl4, &res, FIB_LOOKUP_IGNORE_LINKSTATE))
|
||||
return;
|
||||
diff --git a/net/ipv6/netfilter/nft_fib_ipv6.c b/net/ipv6/netfilter/nft_fib_ipv6.c
|
||||
index 421036a..3005dfb 100644
|
||||
--- a/net/ipv6/netfilter/nft_fib_ipv6.c
|
||||
+++ b/net/ipv6/netfilter/nft_fib_ipv6.c
|
||||
@@ -192,7 +192,7 @@ void nft_fib6_eval(const struct nft_expr *expr, struct nft_regs *regs,
|
||||
|
||||
lookup_flags = nft_fib6_flowi_init(&fl6, priv, pkt, oif, iph);
|
||||
|
||||
- *dest = 0;
|
||||
+ nft_fib_store_result(dest, priv, NULL);
|
||||
rt = (void *)ip6_route_lookup(nft_net(pkt), &fl6, pkt->skb,
|
||||
lookup_flags);
|
||||
if (rt->dst.error)
|
||||
diff --git a/net/netfilter/nft_fib.c b/net/netfilter/nft_fib.c
|
||||
index 96e02a8..2284613 100644
|
||||
--- a/net/netfilter/nft_fib.c
|
||||
+++ b/net/netfilter/nft_fib.c
|
||||
@@ -107,6 +107,12 @@ int nft_fib_init(const struct nft_ctx *ctx, const struct nft_expr *expr,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
+ if (priv->flags & NFTA_FIB_F_PRESENT) {
|
||||
+ if (priv->result != NFT_FIB_RESULT_OIF)
|
||||
+ return -EINVAL;
|
||||
+ len = sizeof(u8);
|
||||
+ }
|
||||
+
|
||||
err = nft_parse_register_store(ctx, tb[NFTA_FIB_DREG], &priv->dreg,
|
||||
NULL, NFT_DATA_VALUE, len);
|
||||
if (err < 0)
|
||||
@ -0,0 +1,185 @@
|
||||
From 2354e975932dabb06fad239f07a3b68fd1809737 Mon Sep 17 00:00:00 2001
|
||||
From: Pablo Neira Ayuso <pablo@netfilter.org>
|
||||
Date: Wed, 10 Jun 2026 00:03:19 +0200
|
||||
Subject: [PATCH] netfilter: nf_dup_netdev: add nf_dev_xmit_recursion*()
|
||||
helpers and use them
|
||||
|
||||
Update nft_dup and nft_fwd to use the nf_dev_xmit_recursion() helpers.
|
||||
This patch also disables BH when transmitting the skb to address a
|
||||
possible migration to different CPU leading to imbalanced decrementation
|
||||
of the recursion counters.
|
||||
|
||||
This is modeled after Florian Westphal's dev_xmit_recursion*() API
|
||||
available since commit 97cdcf37b57e ("net: place xmit recursion in
|
||||
softnet data") according to its current state in the tree.
|
||||
|
||||
Fixes: 1d47b55b36d2 ("netfilter: nft_fwd_netdev: use recursion counter in neigh egress path")
|
||||
Fixes: f37ad9127039 ("netfilter: nf_dup_netdev: Move the recursion counter struct netdev_xmit")
|
||||
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
|
||||
|
||||
diff --git a/include/net/netfilter/nf_dup_netdev.h b/include/net/netfilter/nf_dup_netdev.h
|
||||
index 609bcf4..b175d27 100644
|
||||
--- a/include/net/netfilter/nf_dup_netdev.h
|
||||
+++ b/include/net/netfilter/nf_dup_netdev.h
|
||||
@@ -3,23 +3,10 @@
|
||||
#define _NF_DUP_NETDEV_H_
|
||||
|
||||
#include <net/netfilter/nf_tables.h>
|
||||
-#include <linux/netdevice.h>
|
||||
-#include <linux/sched.h>
|
||||
|
||||
void nf_dup_netdev_egress(const struct nft_pktinfo *pkt, int oif);
|
||||
void nf_fwd_netdev_egress(const struct nft_pktinfo *pkt, int oif);
|
||||
|
||||
-#define NF_RECURSION_LIMIT 2
|
||||
-
|
||||
-static inline u8 *nf_get_nf_dup_skb_recursion(void)
|
||||
-{
|
||||
-#ifndef CONFIG_PREEMPT_RT
|
||||
- return this_cpu_ptr(&softnet_data.xmit.nf_dup_skb_recursion);
|
||||
-#else
|
||||
- return ¤t->net_xmit.nf_dup_skb_recursion;
|
||||
-#endif
|
||||
-}
|
||||
-
|
||||
struct nft_offload_ctx;
|
||||
struct nft_flow_rule;
|
||||
|
||||
diff --git a/net/netfilter/nf_dup_netdev.c b/net/netfilter/nf_dup_netdev.c
|
||||
index 516bcf4..ab39733 100644
|
||||
--- a/net/netfilter/nf_dup_netdev.c
|
||||
+++ b/net/netfilter/nf_dup_netdev.c
|
||||
@@ -17,28 +17,24 @@
|
||||
|
||||
static DEFINE_PER_CPU(u8, nf_dup_skb_recursion);
|
||||
|
||||
-#define NF_RECURSION_LIMIT 2
|
||||
-
|
||||
-#ifndef CONFIG_PREEMPT_RT
|
||||
-static u8 *nf_get_nf_dup_skb_recursion(void)
|
||||
+static bool nf_dev_xmit_recursion(void)
|
||||
{
|
||||
- return this_cpu_ptr(&softnet_data.xmit.nf_dup_skb_recursion);
|
||||
+ return unlikely(__this_cpu_read(nf_dup_skb_recursion) > NF_RECURSION_LIMIT);
|
||||
}
|
||||
-#else
|
||||
|
||||
-static u8 *nf_get_nf_dup_skb_recursion(void)
|
||||
+static void nf_dev_xmit_recursion_inc(void)
|
||||
{
|
||||
- return ¤t->net_xmit.nf_dup_skb_recursion;
|
||||
+ __this_cpu_inc(nf_dup_skb_recursion);
|
||||
}
|
||||
|
||||
-#endif
|
||||
+static void nf_dev_xmit_recursion_dec(void)
|
||||
+{
|
||||
+ __this_cpu_dec(nf_dup_skb_recursion);
|
||||
+}
|
||||
|
||||
static void nf_do_netdev_egress(struct sk_buff *skb, struct net_device *dev,
|
||||
enum nf_dev_hooks hook)
|
||||
{
|
||||
- if (__this_cpu_read(nf_dup_skb_recursion) > NF_RECURSION_LIMIT)
|
||||
- goto err;
|
||||
-
|
||||
if (hook == NF_NETDEV_INGRESS && skb_mac_header_was_set(skb)) {
|
||||
if (skb_cow_head(skb, skb->mac_len))
|
||||
goto err;
|
||||
@@ -48,9 +44,15 @@ static void nf_do_netdev_egress(struct sk_buff *skb, struct net_device *dev,
|
||||
|
||||
skb->dev = dev;
|
||||
skb_clear_tstamp(skb);
|
||||
- __this_cpu_inc(nf_dup_skb_recursion);
|
||||
+ local_bh_disable();
|
||||
+ if (nf_dev_xmit_recursion()) {
|
||||
+ local_bh_enable();
|
||||
+ goto err;
|
||||
+ }
|
||||
+ nf_dev_xmit_recursion_inc();
|
||||
dev_queue_xmit(skb);
|
||||
- __this_cpu_dec(nf_dup_skb_recursion);
|
||||
+ nf_dev_xmit_recursion_dec();
|
||||
+ local_bh_enable();
|
||||
return;
|
||||
err:
|
||||
kfree_skb(skb);
|
||||
diff --git a/net/netfilter/nft_fwd_netdev.c b/net/netfilter/nft_fwd_netdev.c
|
||||
index a9743a1..8b0de2d 100644
|
||||
--- a/net/netfilter/nft_fwd_netdev.c
|
||||
+++ b/net/netfilter/nft_fwd_netdev.c
|
||||
@@ -21,6 +21,25 @@ struct nft_fwd_netdev {
|
||||
u8 sreg_dev;
|
||||
};
|
||||
|
||||
+
|
||||
+#define NF_RECURSION_LIMIT 2
|
||||
+static DEFINE_PER_CPU(u8, nf_dup_skb_recursion);
|
||||
+
|
||||
+static bool nf_dev_xmit_recursion(void)
|
||||
+{
|
||||
+ return unlikely(__this_cpu_read(nf_dup_skb_recursion) > NF_RECURSION_LIMIT);
|
||||
+}
|
||||
+
|
||||
+static void nf_dev_xmit_recursion_inc(void)
|
||||
+{
|
||||
+ __this_cpu_inc(nf_dup_skb_recursion);
|
||||
+}
|
||||
+
|
||||
+static void nf_dev_xmit_recursion_dec(void)
|
||||
+{
|
||||
+ __this_cpu_dec(nf_dup_skb_recursion);
|
||||
+}
|
||||
+
|
||||
static void nft_fwd_netdev_eval(const struct nft_expr *expr,
|
||||
struct nft_regs *regs,
|
||||
const struct nft_pktinfo *pkt)
|
||||
@@ -95,7 +114,6 @@ static void nft_fwd_neigh_eval(const struct nft_expr *expr,
|
||||
struct nft_regs *regs,
|
||||
const struct nft_pktinfo *pkt)
|
||||
{
|
||||
- u8 *nf_dup_skb_recursion = nf_get_nf_dup_skb_recursion();
|
||||
struct nft_fwd_neigh *priv = nft_expr_priv(expr);
|
||||
void *addr = ®s->data[priv->sreg_addr];
|
||||
int oif = regs->data[priv->sreg_dev];
|
||||
@@ -144,13 +162,15 @@ static void nft_fwd_neigh_eval(const struct nft_expr *expr,
|
||||
goto out;
|
||||
}
|
||||
|
||||
- if (*nf_dup_skb_recursion > NF_RECURSION_LIMIT) {
|
||||
+ dev = dev_get_by_index_rcu(nft_net(pkt), oif);
|
||||
+ if (!dev) {
|
||||
verdict = NF_DROP;
|
||||
goto out;
|
||||
}
|
||||
|
||||
- dev = dev_get_by_index_rcu(nft_net(pkt), oif);
|
||||
- if (dev == NULL) {
|
||||
+ local_bh_disable();
|
||||
+ if (nf_dev_xmit_recursion()) {
|
||||
+ local_bh_enable();
|
||||
verdict = NF_DROP;
|
||||
goto out;
|
||||
}
|
||||
@@ -159,16 +179,18 @@ static void nft_fwd_neigh_eval(const struct nft_expr *expr,
|
||||
if (unlikely(skb_headroom(skb) < hh_len && dev->header_ops)) {
|
||||
skb = skb_expand_head(skb, hh_len);
|
||||
if (!skb) {
|
||||
- verdict = NF_STOLEN;
|
||||
+ local_bh_enable();
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
skb->dev = dev;
|
||||
skb_clear_tstamp(skb);
|
||||
- (*nf_dup_skb_recursion)++;
|
||||
+
|
||||
+ nf_dev_xmit_recursion_inc();
|
||||
neigh_xmit(neigh_table, dev, addr, skb);
|
||||
- (*nf_dup_skb_recursion)--;
|
||||
+ nf_dev_xmit_recursion_dec();
|
||||
+ local_bh_enable();
|
||||
out:
|
||||
regs->verdict.code = verdict;
|
||||
}
|
||||
@ -0,0 +1,37 @@
|
||||
From 13e0a1308f7e0d30a339e4d839576bddd419dd69 Mon Sep 17 00:00:00 2001
|
||||
From: Pratham Gupta <pratham36gupta@gmail.com>
|
||||
Date: Mon, 4 May 2026 22:11:57 -0700
|
||||
Subject: [PATCH] netfilter: ctnetlink: use nf_ct_exp_net() in expectation dump
|
||||
|
||||
commit a7f57320bbbc67e347bf5fff4b4a9bab980d5956 upstream.
|
||||
|
||||
Commit 02a3231b6d82 ("netfilter: nf_conntrack_expect: store netns and zone in expectation")
|
||||
introduced exp->net so RCU-only expectation paths no longer need to
|
||||
dereference exp->master for netns lookups.
|
||||
|
||||
Commit 3db5647984de ("netfilter: nf_conntrack_expect: skip expectations in other netns via proc")
|
||||
updated the proc path accordingly, but ctnetlink_exp_dump_table() still
|
||||
compares against nf_ct_net(exp->master).
|
||||
|
||||
Use nf_ct_exp_net(exp) here as well so the netlink dump path matches
|
||||
the rest of the March 2026 expectation netns/RCU cleanup.
|
||||
|
||||
Fixes: 02a3231b6d82 ("netfilter: nf_conntrack_expect: store netns and zone in expectation")
|
||||
Cc: stable@vger.kernel.org
|
||||
Signed-off-by: Pratham Gupta <pratham36gupta@gmail.com>
|
||||
Signed-off-by: Florian Westphal <fw@strlen.de>
|
||||
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
||||
|
||||
diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c
|
||||
index caf0506..b375374 100644
|
||||
--- a/net/netfilter/nf_conntrack_netlink.c
|
||||
+++ b/net/netfilter/nf_conntrack_netlink.c
|
||||
@@ -3194,7 +3194,7 @@ ctnetlink_exp_dump_table(struct sk_buff *skb, struct netlink_callback *cb)
|
||||
if (l3proto && exp->tuple.src.l3num != l3proto)
|
||||
continue;
|
||||
|
||||
- if (!net_eq(nf_ct_net(exp->master), net))
|
||||
+ if (!net_eq(nf_ct_exp_net(exp), net))
|
||||
continue;
|
||||
|
||||
if (cb->args[1]) {
|
||||
@ -0,0 +1,116 @@
|
||||
From 42c3e9df13adbd54053eaacbfe7337844a243a2b Mon Sep 17 00:00:00 2001
|
||||
From: Florian Westphal <fwestpha@redhat.com>
|
||||
Date: Wed, 13 May 2026 16:57:34 +0200
|
||||
Subject: [PATCH] selftests: netfilter: nft_concat_range.sh: add check for
|
||||
double-create bug
|
||||
|
||||
JIRA: https://redhat.atlassian.net/browse/RHEL-168848
|
||||
Upstream Status: commit 94bd247bc25b
|
||||
|
||||
commit 94bd247bc25b7f1560f96e9c912db3ec1fc878ea
|
||||
Author: Florian Westphal <fw@strlen.de>
|
||||
Date: Wed Sep 10 01:39:48 2025 +0200
|
||||
|
||||
selftests: netfilter: nft_concat_range.sh: add check for double-create bug
|
||||
|
||||
Add a test case for bug resolved with:
|
||||
'netfilter: nft_set_pipapo_avx2: fix skip of expired entries'.
|
||||
|
||||
It passes on nf.git (it uses the generic/C version for insertion
|
||||
duplicate check) but fails on unpatched nf-next if AVX2 is supported:
|
||||
|
||||
cannot create same element twice 0s [FAIL]
|
||||
Could create element twice in same transaction
|
||||
table inet filter { # handle 8
|
||||
[..]
|
||||
elements = { 1.2.3.4 . 1.2.4.1 counter packets 0 bytes 0,
|
||||
1.2.4.1 . 1.2.3.4 counter packets 0 bytes 0,
|
||||
1.2.3.4 . 1.2.4.1 counter packets 0 bytes 0,
|
||||
1.2.4.1 . 1.2.3.4 counter packets 0 bytes 0 }
|
||||
|
||||
Reviewed-by: Stefano Brivio <sbrivio@redhat.com>
|
||||
Signed-off-by: Florian Westphal <fw@strlen.de>
|
||||
|
||||
Signed-off-by: Florian Westphal <fwestpha@redhat.com>
|
||||
|
||||
diff --git a/tools/testing/selftests/net/netfilter/nft_concat_range.sh b/tools/testing/selftests/net/netfilter/nft_concat_range.sh
|
||||
index 20e76b3..ad97c62 100755
|
||||
--- a/tools/testing/selftests/net/netfilter/nft_concat_range.sh
|
||||
+++ b/tools/testing/selftests/net/netfilter/nft_concat_range.sh
|
||||
@@ -29,7 +29,7 @@ TYPES="net_port port_net net6_port port_proto net6_port_mac net6_port_mac_proto
|
||||
net6_port_net6_port net_port_mac_proto_net"
|
||||
|
||||
# Reported bugs, also described by TYPE_ variables below
|
||||
-BUGS="flush_remove_add reload net_port_proto_match avx2_mismatch"
|
||||
+BUGS="flush_remove_add reload net_port_proto_match avx2_mismatch doublecreate"
|
||||
|
||||
# List of possible paths to pktgen script from kernel tree for performance tests
|
||||
PKTGEN_SCRIPT_PATHS="
|
||||
@@ -408,6 +408,18 @@ perf_duration 0
|
||||
"
|
||||
|
||||
|
||||
+TYPE_doublecreate="
|
||||
+display cannot create same element twice
|
||||
+type_spec ipv4_addr . ipv4_addr
|
||||
+chain_spec ip saddr . ip daddr
|
||||
+dst addr4
|
||||
+proto icmp
|
||||
+
|
||||
+race_repeat 0
|
||||
+
|
||||
+perf_duration 0
|
||||
+"
|
||||
+
|
||||
# Set template for all tests, types and rules are filled in depending on test
|
||||
set_template='
|
||||
flush ruleset
|
||||
@@ -1900,6 +1912,48 @@ test_bug_avx2_mismatch()
|
||||
fi
|
||||
}
|
||||
|
||||
+test_bug_doublecreate()
|
||||
+{
|
||||
+ local elements="1.2.3.4 . 1.2.4.1, 1.2.4.1 . 1.2.3.4"
|
||||
+ local ret=1
|
||||
+ local i
|
||||
+
|
||||
+ setup veth send_"${proto}" set || return ${ksft_skip}
|
||||
+
|
||||
+ add "{ $elements }" || return 1
|
||||
+ # expected to work: 'add' on existing should be no-op.
|
||||
+ add "{ $elements }" || return 1
|
||||
+
|
||||
+ # 'create' should return an error.
|
||||
+ if nft create element inet filter test "{ $elements }" 2>/dev/null; then
|
||||
+ err "Could create an existing element"
|
||||
+ return 1
|
||||
+ fi
|
||||
+nft -f - <<EOF 2>/dev/null
|
||||
+flush set inet filter test
|
||||
+create element inet filter test { $elements }
|
||||
+create element inet filter test { $elements }
|
||||
+EOF
|
||||
+ ret=$?
|
||||
+ if [ $ret -eq 0 ]; then
|
||||
+ err "Could create element twice in one transaction"
|
||||
+ err "$(nft -a list ruleset)"
|
||||
+ return 1
|
||||
+ fi
|
||||
+
|
||||
+nft -f - <<EOF 2>/dev/null
|
||||
+flush set inet filter test
|
||||
+create element inet filter test { $elements }
|
||||
+EOF
|
||||
+ ret=$?
|
||||
+ if [ $ret -ne 0 ]; then
|
||||
+ err "Could not flush and re-create element in one transaction"
|
||||
+ return 1
|
||||
+ fi
|
||||
+
|
||||
+ return 0
|
||||
+}
|
||||
+
|
||||
test_reported_issues() {
|
||||
eval test_bug_"${subtest}"
|
||||
}
|
||||
@ -0,0 +1,102 @@
|
||||
From 5f36590a1e7231d0c4575a7e243be208dbf96157 Mon Sep 17 00:00:00 2001
|
||||
From: Florian Westphal <fwestpha@redhat.com>
|
||||
Date: Wed, 13 May 2026 17:09:36 +0200
|
||||
Subject: [PATCH] selftests: netfilter: nft_concat_range.sh: add check for
|
||||
overlap detection bug
|
||||
|
||||
JIRA: https://redhat.atlassian.net/browse/RHEL-168848
|
||||
Upstream Status: commit a675d1caa204
|
||||
|
||||
commit a675d1caa2041f05f6343fad67b04f8babf32217
|
||||
Author: Florian Westphal <fw@strlen.de>
|
||||
Date: Thu Dec 4 12:20:36 2025 +0100
|
||||
|
||||
selftests: netfilter: nft_concat_range.sh: add check for overlap detection bug
|
||||
|
||||
without 'netfilter: nft_set_pipapo: fix range overlap detection':
|
||||
|
||||
reject overlapping range on add 0s [FAIL]
|
||||
Returned success for add { 1.2.3.4 . 1.2.4.1-1.2.4.2 } given set:
|
||||
table inet filter {
|
||||
[..]
|
||||
elements = { 1.2.3.4 . 1.2.4.1 counter packets 0 bytes 0,
|
||||
1.2.3.0-1.2.3.4 . 1.2.4.2 counter packets 0 bytes 0 }
|
||||
}
|
||||
|
||||
The element collides with existing ones and was not added, but kernel
|
||||
returned success to userspace.
|
||||
|
||||
Signed-off-by: Florian Westphal <fw@strlen.de>
|
||||
|
||||
Signed-off-by: Florian Westphal <fwestpha@redhat.com>
|
||||
|
||||
diff --git a/tools/testing/selftests/net/netfilter/nft_concat_range.sh b/tools/testing/selftests/net/netfilter/nft_concat_range.sh
|
||||
index ad97c62..394166f 100755
|
||||
--- a/tools/testing/selftests/net/netfilter/nft_concat_range.sh
|
||||
+++ b/tools/testing/selftests/net/netfilter/nft_concat_range.sh
|
||||
@@ -29,7 +29,7 @@ TYPES="net_port port_net net6_port port_proto net6_port_mac net6_port_mac_proto
|
||||
net6_port_net6_port net_port_mac_proto_net"
|
||||
|
||||
# Reported bugs, also described by TYPE_ variables below
|
||||
-BUGS="flush_remove_add reload net_port_proto_match avx2_mismatch doublecreate"
|
||||
+BUGS="flush_remove_add reload net_port_proto_match avx2_mismatch doublecreate insert_overlap"
|
||||
|
||||
# List of possible paths to pktgen script from kernel tree for performance tests
|
||||
PKTGEN_SCRIPT_PATHS="
|
||||
@@ -420,6 +420,18 @@ race_repeat 0
|
||||
perf_duration 0
|
||||
"
|
||||
|
||||
+TYPE_insert_overlap="
|
||||
+display reject overlapping range on add
|
||||
+type_spec ipv4_addr . ipv4_addr
|
||||
+chain_spec ip saddr . ip daddr
|
||||
+dst addr4
|
||||
+proto icmp
|
||||
+
|
||||
+race_repeat 0
|
||||
+
|
||||
+perf_duration 0
|
||||
+"
|
||||
+
|
||||
# Set template for all tests, types and rules are filled in depending on test
|
||||
set_template='
|
||||
flush ruleset
|
||||
@@ -1954,6 +1966,37 @@ EOF
|
||||
return 0
|
||||
}
|
||||
|
||||
+add_fail()
|
||||
+{
|
||||
+ if nft add element inet filter test "$1" 2>/dev/null ; then
|
||||
+ err "Returned success for add ${1} given set:"
|
||||
+ err "$(nft -a list set inet filter test )"
|
||||
+ return 1
|
||||
+ fi
|
||||
+
|
||||
+ return 0
|
||||
+}
|
||||
+
|
||||
+test_bug_insert_overlap()
|
||||
+{
|
||||
+ local elements="1.2.3.4 . 1.2.4.1"
|
||||
+
|
||||
+ setup veth send_"${proto}" set || return ${ksft_skip}
|
||||
+
|
||||
+ add "{ $elements }" || return 1
|
||||
+
|
||||
+ elements="1.2.3.0-1.2.3.4 . 1.2.4.1"
|
||||
+ add_fail "{ $elements }" || return 1
|
||||
+
|
||||
+ elements="1.2.3.0-1.2.3.4 . 1.2.4.2"
|
||||
+ add "{ $elements }" || return 1
|
||||
+
|
||||
+ elements="1.2.3.4 . 1.2.4.1-1.2.4.2"
|
||||
+ add_fail "{ $elements }" || return 1
|
||||
+
|
||||
+ return 0
|
||||
+}
|
||||
+
|
||||
test_reported_issues() {
|
||||
eval test_bug_"${subtest}"
|
||||
}
|
||||
@ -0,0 +1,136 @@
|
||||
From 610978474e5b971198d6181be868483d0da92936 Mon Sep 17 00:00:00 2001
|
||||
From: Florian Westphal <fwestpha@redhat.com>
|
||||
Date: Wed, 13 May 2026 17:16:14 +0200
|
||||
Subject: [PATCH] selftests: netfilter: nft_concat_range.sh: add check for
|
||||
flush+reload bug
|
||||
|
||||
JIRA: https://redhat.atlassian.net/browse/RHEL-168848
|
||||
Upstream Status: commit 6caefcd9491c
|
||||
|
||||
commit 6caefcd9491c408a4d161f7b60c8bb3d956526dd
|
||||
Author: Florian Westphal <fw@strlen.de>
|
||||
Date: Wed Mar 25 14:10:56 2026 +0100
|
||||
|
||||
selftests: netfilter: nft_concat_range.sh: add check for flush+reload bug
|
||||
|
||||
This test will fail without
|
||||
the preceding commit ("netfilter: nft_set_pipapo_avx2: fix match retart if found element is expired"):
|
||||
|
||||
reject overlapping range on add 0s [ OK ]
|
||||
reload with flush /dev/stdin:59:32-52: Error: Could not process rule: File exists
|
||||
add element inet filter test { 10.0.0.29 . 10.0.2.29 }
|
||||
|
||||
Reviewed-by: Stefano Brivio <sbrivio@redhat.com>
|
||||
Signed-off-by: Florian Westphal <fw@strlen.de>
|
||||
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
|
||||
|
||||
Signed-off-by: Florian Westphal <fwestpha@redhat.com>
|
||||
|
||||
diff --git a/tools/testing/selftests/net/netfilter/nft_concat_range.sh b/tools/testing/selftests/net/netfilter/nft_concat_range.sh
|
||||
index 394166f..a387266 100755
|
||||
--- a/tools/testing/selftests/net/netfilter/nft_concat_range.sh
|
||||
+++ b/tools/testing/selftests/net/netfilter/nft_concat_range.sh
|
||||
@@ -29,7 +29,8 @@ TYPES="net_port port_net net6_port port_proto net6_port_mac net6_port_mac_proto
|
||||
net6_port_net6_port net_port_mac_proto_net"
|
||||
|
||||
# Reported bugs, also described by TYPE_ variables below
|
||||
-BUGS="flush_remove_add reload net_port_proto_match avx2_mismatch doublecreate insert_overlap"
|
||||
+BUGS="flush_remove_add reload net_port_proto_match avx2_mismatch doublecreate
|
||||
+ insert_overlap load_flush_load4 load_flush_load8"
|
||||
|
||||
# List of possible paths to pktgen script from kernel tree for performance tests
|
||||
PKTGEN_SCRIPT_PATHS="
|
||||
@@ -432,6 +433,30 @@ race_repeat 0
|
||||
perf_duration 0
|
||||
"
|
||||
|
||||
+TYPE_load_flush_load4="
|
||||
+display reload with flush, 4bit groups
|
||||
+type_spec ipv4_addr . ipv4_addr
|
||||
+chain_spec ip saddr . ip daddr
|
||||
+dst addr4
|
||||
+proto icmp
|
||||
+
|
||||
+race_repeat 0
|
||||
+
|
||||
+perf_duration 0
|
||||
+"
|
||||
+
|
||||
+TYPE_load_flush_load8="
|
||||
+display reload with flush, 8bit groups
|
||||
+type_spec ipv4_addr . ipv4_addr
|
||||
+chain_spec ip saddr . ip daddr
|
||||
+dst addr4
|
||||
+proto icmp
|
||||
+
|
||||
+race_repeat 0
|
||||
+
|
||||
+perf_duration 0
|
||||
+"
|
||||
+
|
||||
# Set template for all tests, types and rules are filled in depending on test
|
||||
set_template='
|
||||
flush ruleset
|
||||
@@ -1981,6 +2006,12 @@ test_bug_insert_overlap()
|
||||
{
|
||||
local elements="1.2.3.4 . 1.2.4.1"
|
||||
|
||||
+ # This test has to be skipped, RHEL-10.2 ntentionally lacks
|
||||
+ # 7711f4bb4b36 ("netfilter: nft_set_pipapo: fix range overlap detection")
|
||||
+ # because this fix could cause issues with existing deployments
|
||||
+ # (ruleset restore failure).
|
||||
+ return ${ksft_skip}
|
||||
+
|
||||
setup veth send_"${proto}" set || return ${ksft_skip}
|
||||
|
||||
add "{ $elements }" || return 1
|
||||
@@ -1997,6 +2028,49 @@ test_bug_insert_overlap()
|
||||
return 0
|
||||
}
|
||||
|
||||
+test_bug_load_flush_load4()
|
||||
+{
|
||||
+ local i
|
||||
+
|
||||
+ setup veth send_"${proto}" set || return ${ksft_skip}
|
||||
+
|
||||
+ for i in $(seq 0 255); do
|
||||
+ local addelem="add element inet filter test"
|
||||
+ local j
|
||||
+
|
||||
+ for j in $(seq 0 20); do
|
||||
+ echo "$addelem { 10.$j.0.$i . 10.$j.1.$i }"
|
||||
+ echo "$addelem { 10.$j.0.$i . 10.$j.2.$i }"
|
||||
+ done
|
||||
+ done > "$tmp"
|
||||
+
|
||||
+ nft -f "$tmp" || return 1
|
||||
+
|
||||
+ ( echo "flush set inet filter test";cat "$tmp") | nft -f -
|
||||
+ [ $? -eq 0 ] || return 1
|
||||
+
|
||||
+ return 0
|
||||
+}
|
||||
+
|
||||
+test_bug_load_flush_load8()
|
||||
+{
|
||||
+ local i
|
||||
+
|
||||
+ setup veth send_"${proto}" set || return ${ksft_skip}
|
||||
+
|
||||
+ for i in $(seq 1 100); do
|
||||
+ echo "add element inet filter test { 10.0.0.$i . 10.0.1.$i }"
|
||||
+ echo "add element inet filter test { 10.0.0.$i . 10.0.2.$i }"
|
||||
+ done > "$tmp"
|
||||
+
|
||||
+ nft -f "$tmp" || return 1
|
||||
+
|
||||
+ ( echo "flush set inet filter test";cat "$tmp") | nft -f -
|
||||
+ [ $? -eq 0 ] || return 1
|
||||
+
|
||||
+ return 0
|
||||
+}
|
||||
+
|
||||
test_reported_issues() {
|
||||
eval test_bug_"${subtest}"
|
||||
}
|
||||
@ -0,0 +1,128 @@
|
||||
From 979c13114c0bb6ab9135e2c93e00c79c412aef09 Mon Sep 17 00:00:00 2001
|
||||
From: Pablo Neira Ayuso <pablo@netfilter.org>
|
||||
Date: Mon, 22 Jun 2026 21:35:14 +0200
|
||||
Subject: [PATCH] netfilter: nf_conntrack_expect: store master_tuple in
|
||||
expectation
|
||||
|
||||
Store master conntrack tuple in the expectation since exp->master might
|
||||
refer to a different conntrack when accessed from rcu read side lock
|
||||
area due to typesafe rcu rules.
|
||||
|
||||
Fixes: 02a3231b6d82 ("netfilter: nf_conntrack_expect: store netns and zone in expectation")
|
||||
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
|
||||
|
||||
diff --git a/include/net/netfilter/nf_conntrack_expect.h b/include/net/netfilter/nf_conntrack_expect.h
|
||||
index 80f50fd..ec4dd61 100644
|
||||
--- a/include/net/netfilter/nf_conntrack_expect.h
|
||||
+++ b/include/net/netfilter/nf_conntrack_expect.h
|
||||
@@ -22,16 +22,10 @@ struct nf_conntrack_expect {
|
||||
/* Hash member */
|
||||
struct hlist_node hnode;
|
||||
|
||||
- /* Network namespace */
|
||||
- possible_net_t net;
|
||||
-
|
||||
/* We expect this tuple, with the following mask */
|
||||
struct nf_conntrack_tuple tuple;
|
||||
struct nf_conntrack_tuple_mask mask;
|
||||
|
||||
-#ifdef CONFIG_NF_CONNTRACK_ZONES
|
||||
- struct nf_conntrack_zone zone;
|
||||
-#endif
|
||||
/* Usage count. */
|
||||
refcount_t use;
|
||||
|
||||
@@ -48,9 +42,6 @@ struct nf_conntrack_expect {
|
||||
/* Helper that created this expectation */
|
||||
struct nf_conntrack_helper __rcu *helper;
|
||||
|
||||
- /* Helper to assign to new connection */
|
||||
- struct nf_conntrack_helper __rcu *assign_helper;
|
||||
-
|
||||
/* The conntrack of the master connection */
|
||||
struct nf_conn *master;
|
||||
|
||||
@@ -67,6 +58,15 @@ struct nf_conntrack_expect {
|
||||
#endif
|
||||
|
||||
struct rcu_head rcu;
|
||||
+
|
||||
+/* Network namespace */
|
||||
+ RH_KABI_EXTEND(possible_net_t net)
|
||||
+#ifdef CONFIG_NF_CONNTRACK_ZONES
|
||||
+ RH_KABI_EXTEND(struct nf_conntrack_zone zone)
|
||||
+#endif
|
||||
+ /* Helper to assign to new connection */
|
||||
+ RH_KABI_EXTEND(struct nf_conntrack_helper __rcu *assign_helper)
|
||||
+ RH_KABI_EXTEND(struct nf_conntrack_tuple master_tuple)
|
||||
};
|
||||
|
||||
static inline struct net *nf_ct_exp_net(struct nf_conntrack_expect *exp)
|
||||
diff --git a/net/netfilter/nf_conntrack_broadcast.c b/net/netfilter/nf_conntrack_broadcast.c
|
||||
index 75e53fd..46218c7 100644
|
||||
--- a/net/netfilter/nf_conntrack_broadcast.c
|
||||
+++ b/net/netfilter/nf_conntrack_broadcast.c
|
||||
@@ -59,6 +59,7 @@ int nf_conntrack_broadcast_help(struct sk_buff *skb,
|
||||
if (exp == NULL)
|
||||
goto out;
|
||||
|
||||
+ exp->master_tuple = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple;
|
||||
exp->tuple = ct->tuplehash[IP_CT_DIR_REPLY].tuple;
|
||||
|
||||
helper = rcu_dereference(help->helper);
|
||||
diff --git a/net/netfilter/nf_conntrack_expect.c b/net/netfilter/nf_conntrack_expect.c
|
||||
index 1bb5bf8..9f74935 100644
|
||||
--- a/net/netfilter/nf_conntrack_expect.c
|
||||
+++ b/net/netfilter/nf_conntrack_expect.c
|
||||
@@ -352,6 +352,8 @@ void nf_ct_expect_init(struct nf_conntrack_expect *exp, unsigned int class,
|
||||
exp->tuple.src.l3num = family;
|
||||
exp->tuple.dst.protonum = proto;
|
||||
|
||||
+ exp->master_tuple = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple;
|
||||
+
|
||||
if (saddr) {
|
||||
memcpy(&exp->tuple.src.u3, saddr, len);
|
||||
if (sizeof(exp->tuple.src.u3) > len)
|
||||
diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c
|
||||
index b375374..8d69397 100644
|
||||
--- a/net/netfilter/nf_conntrack_netlink.c
|
||||
+++ b/net/netfilter/nf_conntrack_netlink.c
|
||||
@@ -3027,7 +3027,6 @@ static int
|
||||
ctnetlink_exp_dump_expect(struct sk_buff *skb,
|
||||
const struct nf_conntrack_expect *exp)
|
||||
{
|
||||
- struct nf_conn *master = exp->master;
|
||||
long timeout = ((long)exp->timeout.expires - (long)jiffies) / HZ;
|
||||
struct nf_conntrack_helper *helper;
|
||||
#if IS_ENABLED(CONFIG_NF_NAT)
|
||||
@@ -3043,9 +3042,7 @@ ctnetlink_exp_dump_expect(struct sk_buff *skb,
|
||||
goto nla_put_failure;
|
||||
if (ctnetlink_exp_dump_mask(skb, &exp->tuple, &exp->mask) < 0)
|
||||
goto nla_put_failure;
|
||||
- if (ctnetlink_exp_dump_tuple(skb,
|
||||
- &master->tuplehash[IP_CT_DIR_ORIGINAL].tuple,
|
||||
- CTA_EXPECT_MASTER) < 0)
|
||||
+ if (ctnetlink_exp_dump_tuple(skb, &exp->master_tuple, CTA_EXPECT_MASTER) < 0)
|
||||
goto nla_put_failure;
|
||||
|
||||
#if IS_ENABLED(CONFIG_NF_NAT)
|
||||
@@ -3058,9 +3055,9 @@ ctnetlink_exp_dump_expect(struct sk_buff *skb,
|
||||
if (nla_put_be32(skb, CTA_EXPECT_NAT_DIR, htonl(exp->dir)))
|
||||
goto nla_put_failure;
|
||||
|
||||
- nat_tuple.src.l3num = nf_ct_l3num(master);
|
||||
+ nat_tuple.src.l3num = exp->master_tuple.src.l3num;
|
||||
nat_tuple.src.u3 = exp->saved_addr;
|
||||
- nat_tuple.dst.protonum = nf_ct_protonum(master);
|
||||
+ nat_tuple.dst.protonum = exp->master_tuple.dst.protonum;
|
||||
nat_tuple.src.u = exp->saved_proto;
|
||||
|
||||
if (ctnetlink_exp_dump_tuple(skb, &nat_tuple,
|
||||
@@ -3606,6 +3603,7 @@ ctnetlink_alloc_expect(const struct nlattr * const cda[], struct nf_conn *ct,
|
||||
#endif
|
||||
rcu_assign_pointer(exp->helper, helper);
|
||||
rcu_assign_pointer(exp->assign_helper, assign_helper);
|
||||
+ exp->master_tuple = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple;
|
||||
exp->tuple = *tuple;
|
||||
exp->mask.src.u3 = mask->src.u3;
|
||||
exp->mask.src.u.all = mask->src.u.all;
|
||||
@ -0,0 +1,36 @@
|
||||
From 4788556d4dd9d717037e385de178974e9649231d Mon Sep 17 00:00:00 2001
|
||||
From: Andrzej Kacprowski <andrzej.kacprowski@linux.intel.com>
|
||||
Date: Mon, 1 Jun 2026 18:16:43 +0200
|
||||
Subject: [PATCH] accel/ivpu: Fix signed integer truncation in IPC receive
|
||||
|
||||
commit d9faef564438d1e4579c692c046603e7ada7bdf4 upstream.
|
||||
|
||||
Fix potential buffer overflow where firmware-supplied data_size is cast
|
||||
to signed int before being used in min_t(). Large unsigned values
|
||||
(>= 0x80000000) become negative, causing unsigned wraparound and
|
||||
oversized memcpy operations that can overflow the stack buffer.
|
||||
|
||||
Change min_t(int, ...) to min() as both values are unsigned and can be
|
||||
handled by min() without explicit cast.
|
||||
|
||||
Fixes: 3b434a3445ff ("accel/ivpu: Use threaded IRQ to handle JOB done messages")
|
||||
Cc: stable@vger.kernel.org # v6.12+
|
||||
Signed-off-by: Andrzej Kacprowski <andrzej.kacprowski@linux.intel.com>
|
||||
Reviewed-by: Karol Wachowski <karol.wachowski@linux.intel.com>
|
||||
Signed-off-by: Karol Wachowski <karol.wachowski@linux.intel.com>
|
||||
Link: https://patch.msgid.link/20260601161643.229342-1-andrzej.kacprowski@linux.intel.com
|
||||
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
||||
|
||||
diff --git a/drivers/accel/ivpu/ivpu_ipc.c b/drivers/accel/ivpu/ivpu_ipc.c
|
||||
index 5f00809..7fea203 100644
|
||||
--- a/drivers/accel/ivpu/ivpu_ipc.c
|
||||
+++ b/drivers/accel/ivpu/ivpu_ipc.c
|
||||
@@ -276,7 +276,7 @@ int ivpu_ipc_receive(struct ivpu_device *vdev, struct ivpu_ipc_consumer *cons,
|
||||
if (ipc_buf)
|
||||
memcpy(ipc_buf, rx_msg->ipc_hdr, sizeof(*ipc_buf));
|
||||
if (rx_msg->jsm_msg) {
|
||||
- u32 size = min_t(int, rx_msg->ipc_hdr->data_size, sizeof(*jsm_msg));
|
||||
+ u32 size = min(rx_msg->ipc_hdr->data_size, sizeof(*jsm_msg));
|
||||
|
||||
if (rx_msg->jsm_msg->result != VPU_JSM_STATUS_SUCCESS) {
|
||||
ivpu_err(vdev, "IPC resp result error: %d\n", rx_msg->jsm_msg->result);
|
||||
@ -0,0 +1,45 @@
|
||||
From 6992f3340e823bbac7290990fb50008cda860209 Mon Sep 17 00:00:00 2001
|
||||
From: Aidan Wallace <awallace@redhat.com>
|
||||
Date: Wed, 22 Jul 2026 00:14:03 -0500
|
||||
Subject: [PATCH] KVM: nVMX: Put vmcs12 pages if nested VM-Enter fails due to
|
||||
invalid guest state
|
||||
|
||||
JIRA: https://redhat.atlassian.net/browse/RHEL-213327
|
||||
|
||||
KVM: nVMX: Put vmcs12 pages if nested VM-Enter fails due to invalid guest state
|
||||
|
||||
Put all vmcs12 pages if KVM synthesizes a nested VM-Exit due to invalid
|
||||
guest while emulating VMLAUNCH or VMRESUME. The invalid guest state path
|
||||
doesn't use nested_vmx_vmexit() as that API is intended to be used if and
|
||||
only if L2 is active, and the open coded equivalent neglects to put the
|
||||
vmcs12 pages. Failure to put the vmcs12 pages leaks any pinned pages
|
||||
(and/or mappings) if L1 retries VMLAUNCH/VMRESUME.
|
||||
|
||||
Note, the !from_vmenter scenario doesn't suffer the same problem, as
|
||||
vmx_get_nested_state_pages() only gets/pins/maps the vmcs12 pages if L2 is
|
||||
active, i.e. if a "full" VM-Exit is guaranteed before KVM will retry
|
||||
getting vmcs12 pages.
|
||||
|
||||
Fixes: 96c66e87deee ("KVM/nVMX: Use kvm_vcpu_map when mapping the virtual APIC page")
|
||||
Fixes: 3278e0492554 ("KVM/nVMX: Use kvm_vcpu_map when mapping the posted interrupt descriptor table")
|
||||
Fixes: fe1911aa443e ("KVM: nVMX: Use kvm_vcpu_map() to get/pin vmcs12's APIC-access page")
|
||||
Reported-by: Minh Nguyen <minhnguyen.080505@gmail.com>
|
||||
Cc: stable@vger.kernel.org
|
||||
Signed-off-by: Sean Christopherson <seanjc@google.com>
|
||||
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
|
||||
(cherry picked from commit 2f2312c422fd2695da772cecb30c69994b795964)
|
||||
Signed-off-by: Aidan Wallace <awallace@redhat.com>
|
||||
|
||||
diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c
|
||||
index 7c55551..5570e0c 100644
|
||||
--- a/arch/x86/kvm/vmx/nested.c
|
||||
+++ b/arch/x86/kvm/vmx/nested.c
|
||||
@@ -3661,6 +3661,8 @@ enum nvmx_vmentry_status nested_vmx_enter_non_root_mode(struct kvm_vcpu *vcpu,
|
||||
if (!from_vmentry)
|
||||
return NVMX_VMENTRY_VMEXIT;
|
||||
|
||||
+ nested_put_vmcs12_pages(vcpu);
|
||||
+
|
||||
load_vmcs12_host_state(vcpu, vmcs12);
|
||||
vmcs12->vm_exit_reason = exit_reason.full;
|
||||
if (enable_shadow_vmcs || nested_vmx_is_evmptr12_valid(vmx))
|
||||
@ -0,0 +1,88 @@
|
||||
From 45246f884605223c7ab63808e12c973c63aba44e Mon Sep 17 00:00:00 2001
|
||||
From: Aidan Wallace <awallace@redhat.com>
|
||||
Date: Wed, 22 Jul 2026 00:15:53 -0500
|
||||
Subject: [PATCH] KVM: x86: Check for invalid/obsolete root *after* making MMU
|
||||
pages available
|
||||
|
||||
JIRA: https://redhat.atlassian.net/browse/RHEL-213327
|
||||
|
||||
KVM: x86: Check for invalid/obsolete root *after* making MMU pages available
|
||||
|
||||
Check for a "stale" page fault, i.e. for an invalid and/or obsolete root,
|
||||
after making MMU pages available for the shadow MMU. If reclaiming shadow
|
||||
pages zaps an in-use root, i.e. marks it invalid, then KVM will attempt to
|
||||
map memory into an invalid root. On its own, populating an invalid root is
|
||||
"fine", but because child shadow pages inherit their parent's role, any
|
||||
children created during the map/fetch will be created as invalid pages,
|
||||
thus violating KVM's invariant that invalid pages are never on the list of
|
||||
active MMU pages.
|
||||
|
||||
Note, the underlying flaw has existed since KVM first started tracking
|
||||
invalid roots in 2008 (commit 2e53d63acba7, "KVM: MMU: ignore zapped root
|
||||
pagetables"), but the true badness only came along in 2020 (Linux 5.9)
|
||||
with the invariant that invalid shadow pages can't be on the list of
|
||||
active pages.
|
||||
|
||||
Note #2, inheriting role.invalid when creating child shadow pages is also
|
||||
far from ideal; that flaw will be addressed separately.
|
||||
|
||||
Reported-by: Hyunwoo Kim <imv4bel@gmail.com>
|
||||
Fixes: f95eec9bed76 ("KVM: x86/mmu: Don't put invalid SPs back on the list of active pages")
|
||||
Cc: stable@vger.kernel.org
|
||||
Signed-off-by: Sean Christopherson <seanjc@google.com>
|
||||
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
|
||||
(cherry picked from commit 2abd5287f08319fa35764566b15c6e22cb1068db)
|
||||
Signed-off-by: Aidan Wallace <awallace@redhat.com>
|
||||
|
||||
diff --git a/arch/x86/kvm/mmu/mmu.c b/arch/x86/kvm/mmu/mmu.c
|
||||
index 6415891..ce15356 100644
|
||||
--- a/arch/x86/kvm/mmu/mmu.c
|
||||
+++ b/arch/x86/kvm/mmu/mmu.c
|
||||
@@ -4788,16 +4788,17 @@ static int direct_page_fault(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault
|
||||
if (r != RET_PF_CONTINUE)
|
||||
return r;
|
||||
|
||||
- r = RET_PF_RETRY;
|
||||
write_lock(&vcpu->kvm->mmu_lock);
|
||||
|
||||
- if (is_page_fault_stale(vcpu, fault))
|
||||
- goto out_unlock;
|
||||
-
|
||||
r = make_mmu_pages_available(vcpu);
|
||||
if (r)
|
||||
goto out_unlock;
|
||||
|
||||
+ if (is_page_fault_stale(vcpu, fault)) {
|
||||
+ r = RET_PF_RETRY;
|
||||
+ goto out_unlock;
|
||||
+ }
|
||||
+
|
||||
r = direct_map(vcpu, fault);
|
||||
|
||||
out_unlock:
|
||||
diff --git a/arch/x86/kvm/mmu/paging_tmpl.h b/arch/x86/kvm/mmu/paging_tmpl.h
|
||||
index ed762bb..af220c9 100644
|
||||
--- a/arch/x86/kvm/mmu/paging_tmpl.h
|
||||
+++ b/arch/x86/kvm/mmu/paging_tmpl.h
|
||||
@@ -827,15 +827,17 @@ static int FNAME(page_fault)(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault
|
||||
}
|
||||
#endif
|
||||
|
||||
- r = RET_PF_RETRY;
|
||||
write_lock(&vcpu->kvm->mmu_lock);
|
||||
|
||||
- if (is_page_fault_stale(vcpu, fault))
|
||||
- goto out_unlock;
|
||||
-
|
||||
r = make_mmu_pages_available(vcpu);
|
||||
if (r)
|
||||
goto out_unlock;
|
||||
+
|
||||
+ if (is_page_fault_stale(vcpu, fault)) {
|
||||
+ r = RET_PF_RETRY;
|
||||
+ goto out_unlock;
|
||||
+ }
|
||||
+
|
||||
r = FNAME(fetch)(vcpu, fault, &walker);
|
||||
|
||||
out_unlock:
|
||||
54
1180-kvm-nvmx-hide-shadow-vmcs-right-after-vmclear.patch
Normal file
54
1180-kvm-nvmx-hide-shadow-vmcs-right-after-vmclear.patch
Normal file
@ -0,0 +1,54 @@
|
||||
From 665c3ad7f18b63fba85a93250a75d44a5454734d Mon Sep 17 00:00:00 2001
|
||||
From: Aidan Wallace <awallace@redhat.com>
|
||||
Date: Wed, 22 Jul 2026 00:43:20 -0500
|
||||
Subject: [PATCH] KVM: nVMX: Hide shadow VMCS right after VMCLEAR
|
||||
|
||||
JIRA: https://redhat.atlassian.net/browse/RHEL-213327
|
||||
|
||||
KVM: nVMX: Hide shadow VMCS right after VMCLEAR
|
||||
|
||||
free_nested() frees the shadow VMCS while vmcs01 still points to it. But
|
||||
because it is asynchronous with respect to loaded_vmcs_clear(), the vCPU
|
||||
might migrate before the pointer is cleared and __loaded_vmcs_clear()
|
||||
may then execute VMCLEAR.
|
||||
|
||||
The VMCS needs to stay attached until its explicit VMCLEAR completes, but
|
||||
then it can be hidden and the page safely freed.
|
||||
|
||||
Fixes: 355f4fb1405e ("kvm: nVMX: VMCLEAR an active shadow VMCS after last use")
|
||||
Cc: stable@vger.kernel.org
|
||||
Signed-off-by: Hyunwoo Kim <imv4bel@gmail.com>
|
||||
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
|
||||
(cherry picked from commit 622ebfac01ba4f9c0060cebd41257fe46fc4a0b3)
|
||||
Signed-off-by: Aidan Wallace <awallace@redhat.com>
|
||||
|
||||
diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c
|
||||
index 5570e0c..49d7cda 100644
|
||||
--- a/arch/x86/kvm/vmx/nested.c
|
||||
+++ b/arch/x86/kvm/vmx/nested.c
|
||||
@@ -331,6 +331,7 @@ static void nested_put_vmcs12_pages(struct kvm_vcpu *vcpu)
|
||||
static void free_nested(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
struct vcpu_vmx *vmx = to_vmx(vcpu);
|
||||
+ struct vmcs *shadow_vmcs;
|
||||
|
||||
if (WARN_ON_ONCE(vmx->loaded_vmcs != &vmx->vmcs01))
|
||||
vmx_switch_vmcs(vcpu, &vmx->vmcs01);
|
||||
@@ -348,9 +349,15 @@ static void free_nested(struct kvm_vcpu *vcpu)
|
||||
vmx->nested.current_vmptr = INVALID_GPA;
|
||||
if (enable_shadow_vmcs) {
|
||||
vmx_disable_shadow_vmcs(vmx);
|
||||
- vmcs_clear(vmx->vmcs01.shadow_vmcs);
|
||||
- free_vmcs(vmx->vmcs01.shadow_vmcs);
|
||||
+
|
||||
+ /*
|
||||
+ * Keep the pointer visible until after VMCLEAR, so migration
|
||||
+ * can clear an active shadow VMCS on the old CPU.
|
||||
+ */
|
||||
+ shadow_vmcs = vmx->vmcs01.shadow_vmcs;
|
||||
+ vmcs_clear(shadow_vmcs);
|
||||
vmx->vmcs01.shadow_vmcs = NULL;
|
||||
+ free_vmcs(shadow_vmcs);
|
||||
}
|
||||
kfree(vmx->nested.cached_vmcs12);
|
||||
vmx->nested.cached_vmcs12 = NULL;
|
||||
@ -0,0 +1,131 @@
|
||||
From 25252dcdc03485bdfaaf4ff5bae084b5e6176ac0 Mon Sep 17 00:00:00 2001
|
||||
From: Aidan Wallace <awallace@redhat.com>
|
||||
Date: Wed, 22 Jul 2026 01:05:50 -0500
|
||||
Subject: [PATCH] KVM: x86/mmu: Ensure hugepage is in by slot before checking
|
||||
max mapping level
|
||||
|
||||
JIRA: https://redhat.atlassian.net/browse/RHEL-213327
|
||||
CVE: CVE-2026-63807
|
||||
|
||||
KVM: x86/mmu: Ensure hugepage is in by slot before checking max mapping level
|
||||
|
||||
When recovering hugepages in the shadow MMU, verify that the base gfn of
|
||||
the shadow page is actually contained within the target memslot, *before*
|
||||
querying the max mapping level given the shadow page's gfn. Failure to
|
||||
pre-check the validity of the gfn can lead to an out-of-bounds access to
|
||||
the slot's lpage_info (which typically manifests as a host #PF because the
|
||||
lpage_info is vmalloc'd) if the guest creates a hugepage mapping (in its
|
||||
PTEs) that extends "below" the bounds of a memslot.
|
||||
|
||||
When faulting in memory for a guest, and the size of the guest mapping is
|
||||
greater than KVM's (current) max mapping, then KVM will create a "direct"
|
||||
shadow page (direct in that there are no gPTEs to shadow, and so the target
|
||||
gfn is a direct calculation given the base gfn of the shadow page). The
|
||||
hugepage recovery flow looks for such direct shadow pages, as forcing 4KiB
|
||||
mappings when dirty logging generates the guest > host mapping size case.
|
||||
When the 4KiB restriction is lifted, then KVM can replace the shadow page
|
||||
with a hugepage.
|
||||
|
||||
But if KVM originally used a smaller mapping than the guest because the
|
||||
range of memory covered by the guest hugepage exceeds the bounds of a
|
||||
memslot, then KVM will link a direct shadow page with a gfn that is outside
|
||||
the bounds of the memslot being used to fault in memory. The rmap entry
|
||||
added for the leaf mapping is correct and within bounds, but the gfn of the
|
||||
leaf SPTE's parent shadow page will be out of bounds.
|
||||
|
||||
BUG: unable to handle page fault for address: ffffc90000806ffc
|
||||
#PF: supervisor read access in kernel mode
|
||||
#PF: error_code(0x0000) - not-present page
|
||||
PGD 100000067 P4D 100000067 PUD 1002a7067 PMD 10612f067 PTE 0
|
||||
Oops: Oops: 0000 [#1] SMP
|
||||
CPU: 13 UID: 1000 PID: 757 Comm: mmu_stress_test Not tainted 7.1.0-rc1-48ce1e26eace-x86_pir_to_irr_comments-vm #341 PREEMPT
|
||||
Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 0.0.0 02/06/2015
|
||||
RIP: 0010:kvm_mmu_max_mapping_level+0x79/0x2b0 [kvm]
|
||||
Call Trace:
|
||||
<TASK>
|
||||
kvm_mmu_recover_huge_pages+0x21b/0x320 [kvm]
|
||||
kvm_set_memslot+0x1ee/0x590 [kvm]
|
||||
kvm_set_memory_region.part.0+0x3a1/0x4d0 [kvm]
|
||||
kvm_vm_ioctl+0x9bf/0x15d0 [kvm]
|
||||
__x64_sys_ioctl+0x8a/0xd0
|
||||
do_syscall_64+0xb7/0xbb0
|
||||
entry_SYSCALL_64_after_hwframe+0x4b/0x53
|
||||
RIP: 0033:0x7f21c0f1a9bf
|
||||
</TASK>
|
||||
|
||||
Don't bother pre-checking the bounds of the potential hugepage, i.e. don't
|
||||
check that e.g. sp->gfn + KVM_PAGES_PER_HPAGE(sp->role.level + 1) is also
|
||||
within the memslot, as the checks performed by kvm_mmu_max_mapping_level()
|
||||
are a superset of the basic bounds checks. I.e. pre-checking the full
|
||||
range would be a dubious micro-optimization.
|
||||
|
||||
Fixes: 9eba50f8d7fc ("KVM: x86/mmu: Consult max mapping level when zapping collapsible SPTEs")
|
||||
Cc: stable@vger.kernel.org
|
||||
Cc: David Matlack <dmatlack@google.com>
|
||||
Cc: James Houghton <jthoughton@google.com>
|
||||
Cc: Alexander Bulekov <bkov@amazon.com>
|
||||
Cc: Fred Griffoul <fgriffo@amazon.co.uk>
|
||||
Cc: Alexander Graf <graf@amazon.de>
|
||||
Cc: David Woodhouse <dwmw@amazon.co.uk>
|
||||
Cc: Filippo Sironi <sironi@amazon.de>
|
||||
Cc: Ivan Orlov <iorlov@amazon.co.uk>
|
||||
Signed-off-by: Sean Christopherson <seanjc@google.com>
|
||||
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
|
||||
(cherry picked from commit ef057cbf825e03b63f6edf5980f96abf3c53089d)
|
||||
Signed-off-by: Aidan Wallace <awallace@redhat.com>
|
||||
|
||||
diff --git a/arch/x86/kvm/mmu/mmu.c b/arch/x86/kvm/mmu/mmu.c
|
||||
index ce15356..04591cf 100644
|
||||
--- a/arch/x86/kvm/mmu/mmu.c
|
||||
+++ b/arch/x86/kvm/mmu/mmu.c
|
||||
@@ -7163,13 +7163,19 @@ static bool kvm_mmu_zap_collapsible_spte(struct kvm *kvm,
|
||||
sp = sptep_to_sp(sptep);
|
||||
|
||||
/*
|
||||
- * We cannot do huge page mapping for indirect shadow pages,
|
||||
- * which are found on the last rmap (level = 1) when not using
|
||||
- * tdp; such shadow pages are synced with the page table in
|
||||
- * the guest, and the guest page table is using 4K page size
|
||||
- * mapping if the indirect sp has level = 1.
|
||||
+ * Direct shadow page can be replaced by a hugepage if the host
|
||||
+ * mapping level allows it and the memslot maps all of the host
|
||||
+ * hugepage. Note! If the memslot maps only part of the
|
||||
+ * hugepage, sp->gfn may be below slot->base_gfn, and querying
|
||||
+ * the max mapping level would cause an out-of-bounds lpage_info
|
||||
+ * access. So the gfn bounds check *must* be done first.
|
||||
+ *
|
||||
+ * Indirect shadow pages are created when the guest page tables
|
||||
+ * are using 4K pages. Since the host mapping is always
|
||||
+ * constrained by the page size in the guest, indirect shadow
|
||||
+ * pages are never collapsible.
|
||||
*/
|
||||
- if (sp->role.direct &&
|
||||
+ if (sp->role.direct && is_gfn_in_memslot(slot, sp->gfn) &&
|
||||
sp->role.level < kvm_mmu_max_mapping_level(kvm, slot, sp->gfn)) {
|
||||
kvm_zap_one_rmap_spte(kvm, rmap_head, sptep);
|
||||
|
||||
diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h
|
||||
index 83ed7a0..2790aed 100644
|
||||
--- a/include/linux/kvm_host.h
|
||||
+++ b/include/linux/kvm_host.h
|
||||
@@ -1768,6 +1768,11 @@ void kvm_unregister_irq_ack_notifier(struct kvm *kvm,
|
||||
struct kvm_irq_ack_notifier *kian);
|
||||
bool kvm_arch_irqfd_allowed(struct kvm *kvm, struct kvm_irqfd *args);
|
||||
|
||||
+static inline bool is_gfn_in_memslot(const struct kvm_memory_slot *slot, gfn_t gfn)
|
||||
+{
|
||||
+ return gfn >= slot->base_gfn && gfn < slot->base_gfn + slot->npages;
|
||||
+}
|
||||
+
|
||||
/*
|
||||
* Returns a pointer to the memslot if it contains gfn.
|
||||
* Otherwise returns NULL.
|
||||
@@ -1778,7 +1783,7 @@ try_get_memslot(struct kvm_memory_slot *slot, gfn_t gfn)
|
||||
if (!slot)
|
||||
return NULL;
|
||||
|
||||
- if (gfn >= slot->base_gfn && gfn < slot->base_gfn + slot->npages)
|
||||
+ if (is_gfn_in_memslot(slot, gfn))
|
||||
return slot;
|
||||
else
|
||||
return NULL;
|
||||
@ -0,0 +1,65 @@
|
||||
From 8b624c005e6d08f4ee577aa8beb4852420d57c3f Mon Sep 17 00:00:00 2001
|
||||
From: Maxim Levitsky <mlevitsk@redhat.com>
|
||||
Date: Fri, 24 Apr 2026 11:43:56 -0400
|
||||
Subject: [PATCH] KVM: x86: hyper-v: Validate all GVAs during PV TLB flush
|
||||
|
||||
JIRA: https://issues.redhat.com/browse/RHEL-151869
|
||||
|
||||
commit a5264387c2ee42fca92ac792199008fc60ee82f1
|
||||
Author: Manuel Andreas <manuel.andreas@tum.de>
|
||||
Date: Thu Feb 19 21:05:49 2026 +0100
|
||||
|
||||
KVM: x86: hyper-v: Validate all GVAs during PV TLB flush
|
||||
|
||||
In KVM guests with Hyper-V hypercalls enabled, the hypercalls
|
||||
HVCALL_FLUSH_VIRTUAL_ADDRESS_LIST and HVCALL_FLUSH_VIRTUAL_ADDRESS_LIST_EX
|
||||
allow a guest to request invalidation of portions of a virtual TLB.
|
||||
For this, the hypercall parameter includes a list of GVAs that are supposed
|
||||
to be invalidated.
|
||||
|
||||
Currently, only the base GVA is checked to be canonical. In reality, this
|
||||
check needs to be performed for the entire range of GVAs, as checking only
|
||||
the base GVA enables guests running on Intel hardware to trigger a
|
||||
WARN_ONCE in the host (see Fixes commit below).
|
||||
|
||||
Move the check for non-canonical addresses to be performed for every GVA
|
||||
of the supplied range to avoid the splat, and to be more in line with the
|
||||
Hyper-V specification, since, although unlikely, a range starting with an
|
||||
invalid GVA may still contain GVAs that are valid.
|
||||
|
||||
Fixes: fa787ac07b3c ("KVM: x86/hyper-v: Skip non-canonical addresses during PV TLB flush")
|
||||
Signed-off-by: Manuel Andreas <manuel.andreas@tum.de>
|
||||
Reviewed-by: Vitaly Kuznetsov <vkuznets@redhat.com>
|
||||
Link: https://patch.msgid.link/00a7a31b-573b-4d92-91f8-7d7e2f88ea48@tum.de
|
||||
[sean: massage changelog]
|
||||
Signed-off-by: Sean Christopherson <seanjc@google.com>
|
||||
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
|
||||
|
||||
Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com>
|
||||
|
||||
diff --git a/arch/x86/kvm/hyperv.c b/arch/x86/kvm/hyperv.c
|
||||
index 75cd48d..4ea49a4 100644
|
||||
--- a/arch/x86/kvm/hyperv.c
|
||||
+++ b/arch/x86/kvm/hyperv.c
|
||||
@@ -1983,16 +1983,17 @@ int kvm_hv_vcpu_flush_tlb(struct kvm_vcpu *vcpu)
|
||||
if (entries[i] == KVM_HV_TLB_FLUSHALL_ENTRY)
|
||||
goto out_flush_all;
|
||||
|
||||
- if (is_noncanonical_invlpg_address(entries[i], vcpu))
|
||||
- continue;
|
||||
-
|
||||
/*
|
||||
* Lower 12 bits of 'address' encode the number of additional
|
||||
* pages to flush.
|
||||
*/
|
||||
gva = entries[i] & PAGE_MASK;
|
||||
- for (j = 0; j < (entries[i] & ~PAGE_MASK) + 1; j++)
|
||||
+ for (j = 0; j < (entries[i] & ~PAGE_MASK) + 1; j++) {
|
||||
+ if (is_noncanonical_invlpg_address(gva + j * PAGE_SIZE, vcpu))
|
||||
+ continue;
|
||||
+
|
||||
kvm_x86_call(flush_tlb_gva)(vcpu, gva + j * PAGE_SIZE);
|
||||
+ }
|
||||
|
||||
++vcpu->stat.tlb_flush;
|
||||
}
|
||||
@ -0,0 +1,217 @@
|
||||
From 26505e1b5b546e2fa9a0296b951ca158460c72d8 Mon Sep 17 00:00:00 2001
|
||||
From: Paolo Bonzini <pbonzini@redhat.com>
|
||||
Date: Thu, 23 Jul 2026 10:15:22 +0200
|
||||
Subject: [PATCH] KVM: SVM: make svm_flush_tlb_gva do a full asid flush if NPT
|
||||
enabled
|
||||
|
||||
Red Hat is seeing multiple reports of Windows memory corruptions
|
||||
(and consequent BSODs) with hv-tlbflush=on, on AMD processors only.
|
||||
The crashes, while extremely rare, happen even with a stock configuration,
|
||||
but with Driver Verifier enabled they can be detected after approximately
|
||||
200 VM hours. In particular, Alexander Lougovski measured the following:
|
||||
|
||||
- on AMD Turin, 15 crashes in 3300 VM hours
|
||||
|
||||
- on AMD Milan, 2 crashes in 500 VM hours (there are fewer hours
|
||||
here due to the host being smaller)
|
||||
|
||||
- on Intel Sapphire Rapids, 0 crashes in 8000 VM hours
|
||||
|
||||
- on AMD Turin with full TLB flush (not exactly this patch but
|
||||
similar), no crashes in ~2 weeks of run time which should also
|
||||
be ~7000 VM hours
|
||||
|
||||
For Turin, the microcode version was 0x0b002162, which (assuming
|
||||
this is the same issue) should not be affected by the problem listed in
|
||||
https://knowledge.broadcom.com/external/article/419026/bsod-on-virtual-machines-running-on-amd.html;
|
||||
on the other hand that problem should not apply to earlier processors.
|
||||
AMD has not provided any information or analysis yet, and when we asked
|
||||
we didn't know yet that it reproduced on Milan as well.
|
||||
|
||||
As to the workload, Alexander threw more or less everything at the same
|
||||
time at the VM:
|
||||
|
||||
- a full Windows Defender scan every 30 minutes
|
||||
|
||||
- a disk I/O job
|
||||
|
||||
- a loop doing repeated mmap of system files (mostly to hope that
|
||||
it triggers some consistency check in the Windows memory manager)
|
||||
|
||||
- SQL Express 2022 + StressDB (1.6M rows), with the host doing queries
|
||||
(75% write/25% read) via sqlcmd
|
||||
|
||||
Driver Verifier is able to detect BSODs more or less at the same time as
|
||||
the pages are freed. They mostly happen in the Windows Defender filter
|
||||
driver, but occasionally also in the networking stack (e.g., afd.sys)
|
||||
or elsewhere in the filesystem stack (e.g., fltmgr.sys).
|
||||
|
||||
The flush is issued from kvm_hv_vcpu_flush_tlb(), which receives the
|
||||
cross-CPU requests from the Hyper-V TLB flush hypercalls via a kfifo
|
||||
and is invoked by the KVM_REQ_HV_TLB_FLUSH request. The mechanism is
|
||||
the same for both Intel and AMD, and the handler for both vendors is
|
||||
a simple INVVPID(ADDR)/INVLPGA instruction.
|
||||
|
||||
Because the request is handled on the destination CPU, there is a question
|
||||
of what happens if the VM is migrated across physical CPUs. In that case,
|
||||
the INVLPGA instruction would use a stale svm->vmcb->control.asid; but
|
||||
if anything that might do an *unnecessary* flush (on an asid that's being
|
||||
used for another VM) and then pre_svm_run() would force a full TLB rebuild.
|
||||
|
||||
So, for lack of better ideas, this patch forces a full ASID bump in
|
||||
svm_flush_tlb_gva(). To avoid paying the price on Intel and also to
|
||||
avoid unnecessary loops on AMD, the flush_tlb_gva op now returns whether
|
||||
it did a full flush or not; kvm_hv_vcpu_flush_tlb() takes note and exits
|
||||
its loops immediately. While there is an obvious performance impact,
|
||||
about half of the benefit from Hyper-V tlbflush is preserved (10% vs. 20%
|
||||
on the SQL Server workload).
|
||||
|
||||
kvm_mmu_invalidate_addr() is the only other caller of the flush_tlb_gva op.
|
||||
The change would have a performance impact on every intercepted INVLPG and,
|
||||
for nested SVM, on every L1 INVLPGA. For INVLPGA specifically, this covers
|
||||
the same suspected issue but for nested hypervisors, so it is correct to
|
||||
apply the workaround; for INVLPG on shadow paging, instead, the impact
|
||||
would be stronger and, due to lack of data, for now the use of INVLPGA is
|
||||
left in place in svm_flush_tlb_gva().
|
||||
|
||||
Analyzed-by: Vitaly Kuznetsov <vkuznets@redhat.com>
|
||||
Analyzed-by: Alexander Lougovski <alougovs@redhat.com>
|
||||
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
|
||||
|
||||
diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
|
||||
index 871c7ff..d378cdf 100644
|
||||
--- a/arch/x86/include/asm/kvm_host.h
|
||||
+++ b/arch/x86/include/asm/kvm_host.h
|
||||
@@ -1761,7 +1761,7 @@ struct kvm_x86_ops {
|
||||
* Can potentially get non-canonical addresses through INVLPGs, which
|
||||
* the implementation may choose to ignore if appropriate.
|
||||
*/
|
||||
- void (*flush_tlb_gva)(struct kvm_vcpu *vcpu, gva_t addr);
|
||||
+ void (*flush_tlb_gva)(struct kvm_vcpu *vcpu, gva_t addr, bool *full);
|
||||
|
||||
/*
|
||||
* Flush any TLB entries created by the guest. Like tlb_flush_gva(),
|
||||
diff --git a/arch/x86/kvm/hyperv.c b/arch/x86/kvm/hyperv.c
|
||||
index 4ea49a4..861a5d8 100644
|
||||
--- a/arch/x86/kvm/hyperv.c
|
||||
+++ b/arch/x86/kvm/hyperv.c
|
||||
@@ -1971,6 +1971,7 @@ int kvm_hv_vcpu_flush_tlb(struct kvm_vcpu *vcpu)
|
||||
u64 entries[KVM_HV_TLB_FLUSH_FIFO_SIZE];
|
||||
int i, j, count;
|
||||
gva_t gva;
|
||||
+ bool full = false;
|
||||
|
||||
if (!tdp_enabled || !hv_vcpu)
|
||||
return -EINVAL;
|
||||
@@ -1979,7 +1980,7 @@ int kvm_hv_vcpu_flush_tlb(struct kvm_vcpu *vcpu)
|
||||
|
||||
count = kfifo_out(&tlb_flush_fifo->entries, entries, KVM_HV_TLB_FLUSH_FIFO_SIZE);
|
||||
|
||||
- for (i = 0; i < count; i++) {
|
||||
+ for (i = 0; i < count && !full; i++) {
|
||||
if (entries[i] == KVM_HV_TLB_FLUSHALL_ENTRY)
|
||||
goto out_flush_all;
|
||||
|
||||
@@ -1988,11 +1989,11 @@ int kvm_hv_vcpu_flush_tlb(struct kvm_vcpu *vcpu)
|
||||
* pages to flush.
|
||||
*/
|
||||
gva = entries[i] & PAGE_MASK;
|
||||
- for (j = 0; j < (entries[i] & ~PAGE_MASK) + 1; j++) {
|
||||
+ for (j = 0; j < (entries[i] & ~PAGE_MASK) + 1 && !full; j++) {
|
||||
if (is_noncanonical_invlpg_address(gva + j * PAGE_SIZE, vcpu))
|
||||
continue;
|
||||
|
||||
- kvm_x86_call(flush_tlb_gva)(vcpu, gva + j * PAGE_SIZE);
|
||||
+ kvm_x86_call(flush_tlb_gva)(vcpu, gva + j * PAGE_SIZE, &full);
|
||||
}
|
||||
|
||||
++vcpu->stat.tlb_flush;
|
||||
diff --git a/arch/x86/kvm/mmu/mmu.c b/arch/x86/kvm/mmu/mmu.c
|
||||
index 04591cf..66bda34 100644
|
||||
--- a/arch/x86/kvm/mmu/mmu.c
|
||||
+++ b/arch/x86/kvm/mmu/mmu.c
|
||||
@@ -6434,7 +6434,7 @@ void kvm_mmu_invalidate_addr(struct kvm_vcpu *vcpu, struct kvm_mmu *mmu,
|
||||
if (is_noncanonical_invlpg_address(addr, vcpu))
|
||||
return;
|
||||
|
||||
- kvm_x86_call(flush_tlb_gva)(vcpu, addr);
|
||||
+ kvm_x86_call(flush_tlb_gva)(vcpu, addr, NULL);
|
||||
}
|
||||
|
||||
if (!mmu->sync_spte)
|
||||
diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c
|
||||
index 23cb4be..4b80716 100644
|
||||
--- a/arch/x86/kvm/svm/svm.c
|
||||
+++ b/arch/x86/kvm/svm/svm.c
|
||||
@@ -4021,11 +4021,24 @@ static void svm_flush_tlb_all(struct kvm_vcpu *vcpu)
|
||||
svm_flush_tlb_asid(vcpu);
|
||||
}
|
||||
|
||||
-static void svm_flush_tlb_gva(struct kvm_vcpu *vcpu, gva_t gva)
|
||||
+static void svm_flush_tlb_gva(struct kvm_vcpu *vcpu, gva_t gva, bool *full)
|
||||
{
|
||||
struct vcpu_svm *svm = to_svm(vcpu);
|
||||
|
||||
- invlpga(gva, svm->vmcb->control.asid);
|
||||
+ /*
|
||||
+ * INVLPGA has had errata on Genoa and Turin, and even on older
|
||||
+ * generations there were reports of Windows BSODs if INVLPGA
|
||||
+ * was used for Hyper-V tlbflush. Use it only for shadow paging
|
||||
+ * where it seems to be okay.
|
||||
+ */
|
||||
+ if (!npt_enabled) {
|
||||
+ invlpga(gva, svm->vmcb->control.asid);
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ svm_flush_tlb_asid(vcpu);
|
||||
+ if (full)
|
||||
+ *full = true;
|
||||
}
|
||||
|
||||
static inline void sync_cr8_to_lapic(struct kvm_vcpu *vcpu)
|
||||
diff --git a/arch/x86/kvm/vmx/main.c b/arch/x86/kvm/vmx/main.c
|
||||
index dbab1c1..f99cae0 100644
|
||||
--- a/arch/x86/kvm/vmx/main.c
|
||||
+++ b/arch/x86/kvm/vmx/main.c
|
||||
@@ -530,12 +530,12 @@ static void vt_flush_tlb_current(struct kvm_vcpu *vcpu)
|
||||
vmx_flush_tlb_current(vcpu);
|
||||
}
|
||||
|
||||
-static void vt_flush_tlb_gva(struct kvm_vcpu *vcpu, gva_t addr)
|
||||
+static void vt_flush_tlb_gva(struct kvm_vcpu *vcpu, gva_t addr, bool *full)
|
||||
{
|
||||
if (is_td_vcpu(vcpu))
|
||||
return;
|
||||
|
||||
- vmx_flush_tlb_gva(vcpu, addr);
|
||||
+ vmx_flush_tlb_gva(vcpu, addr, full);
|
||||
}
|
||||
|
||||
static void vt_flush_tlb_guest(struct kvm_vcpu *vcpu)
|
||||
diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c
|
||||
index 2e68776..5fd9614 100644
|
||||
--- a/arch/x86/kvm/vmx/vmx.c
|
||||
+++ b/arch/x86/kvm/vmx/vmx.c
|
||||
@@ -3202,7 +3202,7 @@ void vmx_flush_tlb_current(struct kvm_vcpu *vcpu)
|
||||
vpid_sync_context(vmx_get_current_vpid(vcpu));
|
||||
}
|
||||
|
||||
-void vmx_flush_tlb_gva(struct kvm_vcpu *vcpu, gva_t addr)
|
||||
+void vmx_flush_tlb_gva(struct kvm_vcpu *vcpu, gva_t addr, bool *full)
|
||||
{
|
||||
/*
|
||||
* vpid_sync_vcpu_addr() is a nop if vpid==0, see the comment in
|
||||
diff --git a/arch/x86/kvm/vmx/x86_ops.h b/arch/x86/kvm/vmx/x86_ops.h
|
||||
index 2b3424f..5fa4e7c 100644
|
||||
--- a/arch/x86/kvm/vmx/x86_ops.h
|
||||
+++ b/arch/x86/kvm/vmx/x86_ops.h
|
||||
@@ -82,7 +82,7 @@ void vmx_set_rflags(struct kvm_vcpu *vcpu, unsigned long rflags);
|
||||
bool vmx_get_if_flag(struct kvm_vcpu *vcpu);
|
||||
void vmx_flush_tlb_all(struct kvm_vcpu *vcpu);
|
||||
void vmx_flush_tlb_current(struct kvm_vcpu *vcpu);
|
||||
-void vmx_flush_tlb_gva(struct kvm_vcpu *vcpu, gva_t addr);
|
||||
+void vmx_flush_tlb_gva(struct kvm_vcpu *vcpu, gva_t addr, bool *full);
|
||||
void vmx_flush_tlb_guest(struct kvm_vcpu *vcpu);
|
||||
void vmx_set_interrupt_shadow(struct kvm_vcpu *vcpu, int mask);
|
||||
u32 vmx_get_interrupt_shadow(struct kvm_vcpu *vcpu);
|
||||
@ -0,0 +1,85 @@
|
||||
From 5dd51e09020c65aa53cf128e5e3517cd53b3c113 Mon Sep 17 00:00:00 2001
|
||||
From: Jamal Hadi Salim <jhs@mojatatu.com>
|
||||
Date: Sun, 31 May 2026 12:08:12 -0400
|
||||
Subject: [PATCH] net/sched: act_api: use RCU with deferred freeing for action
|
||||
lifecycle
|
||||
|
||||
[ Upstream commit 5057e1aca011e51ef51498c940ef96f3d3e8a305 ]
|
||||
|
||||
When NEWTFILTER and DELFILTER are run concurrently it is possible to create a
|
||||
race with an associated action.
|
||||
|
||||
Let's illustrate with CPU0 running NEWTFILTER and CPU1 running DELFILTER:
|
||||
|
||||
0: mutex_lock() <-- holds the idr lock
|
||||
0: rcu_read_lock()
|
||||
0: p = idr_find(idr, index) <-- action p is valid (RCU protects IDR)
|
||||
0: mutex_unlock() <-- releases the idr lock
|
||||
1: refcount_dec_and_mutex_lock() <-- refcnt 1->0, mutex held
|
||||
1: idr_remove(idr, index) <-- Action removed from IDR
|
||||
1: mutex_unlock() <-- mutex released allowing us to delete the action
|
||||
1: tcf_action_cleanup(p); kfree(p) <-- Kfrees p immediately, no deferral
|
||||
0: refcount_inc_not_zero(&p->tcfa_refcnt) <-- ouch, UAF p points to freed memory
|
||||
|
||||
This patch fixes the race condition between NEWTFILTER and DELFILTER by
|
||||
adding struct rcu_head to tc_action used in the deferral and introducing a
|
||||
call_rcu() in the delete path to defer the final kfree().
|
||||
|
||||
Note: this is a revert of commit d7fb60b9cafb ("net_sched: get rid of tcfa_rcu")
|
||||
but also modernization/simplification to directly use kfree_rcu().
|
||||
|
||||
Let's illustrate the new restored code path:
|
||||
|
||||
0: rcu_read_lock()
|
||||
1: refcount_dec_and_mutex_lock() <-- refcnt 1->0, mutex held
|
||||
1: idr_remove(idr, index)
|
||||
1: mutex_unlock()
|
||||
1: call_rcu(&p->tcfa_rcu, tcf_action_rcu_free) <-- defer kfree after grace period
|
||||
0: p = idr_find(idr, index)
|
||||
0: refcount_inc_not_zero(&p->tcfa_refcnt) <-- fails, refcnt already 0
|
||||
1: rcu_read_unlock() <-- release so freeing can run after grace period
|
||||
|
||||
After CPU1 calls idr_remove(), the object is no longer reachable through the IDR.
|
||||
CPU0's subsequent idr_find() will return NULL, and even if it still held a
|
||||
stale pointer, the immediate kfree() is now deferred until after the RCU grace
|
||||
period, so no UAF can occur.
|
||||
|
||||
Fixes: d7fb60b9cafb ("net_sched: get rid of tcfa_rcu")
|
||||
Suggested-by: Jakub Kicinski <kuba@kernel.org>
|
||||
Reported-by: Kyle Zeng <kylebot@openai.com>
|
||||
Tested-by: Victor Nogueira <victor@mojatatu.com>
|
||||
Tested-by: syzbot@syzkaller.appspotmail.com
|
||||
Signed-off-by: Jamal Hadi Salim <jhs@mojatatu.com>
|
||||
Tested-by: Kyle Zeng <kylebot@openai.com>
|
||||
Reviewed-by: Pedro Tammela <pctammela@mojatatu.com>
|
||||
Reviewed-by: Eric Dumazet <edumazet@google.com>
|
||||
Reviewed-by: Victor Nogueira <victor@mojatatu.com>
|
||||
Link: https://patch.msgid.link/20260531160812.68020-1-jhs@mojatatu.com
|
||||
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||
Signed-off-by: Sasha Levin <sashal@kernel.org>
|
||||
|
||||
diff --git a/net/sched/act_api.c b/net/sched/act_api.c
|
||||
index 8397900..8b1402c 100644
|
||||
--- a/net/sched/act_api.c
|
||||
+++ b/net/sched/act_api.c
|
||||
@@ -112,11 +112,6 @@ struct tcf_chain *tcf_action_set_ctrlact(struct tc_action *a, int action,
|
||||
}
|
||||
EXPORT_SYMBOL(tcf_action_set_ctrlact);
|
||||
|
||||
-/* XXX: For standalone actions, we don't need a RCU grace period either, because
|
||||
- * actions are always connected to filters and filters are already destroyed in
|
||||
- * RCU callbacks, so after a RCU grace period actions are already disconnected
|
||||
- * from filters. Readers later can not find us.
|
||||
- */
|
||||
static void free_tcf(struct tc_action *p)
|
||||
{
|
||||
struct tcf_chain *chain = rcu_dereference_protected(p->goto_chain, 1);
|
||||
@@ -129,7 +124,7 @@ static void free_tcf(struct tc_action *p)
|
||||
if (chain)
|
||||
tcf_chain_put_by_act(chain);
|
||||
|
||||
- kfree(p);
|
||||
+ kfree_rcu_mightsleep(p);
|
||||
}
|
||||
|
||||
static void offload_action_hw_count_set(struct tc_action *act,
|
||||
51
1185-perf-aux-fix-page-uaf-in-map-range.patch
Normal file
51
1185-perf-aux-fix-page-uaf-in-map-range.patch
Normal file
@ -0,0 +1,51 @@
|
||||
From 5948aaf64f81f217a25dcc2bf6c0779bca19566c Mon Sep 17 00:00:00 2001
|
||||
From: Lee Jia Jie <jiajie.lee@starlabs.sg>
|
||||
Date: Thu, 9 Jul 2026 21:56:19 +0800
|
||||
Subject: [PATCH] perf/aux: Fix page UAF in map_range()
|
||||
|
||||
map_range() reads rb->aux_pages[], rb->aux_nr_pages and rb->aux_pgoff via
|
||||
perf_mmap_to_page() while holding only event->mmap_mutex. Those fields are
|
||||
serialized by rb->aux_mutex, and mmap_mutex is per event.
|
||||
|
||||
Thus, two events sharing one rb via PERF_EVENT_IOC_SET_OUTPUT can race
|
||||
rb_alloc_aux() with map_range(), leading to a page-UAF scenario as follows:
|
||||
|
||||
CPU 0 CPU 1
|
||||
===== =====
|
||||
rb_alloc_aux() map_range()
|
||||
[1]: allocate rb->aux_pages[0]
|
||||
[2]: rb->aux_nr_pages++
|
||||
[3]: perf_mmap_to_page()
|
||||
returns rb->aux_pages[0]
|
||||
[4]: map it as VM_PFNMAP
|
||||
[5]: rb->aux_pgoff = 1
|
||||
|
||||
munmap the page
|
||||
[6]: free rb->aux_pages[0]
|
||||
|
||||
Pages mapped as VM_PFNMAP have no refcount protection, so CPU 1 holds a
|
||||
mapping to a freed physical frame.
|
||||
|
||||
Fix this by taking rb->aux_mutex across the page walk in map_range().
|
||||
|
||||
Fixes: b709eb872e19 ("perf: map pages in advance")
|
||||
Signed-off-by: Lee Jia Jie <jiajie.lee@starlabs.sg>
|
||||
Signed-off-by: Ingo Molnar <mingo@kernel.org>
|
||||
Cc: stable@vger.kernel.org
|
||||
Cc: Peter Zijlstra <peterz@infradead.org>
|
||||
Cc: Arnaldo Carvalho de Melo <acme@redhat.com>
|
||||
Cc: Namhyung Kim <namhyung@kernel.org>
|
||||
|
||||
diff --git a/kernel/events/core.c b/kernel/events/core.c
|
||||
index 43cee52..e2e7bb8 100644
|
||||
--- a/kernel/events/core.c
|
||||
+++ b/kernel/events/core.c
|
||||
@@ -6896,6 +6896,8 @@ static int map_range(struct perf_buffer *rb, struct vm_area_struct *vma)
|
||||
int err = 0;
|
||||
unsigned long pagenum;
|
||||
|
||||
+ guard(mutex)(&rb->aux_mutex);
|
||||
+
|
||||
/*
|
||||
* We map this as a VM_PFNMAP VMA.
|
||||
*
|
||||
@ -0,0 +1,200 @@
|
||||
From cb0d8148eaca238c8ee3e3a1c3351503194ab167 Mon Sep 17 00:00:00 2001
|
||||
From: Maurizio Lombardi <mlombard@redhat.com>
|
||||
Date: Tue, 9 Jun 2026 07:52:02 +0200
|
||||
Subject: [PATCH] scsi: target: iscsi: Bound iscsi_encode_text_output() appends
|
||||
to rsp_buf
|
||||
|
||||
JIRA: https://redhat.atlassian.net/browse/RHEL-163760
|
||||
|
||||
iscsi_encode_text_output() concatenates "key=value\0" records into
|
||||
login->rsp_buf, an 8192-byte kzalloc(MAX_KEY_VALUE_PAIRS) buffer
|
||||
allocated in iscsit_alloc_login_setup_buffer(). The three sprintf() call
|
||||
sites in this function (lines 1398, 1411, 1424 in v7.1-rc2) never check
|
||||
the remaining buffer capacity:
|
||||
|
||||
*length += sprintf(output_buf, "%s=%s", er->key, er->value);
|
||||
*length += 1;
|
||||
output_buf = textbuf + *length;
|
||||
|
||||
The 8192-byte ceiling at iscsi_target_check_login_request() bounds the
|
||||
*input* Login PDU payload, but a single PDU can carry up to 2048 minimal
|
||||
four-byte "a=b\0" pairs, each unknown key expanding to a 16-byte
|
||||
"a=NotUnderstood\0" output record via iscsi_add_notunderstood_response().
|
||||
2048 * 16 = 32 KiB of output into an 8 KiB buffer, producing a ~24 KiB
|
||||
heap overrun in the kmalloc-8k slab.
|
||||
|
||||
The fix introduces a static iscsi_encode_text_record() helper that uses
|
||||
snprintf() with a per-call bounds check against the remaining buffer,
|
||||
and threads a u32 textbuf_size parameter through
|
||||
iscsi_encode_text_output(). Both call sites in
|
||||
iscsi_target_handle_csg_zero() (PHASE_SECURITY) and
|
||||
iscsi_target_handle_csg_one() (PHASE_OPERATIONAL) pass
|
||||
MAX_KEY_VALUE_PAIRS. On overflow the encoder logs the condition, calls
|
||||
iscsi_release_extra_responses() to drop queued records, and returns -1;
|
||||
both caller sites now emit ISCSI_STATUS_CLS_INITIATOR_ERR /
|
||||
ISCSI_LOGIN_STATUS_INIT_ERR via iscsit_tx_login_rsp() before returning,
|
||||
so the initiator sees an explicit failed-login response rather than a
|
||||
silent connection drop. (Prior to this patch only the PHASE_OPERATIONAL
|
||||
caller did that; the PHASE_SECURITY caller is converted to the same
|
||||
shape.)
|
||||
|
||||
Fixes: e48354ce078c ("iscsi-target: Add iSCSI fabric support for target v4.1")
|
||||
Cc: stable@vger.kernel.org
|
||||
Assisted-by: Claude:claude-opus-4-7
|
||||
Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com>
|
||||
Tested-by: John Garry <john.g.garry@oracle.com>
|
||||
Reviewed-by: John Garry <john.g.garry@oracle.com>
|
||||
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
|
||||
(cherry picked from commit bf33e01f88388c43e285492a63e539df6ffed64c)
|
||||
Signed-off-by: Maurizio Lombardi <mlombard@redhat.com>
|
||||
|
||||
diff --git a/drivers/target/iscsi/iscsi_target_nego.c b/drivers/target/iscsi/iscsi_target_nego.c
|
||||
index 108e253..a1fb869 100644
|
||||
--- a/drivers/target/iscsi/iscsi_target_nego.c
|
||||
+++ b/drivers/target/iscsi/iscsi_target_nego.c
|
||||
@@ -899,10 +899,14 @@ static int iscsi_target_handle_csg_zero(
|
||||
SENDER_TARGET,
|
||||
login->rsp_buf,
|
||||
&login->rsp_length,
|
||||
+ MAX_KEY_VALUE_PAIRS,
|
||||
conn->param_list,
|
||||
conn->tpg->tpg_attrib.login_keys_workaround);
|
||||
- if (ret < 0)
|
||||
+ if (ret < 0) {
|
||||
+ iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR,
|
||||
+ ISCSI_LOGIN_STATUS_INIT_ERR);
|
||||
return -1;
|
||||
+ }
|
||||
|
||||
if (!iscsi_check_negotiated_keys(conn->param_list)) {
|
||||
bool auth_required = iscsi_conn_auth_required(conn);
|
||||
@@ -986,6 +990,7 @@ static int iscsi_target_handle_csg_one(struct iscsit_conn *conn, struct iscsi_lo
|
||||
SENDER_TARGET,
|
||||
login->rsp_buf,
|
||||
&login->rsp_length,
|
||||
+ MAX_KEY_VALUE_PAIRS,
|
||||
conn->param_list,
|
||||
conn->tpg->tpg_attrib.login_keys_workaround);
|
||||
if (ret < 0) {
|
||||
diff --git a/drivers/target/iscsi/iscsi_target_parameters.c b/drivers/target/iscsi/iscsi_target_parameters.c
|
||||
index 1d4e178..14eef58 100644
|
||||
--- a/drivers/target/iscsi/iscsi_target_parameters.c
|
||||
+++ b/drivers/target/iscsi/iscsi_target_parameters.c
|
||||
@@ -1371,19 +1371,42 @@ int iscsi_decode_text_input(
|
||||
return -1;
|
||||
}
|
||||
|
||||
+/*
|
||||
+ * Append "key=value" plus a trailing NUL into @textbuf at *@length.
|
||||
+ * Returns 0 on success and advances *@length, or -EMSGSIZE if the
|
||||
+ * record (including the NUL) would not fit in the remaining buffer.
|
||||
+ */
|
||||
+static int iscsi_encode_text_record(char *textbuf, u32 *length,
|
||||
+ u32 textbuf_size,
|
||||
+ const char *key, const char *value)
|
||||
+{
|
||||
+ int n;
|
||||
+ u32 avail;
|
||||
+
|
||||
+ if (*length >= textbuf_size)
|
||||
+ return -EMSGSIZE;
|
||||
+
|
||||
+ avail = textbuf_size - *length;
|
||||
+ n = snprintf(textbuf + *length, avail, "%s=%s", key, value);
|
||||
+ if (n < 0 || (u32)n + 1 > avail)
|
||||
+ return -EMSGSIZE;
|
||||
+
|
||||
+ *length += n + 1;
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
int iscsi_encode_text_output(
|
||||
u8 phase,
|
||||
u8 sender,
|
||||
char *textbuf,
|
||||
u32 *length,
|
||||
+ u32 textbuf_size,
|
||||
struct iscsi_param_list *param_list,
|
||||
bool keys_workaround)
|
||||
{
|
||||
- char *output_buf = NULL;
|
||||
struct iscsi_extra_response *er;
|
||||
struct iscsi_param *param;
|
||||
-
|
||||
- output_buf = textbuf + *length;
|
||||
+ int ret;
|
||||
|
||||
if (iscsi_enforce_integrity_rules(phase, param_list) < 0)
|
||||
return -1;
|
||||
@@ -1395,10 +1418,12 @@ int iscsi_encode_text_output(
|
||||
!IS_PSTATE_RESPONSE_SENT(param) &&
|
||||
!IS_PSTATE_REPLY_OPTIONAL(param) &&
|
||||
(param->phase & phase)) {
|
||||
- *length += sprintf(output_buf, "%s=%s",
|
||||
- param->name, param->value);
|
||||
- *length += 1;
|
||||
- output_buf = textbuf + *length;
|
||||
+ ret = iscsi_encode_text_record(textbuf, length,
|
||||
+ textbuf_size,
|
||||
+ param->name,
|
||||
+ param->value);
|
||||
+ if (ret < 0)
|
||||
+ goto err_overflow;
|
||||
SET_PSTATE_RESPONSE_SENT(param);
|
||||
pr_debug("Sending key: %s=%s\n",
|
||||
param->name, param->value);
|
||||
@@ -1408,10 +1433,12 @@ int iscsi_encode_text_output(
|
||||
!IS_PSTATE_ACCEPTOR(param) &&
|
||||
!IS_PSTATE_PROPOSER(param) &&
|
||||
(param->phase & phase)) {
|
||||
- *length += sprintf(output_buf, "%s=%s",
|
||||
- param->name, param->value);
|
||||
- *length += 1;
|
||||
- output_buf = textbuf + *length;
|
||||
+ ret = iscsi_encode_text_record(textbuf, length,
|
||||
+ textbuf_size,
|
||||
+ param->name,
|
||||
+ param->value);
|
||||
+ if (ret < 0)
|
||||
+ goto err_overflow;
|
||||
SET_PSTATE_PROPOSER(param);
|
||||
iscsi_check_proposer_for_optional_reply(param,
|
||||
keys_workaround);
|
||||
@@ -1421,14 +1448,21 @@ int iscsi_encode_text_output(
|
||||
}
|
||||
|
||||
list_for_each_entry(er, ¶m_list->extra_response_list, er_list) {
|
||||
- *length += sprintf(output_buf, "%s=%s", er->key, er->value);
|
||||
- *length += 1;
|
||||
- output_buf = textbuf + *length;
|
||||
+ ret = iscsi_encode_text_record(textbuf, length, textbuf_size,
|
||||
+ er->key, er->value);
|
||||
+ if (ret < 0)
|
||||
+ goto err_overflow;
|
||||
pr_debug("Sending key: %s=%s\n", er->key, er->value);
|
||||
}
|
||||
iscsi_release_extra_responses(param_list);
|
||||
|
||||
return 0;
|
||||
+
|
||||
+err_overflow:
|
||||
+ pr_err("iSCSI login response buffer (%u bytes) exhausted, dropping login.\n",
|
||||
+ textbuf_size);
|
||||
+ iscsi_release_extra_responses(param_list);
|
||||
+ return -1;
|
||||
}
|
||||
|
||||
int iscsi_check_negotiated_keys(struct iscsi_param_list *param_list)
|
||||
diff --git a/drivers/target/iscsi/iscsi_target_parameters.h b/drivers/target/iscsi/iscsi_target_parameters.h
|
||||
index c672a97..38d2238 100644
|
||||
--- a/drivers/target/iscsi/iscsi_target_parameters.h
|
||||
+++ b/drivers/target/iscsi/iscsi_target_parameters.h
|
||||
@@ -43,7 +43,7 @@ extern struct iscsi_param *iscsi_find_param_from_key(char *, struct iscsi_param_
|
||||
extern int iscsi_extract_key_value(char *, char **, char **);
|
||||
extern int iscsi_update_param_value(struct iscsi_param *, char *);
|
||||
extern int iscsi_decode_text_input(u8, u8, char *, u32, struct iscsit_conn *);
|
||||
-extern int iscsi_encode_text_output(u8, u8, char *, u32 *,
|
||||
+extern int iscsi_encode_text_output(u8, u8, char *, u32 *, u32,
|
||||
struct iscsi_param_list *, bool);
|
||||
extern int iscsi_check_negotiated_keys(struct iscsi_param_list *);
|
||||
extern void iscsi_set_connection_parameters(struct iscsi_conn_ops *,
|
||||
@ -12,7 +12,7 @@ RHEL_MINOR = 2
|
||||
#
|
||||
# Use this spot to avoid future merge conflicts.
|
||||
# Do not trim this comment.
|
||||
RHEL_RELEASE = 211.46.1
|
||||
RHEL_RELEASE = 211.47.1
|
||||
|
||||
#
|
||||
# RHEL_REBASE_NUM
|
||||
|
||||
@ -1,3 +1,93 @@
|
||||
* Wed Aug 12 2026 CKI KWF Bot <cki-ci-bot+kwf-gitlab-com@redhat.com> [6.12.0-211.47.1.el10_2]
|
||||
- scsi: target: iscsi: Bound iscsi_encode_text_output() appends to rsp_buf (Maurizio Lombardi) [RHEL-213198] {CVE-2026-63887}
|
||||
- perf/aux: Fix page UAF in map_range() (CKI Backport Bot) [RHEL-218475] {CVE-2026-64300}
|
||||
- net/sched: act_api: use RCU with deferred freeing for action lifecycle (CKI Backport Bot) [RHEL-218188] {CVE-2026-53264}
|
||||
- KVM: SVM: make svm_flush_tlb_gva do a full asid flush if NPT enabled (Paolo Bonzini) [RHEL-214436]
|
||||
- KVM: x86: hyper-v: Validate all GVAs during PV TLB flush (Paolo Bonzini) [RHEL-214436]
|
||||
- KVM: x86/mmu: Ensure hugepage is in by slot before checking max mapping level (Aidan Wallace) [RHEL-213472] {CVE-2026-63807}
|
||||
- KVM: nVMX: Hide shadow VMCS right after VMCLEAR (Aidan Wallace) [RHEL-213472]
|
||||
- KVM: x86: Check for invalid/obsolete root *after* making MMU pages available (Aidan Wallace) [RHEL-213472]
|
||||
- KVM: nVMX: Put vmcs12 pages if nested VM-Enter fails due to invalid guest state (Aidan Wallace) [RHEL-213472]
|
||||
- accel/ivpu: Fix signed integer truncation in IPC receive (CKI Backport Bot) [RHEL-190054] {CVE-2026-53202}
|
||||
- netfilter: nf_conntrack_expect: store master_tuple in expectation (Florian Westphal) [RHEL-185311]
|
||||
- selftests: netfilter: nft_concat_range.sh: add check for flush+reload bug (Florian Westphal) [RHEL-185311]
|
||||
- selftests: netfilter: nft_concat_range.sh: add check for overlap detection bug (Florian Westphal) [RHEL-185311]
|
||||
- selftests: netfilter: nft_concat_range.sh: add check for double-create bug (Florian Westphal) [RHEL-185311]
|
||||
- netfilter: ctnetlink: use nf_ct_exp_net() in expectation dump (Florian Westphal) [RHEL-185311]
|
||||
- netfilter: nf_dup_netdev: add nf_dev_xmit_recursion*() helpers and use them (Florian Westphal) [RHEL-185311]
|
||||
- netfilter: nft_fib: fix stale stack leak via the OIFNAME register (Florian Westphal) [RHEL-185311]
|
||||
- netfilter: nft_exthdr: fix register tracking for F_PRESENT flag (Florian Westphal) [RHEL-185311]
|
||||
- netfilter: nf_log: validate MAC header was set before dumping it (Florian Westphal) [RHEL-185311]
|
||||
- netfilter: nf_conntrack: destroy stale expectfn expectations on unregister (Florian Westphal) [RHEL-185311]
|
||||
- netfilter: revalidate bridge ports (Florian Westphal) [RHEL-185311]
|
||||
- netfilter: nft_ct: bail out on template ct in get eval (Florian Westphal) [RHEL-185311]
|
||||
- netfilter: nft_tunnel: fix use-after-free on object destroy (Florian Westphal) [RHEL-185311]
|
||||
- netfilter: conntrack_irc: fix possible out-of-bounds read (Florian Westphal) [RHEL-185311]
|
||||
- netfilter: synproxy: add mutex to guard hook reference counting (Florian Westphal) [RHEL-185311]
|
||||
- netfilter: disable payload mangling in userns (Florian Westphal) [RHEL-185311]
|
||||
- netfilter: nf_conntrack_gre: fix gre keymap list corruption (Florian Westphal) [RHEL-185311]
|
||||
- netfilter: synproxy: refresh tcphdr after skb_ensure_writable (Florian Westphal) [RHEL-185311]
|
||||
- netfilter: conntrack: tcp: do not force CLOSE on invalid-seq RST without direction check (Florian Westphal) [RHEL-185311]
|
||||
- netfilter: nf_queue: hold bridge skb->dev while queued (Florian Westphal) [RHEL-185311]
|
||||
- netfilter: br_netfilter: Reallocate headroom if necessary in neigh_hh_bridge() (Florian Westphal) [RHEL-185311]
|
||||
- netfilter: ip6t_hbh: reject oversized option lists (Florian Westphal) [RHEL-185311]
|
||||
- netfilter: nf_conntrack_helper: fix possible null deref during error log (Florian Westphal) [RHEL-185311]
|
||||
- netfilter: nft_ct: fix missing expect put in obj eval (Florian Westphal) [RHEL-185311]
|
||||
- netfilter: nf_conntrack_sip: get helper before allocating expectation (Florian Westphal) [RHEL-185311]
|
||||
- netfilter: ctnetlink: check tuple and mask in expectations created via nfqueue (Florian Westphal) [RHEL-185311]
|
||||
- netfilter: nf_conntrack_expect: restore helper propagation via expectation (Florian Westphal) [RHEL-185311]
|
||||
- netfilter: nf_tables: fix netdev hook allocation memleak with dormant tables (Florian Westphal) [RHEL-185311]
|
||||
- netfilter: xt_CT: fix usersize for v1 and v2 revision (Florian Westphal) [RHEL-185311]
|
||||
- netfilter: nft_compat: run xt_check_hooks_{match,target}() from .validate (Florian Westphal) [RHEL-185311]
|
||||
- netfilter: x_tables: add .check_hooks to matches and targets (Florian Westphal) [RHEL-185311]
|
||||
- netfilter: xtables: restrict several matches to inet family (Florian Westphal) [RHEL-185311]
|
||||
- netfilter: nft_fwd_netdev: use recursion counter in neigh egress path (Florian Westphal) [RHEL-185311]
|
||||
- netfilter: nft_fwd_netdev: add device and headroom validate with neigh forwarding (Florian Westphal) [RHEL-185311]
|
||||
- netfilter: replace skb_try_make_writable() by skb_ensure_writable() (Florian Westphal) [RHEL-185311]
|
||||
- netfilter: nf_conntrack_sip: don't use simple_strtoul (Florian Westphal) [RHEL-185311]
|
||||
- netfilter: xt_policy: fix strict mode inbound policy matching (Florian Westphal) [RHEL-185311]
|
||||
- netfilter: nf_tables: add hook transactions for device deletions (Florian Westphal) [RHEL-185311]
|
||||
- netfilter: nf_tables: join hook list via splice_list_rcu() in commit phase (Florian Westphal) [RHEL-185311]
|
||||
- rculist: add list_splice_rcu() for private lists (Florian Westphal) [RHEL-185311]
|
||||
- netfilter: nf_tables: use list_del_rcu for netlink hooks (Florian Westphal) [RHEL-185311] {CVE-2026-46324}
|
||||
- netfilter: nfnetlink_osf: fix potential NULL dereference in ttl check (Florian Westphal) [RHEL-185311]
|
||||
- netfilter: nfnetlink_osf: fix out-of-bounds read on option matching (Florian Westphal) [RHEL-185311]
|
||||
- netfilter: nat: use kfree_rcu to release ops (Florian Westphal) [RHEL-185311]
|
||||
- netfilter: conntrack: remove sprintf usage (Florian Westphal) [RHEL-185311]
|
||||
- netfilter: nfnetlink_osf: fix divide-by-zero in OSF_WSS_MODULO (Florian Westphal) [RHEL-185311] {CVE-2026-45841}
|
||||
- nfnetlink_osf: validate individual option lengths in fingerprints (Florian Westphal) [RHEL-185311] {CVE-2026-23397}
|
||||
- netfilter: nft_osf: restrict it to ipv4 (Florian Westphal) [RHEL-185311]
|
||||
- netfilter: nft_ct: fix use-after-free in timeout object destroy (Florian Westphal) [RHEL-185311] {CVE-2026-31665}
|
||||
- netfilter: xt_multiport: validate range encoding in checkentry (Florian Westphal) [RHEL-185311] {CVE-2026-31681}
|
||||
- netfilter: nfnetlink_log: initialize nfgenmsg in NLMSG_DONE terminator (Florian Westphal) [RHEL-185311] {CVE-2026-43085}
|
||||
- netfilter: nf_tables: reject immediate NF_QUEUE verdict (Florian Westphal) [RHEL-185311] {CVE-2026-43024}
|
||||
- netfilter: x_tables: restrict xt_check_match/xt_check_target extensions for NFPROTO_ARP (Florian Westphal) [RHEL-185311] {CVE-2026-31424}
|
||||
- netfilter: ctnetlink: ignore explicit helper on new expectations (Florian Westphal) [RHEL-185311] {CVE-2026-43025}
|
||||
- netfilter: ctnetlink: zero expect NAT fields when CTA_EXPECT_NAT absent (Florian Westphal) [RHEL-185311] {CVE-2026-43026}
|
||||
- netfilter: ipset: use nla_strcmp for IPSET_ATTR_NAME attr (Florian Westphal) [RHEL-185311]
|
||||
- netfilter: x_tables: ensure names are nul-terminated (Florian Westphal) [RHEL-185311] {CVE-2026-43028}
|
||||
- netfilter: nfnetlink_log: account for netlink header size (Florian Westphal) [RHEL-185311] {CVE-2026-31416}
|
||||
- netfilter: ctnetlink: use netlink policy range checks (Florian Westphal) [RHEL-185311] {CVE-2026-31495}
|
||||
- netfilter: ip6t_rt: reject oversized addrnr in rt_mt6_check() (Florian Westphal) [RHEL-185311] {CVE-2026-31674}
|
||||
- netfilter: nf_conntrack_expect: store netns and zone in expectation (Florian Westphal) [RHEL-185311]
|
||||
- netfilter: nf_conntrack_expect: use expect->helper (Florian Westphal) [RHEL-185311]
|
||||
- netfilter: nf_conntrack_expect: honor expectation helper field (Florian Westphal) [RHEL-185311]
|
||||
- netfilter: nfnetlink_log: fix uninitialized padding leak in NFULA_PAYLOAD (Florian Westphal) [RHEL-185311] {CVE-2026-31428}
|
||||
- netfilter: nft_set_pipapo_avx2: don't return non-matching entry on expiry (Florian Westphal) [RHEL-185311] {CVE-2026-43114}
|
||||
- nf_tables: nft_dynset: fix possible stateful expression memleak in error path (Florian Westphal) [RHEL-185311] {CVE-2026-23399}
|
||||
- netfilter: nf_conntrack_h323: fix OOB read in decode_int() CONS case (Florian Westphal) [RHEL-185311] {CVE-2026-23456}
|
||||
- netfilter: nf_conntrack_sip: fix Content-Length u32 truncation in sip_help_tcp() (Florian Westphal) [RHEL-185311] {CVE-2026-23457}
|
||||
- netfilter: conntrack: add missing netlink policy validations (Florian Westphal) [RHEL-185311] {CVE-2026-31407}
|
||||
- netfilter: ctnetlink: fix use-after-free in ctnetlink_dump_exp_ct() (Florian Westphal) [RHEL-185311] {CVE-2026-23458}
|
||||
- netfilter: nfnetlink_queue: fix entry leak in bridge verdict error path (Florian Westphal) [RHEL-185311] {CVE-2026-43451}
|
||||
- netfilter: nft_set_pipapo: fix stack out-of-bounds read in pipapo_drop() (Florian Westphal) [RHEL-185311] {CVE-2026-43453}
|
||||
- netfilter: nf_tables: unconditionally bump set->nelems before insertion (Florian Westphal) [RHEL-185311] {CVE-2026-23272}
|
||||
- netfilter: nf_conntrack_h323: fix OOB read in decode_choice() (Florian Westphal) [RHEL-185311] {CVE-2026-43233}
|
||||
- netfilter: nft_set_hash: fix get operation on big endian (Florian Westphal) [RHEL-185311]
|
||||
- netfilter: nf_tables: always walk all pending catchall elements (Florian Westphal) [RHEL-185311] {CVE-2026-23278}
|
||||
- netfilter: nft_set_pipapo: split gc into unlink and reclaim phase (Florian Westphal) [RHEL-185311] {CVE-2026-23351}
|
||||
Resolves: RHEL-185311, RHEL-190054, RHEL-213198, RHEL-213472, RHEL-214436, RHEL-218188, RHEL-218475
|
||||
|
||||
* Mon Aug 10 2026 CKI KWF Bot <cki-ci-bot+kwf-gitlab-com@redhat.com> [6.12.0-211.46.1.el10_2]
|
||||
- mm/slab: do not limit zeroing to orig_size when only red zoning is enabled (Rafael Aquini) [RHEL-223405] {CVE-2026-64368}
|
||||
Resolves: RHEL-223405
|
||||
|
||||
271
kernel.spec
271
kernel.spec
@ -176,13 +176,13 @@ Summary: The Linux kernel
|
||||
%define specrpmversion 6.12.0
|
||||
%define specversion 6.12.0
|
||||
%define patchversion 6.12
|
||||
%define pkgrelease 211.46.1
|
||||
%define pkgrelease 211.47.1
|
||||
%define kversion 6
|
||||
%define tarfile_release 6.12.0-211.46.1.el10_2
|
||||
# This is needed to do merge window version magic
|
||||
%define patchlevel 12
|
||||
# This allows pkg_release to have configurable %%{?dist} tag
|
||||
%define specrelease 211.46.1%{?buildid}%{?dist}
|
||||
%define specrelease 211.47.1%{?buildid}%{?dist}
|
||||
# This defines the kabi tarball version
|
||||
%define kabiversion 6.12.0-211.46.1.el10_2
|
||||
|
||||
@ -1127,6 +1127,95 @@ Patch1: patch-%{patchversion}-redhat.patch
|
||||
# empty final patch to facilitate testing of kernel patches
|
||||
Patch999999: linux-kernel-test.patch
|
||||
|
||||
# Backports for 6.12.0-211.47.1.el10_2
|
||||
Patch1100: 1100-netfilter-nft-set-pipapo-split-gc-into-unlink-and-reclaim-phase.patch
|
||||
Patch1101: 1101-netfilter-nf-tables-always-walk-all-pending-catchall-elements.patch
|
||||
Patch1102: 1102-netfilter-nft-set-hash-fix-get-operation-on-big-endian.patch
|
||||
Patch1103: 1103-netfilter-nf-conntrack-h323-fix-oob-read-in-decode-choice.patch
|
||||
Patch1104: 1104-netfilter-nf-tables-unconditionally-bump-set-nelems-before-insertion.patch
|
||||
Patch1105: 1105-netfilter-nft-set-pipapo-fix-stack-out-of-bounds-read-in-pipapo-drop.patch
|
||||
Patch1106: 1106-netfilter-nfnetlink-queue-fix-entry-leak-in-bridge-verdict-error-path.patch
|
||||
Patch1107: 1107-netfilter-ctnetlink-fix-use-after-free-in-ctnetlink-dump-exp-ct.patch
|
||||
Patch1108: 1108-netfilter-conntrack-add-missing-netlink-policy-validations.patch
|
||||
Patch1109: 1109-netfilter-nf-conntrack-sip-fix-content-length-u32-truncation-in-sip-help-tcp.patch
|
||||
Patch1110: 1110-netfilter-nf-conntrack-h323-fix-oob-read-in-decode-int-cons-case.patch
|
||||
Patch1111: 1111-nf-tables-nft-dynset-fix-possible-stateful-expression-memleak-in-error-path.patch
|
||||
Patch1112: 1112-netfilter-nft-set-pipapo-avx2-don-t-return-non-matching-entry-on-expiry.patch
|
||||
Patch1113: 1113-netfilter-nfnetlink-log-fix-uninitialized-padding-leak-in-nfula-payload.patch
|
||||
Patch1114: 1114-netfilter-nf-conntrack-expect-honor-expectation-helper-field.patch
|
||||
Patch1115: 1115-netfilter-nf-conntrack-expect-use-expect-helper.patch
|
||||
Patch1116: 1116-netfilter-nf-conntrack-expect-store-netns-and-zone-in-expectation.patch
|
||||
Patch1117: 1117-netfilter-ip6t-rt-reject-oversized-addrnr-in-rt-mt6-check.patch
|
||||
Patch1118: 1118-netfilter-ctnetlink-use-netlink-policy-range-checks.patch
|
||||
Patch1119: 1119-netfilter-nfnetlink-log-account-for-netlink-header-size.patch
|
||||
Patch1120: 1120-netfilter-x-tables-ensure-names-are-nul-terminated.patch
|
||||
Patch1121: 1121-netfilter-ipset-use-nla-strcmp-for-ipset-attr-name-attr.patch
|
||||
Patch1122: 1122-netfilter-ctnetlink-zero-expect-nat-fields-when-cta-expect-nat-absent.patch
|
||||
Patch1123: 1123-netfilter-ctnetlink-ignore-explicit-helper-on-new-expectations.patch
|
||||
Patch1124: 1124-netfilter-x-tables-restrict-xt-check-match-xt-check-target-extensions-for-nfprot.patch
|
||||
Patch1125: 1125-netfilter-nf-tables-reject-immediate-nf-queue-verdict.patch
|
||||
Patch1126: 1126-netfilter-nfnetlink-log-initialize-nfgenmsg-in-nlmsg-done-terminator.patch
|
||||
Patch1127: 1127-netfilter-xt-multiport-validate-range-encoding-in-checkentry.patch
|
||||
Patch1128: 1128-netfilter-nft-ct-fix-use-after-free-in-timeout-object-destroy.patch
|
||||
Patch1129: 1129-netfilter-nft-osf-restrict-it-to-ipv4.patch
|
||||
Patch1130: 1130-nfnetlink-osf-validate-individual-option-lengths-in-fingerprints.patch
|
||||
Patch1131: 1131-netfilter-nfnetlink-osf-fix-divide-by-zero-in-osf-wss-modulo.patch
|
||||
Patch1132: 1132-netfilter-conntrack-remove-sprintf-usage.patch
|
||||
Patch1133: 1133-netfilter-nat-use-kfree-rcu-to-release-ops.patch
|
||||
Patch1134: 1134-netfilter-nfnetlink-osf-fix-out-of-bounds-read-on-option-matching.patch
|
||||
Patch1135: 1135-netfilter-nfnetlink-osf-fix-potential-null-dereference-in-ttl-check.patch
|
||||
Patch1136: 1136-netfilter-nf-tables-use-list-del-rcu-for-netlink-hooks.patch
|
||||
Patch1137: 1137-rculist-add-list-splice-rcu-for-private-lists.patch
|
||||
Patch1138: 1138-netfilter-nf-tables-join-hook-list-via-splice-list-rcu-in-commit-phase.patch
|
||||
Patch1139: 1139-netfilter-nf-tables-add-hook-transactions-for-device-deletions.patch
|
||||
Patch1140: 1140-netfilter-xt-policy-fix-strict-mode-inbound-policy-matching.patch
|
||||
Patch1141: 1141-netfilter-nf-conntrack-sip-don-t-use-simple-strtoul.patch
|
||||
Patch1142: 1142-netfilter-replace-skb-try-make-writable-by-skb-ensure-writable.patch
|
||||
Patch1143: 1143-netfilter-nft-fwd-netdev-add-device-and-headroom-validate-with-neigh-forwarding.patch
|
||||
Patch1144: 1144-netfilter-nft-fwd-netdev-use-recursion-counter-in-neigh-egress-path.patch
|
||||
Patch1145: 1145-netfilter-xtables-restrict-several-matches-to-inet-family.patch
|
||||
Patch1146: 1146-netfilter-x-tables-add-check-hooks-to-matches-and-targets.patch
|
||||
Patch1147: 1147-netfilter-nft-compat-run-xt-check-hooks-match-target-from-validate.patch
|
||||
Patch1148: 1148-netfilter-xt-ct-fix-usersize-for-v1-and-v2-revision.patch
|
||||
Patch1149: 1149-netfilter-nf-tables-fix-netdev-hook-allocation-memleak-with-dormant-tables.patch
|
||||
Patch1150: 1150-netfilter-nf-conntrack-expect-restore-helper-propagation-via-expectation.patch
|
||||
Patch1151: 1151-netfilter-ctnetlink-check-tuple-and-mask-in-expectations-created-via-nfqueue.patch
|
||||
Patch1152: 1152-netfilter-nf-conntrack-sip-get-helper-before-allocating-expectation.patch
|
||||
Patch1153: 1153-netfilter-nft-ct-fix-missing-expect-put-in-obj-eval.patch
|
||||
Patch1154: 1154-netfilter-nf-conntrack-helper-fix-possible-null-deref-during-error-log.patch
|
||||
Patch1155: 1155-netfilter-ip6t-hbh-reject-oversized-option-lists.patch
|
||||
Patch1156: 1156-netfilter-br-netfilter-reallocate-headroom-if-necessary-in-neigh-hh-bridge.patch
|
||||
Patch1157: 1157-netfilter-nf-queue-hold-bridge-skb-dev-while-queued.patch
|
||||
Patch1158: 1158-netfilter-conntrack-tcp-do-not-force-close-on-invalid-seq-rst-without-direction-.patch
|
||||
Patch1159: 1159-netfilter-synproxy-refresh-tcphdr-after-skb-ensure-writable.patch
|
||||
Patch1160: 1160-netfilter-nf-conntrack-gre-fix-gre-keymap-list-corruption.patch
|
||||
Patch1161: 1161-netfilter-disable-payload-mangling-in-userns.patch
|
||||
Patch1162: 1162-netfilter-synproxy-add-mutex-to-guard-hook-reference-counting.patch
|
||||
Patch1163: 1163-netfilter-conntrack-irc-fix-possible-out-of-bounds-read.patch
|
||||
Patch1164: 1164-netfilter-nft-tunnel-fix-use-after-free-on-object-destroy.patch
|
||||
Patch1165: 1165-netfilter-nft-ct-bail-out-on-template-ct-in-get-eval.patch
|
||||
Patch1166: 1166-netfilter-revalidate-bridge-ports.patch
|
||||
Patch1167: 1167-netfilter-nf-conntrack-destroy-stale-expectfn-expectations-on-unregister.patch
|
||||
Patch1168: 1168-netfilter-nf-log-validate-mac-header-was-set-before-dumping-it.patch
|
||||
Patch1169: 1169-netfilter-nft-exthdr-fix-register-tracking-for-f-present-flag.patch
|
||||
Patch1170: 1170-netfilter-nft-fib-fix-stale-stack-leak-via-the-oifname-register.patch
|
||||
Patch1171: 1171-netfilter-nf-dup-netdev-add-nf-dev-xmit-recursion-helpers-and-use-them.patch
|
||||
Patch1172: 1172-netfilter-ctnetlink-use-nf-ct-exp-net-in-expectation-dump.patch
|
||||
Patch1173: 1173-selftests-netfilter-nft-concat-range-sh-add-check-for-double-create-bug.patch
|
||||
Patch1174: 1174-selftests-netfilter-nft-concat-range-sh-add-check-for-overlap-detection-bug.patch
|
||||
Patch1175: 1175-selftests-netfilter-nft-concat-range-sh-add-check-for-flush-reload-bug.patch
|
||||
Patch1176: 1176-netfilter-nf-conntrack-expect-store-master-tuple-in-expectation.patch
|
||||
Patch1177: 1177-accel-ivpu-fix-signed-integer-truncation-in-ipc-receive.patch
|
||||
Patch1178: 1178-kvm-nvmx-put-vmcs12-pages-if-nested-vm-enter-fails-due-to-invalid-guest-state.patch
|
||||
Patch1179: 1179-kvm-x86-check-for-invalid-obsolete-root-after-making-mmu-pages-available.patch
|
||||
Patch1180: 1180-kvm-nvmx-hide-shadow-vmcs-right-after-vmclear.patch
|
||||
Patch1181: 1181-kvm-x86-mmu-ensure-hugepage-is-in-by-slot-before-checking-max-mapping-level.patch
|
||||
Patch1182: 1182-kvm-x86-hyper-v-validate-all-gvas-during-pv-tlb-flush.patch
|
||||
Patch1183: 1183-kvm-svm-make-svm-flush-tlb-gva-do-a-full-asid-flush-if-npt-enabled.patch
|
||||
Patch1184: 1184-net-sched-act-api-use-rcu-with-deferred-freeing-for-action-lifecycle.patch
|
||||
Patch1185: 1185-perf-aux-fix-page-uaf-in-map-range.patch
|
||||
Patch1186: 1186-scsi-target-iscsi-bound-iscsi-encode-text-output-appends-to-rsp-buf.patch
|
||||
|
||||
# END OF PATCH DEFINITIONS
|
||||
|
||||
%description
|
||||
@ -1974,6 +2063,95 @@ ApplyOptionalPatch patch-%{patchversion}-redhat.patch
|
||||
|
||||
ApplyOptionalPatch linux-kernel-test.patch
|
||||
|
||||
# Applying backports for 6.12.0-211.47.1.el10_2
|
||||
ApplyPatch 1100-netfilter-nft-set-pipapo-split-gc-into-unlink-and-reclaim-phase.patch
|
||||
ApplyPatch 1101-netfilter-nf-tables-always-walk-all-pending-catchall-elements.patch
|
||||
ApplyPatch 1102-netfilter-nft-set-hash-fix-get-operation-on-big-endian.patch
|
||||
ApplyPatch 1103-netfilter-nf-conntrack-h323-fix-oob-read-in-decode-choice.patch
|
||||
ApplyPatch 1104-netfilter-nf-tables-unconditionally-bump-set-nelems-before-insertion.patch
|
||||
ApplyPatch 1105-netfilter-nft-set-pipapo-fix-stack-out-of-bounds-read-in-pipapo-drop.patch
|
||||
ApplyPatch 1106-netfilter-nfnetlink-queue-fix-entry-leak-in-bridge-verdict-error-path.patch
|
||||
ApplyPatch 1107-netfilter-ctnetlink-fix-use-after-free-in-ctnetlink-dump-exp-ct.patch
|
||||
ApplyPatch 1108-netfilter-conntrack-add-missing-netlink-policy-validations.patch
|
||||
ApplyPatch 1109-netfilter-nf-conntrack-sip-fix-content-length-u32-truncation-in-sip-help-tcp.patch
|
||||
ApplyPatch 1110-netfilter-nf-conntrack-h323-fix-oob-read-in-decode-int-cons-case.patch
|
||||
ApplyPatch 1111-nf-tables-nft-dynset-fix-possible-stateful-expression-memleak-in-error-path.patch
|
||||
ApplyPatch 1112-netfilter-nft-set-pipapo-avx2-don-t-return-non-matching-entry-on-expiry.patch
|
||||
ApplyPatch 1113-netfilter-nfnetlink-log-fix-uninitialized-padding-leak-in-nfula-payload.patch
|
||||
ApplyPatch 1114-netfilter-nf-conntrack-expect-honor-expectation-helper-field.patch
|
||||
ApplyPatch 1115-netfilter-nf-conntrack-expect-use-expect-helper.patch
|
||||
ApplyPatch 1116-netfilter-nf-conntrack-expect-store-netns-and-zone-in-expectation.patch
|
||||
ApplyPatch 1117-netfilter-ip6t-rt-reject-oversized-addrnr-in-rt-mt6-check.patch
|
||||
ApplyPatch 1118-netfilter-ctnetlink-use-netlink-policy-range-checks.patch
|
||||
ApplyPatch 1119-netfilter-nfnetlink-log-account-for-netlink-header-size.patch
|
||||
ApplyPatch 1120-netfilter-x-tables-ensure-names-are-nul-terminated.patch
|
||||
ApplyPatch 1121-netfilter-ipset-use-nla-strcmp-for-ipset-attr-name-attr.patch
|
||||
ApplyPatch 1122-netfilter-ctnetlink-zero-expect-nat-fields-when-cta-expect-nat-absent.patch
|
||||
ApplyPatch 1123-netfilter-ctnetlink-ignore-explicit-helper-on-new-expectations.patch
|
||||
ApplyPatch 1124-netfilter-x-tables-restrict-xt-check-match-xt-check-target-extensions-for-nfprot.patch
|
||||
ApplyPatch 1125-netfilter-nf-tables-reject-immediate-nf-queue-verdict.patch
|
||||
ApplyPatch 1126-netfilter-nfnetlink-log-initialize-nfgenmsg-in-nlmsg-done-terminator.patch
|
||||
ApplyPatch 1127-netfilter-xt-multiport-validate-range-encoding-in-checkentry.patch
|
||||
ApplyPatch 1128-netfilter-nft-ct-fix-use-after-free-in-timeout-object-destroy.patch
|
||||
ApplyPatch 1129-netfilter-nft-osf-restrict-it-to-ipv4.patch
|
||||
ApplyPatch 1130-nfnetlink-osf-validate-individual-option-lengths-in-fingerprints.patch
|
||||
ApplyPatch 1131-netfilter-nfnetlink-osf-fix-divide-by-zero-in-osf-wss-modulo.patch
|
||||
ApplyPatch 1132-netfilter-conntrack-remove-sprintf-usage.patch
|
||||
ApplyPatch 1133-netfilter-nat-use-kfree-rcu-to-release-ops.patch
|
||||
ApplyPatch 1134-netfilter-nfnetlink-osf-fix-out-of-bounds-read-on-option-matching.patch
|
||||
ApplyPatch 1135-netfilter-nfnetlink-osf-fix-potential-null-dereference-in-ttl-check.patch
|
||||
ApplyPatch 1136-netfilter-nf-tables-use-list-del-rcu-for-netlink-hooks.patch
|
||||
ApplyPatch 1137-rculist-add-list-splice-rcu-for-private-lists.patch
|
||||
ApplyPatch 1138-netfilter-nf-tables-join-hook-list-via-splice-list-rcu-in-commit-phase.patch
|
||||
ApplyPatch 1139-netfilter-nf-tables-add-hook-transactions-for-device-deletions.patch
|
||||
ApplyPatch 1140-netfilter-xt-policy-fix-strict-mode-inbound-policy-matching.patch
|
||||
ApplyPatch 1141-netfilter-nf-conntrack-sip-don-t-use-simple-strtoul.patch
|
||||
ApplyPatch 1142-netfilter-replace-skb-try-make-writable-by-skb-ensure-writable.patch
|
||||
ApplyPatch 1143-netfilter-nft-fwd-netdev-add-device-and-headroom-validate-with-neigh-forwarding.patch
|
||||
ApplyPatch 1144-netfilter-nft-fwd-netdev-use-recursion-counter-in-neigh-egress-path.patch
|
||||
ApplyPatch 1145-netfilter-xtables-restrict-several-matches-to-inet-family.patch
|
||||
ApplyPatch 1146-netfilter-x-tables-add-check-hooks-to-matches-and-targets.patch
|
||||
ApplyPatch 1147-netfilter-nft-compat-run-xt-check-hooks-match-target-from-validate.patch
|
||||
ApplyPatch 1148-netfilter-xt-ct-fix-usersize-for-v1-and-v2-revision.patch
|
||||
ApplyPatch 1149-netfilter-nf-tables-fix-netdev-hook-allocation-memleak-with-dormant-tables.patch
|
||||
ApplyPatch 1150-netfilter-nf-conntrack-expect-restore-helper-propagation-via-expectation.patch
|
||||
ApplyPatch 1151-netfilter-ctnetlink-check-tuple-and-mask-in-expectations-created-via-nfqueue.patch
|
||||
ApplyPatch 1152-netfilter-nf-conntrack-sip-get-helper-before-allocating-expectation.patch
|
||||
ApplyPatch 1153-netfilter-nft-ct-fix-missing-expect-put-in-obj-eval.patch
|
||||
ApplyPatch 1154-netfilter-nf-conntrack-helper-fix-possible-null-deref-during-error-log.patch
|
||||
ApplyPatch 1155-netfilter-ip6t-hbh-reject-oversized-option-lists.patch
|
||||
ApplyPatch 1156-netfilter-br-netfilter-reallocate-headroom-if-necessary-in-neigh-hh-bridge.patch
|
||||
ApplyPatch 1157-netfilter-nf-queue-hold-bridge-skb-dev-while-queued.patch
|
||||
ApplyPatch 1158-netfilter-conntrack-tcp-do-not-force-close-on-invalid-seq-rst-without-direction-.patch
|
||||
ApplyPatch 1159-netfilter-synproxy-refresh-tcphdr-after-skb-ensure-writable.patch
|
||||
ApplyPatch 1160-netfilter-nf-conntrack-gre-fix-gre-keymap-list-corruption.patch
|
||||
ApplyPatch 1161-netfilter-disable-payload-mangling-in-userns.patch
|
||||
ApplyPatch 1162-netfilter-synproxy-add-mutex-to-guard-hook-reference-counting.patch
|
||||
ApplyPatch 1163-netfilter-conntrack-irc-fix-possible-out-of-bounds-read.patch
|
||||
ApplyPatch 1164-netfilter-nft-tunnel-fix-use-after-free-on-object-destroy.patch
|
||||
ApplyPatch 1165-netfilter-nft-ct-bail-out-on-template-ct-in-get-eval.patch
|
||||
ApplyPatch 1166-netfilter-revalidate-bridge-ports.patch
|
||||
ApplyPatch 1167-netfilter-nf-conntrack-destroy-stale-expectfn-expectations-on-unregister.patch
|
||||
ApplyPatch 1168-netfilter-nf-log-validate-mac-header-was-set-before-dumping-it.patch
|
||||
ApplyPatch 1169-netfilter-nft-exthdr-fix-register-tracking-for-f-present-flag.patch
|
||||
ApplyPatch 1170-netfilter-nft-fib-fix-stale-stack-leak-via-the-oifname-register.patch
|
||||
ApplyPatch 1171-netfilter-nf-dup-netdev-add-nf-dev-xmit-recursion-helpers-and-use-them.patch
|
||||
ApplyPatch 1172-netfilter-ctnetlink-use-nf-ct-exp-net-in-expectation-dump.patch
|
||||
ApplyPatch 1173-selftests-netfilter-nft-concat-range-sh-add-check-for-double-create-bug.patch
|
||||
ApplyPatch 1174-selftests-netfilter-nft-concat-range-sh-add-check-for-overlap-detection-bug.patch
|
||||
ApplyPatch 1175-selftests-netfilter-nft-concat-range-sh-add-check-for-flush-reload-bug.patch
|
||||
ApplyPatch 1176-netfilter-nf-conntrack-expect-store-master-tuple-in-expectation.patch
|
||||
ApplyPatch 1177-accel-ivpu-fix-signed-integer-truncation-in-ipc-receive.patch
|
||||
ApplyPatch 1178-kvm-nvmx-put-vmcs12-pages-if-nested-vm-enter-fails-due-to-invalid-guest-state.patch
|
||||
ApplyPatch 1179-kvm-x86-check-for-invalid-obsolete-root-after-making-mmu-pages-available.patch
|
||||
ApplyPatch 1180-kvm-nvmx-hide-shadow-vmcs-right-after-vmclear.patch
|
||||
ApplyPatch 1181-kvm-x86-mmu-ensure-hugepage-is-in-by-slot-before-checking-max-mapping-level.patch
|
||||
ApplyPatch 1182-kvm-x86-hyper-v-validate-all-gvas-during-pv-tlb-flush.patch
|
||||
ApplyPatch 1183-kvm-svm-make-svm-flush-tlb-gva-do-a-full-asid-flush-if-npt-enabled.patch
|
||||
ApplyPatch 1184-net-sched-act-api-use-rcu-with-deferred-freeing-for-action-lifecycle.patch
|
||||
ApplyPatch 1185-perf-aux-fix-page-uaf-in-map-range.patch
|
||||
ApplyPatch 1186-scsi-target-iscsi-bound-iscsi-encode-text-output-appends-to-rsp-buf.patch
|
||||
|
||||
%{log_msg "End of patch applications"}
|
||||
# END OF PATCH APPLICATIONS
|
||||
|
||||
@ -4491,6 +4669,95 @@ fi\
|
||||
#
|
||||
#
|
||||
%changelog
|
||||
* Wed Aug 12 2026 CKI KWF Bot <cki-ci-bot+kwf-gitlab-com@redhat.com> [6.12.0-211.47.1.el10_2]
|
||||
- scsi: target: iscsi: Bound iscsi_encode_text_output() appends to rsp_buf (Maurizio Lombardi) [RHEL-213198] {CVE-2026-63887}
|
||||
- perf/aux: Fix page UAF in map_range() (CKI Backport Bot) [RHEL-218475] {CVE-2026-64300}
|
||||
- net/sched: act_api: use RCU with deferred freeing for action lifecycle (CKI Backport Bot) [RHEL-218188] {CVE-2026-53264}
|
||||
- KVM: SVM: make svm_flush_tlb_gva do a full asid flush if NPT enabled (Paolo Bonzini) [RHEL-214436]
|
||||
- KVM: x86: hyper-v: Validate all GVAs during PV TLB flush (Paolo Bonzini) [RHEL-214436]
|
||||
- KVM: x86/mmu: Ensure hugepage is in by slot before checking max mapping level (Aidan Wallace) [RHEL-213472] {CVE-2026-63807}
|
||||
- KVM: nVMX: Hide shadow VMCS right after VMCLEAR (Aidan Wallace) [RHEL-213472]
|
||||
- KVM: x86: Check for invalid/obsolete root *after* making MMU pages available (Aidan Wallace) [RHEL-213472]
|
||||
- KVM: nVMX: Put vmcs12 pages if nested VM-Enter fails due to invalid guest state (Aidan Wallace) [RHEL-213472]
|
||||
- accel/ivpu: Fix signed integer truncation in IPC receive (CKI Backport Bot) [RHEL-190054] {CVE-2026-53202}
|
||||
- netfilter: nf_conntrack_expect: store master_tuple in expectation (Florian Westphal) [RHEL-185311]
|
||||
- selftests: netfilter: nft_concat_range.sh: add check for flush+reload bug (Florian Westphal) [RHEL-185311]
|
||||
- selftests: netfilter: nft_concat_range.sh: add check for overlap detection bug (Florian Westphal) [RHEL-185311]
|
||||
- selftests: netfilter: nft_concat_range.sh: add check for double-create bug (Florian Westphal) [RHEL-185311]
|
||||
- netfilter: ctnetlink: use nf_ct_exp_net() in expectation dump (Florian Westphal) [RHEL-185311]
|
||||
- netfilter: nf_dup_netdev: add nf_dev_xmit_recursion*() helpers and use them (Florian Westphal) [RHEL-185311]
|
||||
- netfilter: nft_fib: fix stale stack leak via the OIFNAME register (Florian Westphal) [RHEL-185311]
|
||||
- netfilter: nft_exthdr: fix register tracking for F_PRESENT flag (Florian Westphal) [RHEL-185311]
|
||||
- netfilter: nf_log: validate MAC header was set before dumping it (Florian Westphal) [RHEL-185311]
|
||||
- netfilter: nf_conntrack: destroy stale expectfn expectations on unregister (Florian Westphal) [RHEL-185311]
|
||||
- netfilter: revalidate bridge ports (Florian Westphal) [RHEL-185311]
|
||||
- netfilter: nft_ct: bail out on template ct in get eval (Florian Westphal) [RHEL-185311]
|
||||
- netfilter: nft_tunnel: fix use-after-free on object destroy (Florian Westphal) [RHEL-185311]
|
||||
- netfilter: conntrack_irc: fix possible out-of-bounds read (Florian Westphal) [RHEL-185311]
|
||||
- netfilter: synproxy: add mutex to guard hook reference counting (Florian Westphal) [RHEL-185311]
|
||||
- netfilter: disable payload mangling in userns (Florian Westphal) [RHEL-185311]
|
||||
- netfilter: nf_conntrack_gre: fix gre keymap list corruption (Florian Westphal) [RHEL-185311]
|
||||
- netfilter: synproxy: refresh tcphdr after skb_ensure_writable (Florian Westphal) [RHEL-185311]
|
||||
- netfilter: conntrack: tcp: do not force CLOSE on invalid-seq RST without direction check (Florian Westphal) [RHEL-185311]
|
||||
- netfilter: nf_queue: hold bridge skb->dev while queued (Florian Westphal) [RHEL-185311]
|
||||
- netfilter: br_netfilter: Reallocate headroom if necessary in neigh_hh_bridge() (Florian Westphal) [RHEL-185311]
|
||||
- netfilter: ip6t_hbh: reject oversized option lists (Florian Westphal) [RHEL-185311]
|
||||
- netfilter: nf_conntrack_helper: fix possible null deref during error log (Florian Westphal) [RHEL-185311]
|
||||
- netfilter: nft_ct: fix missing expect put in obj eval (Florian Westphal) [RHEL-185311]
|
||||
- netfilter: nf_conntrack_sip: get helper before allocating expectation (Florian Westphal) [RHEL-185311]
|
||||
- netfilter: ctnetlink: check tuple and mask in expectations created via nfqueue (Florian Westphal) [RHEL-185311]
|
||||
- netfilter: nf_conntrack_expect: restore helper propagation via expectation (Florian Westphal) [RHEL-185311]
|
||||
- netfilter: nf_tables: fix netdev hook allocation memleak with dormant tables (Florian Westphal) [RHEL-185311]
|
||||
- netfilter: xt_CT: fix usersize for v1 and v2 revision (Florian Westphal) [RHEL-185311]
|
||||
- netfilter: nft_compat: run xt_check_hooks_{match,target}() from .validate (Florian Westphal) [RHEL-185311]
|
||||
- netfilter: x_tables: add .check_hooks to matches and targets (Florian Westphal) [RHEL-185311]
|
||||
- netfilter: xtables: restrict several matches to inet family (Florian Westphal) [RHEL-185311]
|
||||
- netfilter: nft_fwd_netdev: use recursion counter in neigh egress path (Florian Westphal) [RHEL-185311]
|
||||
- netfilter: nft_fwd_netdev: add device and headroom validate with neigh forwarding (Florian Westphal) [RHEL-185311]
|
||||
- netfilter: replace skb_try_make_writable() by skb_ensure_writable() (Florian Westphal) [RHEL-185311]
|
||||
- netfilter: nf_conntrack_sip: don't use simple_strtoul (Florian Westphal) [RHEL-185311]
|
||||
- netfilter: xt_policy: fix strict mode inbound policy matching (Florian Westphal) [RHEL-185311]
|
||||
- netfilter: nf_tables: add hook transactions for device deletions (Florian Westphal) [RHEL-185311]
|
||||
- netfilter: nf_tables: join hook list via splice_list_rcu() in commit phase (Florian Westphal) [RHEL-185311]
|
||||
- rculist: add list_splice_rcu() for private lists (Florian Westphal) [RHEL-185311]
|
||||
- netfilter: nf_tables: use list_del_rcu for netlink hooks (Florian Westphal) [RHEL-185311] {CVE-2026-46324}
|
||||
- netfilter: nfnetlink_osf: fix potential NULL dereference in ttl check (Florian Westphal) [RHEL-185311]
|
||||
- netfilter: nfnetlink_osf: fix out-of-bounds read on option matching (Florian Westphal) [RHEL-185311]
|
||||
- netfilter: nat: use kfree_rcu to release ops (Florian Westphal) [RHEL-185311]
|
||||
- netfilter: conntrack: remove sprintf usage (Florian Westphal) [RHEL-185311]
|
||||
- netfilter: nfnetlink_osf: fix divide-by-zero in OSF_WSS_MODULO (Florian Westphal) [RHEL-185311] {CVE-2026-45841}
|
||||
- nfnetlink_osf: validate individual option lengths in fingerprints (Florian Westphal) [RHEL-185311] {CVE-2026-23397}
|
||||
- netfilter: nft_osf: restrict it to ipv4 (Florian Westphal) [RHEL-185311]
|
||||
- netfilter: nft_ct: fix use-after-free in timeout object destroy (Florian Westphal) [RHEL-185311] {CVE-2026-31665}
|
||||
- netfilter: xt_multiport: validate range encoding in checkentry (Florian Westphal) [RHEL-185311] {CVE-2026-31681}
|
||||
- netfilter: nfnetlink_log: initialize nfgenmsg in NLMSG_DONE terminator (Florian Westphal) [RHEL-185311] {CVE-2026-43085}
|
||||
- netfilter: nf_tables: reject immediate NF_QUEUE verdict (Florian Westphal) [RHEL-185311] {CVE-2026-43024}
|
||||
- netfilter: x_tables: restrict xt_check_match/xt_check_target extensions for NFPROTO_ARP (Florian Westphal) [RHEL-185311] {CVE-2026-31424}
|
||||
- netfilter: ctnetlink: ignore explicit helper on new expectations (Florian Westphal) [RHEL-185311] {CVE-2026-43025}
|
||||
- netfilter: ctnetlink: zero expect NAT fields when CTA_EXPECT_NAT absent (Florian Westphal) [RHEL-185311] {CVE-2026-43026}
|
||||
- netfilter: ipset: use nla_strcmp for IPSET_ATTR_NAME attr (Florian Westphal) [RHEL-185311]
|
||||
- netfilter: x_tables: ensure names are nul-terminated (Florian Westphal) [RHEL-185311] {CVE-2026-43028}
|
||||
- netfilter: nfnetlink_log: account for netlink header size (Florian Westphal) [RHEL-185311] {CVE-2026-31416}
|
||||
- netfilter: ctnetlink: use netlink policy range checks (Florian Westphal) [RHEL-185311] {CVE-2026-31495}
|
||||
- netfilter: ip6t_rt: reject oversized addrnr in rt_mt6_check() (Florian Westphal) [RHEL-185311] {CVE-2026-31674}
|
||||
- netfilter: nf_conntrack_expect: store netns and zone in expectation (Florian Westphal) [RHEL-185311]
|
||||
- netfilter: nf_conntrack_expect: use expect->helper (Florian Westphal) [RHEL-185311]
|
||||
- netfilter: nf_conntrack_expect: honor expectation helper field (Florian Westphal) [RHEL-185311]
|
||||
- netfilter: nfnetlink_log: fix uninitialized padding leak in NFULA_PAYLOAD (Florian Westphal) [RHEL-185311] {CVE-2026-31428}
|
||||
- netfilter: nft_set_pipapo_avx2: don't return non-matching entry on expiry (Florian Westphal) [RHEL-185311] {CVE-2026-43114}
|
||||
- nf_tables: nft_dynset: fix possible stateful expression memleak in error path (Florian Westphal) [RHEL-185311] {CVE-2026-23399}
|
||||
- netfilter: nf_conntrack_h323: fix OOB read in decode_int() CONS case (Florian Westphal) [RHEL-185311] {CVE-2026-23456}
|
||||
- netfilter: nf_conntrack_sip: fix Content-Length u32 truncation in sip_help_tcp() (Florian Westphal) [RHEL-185311] {CVE-2026-23457}
|
||||
- netfilter: conntrack: add missing netlink policy validations (Florian Westphal) [RHEL-185311] {CVE-2026-31407}
|
||||
- netfilter: ctnetlink: fix use-after-free in ctnetlink_dump_exp_ct() (Florian Westphal) [RHEL-185311] {CVE-2026-23458}
|
||||
- netfilter: nfnetlink_queue: fix entry leak in bridge verdict error path (Florian Westphal) [RHEL-185311] {CVE-2026-43451}
|
||||
- netfilter: nft_set_pipapo: fix stack out-of-bounds read in pipapo_drop() (Florian Westphal) [RHEL-185311] {CVE-2026-43453}
|
||||
- netfilter: nf_tables: unconditionally bump set->nelems before insertion (Florian Westphal) [RHEL-185311] {CVE-2026-23272}
|
||||
- netfilter: nf_conntrack_h323: fix OOB read in decode_choice() (Florian Westphal) [RHEL-185311] {CVE-2026-43233}
|
||||
- netfilter: nft_set_hash: fix get operation on big endian (Florian Westphal) [RHEL-185311]
|
||||
- netfilter: nf_tables: always walk all pending catchall elements (Florian Westphal) [RHEL-185311] {CVE-2026-23278}
|
||||
- netfilter: nft_set_pipapo: split gc into unlink and reclaim phase (Florian Westphal) [RHEL-185311] {CVE-2026-23351}
|
||||
|
||||
* Mon Aug 10 2026 CKI KWF Bot <cki-ci-bot+kwf-gitlab-com@redhat.com> [6.12.0-211.46.1.el10_2]
|
||||
- mm/slab: do not limit zeroing to orig_size when only red zoning is enabled (Rafael Aquini) [RHEL-223405] {CVE-2026-64368}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user