Commit 49636bb12892786e4a7b207b37ca7b0c5ca1cae0

Authored by Herbert Xu
1 parent 6fb9974f49

[NEIGH] Fix timer leak in neigh_changeaddr

neigh_changeaddr attempts to delete neighbour timers without setting
nud_state.  This doesn't work because the timer may have already fired
when we acquire the write lock in neigh_changeaddr.  The result is that
the timer may keep firing for quite a while until the entry reaches
NEIGH_FAILED.

It should be setting the nud_state straight away so that if the timer
has already fired it can simply exit once we relinquish the lock.

In fact, this whole function is simply duplicating the logic in
neigh_ifdown which in turn is already doing the right thing when
it comes to deleting timers and setting nud_state.

So all we have to do is take that code out and put it into a common
function and make both neigh_changeaddr and neigh_ifdown call it.

Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>

Showing 1 changed file with 13 additions and 30 deletions Side-by-side Diff

net/core/neighbour.c
... ... @@ -175,39 +175,10 @@
175 175 }
176 176 }
177 177  
178   -void neigh_changeaddr(struct neigh_table *tbl, struct net_device *dev)
  178 +static void neigh_flush_dev(struct neigh_table *tbl, struct net_device *dev)
179 179 {
180 180 int i;
181 181  
182   - write_lock_bh(&tbl->lock);
183   -
184   - for (i=0; i <= tbl->hash_mask; i++) {
185   - struct neighbour *n, **np;
186   -
187   - np = &tbl->hash_buckets[i];
188   - while ((n = *np) != NULL) {
189   - if (dev && n->dev != dev) {
190   - np = &n->next;
191   - continue;
192   - }
193   - *np = n->next;
194   - write_lock_bh(&n->lock);
195   - n->dead = 1;
196   - neigh_del_timer(n);
197   - write_unlock_bh(&n->lock);
198   - neigh_release(n);
199   - }
200   - }
201   -
202   - write_unlock_bh(&tbl->lock);
203   -}
204   -
205   -int neigh_ifdown(struct neigh_table *tbl, struct net_device *dev)
206   -{
207   - int i;
208   -
209   - write_lock_bh(&tbl->lock);
210   -
211 182 for (i = 0; i <= tbl->hash_mask; i++) {
212 183 struct neighbour *n, **np = &tbl->hash_buckets[i];
213 184  
214 185  
... ... @@ -243,7 +214,19 @@
243 214 neigh_release(n);
244 215 }
245 216 }
  217 +}
246 218  
  219 +void neigh_changeaddr(struct neigh_table *tbl, struct net_device *dev)
  220 +{
  221 + write_lock_bh(&tbl->lock);
  222 + neigh_flush_dev(tbl, dev);
  223 + write_unlock_bh(&tbl->lock);
  224 +}
  225 +
  226 +int neigh_ifdown(struct neigh_table *tbl, struct net_device *dev)
  227 +{
  228 + write_lock_bh(&tbl->lock);
  229 + neigh_flush_dev(tbl, dev);
247 230 pneigh_ifdown(tbl, dev);
248 231 write_unlock_bh(&tbl->lock);
249 232