Conversation
jira VULN-68359 cve-pre CVE-2024-27397 commit-author Pablo Neira Ayuso <pablo@netfilter.org> commit d111692 This allows to remove an expired element which is not possible in other existing set backends, this is more noticeable if gc-interval is high so expired elements remain in the tree. On-demand gc also does not help in this case, because this is delete element path. Return NULL if element has expired. Fixes: 8d8540c ("netfilter: nft_set_rbtree: add timeout support") Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org> Signed-off-by: Florian Westphal <fw@strlen.de> (cherry picked from commit f8710dd1d382c766f639e05bdcd22443cd06e807) Signed-off-by: Marcin Wcisło <marcin.wcislo@conclusive.pl>
jira VULN-68359 cve CVE-2024-27397 commit-author Pablo Neira Ayuso <pablo@netfilter.org> commit 7395dfa upstream-diff This commit combines the upstream fix 7395dfa and fragments of 0854db2 (`netfilter: nf_tables: use net_generic infra for transaction data') and its follow-up d59d2f8 (`netfilter: nftables: add nft_pernet() helper function'). Commit 0854db2 moves most of the nft-related data out of the general `net' struct to a `net_generic' extension. The CVE-2024-27397 fix introduces a new field to that extension. It could not have been put where the other moved fields resided originally (`struct netns_nftables') because it would break kABI. At the same time 0854db2 could not have been backported fully because it would also break kABI. The solution was to introduce the `net_generic' extension as in 0854db2 but to keep it narrowed to just the `tstamp' field needed by the CVE-2024-27397 bugfix. As a result the following differences from the upstream fix can be observed: nf_tables.h 1. No `nftables_pernet' defined to place `tstamp' in (done in 7395dfa), so defined the singleton. 2. Added `nf_tables_net_id' global, `nft_pernet' helper and `generic.h' inclusion - taken directly from d59d2f8. nf_tables_api.c 1. Defined `nf_tables_net_id' as it was done in 0854db2. It required `EXPORT_SYMBOL_GPL' in LTS 8.6 for the proper loading of the compiled `nf_tables_set.ko' module. 2. Omitted changes to `nft_trans_gc_catchall_sync()', because LTS 8.6 doesn't have this function nor any equivalent. 3. In `nf_tables_valid_genid()' added the `nft_net' initialization (0854db2 + d59d2f8). 4. Added fields `.id' and `.size' to the `nf_tables_net_ops' struct as it was done in 0854db2. net/netfilter/nft_set_pipapo.c 1. Context conflicts in `nft_pipapo_get()' due to missing 9dad402, otherwise no diffs. 2. In `nft_pipapo_insert()' addded `tstamp' argument to the two `pipapo_get' calls, just like in the upstream, although in the upstream these two are further apart. 3. In `pipapo_gc()' needed to include `net' variable. 4. Function `__nft_rbtree_insert' underwent some substantial changes across many non-backported commits, but they were irrelevant to the introduced change. Preserved the spirit of the fix by changing all `nft_set_elem_expired(&rbe->ext)' calls to `__nft_set_elem_expired(&rbe->ext, tstamp)'. 5. In `nft_rbtree_gc()' adapted the change to read `net' from `set' obtained from `work' provided as argument instead of from `set' given in the argument directly like it's done in the upstream. Add a timestamp field at the beginning of the transaction, store it in the nftables per-netns area. Update set backend .insert, .deactivate and sync gc path to use the timestamp, this avoids that an element expires while control plane transaction is still unfinished. .lookup and .update, which are used from packet path, still use the current time to check if the element has expired. And .get path and dump also since this runs lockless under rcu read size lock. Then, there is async gc which also needs to check the current time since it runs asynchronously from a workqueue. Fixes: c3e1b00 ("netfilter: nf_tables: add set element timeout support") Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org> (cherry picked from commit 6af6b4227e09d8b93ab9a2a26f24fa85cead58d1) Signed-off-by: Marcin Wcisło <marcin.wcislo@conclusive.pl>
jira VULN-184372 cve CVE-2026-43125 commit-author Ezrak1e <ezrakiez@gmail.com> commit 080e556 upstream-diff Kept the `len' check within the `dlm_search_rsb_tree' function like in the upstream, despite the fact that in LTS 8.6 the out-of-bounds write can happen not in `dlm_search_rsb_tree' but in the `rsb_cmp' called by it. Function `rsb_cmp' is not suited for returning error codes, assumed to be always called with correct arguments. The code analysis established that `dlm_search_rsb_tree' was the last place where this may not have been the case. The len parameter in dlm_dump_rsb_name() is not validated and comes from network messages. When it exceeds DLM_RESNAME_MAXLEN, it can cause out-of-bounds write in dlm_search_rsb_tree(). Add length validation to prevent potential buffer overflow. Signed-off-by: Ezrak1e <ezrakiez@gmail.com> Signed-off-by: Alexander Aring <aahringo@redhat.com> Signed-off-by: David Teigland <teigland@redhat.com> (cherry picked from commit 118986f3da1b4b0687e6f061fdedc60e6dcb7d05) Signed-off-by: Marcin Wcisło <marcin.wcislo@conclusive.pl>
jira VULN-184338 cve CVE-2026-43116 commit-author Pablo Neira Ayuso <pablo@netfilter.org> commit bffcaad upstream-diff In `ctnetlink_get_expect()' preserved the intent of the change (spinlock protection spanning from `nf_ct_expect_find_get()' to `nf_ct_expect_put()' + moving `nlmsg_new()' out of the critical section) and adjusted for the existing goto-ladder teardown pattern. In the upstream it was scrapped in e0241ae ("netfilter: use nfnetlink_unicast()"), but that commit introduces way too many unrealated changes to be backported just for this port's convenience. Holding reference on the expectation is not sufficient, the master conntrack object can just go away, making exp->master invalid. To access exp->master safely: - Grab the nf_conntrack_expect_lock, this gets serialized with clean_from_lists() which also holds this lock when the master conntrack goes away. - Hold reference on master conntrack via nf_conntrack_find_get(). Not so easy since the master tuple to look up for the master conntrack is not available in the existing problematic paths. This patch goes for extending the nf_conntrack_expect_lock section to address this issue for simplicity, in the cases that are described below this is just slightly extending the lock section. The add expectation command already holds a reference to the master conntrack from ctnetlink_create_expect(). However, the delete expectation command needs to grab the spinlock before looking up for the expectation. Expand the existing spinlock section to address this to cover the expectation lookup. Note that, the nf_ct_expect_iterate_net() calls already grabs the spinlock while iterating over the expectation table, which is correct. The get expectation command needs to grab the spinlock to ensure master conntrack does not go away. This also expands the existing spinlock section to cover the expectation lookup too. I needed to move the netlink skb allocation out of the spinlock to keep it GFP_KERNEL. For the expectation events, the IPEXP_DESTROY event is already delivered under the spinlock, just move the delivery of IPEXP_NEW under the spinlock too because the master conntrack event cache is reached through exp->master. While at it, add lockdep notations to help identify what codepaths need to grab the spinlock. Signed-off-by: Florian Westphal <fw@strlen.de> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org> (cherry picked from commit 79f21a8db667336089a743cbda1e03bf3e348266) Signed-off-by: Marcin Wcisło <marcin.wcislo@conclusive.pl>
jira VULN-183687 cve CVE-2026-43027 commit-author Qi Tang <tpluszz77@gmail.com> commit a242a9a nf_conntrack_helper_unregister() calls nf_ct_expect_iterate_destroy() to remove expectations belonging to the helper being unregistered. However, it passes NULL instead of the helper pointer as the data argument, so expect_iter_me() never matches any expectation and all of them survive the cleanup. After unregister returns, nfnl_cthelper_del() frees the helper object immediately. Subsequent expectation dumps or packet-driven init_conntrack() calls then dereference the freed exp->helper, causing a use-after-free. Pass the actual helper pointer so expectations referencing it are properly destroyed before the helper object is freed. BUG: KASAN: slab-use-after-free in string+0x38f/0x430 Read of size 1 at addr ffff888003b14d20 by task poc/103 Call Trace: string+0x38f/0x430 vsnprintf+0x3cc/0x1170 seq_printf+0x17a/0x240 exp_seq_show+0x2e5/0x560 seq_read_iter+0x419/0x1280 proc_reg_read+0x1ac/0x270 vfs_read+0x179/0x930 ksys_read+0xef/0x1c0 Freed by task 103: The buggy address is located 32 bytes inside of freed 192-byte region [ffff888003b14d00, ffff888003b14dc0) Fixes: ac7b848 ("netfilter: expect: add and use nf_ct_expect_iterate helpers") Signed-off-by: Qi Tang <tpluszz77@gmail.com> Reviewed-by: Phil Sutter <phil@nwl.cc> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org> (cherry picked from commit b1bf205e1e2a88fc5039fe80e221c10218119f05) Signed-off-by: Marcin Wcisło <marcin.wcislo@conclusive.pl>
|
🤖 Validation Checks In Progress Workflow run: https://github.com/ctrliq/kernel-src-tree/actions/runs/35283014341 |
🔍 Upstream Linux Kernel Commit Check
This is an automated message from the kernel commit checker workflow. |
🔍 Interdiff Analysis
================================================================================
* DELTA DIFFERENCES - code changes that differ between the patches *
================================================================================
--- b/include/net/netfilter/nf_tables.h
+++ b/include/net/netfilter/nf_tables.h
@@ -13,7 +13,6 @@
#include <net/netfilter/nf_flow_table.h>
#include <net/netlink.h>
#include <net/flow_offload.h>
-#include <net/netns/generic.h>
struct module;
@@ -1539,19 +1538,2 @@
#endif
-
-struct nftables_pernet {
- u64 tstamp;
-};
-
-extern unsigned int nf_tables_net_id;
-
-static inline struct nftables_pernet *nft_pernet(const struct net *net)
-{
- return net_generic(net, nf_tables_net_id);
-}
-
-static inline u64 nft_net_tstamp(const struct net *net)
-{
- return nft_pernet(net)->tstamp;
-}
-
#endif /* _NET_NF_TABLES_H */
--- b/net/netfilter/nf_tables_api.c
+++ b/net/netfilter/nf_tables_api.c
@@ -28,9 +28,6 @@
#define NFT_MODULE_AUTOLOAD_LIMIT (MODULE_NAME_LEN - sizeof("nft-expr-255-"))
-unsigned int nf_tables_net_id __read_mostly;
-EXPORT_SYMBOL_GPL(nf_tables_net_id);
-
static LIST_HEAD(nf_tables_expressions);
static LIST_HEAD(nf_tables_objects);
static LIST_HEAD(nf_tables_flowtables);
@@ -8141,11 +8138,9 @@
static bool nf_tables_valid_genid(struct net *net, u32 genid)
{
- struct nftables_pernet *nft_net = nft_pernet(net);
bool genid_ok;
mutex_lock(&net->nft_commit_mutex);
- nft_net->tstamp = get_jiffies_64();
genid_ok = genid == 0 || net->nft.base_seq == genid;
if (!genid_ok)
@@ -8763,8 +8758,6 @@
.init = nf_tables_init_net,
.pre_exit = nf_tables_pre_exit_net,
.exit = nf_tables_exit_net,
- .id = &nf_tables_net_id,
- .size = sizeof(struct nftables_pernet),
};
static int __init nf_tables_module_init(void)
--- b/net/netfilter/nft_set_pipapo.c
+++ b/net/netfilter/nft_set_pipapo.c
@@ -743,7 +743,7 @@
const struct nft_set_elem *elem, unsigned int flags)
{
return pipapo_get(net, set, (const u8 *)elem->key.val.data,
- nft_genmask_cur(net), get_jiffies_64());
+ nft_genmask_cur(net));
}
/**
@@ -1095,19 +1095,17 @@
union nft_pipapo_map_bucket rulemap[NFT_PIPAPO_MAX_FIELDS];
const u8 *start = (const u8 *)elem->key.val.data, *end;
struct nft_pipapo_elem *e = elem->priv, *dup;
- u64 tstamp = nft_net_tstamp(net);
struct nft_pipapo *priv = nft_set_priv(set);
struct nft_pipapo_match *m = priv->clone;
u8 genmask = nft_genmask_next(net);
struct nft_pipapo_field *f;
int i, bsize_max, err = 0;
- dup = pipapo_get(net, set, start, genmask, tstamp);
+ dup = pipapo_get(net, set, start, genmask);
if (PTR_ERR(dup) == -ENOENT) {
if (nft_set_ext_exists(ext, NFT_SET_EXT_KEY_END)) {
end = (const u8 *)nft_set_ext_key_end(ext)->data;
- dup = pipapo_get(net, set, end, nft_genmask_next(net),
- tstamp);
+ dup = pipapo_get(net, set, end, nft_genmask_next(net));
} else {
end = start;
}
@@ -1441,8 +1439,6 @@
static void pipapo_gc(const struct nft_set *set, struct nft_pipapo_match *m)
{
struct nft_pipapo *priv = nft_set_priv(set);
- struct net *net = read_pnet(&set->net);
- u64 tstamp = nft_net_tstamp(net);
int rules_f0, first_rule = 0;
while ((rules_f0 = pipapo_rules_same_key(m->f, first_rule))) {
@@ -1468,7 +1464,7 @@
f--;
i--;
e = f->mt[rulemap[i].to].e;
- if (__nft_set_elem_expired(&e->ext, tstamp) &&
+ if (nft_set_elem_expired(&e->ext) &&
!nft_set_elem_mark_busy(&e->ext)) {
priv->dirty = true;
pipapo_drop(m, rulemap);
--- b/net/netfilter/nft_set_rbtree.c
+++ b/net/netfilter/nft_set_rbtree.c
@@ -282,13 +281,13 @@
if (nft_rbtree_interval_start(new)) {
if (nft_rbtree_interval_end(rbe) &&
nft_set_elem_active(&rbe->ext, genmask) &&
- !__nft_set_elem_expired(&rbe->ext, tstamp) && !*p)
+ !nft_set_elem_expired(&rbe->ext) && !*p)
overlap = false;
} else {
overlap = nft_rbtree_interval_end(rbe) &&
nft_set_elem_active(&rbe->ext,
genmask) &&
- !__nft_set_elem_expired(&rbe->ext, tstamp);
+ !nft_set_elem_expired(&rbe->ext);
}
} else if (d > 0) {
p = &parent->rb_right;
@@ -297,9 +296,9 @@
overlap = nft_rbtree_interval_end(rbe) &&
nft_set_elem_active(&rbe->ext,
genmask) &&
- !__nft_set_elem_expired(&rbe->ext, tstamp);
+ !nft_set_elem_expired(&rbe->ext);
} else if (nft_set_elem_active(&rbe->ext, genmask) &&
- !__nft_set_elem_expired(&rbe->ext, tstamp)) {
+ !nft_set_elem_expired(&rbe->ext)) {
overlap = nft_rbtree_interval_end(rbe);
}
} else {
@@ -308,17 +307,17 @@
p = &parent->rb_left;
if (nft_set_elem_active(&rbe->ext, genmask) &&
- !__nft_set_elem_expired(&rbe->ext, tstamp))
+ !nft_set_elem_expired(&rbe->ext))
overlap = false;
} else if (nft_rbtree_interval_start(rbe) &&
nft_rbtree_interval_end(new)) {
p = &parent->rb_right;
if (nft_set_elem_active(&rbe->ext, genmask) &&
- !__nft_set_elem_expired(&rbe->ext, tstamp))
+ !nft_set_elem_expired(&rbe->ext))
overlap = false;
} else if (nft_set_elem_active(&rbe->ext, genmask) &&
- !__nft_set_elem_expired(&rbe->ext, tstamp)) {
+ !nft_set_elem_expired(&rbe->ext)) {
*ext = &rbe->ext;
return -EEXIST;
} else {
@@ -469,11 +468,9 @@
struct nft_rbtree *priv;
struct rb_node *node;
struct nft_set *set;
- u64 tstamp;
priv = container_of(work, struct nft_rbtree, gc_work.work);
set = nft_set_container_of(priv);
- tstamp = nft_net_tstamp(read_pnet(&set->net));
write_lock_bh(&priv->lock);
write_seqcount_begin(&priv->count);
################################################################################
! REJECTED PATCH2 HUNKS - could not be compared; manual review needed !
################################################################################
--- b/include/net/netfilter/nf_tables.h
+++ b/include/net/netfilter/nf_tables.h
@@ -1785,6 +1791,7 @@
struct list_head notify_list;
struct mutex commit_mutex;
u64 table_handle;
+ u64 tstamp;
unsigned int base_seq;
unsigned int gc_seq;
u8 validate_state;
@@ -1797,6 +1804,11 @@
return net_generic(net, nf_tables_net_id);
}
+static inline u64 nft_net_tstamp(const struct net *net)
+{
+ return nft_pernet(net)->tstamp;
+}
+
#define __NFT_REDUCE_READONLY 1UL
#define NFT_REDUCE_READONLY (void *)__NFT_REDUCE_READONLY
--- b/net/netfilter/nf_tables_api.c
+++ b/net/netfilter/nf_tables_api.c
@@ -9827,6 +9827,7 @@
struct nft_trans_gc *nft_trans_gc_catchall_sync(struct nft_trans_gc *gc)
{
struct nft_set_elem_catchall *catchall, *next;
+ u64 tstamp = nft_net_tstamp(gc->net);
const struct nft_set *set = gc->set;
struct nft_elem_priv *elem_priv;
struct nft_set_ext *ext;
@@ -9836,7 +9837,7 @@
list_for_each_entry_safe(catchall, next, &set->catchall_list, list) {
ext = nft_set_elem_ext(set, catchall->elem);
- if (!nft_set_elem_expired(ext))
+ if (!__nft_set_elem_expired(ext, tstamp))
continue;
gc = nft_trans_gc_queue_sync(gc, GFP_KERNEL);
@@ -10622,6 +10623,7 @@
bool genid_ok;
mutex_lock(&nft_net->commit_mutex);
+ nft_net->tstamp = get_jiffies_64();
genid_ok = genid == 0 || nft_net->base_seq == genid;
if (!genid_ok)
--- b/net/netfilter/nft_set_pipapo.c
+++ b/net/netfilter/nft_set_pipapo.c
@@ -608,7 +610,7 @@
struct nft_pipapo_elem *e;
e = pipapo_get(net, set, (const u8 *)elem->key.val.data,
- nft_genmask_cur(net));
+ nft_genmask_cur(net), get_jiffies_64());
if (IS_ERR(e))
return ERR_CAST(e);
@@ -1175,6 +1177,7 @@
struct nft_pipapo_match *m = priv->clone;
u8 genmask = nft_genmask_next(net);
struct nft_pipapo_elem *e, *dup;
+ u64 tstamp = nft_net_tstamp(net);
struct nft_pipapo_field *f;
const u8 *start_p, *end_p;
int i, bsize_max, err = 0;
@@ -1184,7 +1187,7 @@
else
end = start;
- dup = pipapo_get(net, set, start, genmask);
+ dup = pipapo_get(net, set, start, genmask, tstamp);
if (!IS_ERR(dup)) {
/* Check if we already have the same exact entry */
const struct nft_data *dup_key, *dup_end;
@@ -1206,7 +1209,7 @@
if (PTR_ERR(dup) == -ENOENT) {
/* Look for partially overlapping entries */
- dup = pipapo_get(net, set, end, nft_genmask_next(net));
+ dup = pipapo_get(net, set, end, nft_genmask_next(net), tstamp);
}
if (PTR_ERR(dup) != -ENOENT) {
@@ -1562,6 +1565,7 @@
{
struct nft_pipapo *priv = nft_set_priv(set);
struct net *net = read_pnet(&set->net);
+ u64 tstamp = nft_net_tstamp(net);
int rules_f0, first_rule = 0;
struct nft_pipapo_elem *e;
struct nft_trans_gc *gc;
@@ -1596,7 +1600,7 @@
/* synchronous gc never fails, there is no need to set on
* NFT_SET_ELEM_DEAD_BIT.
*/
- if (nft_set_elem_expired(&e->ext)) {
+ if (__nft_set_elem_expired(&e->ext, tstamp)) {
priv->dirty = true;
gc = nft_trans_gc_queue_sync(gc, GFP_KERNEL);
--- b/net/netfilter/nft_set_rbtree.c
+++ b/net/netfilter/nft_set_rbtree.c
@@ -361,7 +362,7 @@
/* perform garbage collection to avoid bogus overlap reports
* but skip new elements in this transaction.
*/
- if (nft_set_elem_expired(&rbe->ext) &&
+ if (__nft_set_elem_expired(&rbe->ext, tstamp) &&
nft_set_elem_active(&rbe->ext, cur_genmask)) {
const struct nft_rbtree_elem *removed_end;
@@ -552,6 +553,7 @@
const struct nft_rbtree *priv = nft_set_priv(set);
const struct rb_node *parent = priv->root.rb_node;
u8 genmask = nft_genmask_next(net);
+ u64 tstamp = nft_net_tstamp(net);
int d;
while (parent != NULL) {
@@ -625,6 +627,8 @@
{
struct nft_rbtree *priv = nft_set_priv(set);
struct nft_rbtree_elem *rbe, *rbe_end = NULL;
+ struct net *net = read_pnet(&set->net);
+ u64 tstamp = nft_net_tstamp(net);
struct rb_node *node, *next;
struct nft_trans_gc *gc;
struct net *net;
@@ -627,7 +631,6 @@
struct nft_rbtree_elem *rbe, *rbe_end = NULL;
struct rb_node *node, *next;
struct nft_trans_gc *gc;
- struct net *net;
set = nft_set_container_of(priv);
net = read_pnet(&set->net);
================================================================================
* CONTEXT DIFFERENCES - surrounding code differences between the patches *
================================================================================
--- b/net/netfilter/nf_tables_api.c
+++ b/net/netfilter/nf_tables_api.c
@@ -8135,7 +8130,12 @@
{
- bool genid_ok;
+ struct nft_set_elem_catchall *catchall, *next;
+ const struct nft_set *set = gc->set;
+ struct nft_elem_priv *elem_priv;
+ struct nft_set_ext *ext;
+ list_for_each_entry_safe(catchall, next, &set->catchall_list, list) {
+ ext = nft_set_elem_ext(set, catchall->elem);
- mutex_lock(&net->nft_commit_mutex);
+ if (!nft_set_elem_expired(ext))
+ continue;
- genid_ok = genid == 0 || net->nft.base_seq == genid;
- if (!genid_ok)
+ gc = nft_trans_gc_queue_sync(gc, GFP_KERNEL);
@@ -8755,2 +10508,3 @@
-static int __init nf_tables_module_init(void)
+ genid_ok = genid == 0 || nft_net->base_seq == genid;
+ if (!genid_ok)
--- b/net/netfilter/nft_set_hash.c
+++ b/net/netfilter/nft_set_hash.c
@@ -59,5 +57,5 @@
-
- if (memcmp(nft_set_ext_key(&he->ext), x->key, x->set->klen))
+ return 1;
+ if (nft_set_elem_is_dead(&he->ext))
return 1;
if (nft_set_elem_expired(&he->ext))
return 1;
--- b/net/netfilter/nft_set_pipapo.c
+++ b/net/netfilter/nft_set_pipapo.c
@@ -1094,6 +1170,10 @@
struct nft_pipapo_match *m = priv->clone;
u8 genmask = nft_genmask_next(net);
+ struct nft_pipapo_elem *e, *dup;
struct nft_pipapo_field *f;
+ const u8 *start_p, *end_p;
int i, bsize_max, err = 0;
+ else
+ end = start;
dup = pipapo_get(net, set, start, genmask);
@@ -1100,7 +1184,6 @@
if (PTR_ERR(dup) == -ENOENT) {
- if (nft_set_ext_exists(ext, NFT_SET_EXT_KEY_END)) {
- end = (const u8 *)nft_set_ext_key_end(ext)->data;
- dup = pipapo_get(net, set, end, nft_genmask_next(net));
- } else {
- end = start;
- }
+ /* Look for partially overlapping entries */
+ dup = pipapo_get(net, set, end, nft_genmask_next(net));
+ }
+
+ if (PTR_ERR(dup) != -ENOENT) {
@@ -1435,5 +1557,6 @@
{
struct nft_pipapo *priv = nft_set_priv(set);
+ struct net *net = read_pnet(&set->net);
int rules_f0, first_rule = 0;
-
- while ((rules_f0 = pipapo_rules_same_key(m->f, first_rule))) {
+ struct nft_pipapo_elem *e;
+ struct nft_trans_gc *gc;
@@ -1459,7 +1586,7 @@
- f--;
- i--;
- e = f->mt[rulemap[i].to].e;
- if (nft_set_elem_expired(&e->ext) &&
- !nft_set_elem_mark_busy(&e->ext)) {
+ /* synchronous gc never fails, there is no need to set on
+ * NFT_SET_ELEM_DEAD_BIT.
+ */
+ if (nft_set_elem_expired(&e->ext)) {
priv->dirty = true;
- pipapo_drop(m, rulemap);
+
+ gc = nft_trans_gc_queue_sync(gc, GFP_KERNEL);
--- b/net/netfilter/nft_set_rbtree.c
+++ b/net/netfilter/nft_set_rbtree.c
@@ -278,7 +276,2 @@
- if (nft_set_elem_active(&rbe->ext, genmask) &&
- !nft_set_elem_expired(&rbe->ext))
- overlap = false;
- } else if (nft_rbtree_interval_start(rbe) &&
- nft_rbtree_interval_end(new)) {
- p = &parent->rb_right;
+ /* Descend the tree to search for an existing element greater than the
@@ -393,5 +437,4 @@
const struct rb_node *parent = priv->root.rb_node;
- struct nft_rbtree_elem *rbe, *this = elem->priv;
u8 genmask = nft_genmask_next(net);
int d;
@@ -464,9 +619,9 @@
- struct nft_rbtree *priv;
- struct rb_node *node;
- struct nft_set *set;
+{
+ struct nft_rbtree *priv = nft_set_priv(set);
+ struct nft_rbtree_elem *rbe, *rbe_end = NULL;
+ struct rb_node *node, *next;
+ struct nft_trans_gc *gc;
+ struct net *net;
- priv = container_of(work, struct nft_rbtree, gc_work.work);
set = nft_set_container_of(priv);
-
- write_lock_bh(&priv->lock);
- write_seqcount_begin(&priv->count);
+ net = read_pnet(&set->net);
@@ -482,5 +650,5 @@
}
if (!nft_set_elem_expired(&rbe->ext))
continue;
- if (nft_set_elem_mark_busy(&rbe->ext))
- continue;
+
+ gc = nft_trans_gc_queue_sync(gc, GFP_KERNEL);
================================================================================
* DELTA DIFFERENCES - code changes that differ between the patches *
================================================================================
--- b/fs/dlm/lock.c
+++ b/fs/dlm/lock.c
@@ -451,9 +451,6 @@
struct dlm_rsb *r;
int rc;
- if (len > DLM_RESNAME_MAXLEN)
- return -EINVAL;
-
while (node) {
r = rb_entry(node, struct dlm_rsb, res_hashnode);
rc = rsb_cmp(r, name, len);
################################################################################
! REJECTED PATCH2 HUNKS - could not be compared; manual review needed !
################################################################################
--- b/fs/dlm/lock.c
+++ b/fs/dlm/lock.c
@@ -626,7 +626,8 @@
struct dlm_rsb **r_ret)
{
char key[DLM_RESNAME_MAXLEN] = {};
-
+ if (len > DLM_RESNAME_MAXLEN)
+ return -EINVAL;
memcpy(key, name, len);
*r_ret = rhashtable_lookup_fast(rhash, &key, dlm_rhash_rsb_params);
if (*r_ret)
================================================================================
* CONTEXT DIFFERENCES - surrounding code differences between the patches *
================================================================================
--- b/fs/dlm/lock.c
+++ b/fs/dlm/lock.c
@@ -448,4 +451,4 @@
- while (node) {
- r = rb_entry(node, struct dlm_rsb, res_hashnode);
- rc = rsb_cmp(r, name, len);
+ memcpy(key, name, len);
+ *r_ret = rhashtable_lookup_fast(rhash, &key, dlm_rhash_rsb_params);
+ if (*r_ret)
================================================================================
* DELTA DIFFERENCES - code changes that differ between the patches *
================================================================================
--- b/include/net/netfilter/nf_conntrack_core.h
+++ b/include/net/netfilter/nf_conntrack_core.h
@@ -77,6 +77,2 @@
-static inline void lockdep_nfct_expect_lock_held(void)
-{
- lockdep_assert_held(&nf_conntrack_expect_lock);
-}
#endif /* _NF_CONNTRACK_CORE_H */
--- b/net/netfilter/nf_conntrack_netlink.c
+++ b/net/netfilter/nf_conntrack_netlink.c
@@ -3409,18 +3409,9 @@
if (err < 0)
return err;
- err = -ENOMEM;
- skb2 = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
- if (skb2 == NULL) {
- goto out;
- }
-
- spin_lock_bh(&nf_conntrack_expect_lock);
exp = nf_ct_expect_find_get(net, &zone, &tuple);
- if (!exp) {
- spin_unlock_bh(&nf_conntrack_expect_lock);
- err = -ENOENT;
- goto free;
+ if (!exp)
+ return -ENOENT;
}
if (cda[CTA_EXPECT_ID]) {
@@ -3429,18 +3420,23 @@
if (id != nf_expect_get_id(exp)) {
nf_ct_expect_put(exp);
spin_unlock_bh(&nf_conntrack_expect_lock);
- err = -ENOENT;
- goto free;
+ kfree_skb(skb2);
+ return -ENOENT;
}
}
+ err = -ENOMEM;
+ skb2 = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+ if (skb2 == NULL) {
+ nf_ct_expect_put(exp);
+ goto out;
+ }
+
rcu_read_lock();
err = ctnetlink_exp_fill_info(skb2, NETLINK_CB(skb).portid,
nlh->nlmsg_seq, IPCTNL_MSG_EXP_NEW, exp);
rcu_read_unlock();
nf_ct_expect_put(exp);
- spin_unlock_bh(&nf_conntrack_expect_lock);
-
if (err <= 0)
goto free;
################################################################################
! REJECTED PATCH2 HUNKS - could not be compared; manual review needed !
################################################################################
--- b/include/net/netfilter/nf_conntrack_core.h
+++ b/include/net/netfilter/nf_conntrack_core.h
@@ -83,6 +83,11 @@
extern spinlock_t nf_conntrack_expect_lock;
+static inline void lockdep_nfct_expect_lock_held(void)
+{
+ lockdep_assert_held(&nf_conntrack_expect_lock);
+}
+
/* ctnetlink code shared by both ctnetlink and nf_conntrack_bpf */
static inline void __nf_ct_set_timeout(struct nf_conn *ct, u64 timeout)
--- b/net/netfilter/nf_conntrack_expect.c
+++ b/net/netfilter/nf_conntrack_expect.c
@@ -119,6 +120,8 @@
bool nf_ct_remove_expect(struct nf_conntrack_expect *exp)
{
+ lockdep_nfct_expect_lock_held();
+
if (timer_delete(&exp->timeout)) {
nf_ct_unlink_expect(exp);
nf_ct_expect_put(exp);
@@ -178,6 +181,8 @@
struct nf_conntrack_expect *i, *exp = NULL;
unsigned int h;
+ lockdep_nfct_expect_lock_held();
+
if (!cnet->expect_count)
return NULL;
--- b/net/netfilter/nf_conntrack_netlink.c
+++ b/net/netfilter/nf_conntrack_netlink.c
@@ -3355,6 +3355,11 @@
if (err < 0)
return err;
+ skb2 = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+ if (!skb2)
+ return -ENOMEM;
+
+ spin_lock_bh(&nf_conntrack_expect_lock);
exp = nf_ct_expect_find_get(info->net, &zone, &tuple);
if (!exp)
return -ENOENT;
@@ -3356,7 +3361,9 @@
return err;
exp = nf_ct_expect_find_get(info->net, &zone, &tuple);
- if (!exp)
+ if (!exp) {
+ spin_unlock_bh(&nf_conntrack_expect_lock);
+ kfree_skb(skb2);
return -ENOENT;
if (cda[CTA_EXPECT_ID]) {
@@ -3367,12 +3377,6 @@
}
}
- skb2 = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
- if (!skb2) {
- nf_ct_expect_put(exp);
- return -ENOMEM;
- }
-
rcu_read_lock();
err = ctnetlink_exp_fill_info(skb2, NETLINK_CB(skb).portid,
info->nlh->nlmsg_seq, IPCTNL_MSG_EXP_NEW,
@@ -3376,6 +3380,8 @@
exp);
rcu_read_unlock();
nf_ct_expect_put(exp);
+ spin_unlock_bh(&nf_conntrack_expect_lock);
+
if (err <= 0) {
kfree_skb(skb2);
return -ENOMEM;
@@ -3433,7 +3441,8 @@
/* bump usage count to 2 */
exp = nf_ct_expect_find_get(info->net, &zone, &tuple);
- if (!exp)
+ if (!exp) {
+ spin_unlock_bh(&nf_conntrack_expect_lock);
return -ENOENT;
if (cda[CTA_EXPECT_ID]) {
@@ -3443,7 +3454,6 @@
}
/* after list removal, usage count == 1 */
- spin_lock_bh(&nf_conntrack_expect_lock);
if (timer_delete(&exp->timeout)) {
nf_ct_unlink_expect_report(exp, NETLINK_CB(skb).portid,
nlmsg_report(info->nlh));
================================================================================
* CONTEXT DIFFERENCES - surrounding code differences between the patches *
================================================================================
--- b/include/net/netfilter/nf_conntrack_core.h
+++ b/include/net/netfilter/nf_conntrack_core.h
@@ -72,4 +72,6 @@
extern spinlock_t nf_conntrack_expect_lock;
-#endif /* _NF_CONNTRACK_CORE_H */
+/* ctnetlink code shared by both ctnetlink and nf_conntrack_bpf */
+
+static inline void __nf_ct_set_timeout(struct nf_conn *ct, u64 timeout)
--- b/net/netfilter/nf_conntrack_ecache.c
+++ b/net/netfilter/nf_conntrack_ecache.c
@@ -238,3 +239,3 @@
rcu_read_lock();
- notify = rcu_dereference(net->ct.nf_expect_event_cb);
+ notify = rcu_dereference(net->ct.nf_conntrack_event_cb);
if (!notify)
--- b/net/netfilter/nf_conntrack_expect.c
+++ b/net/netfilter/nf_conntrack_expect.c
@@ -49,4 +48,5 @@
struct net *net = nf_ct_exp_net(exp);
+ struct nf_conntrack_net *cnet;
WARN_ON(!master_help);
WARN_ON(timer_pending(&exp->timeout));
@@ -105,5 +103,5 @@
bool nf_ct_remove_expect(struct nf_conntrack_expect *exp)
{
- if (del_timer(&exp->timeout)) {
+ if (timer_delete(&exp->timeout)) {
nf_ct_unlink_expect(exp);
nf_ct_expect_put(exp);
@@ -162,5 +173,5 @@
unsigned int h;
- if (!net->ct.expect_count)
+ if (!cnet->expect_count)
return NULL;
--- b/net/netfilter/nf_conntrack_netlink.c
+++ b/net/netfilter/nf_conntrack_netlink.c
@@ -3352,7 +3352,7 @@
if (err < 0)
return err;
- exp = nf_ct_expect_find_get(net, &zone, &tuple);
+ exp = nf_ct_expect_find_get(info->net, &zone, &tuple);
if (!exp)
return -ENOENT;
@@ -3423,14 +3369,14 @@
}
- err = -ENOMEM;
skb2 = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
- if (skb2 == NULL) {
+ if (!skb2) {
nf_ct_expect_put(exp);
- goto out;
+ return -ENOMEM;
}
rcu_read_lock();
err = ctnetlink_exp_fill_info(skb2, NETLINK_CB(skb).portid,
- nlh->nlmsg_seq, IPCTNL_MSG_EXP_NEW, exp);
+ info->nlh->nlmsg_seq, IPCTNL_MSG_EXP_NEW,
+ exp);
rcu_read_unlock();
nf_ct_expect_put(exp);
@@ -3489,13 +3427,14 @@
return err;
/* bump usage count to 2 */
- exp = nf_ct_expect_find_get(net, &zone, &tuple);
+ exp = nf_ct_expect_find_get(info->net, &zone, &tuple);
if (!exp)
return -ENOENT;
if (cda[CTA_EXPECT_ID]) {
__be32 id = nla_get_be32(cda[CTA_EXPECT_ID]);
- if (ntohl(id) != (u32)(unsigned long)exp) {
+
+ if (id != nf_expect_get_id(exp)) {
nf_ct_expect_put(exp);
return -ENOENT;
}
@@ -3503,6 +3442,6 @@
/* after list removal, usage count == 1 */
spin_lock_bh(&nf_conntrack_expect_lock);
- if (del_timer(&exp->timeout)) {
+ if (timer_delete(&exp->timeout)) {
nf_ct_unlink_expect_report(exp, NETLINK_CB(skb).portid,
- nlmsg_report(nlh));
+ nlmsg_report(info->nlh));
================================================================================
* CONTEXT DIFFERENCES - surrounding code differences between the patches *
================================================================================
--- b/net/netfilter/nf_conntrack_helper.c
+++ b/net/netfilter/nf_conntrack_helper.c
@@ -412,7 +412,7 @@
*/
synchronize_rcu();
nf_ct_expect_iterate_destroy(expect_iter_me, NULL);
nf_ct_iterate_destroy(unhelp, me);
- /* Maybe someone has gotten the helper already when unhelp above.
+ /* nf_ct_iterate_destroy() does an unconditional synchronize_rcu() asThis is an automated interdiff check for backported commits. |
JIRA PR Check Results5 commit(s) with issues found: Commit
|
|
❌ Validation checks completed with issues View full results: https://github.com/ctrliq/kernel-src-tree/actions/runs/35283014341 |
[LTS 8.6]
Commits
CVE-2026-43027
CVE-2026-43116
Commit's intent
The fixing commit has two goals, which overlap, but not fully, so it may be a bit confusing.
nf_conntrack_expect::masteras requiring thenf_conntrack_expect_lockheld when called:nf_ct_expect_event_report(),nf_ct_unlink_expect_report(),nf_ct_remove_expect(),nf_ct_find_expectation(),__nf_ct_expect_check().nf_conntrack_expect::master:nf_ct_expect_related_report(): The extended lock covers the newly annotated functionnf_ct_expect_event_report().ctnetlink_get_expect(): The newly introduced lock doesn't cover any of the annotated functions. However, it protects thenf_ct_expect_find_get(),nf_ct_expect_put()andctnetlink_exp_fill_info()calls, which could not have been annotated.ctnetlink_del_expect(): Similarly toctnetlink_get_expect(), the (expanded, this time) lock protection coversnf_ct_expect_find_get()andnf_ct_expect_put()calls. Thenf_ct_unlink_expect_report(), already being under lock before, was among those annotated.So, in total, the bffcaad fix
nf_conntrack_expect_lock-annotates the functions which were not put under the lock in the same commit, because it was already done (nf_ct_unlink_expect_report(),nf_ct_remove_expect(),nf_ct_find_expectation(),__nf_ct_expect_check()),nf_conntrack_expect_lockthe calls of functions which were not annotated in the same commit, and won't be (ctnetlink_del_expect(),ctnetlink_get_expect()), andnf_conntrack_expect_lock(nf_ct_expect_related_report()andnf_ct_expect_event_report()call).Including the
nf_ct_expect_find_get()call under thenf_conntrack_expect_locklock achieves what was identified in the commit message as sufficient condition for accessingnf_conntrack_expect::mastersafely:In the backported version the annotated functions were checked if their invariant also holds for all their calls in LTS 8.6. It does, so no new locks were introduced besides those the upstream fix introduced.
The CVE-2026-80668 issue
Formally, the CVE-2026-43116 fix bffcaad has a bugfix b8b09dc
netfilter: nf_conntrack_expect: use conntrack GC to reap expectationson the upstream, which has its own CVE-2026-80668. (Additionally it has its own bugfixes 322371b and 4aa6384.) However, the CVE-2026-80668 issue is not something CVE-2026-43116 fix introduced. Tag "Fixes: bffcaad" in this context means more should have addressed this problem as well but didn't, here's the missing piece rather than introduced a new problem, here's the fix for it.The CVE-2026-43116 fix covers control-plane reader vs. background conntrack reclamation race. The follow-up covers a race where both sides are kernel-internal: the expectation expiry timer vs. whichever code is synchronously removing that same expectation - whoever loses the
timer_delete()call loses the race.Taking this into account, and the fact that the CVE-2026-80668 fix is challenging to backport on LTS 8.6 (~250 lines, extensive conflicts in all 9 modified files, complicated synchronization problem) with no precedent backports to support, as well as this CVE not being addressed by RH for any version yet, it was decided to not include it in this PR.
CVE-2026-43125
The modified
dlm_search_rsb_treefunction underwent major rewrite in the non-backported 6c64803dlm: switch to use rhashtable for rsbs. In the upstream, at the moment right before the CVE-2026-43125 fix 080e556, the out-of-bounds condition can occur directly indlm_search_rsb_treefunction:kernel-src-tree/fs/dlm/lock.c
Lines 625 to 636 in 1416bd5
In LTS 8.6 this may happen in the
rsb_cmpfunction:kernel-src-tree/fs/dlm/lock.c
Lines 438 to 445 in 67edd7c
called from
dlm_search_rsb_tree:kernel-src-tree/fs/dlm/lock.c
Line 456 in 67edd7c
The
rsb_cmpfunction is not suited for performing the validity check of thelenargument - the return value decodes the result of strings comparison. The check can be done in thedlm_search_rsb_tree, however, as long as no otherrsb_cmpcalls are vulnerable to this problem.Beside
dlm_search_rsb_treethersb_cmpfunction is called in two places, both of them invokingrsb_cmpproperly, what is shown below.wait_pending_removekernel-src-tree/fs/dlm/lock.c
Line 1632 in 67edd7c
The only non-zero assignments of
ls->ls_remove_lenare insend_repeat_remove:kernel-src-tree/fs/dlm/lock.c
Line 4052 in 67edd7c
and in
shrink_bucket:kernel-src-tree/fs/dlm/lock.c
Line 1784 in 67edd7c
In
shrink_bucket, beforels->ls_remove_len = lenassignment with potentially wronglen, the functiondlm_search_rsb_treewill be called, and with the newly introduced check it will fail and skip the iteration.kernel-src-tree/fs/dlm/lock.c
Lines 1742 to 1747 in 67edd7c
On the other hand the validity of
ls->ls_remove_len = lenassignment in thesend_repeat_removefunction depends on what this particular function was called with. It's called only inreceive_requestwith the value ofnamelen:kernel-src-tree/fs/dlm/lock.c
Line 4157 in 67edd7c
For the
send_repeat_removeto be called with too bignamelenthe code has to go throughkernel-src-tree/fs/dlm/lock.c
Lines 4100 to 4103 in 67edd7c
(the first two
goto failwill yieldnamelen == 0from its initialization). Butfind_rsbalready checkslenand returns-EINVALkernel-src-tree/fs/dlm/lock.c
Lines 824 to 825 in 67edd7c
Then the
send_repeat_removecall under thefaillabel is not reached becausenamelen && error == -EBADRis false.This means that the
ls->ls_remove_lenpassed asleninwait_pending_removeis never greater thanDLM_RESNAME_MAXLEN.rsb_insertkernel-src-tree/fs/dlm/lock.c
Line 483 in 67edd7c
The
rsb->res_length, passed aslen, is only assigned inget_rsb_structkernel-src-tree/fs/dlm/lock.c
Line 423 in 67edd7c
The
get_rsb_structfunction doesn't modify or checklenin any way, so the check is passed to its callers. It's called in 3 places:find_rsb_dirkernel-src-tree/fs/dlm/lock.c
Line 663 in 67edd7c
find_rsb_nodirkernel-src-tree/fs/dlm/lock.c
Line 795 in 67edd7c
dlm_master_lookupkernel-src-tree/fs/dlm/lock.c
Line 1052 in 67edd7c
In case of the last one the issue is simple, because
lenis checked right at the beginning of the function and not modified later:kernel-src-tree/fs/dlm/lock.c
Lines 1268 to 1269 in 1416bd5
Functions
find_rsb_dirandfind_rsb_nodir, in turn, are always called with the properlen- their only usage is in thefind_rsbfunctionkernel-src-tree/fs/dlm/lock.c
Lines 1081 to 1104 in 1416bd5
CVE-2024-27397
As the
upstream-diffexplains, thenetfilter: nf_tables: use timestamp to check for set element timeoutbackport is actually 3 commits in one:netfilter: nf_tables: use net_generic infra for transaction data,netfilter: nftables: add nft_pernet() helper function,netfilter: nf_tables: use timestamp to check for set element timeout.Commit 0854db2 moves netfilter-related data out of the
netns_nftablesstructkernel-src-tree/include/net/netns/nftables.h
Lines 7 to 16 in 0854db2
into a dynamically associaged net_generic struct
nftables_pernetkernel-src-tree/include/net/netfilter/nf_tables.h
Lines 1571 to 1579 in 0854db2
(Actually the removal of the fields in
netns_nftablescompleting the migration takes place in a separate commit db3685b.) The issue is thatnetns_nftablesis embedded in thenetstruct, heavily used in the data plane:kernel-src-tree/include/net/net_namespace.h
Lines 142 to 144 in 0854db2
The fix of CVE-2024-27397 adds a new
tstampfield tonftables_pernet. This gives 3 backporting options:netstruct in LTS 8.6 has frozen ABI. While multiple fields were added at the endkernel-src-tree/include/net/net_namespace.h
Lines 172 to 176 in 3e82baa
the
netns_nftablesstruct is embedded in the middle. Modifying it in any way breaks kABI. Althoughnftables_pernetcan be introduced without removing the migrated fields fromnetns_nftablesthis would make no technical sense.tstampfield to the old structnetns_nftables. However, this also breaks kABI and none of theRH_KABI_*magic macros can be justified in silencing the checker.nftables_pernetstruct just for thetstampfield alone as part of the CVE-2024-27397 fix, backporting some of the necessary boiler plate code from 0854db2 to make the net_genric infrastructure functional.The last option was chosen.
kABI check: passed
Boot test: passed
boot-test.log
Kselftests: passed relative
Reference
kselftests–ciqlts8_6–run1.log
kselftests–ciqlts8_6–run2.log
Patch
kselftests–ciqlts8_6-CVE-batch-45–run1.log
kselftests–ciqlts8_6-CVE-batch-45–run2.log
kselftests–ciqlts8_6-CVE-batch-45–run3.log
Comparison
The tests results for the reference and the patch are the same.
full-test-results-comparison.log