diff options
author | Artem Savkov <asavkov@redhat.com> | 2017-09-26 12:35:45 -0400 |
---|---|---|
committer | Pablo Neira Ayuso <pablo@netfilter.org> | 2017-09-29 07:36:06 -0400 |
commit | e6b72ee88a56bcfe63f72e9c30766484c45bec72 (patch) | |
tree | 6b3cafcc56342070fa0da916ddbb9672138411f1 /net | |
parent | 0d18779be13766b33c69cbc26df38383598da373 (diff) |
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>
Diffstat (limited to 'net')
-rw-r--r-- | net/bridge/netfilter/ebtable_broute.c | 4 | ||||
-rw-r--r-- | net/bridge/netfilter/ebtable_filter.c | 4 | ||||
-rw-r--r-- | net/bridge/netfilter/ebtable_nat.c | 4 | ||||
-rw-r--r-- | net/bridge/netfilter/ebtables.c | 17 |
4 files changed, 15 insertions, 14 deletions
diff --git a/net/bridge/netfilter/ebtable_broute.c b/net/bridge/netfilter/ebtable_broute.c index 2585b100ebbb..276b60262981 100644 --- a/net/bridge/netfilter/ebtable_broute.c +++ b/net/bridge/netfilter/ebtable_broute.c | |||
@@ -65,8 +65,8 @@ static int ebt_broute(struct sk_buff *skb) | |||
65 | 65 | ||
66 | static int __net_init broute_net_init(struct net *net) | 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); | 68 | return ebt_register_table(net, &broute_table, NULL, |
69 | return PTR_ERR_OR_ZERO(net->xt.broute_table); | 69 | &net->xt.broute_table); |
70 | } | 70 | } |
71 | 71 | ||
72 | static void __net_exit broute_net_exit(struct net *net) | 72 | static void __net_exit broute_net_exit(struct net *net) |
diff --git a/net/bridge/netfilter/ebtable_filter.c b/net/bridge/netfilter/ebtable_filter.c index 45a00dbdbcad..c41da5fac84f 100644 --- a/net/bridge/netfilter/ebtable_filter.c +++ b/net/bridge/netfilter/ebtable_filter.c | |||
@@ -93,8 +93,8 @@ static const struct nf_hook_ops ebt_ops_filter[] = { | |||
93 | 93 | ||
94 | static int __net_init frame_filter_net_init(struct net *net) | 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); | 96 | return ebt_register_table(net, &frame_filter, ebt_ops_filter, |
97 | return PTR_ERR_OR_ZERO(net->xt.frame_filter); | 97 | &net->xt.frame_filter); |
98 | } | 98 | } |
99 | 99 | ||
100 | static void __net_exit frame_filter_net_exit(struct net *net) | 100 | static void __net_exit frame_filter_net_exit(struct net *net) |
diff --git a/net/bridge/netfilter/ebtable_nat.c b/net/bridge/netfilter/ebtable_nat.c index 57cd5bb154e7..08df7406ecb3 100644 --- a/net/bridge/netfilter/ebtable_nat.c +++ b/net/bridge/netfilter/ebtable_nat.c | |||
@@ -93,8 +93,8 @@ static const struct nf_hook_ops ebt_ops_nat[] = { | |||
93 | 93 | ||
94 | static int __net_init frame_nat_net_init(struct net *net) | 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); | 96 | return ebt_register_table(net, &frame_nat, ebt_ops_nat, |
97 | return PTR_ERR_OR_ZERO(net->xt.frame_nat); | 97 | &net->xt.frame_nat); |
98 | } | 98 | } |
99 | 99 | ||
100 | static void __net_exit frame_nat_net_exit(struct net *net) | 100 | static void __net_exit frame_nat_net_exit(struct net *net) |
diff --git a/net/bridge/netfilter/ebtables.c b/net/bridge/netfilter/ebtables.c index 83951f978445..3b3dcf719e07 100644 --- a/net/bridge/netfilter/ebtables.c +++ b/net/bridge/netfilter/ebtables.c | |||
@@ -1169,9 +1169,8 @@ static void __ebt_unregister_table(struct net *net, struct ebt_table *table) | |||
1169 | kfree(table); | 1169 | kfree(table); |
1170 | } | 1170 | } |
1171 | 1171 | ||
1172 | struct ebt_table * | 1172 | int ebt_register_table(struct net *net, const struct ebt_table *input_table, |
1173 | ebt_register_table(struct net *net, const struct ebt_table *input_table, | 1173 | const struct nf_hook_ops *ops, struct ebt_table **res) |
1174 | const struct nf_hook_ops *ops) | ||
1175 | { | 1174 | { |
1176 | struct ebt_table_info *newinfo; | 1175 | struct ebt_table_info *newinfo; |
1177 | struct ebt_table *t, *table; | 1176 | struct ebt_table *t, *table; |
@@ -1183,7 +1182,7 @@ ebt_register_table(struct net *net, const struct ebt_table *input_table, | |||
1183 | repl->entries == NULL || repl->entries_size == 0 || | 1182 | repl->entries == NULL || repl->entries_size == 0 || |
1184 | repl->counters != NULL || input_table->private != NULL) { | 1183 | repl->counters != NULL || input_table->private != NULL) { |
1185 | BUGPRINT("Bad table data for ebt_register_table!!!\n"); | 1184 | BUGPRINT("Bad table data for ebt_register_table!!!\n"); |
1186 | return ERR_PTR(-EINVAL); | 1185 | return -EINVAL; |
1187 | } | 1186 | } |
1188 | 1187 | ||
1189 | /* Don't add one table to multiple lists. */ | 1188 | /* Don't add one table to multiple lists. */ |
@@ -1252,16 +1251,18 @@ ebt_register_table(struct net *net, const struct ebt_table *input_table, | |||
1252 | list_add(&table->list, &net->xt.tables[NFPROTO_BRIDGE]); | 1251 | list_add(&table->list, &net->xt.tables[NFPROTO_BRIDGE]); |
1253 | mutex_unlock(&ebt_mutex); | 1252 | mutex_unlock(&ebt_mutex); |
1254 | 1253 | ||
1254 | WRITE_ONCE(*res, table); | ||
1255 | |||
1255 | if (!ops) | 1256 | if (!ops) |
1256 | return table; | 1257 | return 0; |
1257 | 1258 | ||
1258 | ret = nf_register_net_hooks(net, ops, hweight32(table->valid_hooks)); | 1259 | ret = nf_register_net_hooks(net, ops, hweight32(table->valid_hooks)); |
1259 | if (ret) { | 1260 | if (ret) { |
1260 | __ebt_unregister_table(net, table); | 1261 | __ebt_unregister_table(net, table); |
1261 | return ERR_PTR(ret); | 1262 | *res = NULL; |
1262 | } | 1263 | } |
1263 | 1264 | ||
1264 | return table; | 1265 | return ret; |
1265 | free_unlock: | 1266 | free_unlock: |
1266 | mutex_unlock(&ebt_mutex); | 1267 | mutex_unlock(&ebt_mutex); |
1267 | free_chainstack: | 1268 | free_chainstack: |
@@ -1276,7 +1277,7 @@ free_newinfo: | |||
1276 | free_table: | 1277 | free_table: |
1277 | kfree(table); | 1278 | kfree(table); |
1278 | out: | 1279 | out: |
1279 | return ERR_PTR(ret); | 1280 | return ret; |
1280 | } | 1281 | } |
1281 | 1282 | ||
1282 | void ebt_unregister_table(struct net *net, struct ebt_table *table, | 1283 | void ebt_unregister_table(struct net *net, struct ebt_table *table, |