]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
netfilter: nat: use kfree_rcu to release ops
authorPablo Neira Ayuso <pablo@netfilter.org>
Wed, 15 Apr 2026 15:29:45 +0000 (17:29 +0200)
committerPablo Neira Ayuso <pablo@netfilter.org>
Mon, 20 Apr 2026 21:45:41 +0000 (23:45 +0200)
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>
net/ipv4/netfilter/iptable_nat.c
net/ipv6/netfilter/ip6table_nat.c
net/netfilter/nf_nat_core.c

index a5db7c67d61befe2a064c82051ff8ac7d65ac590..625a1ca13b1bad060f690e289a41fc2ccc572bb3 100644 (file)
@@ -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)
index e119d4f090cc88d91a81a73be40c76d71e948018..5be723232df8f1cb33a595fb0fc3c3ac66199913 100644 (file)
@@ -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)
index 83b2b5e9759a2bc6d4bbe4d225644aa1e2f8345f..74ec224ce0d63d1bb116d939ec97072c7e0e2b64 100644 (file)
@@ -1222,9 +1222,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;
                }
 
@@ -1288,7 +1290,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);