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

netfilter: ebtables: fix race condition in frame_filter_net_init()

It is possible for ebt_in_hook to be triggered before ebt_table is assigned
resulting in a NULL-pointer dereference. Make sure hooks are
registered as the last step.

Fixes: aee12a0a3727 ("ebtables: remove nf_hook_register usage")
Signed-off-by: Artem Savkov <asavkov@redhat.com>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>

authored by

Artem Savkov and committed by
Pablo Neira Ayuso
e6b72ee8 0d18779b

+19 -17
+4 -3
include/linux/netfilter_bridge/ebtables.h
··· 108 108 109 109 #define EBT_ALIGN(s) (((s) + (__alignof__(struct _xt_align)-1)) & \ 110 110 ~(__alignof__(struct _xt_align)-1)) 111 - extern struct ebt_table *ebt_register_table(struct net *net, 112 - const struct ebt_table *table, 113 - const struct nf_hook_ops *); 111 + extern int ebt_register_table(struct net *net, 112 + const struct ebt_table *table, 113 + const struct nf_hook_ops *ops, 114 + struct ebt_table **res); 114 115 extern void ebt_unregister_table(struct net *net, struct ebt_table *table, 115 116 const struct nf_hook_ops *); 116 117 extern unsigned int ebt_do_table(struct sk_buff *skb,
+2 -2
net/bridge/netfilter/ebtable_broute.c
··· 65 65 66 66 static int __net_init broute_net_init(struct net *net) 67 67 { 68 - net->xt.broute_table = ebt_register_table(net, &broute_table, NULL); 69 - return PTR_ERR_OR_ZERO(net->xt.broute_table); 68 + return ebt_register_table(net, &broute_table, NULL, 69 + &net->xt.broute_table); 70 70 } 71 71 72 72 static void __net_exit broute_net_exit(struct net *net)
+2 -2
net/bridge/netfilter/ebtable_filter.c
··· 93 93 94 94 static int __net_init frame_filter_net_init(struct net *net) 95 95 { 96 - net->xt.frame_filter = ebt_register_table(net, &frame_filter, ebt_ops_filter); 97 - return PTR_ERR_OR_ZERO(net->xt.frame_filter); 96 + return ebt_register_table(net, &frame_filter, ebt_ops_filter, 97 + &net->xt.frame_filter); 98 98 } 99 99 100 100 static void __net_exit frame_filter_net_exit(struct net *net)
+2 -2
net/bridge/netfilter/ebtable_nat.c
··· 93 93 94 94 static int __net_init frame_nat_net_init(struct net *net) 95 95 { 96 - net->xt.frame_nat = ebt_register_table(net, &frame_nat, ebt_ops_nat); 97 - return PTR_ERR_OR_ZERO(net->xt.frame_nat); 96 + return ebt_register_table(net, &frame_nat, ebt_ops_nat, 97 + &net->xt.frame_nat); 98 98 } 99 99 100 100 static void __net_exit frame_nat_net_exit(struct net *net)
+9 -8
net/bridge/netfilter/ebtables.c
··· 1169 1169 kfree(table); 1170 1170 } 1171 1171 1172 - struct ebt_table * 1173 - ebt_register_table(struct net *net, const struct ebt_table *input_table, 1174 - const struct nf_hook_ops *ops) 1172 + int ebt_register_table(struct net *net, const struct ebt_table *input_table, 1173 + const struct nf_hook_ops *ops, struct ebt_table **res) 1175 1174 { 1176 1175 struct ebt_table_info *newinfo; 1177 1176 struct ebt_table *t, *table; ··· 1182 1183 repl->entries == NULL || repl->entries_size == 0 || 1183 1184 repl->counters != NULL || input_table->private != NULL) { 1184 1185 BUGPRINT("Bad table data for ebt_register_table!!!\n"); 1185 - return ERR_PTR(-EINVAL); 1186 + return -EINVAL; 1186 1187 } 1187 1188 1188 1189 /* Don't add one table to multiple lists. */ ··· 1251 1252 list_add(&table->list, &net->xt.tables[NFPROTO_BRIDGE]); 1252 1253 mutex_unlock(&ebt_mutex); 1253 1254 1255 + WRITE_ONCE(*res, table); 1256 + 1254 1257 if (!ops) 1255 - return table; 1258 + return 0; 1256 1259 1257 1260 ret = nf_register_net_hooks(net, ops, hweight32(table->valid_hooks)); 1258 1261 if (ret) { 1259 1262 __ebt_unregister_table(net, table); 1260 - return ERR_PTR(ret); 1263 + *res = NULL; 1261 1264 } 1262 1265 1263 - return table; 1266 + return ret; 1264 1267 free_unlock: 1265 1268 mutex_unlock(&ebt_mutex); 1266 1269 free_chainstack: ··· 1277 1276 free_table: 1278 1277 kfree(table); 1279 1278 out: 1280 - return ERR_PTR(ret); 1279 + return ret; 1281 1280 } 1282 1281 1283 1282 void ebt_unregister_table(struct net *net, struct ebt_table *table,