diff options
author | Xin Long <lucien.xin@gmail.com> | 2016-12-20 06:14:34 -0500 |
---|---|---|
committer | Pablo Neira Ayuso <pablo@netfilter.org> | 2016-12-23 09:20:01 -0500 |
commit | 6c5d5cfbe3c59429e6d6f66477a7609aacf69751 (patch) | |
tree | 19435ae7ae7d2fe3d802de8672a535597eca32ab | |
parent | 053d20f5712529016eae10356e0dea9b360325bd (diff) |
netfilter: ipt_CLUSTERIP: check duplicate config when initializing
Now when adding an ipt_CLUSTERIP rule, it only checks duplicate config in
clusterip_config_find_get(). But after that, there may be still another
thread to insert a config with the same ip, then it leaves proc_create_data
to do duplicate check.
It's more reasonable to check duplicate config by ipt_CLUSTERIP itself,
instead of checking it by proc fs duplicate file check. Before, when proc
fs allowed duplicate name files in a directory, It could even crash kernel
because of use-after-free.
This patch is to check duplicate config under the protection of clusterip
net lock when initializing a new config and correct the return err.
Note that it also moves proc file node creation after adding new config, as
proc_create_data may sleep, it couldn't be called under the clusterip_net
lock. clusterip_config_find_get returns NULL if c->pde is null to make sure
it can't be used until the proc file node creation is done.
Suggested-by: Marcelo Ricardo Leitner <marcelo.leitner@gmail.com>
Signed-off-by: Xin Long <lucien.xin@gmail.com>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
-rw-r--r-- | net/ipv4/netfilter/ipt_CLUSTERIP.c | 34 |
1 files changed, 23 insertions, 11 deletions
diff --git a/net/ipv4/netfilter/ipt_CLUSTERIP.c b/net/ipv4/netfilter/ipt_CLUSTERIP.c index 21db00d0362b..a6b8c1a4102b 100644 --- a/net/ipv4/netfilter/ipt_CLUSTERIP.c +++ b/net/ipv4/netfilter/ipt_CLUSTERIP.c | |||
@@ -144,7 +144,7 @@ clusterip_config_find_get(struct net *net, __be32 clusterip, int entry) | |||
144 | rcu_read_lock_bh(); | 144 | rcu_read_lock_bh(); |
145 | c = __clusterip_config_find(net, clusterip); | 145 | c = __clusterip_config_find(net, clusterip); |
146 | if (c) { | 146 | if (c) { |
147 | if (unlikely(!atomic_inc_not_zero(&c->refcount))) | 147 | if (!c->pde || unlikely(!atomic_inc_not_zero(&c->refcount))) |
148 | c = NULL; | 148 | c = NULL; |
149 | else if (entry) | 149 | else if (entry) |
150 | atomic_inc(&c->entries); | 150 | atomic_inc(&c->entries); |
@@ -166,14 +166,15 @@ clusterip_config_init_nodelist(struct clusterip_config *c, | |||
166 | 166 | ||
167 | static struct clusterip_config * | 167 | static struct clusterip_config * |
168 | clusterip_config_init(const struct ipt_clusterip_tgt_info *i, __be32 ip, | 168 | clusterip_config_init(const struct ipt_clusterip_tgt_info *i, __be32 ip, |
169 | struct net_device *dev) | 169 | struct net_device *dev) |
170 | { | 170 | { |
171 | struct net *net = dev_net(dev); | ||
171 | struct clusterip_config *c; | 172 | struct clusterip_config *c; |
172 | struct clusterip_net *cn = net_generic(dev_net(dev), clusterip_net_id); | 173 | struct clusterip_net *cn = net_generic(net, clusterip_net_id); |
173 | 174 | ||
174 | c = kzalloc(sizeof(*c), GFP_ATOMIC); | 175 | c = kzalloc(sizeof(*c), GFP_ATOMIC); |
175 | if (!c) | 176 | if (!c) |
176 | return NULL; | 177 | return ERR_PTR(-ENOMEM); |
177 | 178 | ||
178 | c->dev = dev; | 179 | c->dev = dev; |
179 | c->clusterip = ip; | 180 | c->clusterip = ip; |
@@ -185,6 +186,17 @@ clusterip_config_init(const struct ipt_clusterip_tgt_info *i, __be32 ip, | |||
185 | atomic_set(&c->refcount, 1); | 186 | atomic_set(&c->refcount, 1); |
186 | atomic_set(&c->entries, 1); | 187 | atomic_set(&c->entries, 1); |
187 | 188 | ||
189 | spin_lock_bh(&cn->lock); | ||
190 | if (__clusterip_config_find(net, ip)) { | ||
191 | spin_unlock_bh(&cn->lock); | ||
192 | kfree(c); | ||
193 | |||
194 | return ERR_PTR(-EBUSY); | ||
195 | } | ||
196 | |||
197 | list_add_rcu(&c->list, &cn->configs); | ||
198 | spin_unlock_bh(&cn->lock); | ||
199 | |||
188 | #ifdef CONFIG_PROC_FS | 200 | #ifdef CONFIG_PROC_FS |
189 | { | 201 | { |
190 | char buffer[16]; | 202 | char buffer[16]; |
@@ -195,16 +207,16 @@ clusterip_config_init(const struct ipt_clusterip_tgt_info *i, __be32 ip, | |||
195 | cn->procdir, | 207 | cn->procdir, |
196 | &clusterip_proc_fops, c); | 208 | &clusterip_proc_fops, c); |
197 | if (!c->pde) { | 209 | if (!c->pde) { |
210 | spin_lock_bh(&cn->lock); | ||
211 | list_del_rcu(&c->list); | ||
212 | spin_unlock_bh(&cn->lock); | ||
198 | kfree(c); | 213 | kfree(c); |
199 | return NULL; | 214 | |
215 | return ERR_PTR(-ENOMEM); | ||
200 | } | 216 | } |
201 | } | 217 | } |
202 | #endif | 218 | #endif |
203 | 219 | ||
204 | spin_lock_bh(&cn->lock); | ||
205 | list_add_rcu(&c->list, &cn->configs); | ||
206 | spin_unlock_bh(&cn->lock); | ||
207 | |||
208 | return c; | 220 | return c; |
209 | } | 221 | } |
210 | 222 | ||
@@ -410,9 +422,9 @@ static int clusterip_tg_check(const struct xt_tgchk_param *par) | |||
410 | 422 | ||
411 | config = clusterip_config_init(cipinfo, | 423 | config = clusterip_config_init(cipinfo, |
412 | e->ip.dst.s_addr, dev); | 424 | e->ip.dst.s_addr, dev); |
413 | if (!config) { | 425 | if (IS_ERR(config)) { |
414 | dev_put(dev); | 426 | dev_put(dev); |
415 | return -ENOMEM; | 427 | return PTR_ERR(config); |
416 | } | 428 | } |
417 | dev_mc_add(config->dev, config->clustermac); | 429 | dev_mc_add(config->dev, config->clustermac); |
418 | } | 430 | } |