Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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>

+10 -8
+2 -2
net/ipv4/netfilter/iptable_nat.c
··· 79 79 while (i) 80 80 nf_nat_ipv4_unregister_fn(net, &ops[--i]); 81 81 82 - kfree(ops); 82 + kfree_rcu(ops, rcu); 83 83 return ret; 84 84 } 85 85 } ··· 100 100 for (i = 0; i < ARRAY_SIZE(nf_nat_ipv4_ops); i++) 101 101 nf_nat_ipv4_unregister_fn(net, &ops[i]); 102 102 103 - kfree(ops); 103 + kfree_rcu(ops, rcu); 104 104 } 105 105 106 106 static int iptable_nat_table_init(struct net *net)
+2 -2
net/ipv6/netfilter/ip6table_nat.c
··· 81 81 while (i) 82 82 nf_nat_ipv6_unregister_fn(net, &ops[--i]); 83 83 84 - kfree(ops); 84 + kfree_rcu(ops, rcu); 85 85 return ret; 86 86 } 87 87 } ··· 102 102 for (i = 0; i < ARRAY_SIZE(nf_nat_ipv6_ops); i++) 103 103 nf_nat_ipv6_unregister_fn(net, &ops[i]); 104 104 105 - kfree(ops); 105 + kfree_rcu(ops, rcu); 106 106 } 107 107 108 108 static int ip6table_nat_table_init(struct net *net)
+6 -4
net/netfilter/nf_nat_core.c
··· 1222 1222 ret = nf_register_net_hooks(net, nat_ops, ops_count); 1223 1223 if (ret < 0) { 1224 1224 mutex_unlock(&nf_nat_proto_mutex); 1225 - for (i = 0; i < ops_count; i++) 1226 - kfree(nat_ops[i].priv); 1227 - kfree(nat_ops); 1225 + for (i = 0; i < ops_count; i++) { 1226 + priv = nat_ops[i].priv; 1227 + kfree_rcu(priv, rcu_head); 1228 + } 1229 + kfree_rcu(nat_ops, rcu); 1228 1230 return ret; 1229 1231 } 1230 1232 ··· 1290 1288 } 1291 1289 1292 1290 nat_proto_net->nat_hook_ops = NULL; 1293 - kfree(nat_ops); 1291 + kfree_rcu(nat_ops, rcu); 1294 1292 } 1295 1293 unlock: 1296 1294 mutex_unlock(&nf_nat_proto_mutex);