| From stable-bounces@linux.kernel.org Thu Aug 17 22:57:51 2006 |
| Date: Thu, 17 Aug 2006 22:57:22 -0700 (PDT) |
| Message-Id: <20060817.225722.41634450.davem@davemloft.net> |
| To: stable@kernel.org |
| From: David Miller <davem@davemloft.net> |
| Subject: Fix ipv4 routing locking bug |
| |
| |
| From: Alexey Kuznetsov <kuznet@ms2.inr.ac.ru> |
| |
| [IPV4]: severe locking bug in fib_semantics.c |
| |
| Found in 2.4 by Yixin Pan <yxpan@hotmail.com>. |
| |
| > When I read fib_semantics.c of Linux-2.4.32, write_lock(&fib_info_lock) = |
| > is used in fib_release_info() instead of write_lock_bh(&fib_info_lock). = |
| > Is the following case possible: a BH interrupts fib_release_info() while = |
| > holding the write lock, and calls ip_check_fib_default() which calls = |
| > read_lock(&fib_info_lock), and spin forever. |
| |
| Signed-off-by: Alexey Kuznetsov <kuznet@ms2.inr.ac.ru> |
| Signed-off-by: David S. Miller <davem@davemloft.net> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> |
| |
| --- |
| net/ipv4/fib_semantics.c | 12 ++++++------ |
| 1 file changed, 6 insertions(+), 6 deletions(-) |
| |
| --- linux-2.6.17.9.orig/net/ipv4/fib_semantics.c |
| +++ linux-2.6.17.9/net/ipv4/fib_semantics.c |
| @@ -160,7 +160,7 @@ void free_fib_info(struct fib_info *fi) |
| |
| void fib_release_info(struct fib_info *fi) |
| { |
| - write_lock(&fib_info_lock); |
| + write_lock_bh(&fib_info_lock); |
| if (fi && --fi->fib_treeref == 0) { |
| hlist_del(&fi->fib_hash); |
| if (fi->fib_prefsrc) |
| @@ -173,7 +173,7 @@ void fib_release_info(struct fib_info *f |
| fi->fib_dead = 1; |
| fib_info_put(fi); |
| } |
| - write_unlock(&fib_info_lock); |
| + write_unlock_bh(&fib_info_lock); |
| } |
| |
| static __inline__ int nh_comp(const struct fib_info *fi, const struct fib_info *ofi) |
| @@ -599,7 +599,7 @@ static void fib_hash_move(struct hlist_h |
| unsigned int old_size = fib_hash_size; |
| unsigned int i, bytes; |
| |
| - write_lock(&fib_info_lock); |
| + write_lock_bh(&fib_info_lock); |
| old_info_hash = fib_info_hash; |
| old_laddrhash = fib_info_laddrhash; |
| fib_hash_size = new_size; |
| @@ -640,7 +640,7 @@ static void fib_hash_move(struct hlist_h |
| } |
| fib_info_laddrhash = new_laddrhash; |
| |
| - write_unlock(&fib_info_lock); |
| + write_unlock_bh(&fib_info_lock); |
| |
| bytes = old_size * sizeof(struct hlist_head *); |
| fib_hash_free(old_info_hash, bytes); |
| @@ -822,7 +822,7 @@ link_it: |
| |
| fi->fib_treeref++; |
| atomic_inc(&fi->fib_clntref); |
| - write_lock(&fib_info_lock); |
| + write_lock_bh(&fib_info_lock); |
| hlist_add_head(&fi->fib_hash, |
| &fib_info_hash[fib_info_hashfn(fi)]); |
| if (fi->fib_prefsrc) { |
| @@ -841,7 +841,7 @@ link_it: |
| head = &fib_info_devhash[hash]; |
| hlist_add_head(&nh->nh_hash, head); |
| } endfor_nexthops(fi) |
| - write_unlock(&fib_info_lock); |
| + write_unlock_bh(&fib_info_lock); |
| return fi; |
| |
| err_inval: |