diff options
| author | Jakub Kicinski <kuba@kernel.org> | 2026-09-14 17:06:52 -0700 |
|---|---|---|
| committer | Jakub Kicinski <kuba@kernel.org> | 2026-09-14 17:06:52 -0700 |
| commit | 6c21ebc81338dd8abe021dec3d01acebd789ea2c (patch) | |
| tree | 2a00925dd85ee70571bc7105721b629c3692dce0 | |
| parent | e6b6078ea1731b05b3b552497b3bce4bf8b014ae (diff) | |
| parent | e75a9fa1d44bcbd66ea02e8781bcca6ea4076e0d (diff) | |
| download | linux-next-6c21ebc81338dd8abe021dec3d01acebd789ea2c.tar.gz linux-next-6c21ebc81338dd8abe021dec3d01acebd789ea2c.zip | |
Merge tag 'nf-26-09-11' of git://git.kernel.org/pub/scm/linux/kernel/git/netfilter/nf
Pablo Neira Ayuso says:
====================
Netfilter fixes for net
1) Fix KMSAN reports an uninit-value in nf_nat_setup_info() for netmap,
from Theodor Arsenij Larionov Trichkine.
2) Restrict deletion of netdevice in basechain and flowtable to exact
matching only, from Fernando F. Mancera.
3) Fix nf_nat_register_fn() error path allowing for a memleak.
4) Hold reference on ct until flow is released to address, otherwise
access to release ct->ext or different ct due to typesafe RCU
semantics.
* tag 'nf-26-09-11' of git://git.kernel.org/pub/scm/linux/kernel/git/netfilter/nf:
netfilter: flowtable: hold reference on ct until flow is released
netfilter: nf_nat: unregister and release hooks on error
netfilter: nf_tables: fix device name and prefix match in hook lookup
netfilter: nft_nat: fully initialise new_addr in netmap setup
====================
Link: https://patch.msgid.link/20260913205447.1889203-1-pablo@netfilter.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
| -rw-r--r-- | net/netfilter/nf_flow_table_core.c | 12 | ||||
| -rw-r--r-- | net/netfilter/nf_nat_core.c | 46 | ||||
| -rw-r--r-- | net/netfilter/nf_tables_api.c | 22 | ||||
| -rw-r--r-- | net/netfilter/nft_nat.c | 2 |
4 files changed, 54 insertions, 28 deletions
diff --git a/net/netfilter/nf_flow_table_core.c b/net/netfilter/nf_flow_table_core.c index 03241d4bfd5e..934c6151f558 100644 --- a/net/netfilter/nf_flow_table_core.c +++ b/net/netfilter/nf_flow_table_core.c @@ -258,6 +258,14 @@ static void flow_offload_route_release(struct flow_offload *flow) nft_flow_dst_release(flow, FLOW_OFFLOAD_DIR_REPLY); } +static void flow_offload_free_rcu(struct rcu_head *rcu_head) +{ + struct flow_offload *flow = container_of(rcu_head, struct flow_offload, rcu_head); + + nf_ct_put(flow->ct); + kfree(flow); +} + void flow_offload_free(struct flow_offload *flow) { switch (flow->type) { @@ -267,8 +275,7 @@ void flow_offload_free(struct flow_offload *flow) default: break; } - nf_ct_put(flow->ct); - kfree_rcu(flow, rcu_head); + call_rcu(&flow->rcu_head, flow_offload_free_rcu); } EXPORT_SYMBOL_GPL(flow_offload_free); @@ -854,6 +861,7 @@ out_pernet: static void __exit nf_flow_table_module_exit(void) { + rcu_barrier(); nf_flow_table_offload_exit(); unregister_pernet_subsys(&nf_flow_table_net_ops); kmem_cache_destroy(flow_offload_cachep); diff --git a/net/netfilter/nf_nat_core.c b/net/netfilter/nf_nat_core.c index 8ac326e1eb5b..a4858c2b2d65 100644 --- a/net/netfilter/nf_nat_core.c +++ b/net/netfilter/nf_nat_core.c @@ -1224,31 +1224,45 @@ 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++) { - priv = nat_ops[i].priv; - kfree_rcu(priv, rcu_head); - } - kfree_rcu(nat_ops, rcu); - return ret; - } - - nat_proto_net->nat_hook_ops = nat_ops; + if (ret < 0) + goto err_free_hooks; + } else { + nat_ops = nat_proto_net->nat_hook_ops; } - nat_ops = nat_proto_net->nat_hook_ops; priv = nat_ops[hooknum].priv; if (WARN_ON_ONCE(!priv)) { - mutex_unlock(&nf_nat_proto_mutex); - return -EOPNOTSUPP; + ret = -EOPNOTSUPP; + goto err_unregister_hooks; } ret = nf_hook_entries_insert_raw(&priv->entries, ops); - if (ret == 0) - nat_proto_net->users++; + if (ret) + goto err_unregister_hooks; + + if (!nat_proto_net->nat_hook_ops) + nat_proto_net->nat_hook_ops = nat_ops; + + nat_proto_net->users++; mutex_unlock(&nf_nat_proto_mutex); + + return 0; + +err_unregister_hooks: + if (nat_proto_net->nat_hook_ops) { + mutex_unlock(&nf_nat_proto_mutex); + return ret; + } + nf_unregister_net_hooks(net, nat_ops, ops_count); +err_free_hooks: + mutex_unlock(&nf_nat_proto_mutex); + for (i = 0; i < ops_count; i++) { + priv = nat_ops[i].priv; + kfree_rcu(priv, rcu_head); + } + kfree_rcu(nat_ops, rcu); + return ret; } diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index 31fbd5a28937..c0b754a2d45b 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -2440,11 +2440,14 @@ err_hook_free: } static struct nft_hook *nft_hook_list_find(struct list_head *hook_list, - const struct nft_hook *this) + const struct nft_hook *this, + bool strict) { struct nft_hook *hook; list_for_each_entry(hook, hook_list, list) { + if (strict && hook->ifnamelen != this->ifnamelen) + continue; if (!strncmp(hook->ifname, this->ifname, min(hook->ifnamelen, this->ifnamelen))) { if (hook->flags & NFT_HOOK_REMOVE) @@ -2486,7 +2489,7 @@ static int nf_tables_parse_netdev_hooks(struct net *net, err = PTR_ERR(hook); goto err_hook; } - if (nft_hook_list_find(hook_list, hook)) { + if (nft_hook_list_find(hook_list, hook, false)) { NL_SET_BAD_ATTR(extack, tmp); nft_netdev_hook_free(hook); err = -EEXIST; @@ -2943,7 +2946,7 @@ static int nf_tables_updchain(struct nft_ctx *ctx, u8 genmask, u8 policy, ops->hook = basechain->ops.hook; } - if (nft_hook_list_find(&basechain->hook_list, h)) { + if (nft_hook_list_find(&basechain->hook_list, h, false)) { list_del(&h->list); nft_netdev_hook_free(h); continue; @@ -2956,7 +2959,8 @@ static int nf_tables_updchain(struct nft_ctx *ctx, u8 genmask, u8 policy, !nft_trans_chain_update(trans)) continue; - if (nft_hook_list_find(&nft_trans_chain_hooks(trans), h)) { + if (nft_hook_list_find(&nft_trans_chain_hooks(trans), + h, false)) { nft_chain_release_hook(&hook); return -EEXIST; } @@ -3257,7 +3261,7 @@ static int nft_delchain_hook(struct nft_ctx *ctx, return err; list_for_each_entry(this, &chain_hook.list, list) { - hook = nft_hook_list_find(&basechain->hook_list, this); + hook = nft_hook_list_find(&basechain->hook_list, this, true); if (!hook) { err = -ENOENT; goto err_chain_del_hook; @@ -9053,7 +9057,7 @@ static int nft_register_flowtable_net_hooks(struct net *net, if (!nft_is_active_next(net, ft)) continue; - if (nft_hook_list_find(&ft->hook_list, hook)) { + if (nft_hook_list_find(&ft->hook_list, hook, false)) { err = -EEXIST; goto err_unregister_net_hooks; } @@ -9130,7 +9134,7 @@ static int nft_flowtable_update(struct nft_ctx *ctx, const struct nlmsghdr *nlh, return err; list_for_each_entry_safe(hook, next, &flowtable_hook.list, list) { - if (nft_hook_list_find(&flowtable->hook_list, hook)) { + if (nft_hook_list_find(&flowtable->hook_list, hook, false)) { list_del(&hook->list); nft_netdev_hook_free(hook); continue; @@ -9143,7 +9147,7 @@ static int nft_flowtable_update(struct nft_ctx *ctx, const struct nlmsghdr *nlh, !nft_trans_flowtable_update(trans)) continue; - if (nft_hook_list_find(&nft_trans_flowtable_hooks(trans), hook)) { + if (nft_hook_list_find(&nft_trans_flowtable_hooks(trans), hook, false)) { err = -EEXIST; goto err_flowtable_update_hook; } @@ -9363,7 +9367,7 @@ static int nft_delflowtable_hook(struct nft_ctx *ctx, return err; list_for_each_entry(this, &flowtable_hook.list, list) { - hook = nft_hook_list_find(&flowtable->hook_list, this); + hook = nft_hook_list_find(&flowtable->hook_list, this, true); if (!hook) { err = -ENOENT; goto err_flowtable_del_hook; diff --git a/net/netfilter/nft_nat.c b/net/netfilter/nft_nat.c index e32cd9fbc7c2..cdbd800cac96 100644 --- a/net/netfilter/nft_nat.c +++ b/net/netfilter/nft_nat.c @@ -64,8 +64,8 @@ static void nft_nat_setup_netmap(struct nf_nat_range2 *range, const struct nft_pktinfo *pkt, const struct nft_nat *priv) { + union nf_inet_addr new_addr = {}; struct sk_buff *skb = pkt->skb; - union nf_inet_addr new_addr; __be32 netmask; int i, len = 0; |
