]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
netfilter: synproxy: add mutex to guard hook reference counting
authorFernando Fernandez Mancera <fmancera@suse.de>
Tue, 26 May 2026 21:58:31 +0000 (23:58 +0200)
committerPablo Neira Ayuso <pablo@netfilter.org>
Mon, 1 Jun 2026 11:43:53 +0000 (13:43 +0200)
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>
net/netfilter/nf_synproxy_core.c

index 036c8586f49b754d0df5a40dedc7085c265585df..ed00114f65f392b61763cb298e6f627829b13be1 100644 (file)
@@ -22,6 +22,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);
 
@@ -769,26 +771,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);
 
@@ -1193,27 +1200,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 */