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

netfilter: conntrack: fix boot failure with nf_conntrack.enable_hooks=1

This is a revert of
7b1957b049 ("netfilter: nf_defrag_ipv4: use net_generic infra")
and a partial revert of
8b0adbe3e3 ("netfilter: nf_defrag_ipv6: use net_generic infra").

If conntrack is builtin and kernel is booted with:
nf_conntrack.enable_hooks=1

.... kernel will fail to boot due to a NULL deref in
nf_defrag_ipv4_enable(): Its called before the ipv4 defrag initcall is
made, so net_generic() returns NULL.

To resolve this, move the user refcount back to struct net so calls
to those functions are possible even before their initcalls have run.

Fixes: 7b1957b04956 ("netfilter: nf_defrag_ipv4: use net_generic infra")
Fixes: 8b0adbe3e38d ("netfilter: nf_defrag_ipv6: use net_generic infra").
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>

authored by

Florian Westphal and committed by
Pablo Neira Ayuso
339031ba 3b1b6e82

+25 -39
-1
include/net/netfilter/ipv6/nf_defrag_ipv6.h
··· 17 17 struct nft_ct_frag6_pernet { 18 18 struct ctl_table_header *nf_frag_frags_hdr; 19 19 struct fqdir *fqdir; 20 - unsigned int users; 21 20 }; 22 21 23 22 #endif /* _NF_DEFRAG_IPV6_H */
+6
include/net/netns/netfilter.h
··· 27 27 #if IS_ENABLED(CONFIG_DECNET) 28 28 struct nf_hook_entries __rcu *hooks_decnet[NF_DN_NUMHOOKS]; 29 29 #endif 30 + #if IS_ENABLED(CONFIG_NF_DEFRAG_IPV4) 31 + unsigned int defrag_ipv4_users; 32 + #endif 33 + #if IS_ENABLED(CONFIG_NF_DEFRAG_IPV6) 34 + unsigned int defrag_ipv6_users; 35 + #endif 30 36 }; 31 37 #endif
+9 -21
net/ipv4/netfilter/nf_defrag_ipv4.c
··· 20 20 #endif 21 21 #include <net/netfilter/nf_conntrack_zones.h> 22 22 23 - static unsigned int defrag4_pernet_id __read_mostly; 24 23 static DEFINE_MUTEX(defrag4_mutex); 25 - 26 - struct defrag4_pernet { 27 - unsigned int users; 28 - }; 29 24 30 25 static int nf_ct_ipv4_gather_frags(struct net *net, struct sk_buff *skb, 31 26 u_int32_t user) ··· 106 111 107 112 static void __net_exit defrag4_net_exit(struct net *net) 108 113 { 109 - struct defrag4_pernet *nf_defrag = net_generic(net, defrag4_pernet_id); 110 - 111 - if (nf_defrag->users) { 114 + if (net->nf.defrag_ipv4_users) { 112 115 nf_unregister_net_hooks(net, ipv4_defrag_ops, 113 116 ARRAY_SIZE(ipv4_defrag_ops)); 114 - nf_defrag->users = 0; 117 + net->nf.defrag_ipv4_users = 0; 115 118 } 116 119 } 117 120 118 121 static struct pernet_operations defrag4_net_ops = { 119 122 .exit = defrag4_net_exit, 120 - .id = &defrag4_pernet_id, 121 - .size = sizeof(struct defrag4_pernet), 122 123 }; 123 124 124 125 static int __init nf_defrag_init(void) ··· 129 138 130 139 int nf_defrag_ipv4_enable(struct net *net) 131 140 { 132 - struct defrag4_pernet *nf_defrag = net_generic(net, defrag4_pernet_id); 133 141 int err = 0; 134 142 135 143 mutex_lock(&defrag4_mutex); 136 - if (nf_defrag->users == UINT_MAX) { 144 + if (net->nf.defrag_ipv4_users == UINT_MAX) { 137 145 err = -EOVERFLOW; 138 146 goto out_unlock; 139 147 } 140 148 141 - if (nf_defrag->users) { 142 - nf_defrag->users++; 149 + if (net->nf.defrag_ipv4_users) { 150 + net->nf.defrag_ipv4_users++; 143 151 goto out_unlock; 144 152 } 145 153 146 154 err = nf_register_net_hooks(net, ipv4_defrag_ops, 147 155 ARRAY_SIZE(ipv4_defrag_ops)); 148 156 if (err == 0) 149 - nf_defrag->users = 1; 157 + net->nf.defrag_ipv4_users = 1; 150 158 151 159 out_unlock: 152 160 mutex_unlock(&defrag4_mutex); ··· 155 165 156 166 void nf_defrag_ipv4_disable(struct net *net) 157 167 { 158 - struct defrag4_pernet *nf_defrag = net_generic(net, defrag4_pernet_id); 159 - 160 168 mutex_lock(&defrag4_mutex); 161 - if (nf_defrag->users) { 162 - nf_defrag->users--; 163 - if (nf_defrag->users == 0) 169 + if (net->nf.defrag_ipv4_users) { 170 + net->nf.defrag_ipv4_users--; 171 + if (net->nf.defrag_ipv4_users == 0) 164 172 nf_unregister_net_hooks(net, ipv4_defrag_ops, 165 173 ARRAY_SIZE(ipv4_defrag_ops)); 166 174 }
+1 -1
net/ipv6/netfilter/nf_conntrack_reasm.c
··· 33 33 34 34 static const char nf_frags_cache_name[] = "nf-frags"; 35 35 36 - unsigned int nf_frag_pernet_id __read_mostly; 36 + static unsigned int nf_frag_pernet_id __read_mostly; 37 37 static struct inet_frags nf_frags; 38 38 39 39 static struct nft_ct_frag6_pernet *nf_frag_pernet(struct net *net)
+9 -16
net/ipv6/netfilter/nf_defrag_ipv6_hooks.c
··· 25 25 #include <net/netfilter/nf_conntrack_zones.h> 26 26 #include <net/netfilter/ipv6/nf_defrag_ipv6.h> 27 27 28 - extern unsigned int nf_frag_pernet_id; 29 - 30 28 static DEFINE_MUTEX(defrag6_mutex); 31 29 32 30 static enum ip6_defrag_users nf_ct6_defrag_user(unsigned int hooknum, ··· 89 91 90 92 static void __net_exit defrag6_net_exit(struct net *net) 91 93 { 92 - struct nft_ct_frag6_pernet *nf_frag = net_generic(net, nf_frag_pernet_id); 93 - 94 - if (nf_frag->users) { 94 + if (net->nf.defrag_ipv6_users) { 95 95 nf_unregister_net_hooks(net, ipv6_defrag_ops, 96 96 ARRAY_SIZE(ipv6_defrag_ops)); 97 - nf_frag->users = 0; 97 + net->nf.defrag_ipv6_users = 0; 98 98 } 99 99 } 100 100 ··· 130 134 131 135 int nf_defrag_ipv6_enable(struct net *net) 132 136 { 133 - struct nft_ct_frag6_pernet *nf_frag = net_generic(net, nf_frag_pernet_id); 134 137 int err = 0; 135 138 136 139 mutex_lock(&defrag6_mutex); 137 - if (nf_frag->users == UINT_MAX) { 140 + if (net->nf.defrag_ipv6_users == UINT_MAX) { 138 141 err = -EOVERFLOW; 139 142 goto out_unlock; 140 143 } 141 144 142 - if (nf_frag->users) { 143 - nf_frag->users++; 145 + if (net->nf.defrag_ipv6_users) { 146 + net->nf.defrag_ipv6_users++; 144 147 goto out_unlock; 145 148 } 146 149 147 150 err = nf_register_net_hooks(net, ipv6_defrag_ops, 148 151 ARRAY_SIZE(ipv6_defrag_ops)); 149 152 if (err == 0) 150 - nf_frag->users = 1; 153 + net->nf.defrag_ipv6_users = 1; 151 154 152 155 out_unlock: 153 156 mutex_unlock(&defrag6_mutex); ··· 156 161 157 162 void nf_defrag_ipv6_disable(struct net *net) 158 163 { 159 - struct nft_ct_frag6_pernet *nf_frag = net_generic(net, nf_frag_pernet_id); 160 - 161 164 mutex_lock(&defrag6_mutex); 162 - if (nf_frag->users) { 163 - nf_frag->users--; 164 - if (nf_frag->users == 0) 165 + if (net->nf.defrag_ipv6_users) { 166 + net->nf.defrag_ipv6_users--; 167 + if (net->nf.defrag_ipv6_users == 0) 165 168 nf_unregister_net_hooks(net, ipv6_defrag_ops, 166 169 ARRAY_SIZE(ipv6_defrag_ops)); 167 170 }