| From stable-bounces@linux.kernel.org Sat Feb 10 01:47:53 2007 |
| From: Nick Piggin <npiggin@suse.de> |
| Date: Sat, 10 Feb 2007 01:46:22 -0800 |
| Subject: buffer: memorder fix |
| To: torvalds@linux-foundation.org |
| Cc: npiggin@suse.de, akpm@linux-foundation.org, cmm@us.ibm.com, stable@kernel.org |
| Message-ID: <200702100946.l1A9kM3s009363@shell0.pdx.osdl.net> |
| |
| |
| From: Nick Piggin <npiggin@suse.de> |
| |
| unlock_buffer(), like unlock_page(), must not clear the lock without |
| ensuring that the critical section is closed. |
| |
| |
| Mingming later sent the same patch, saying: |
| |
| We are running SDET benchmark and saw double free issue for ext3 extended |
| attributes block, which complains the same xattr block already being freed (in |
| ext3_xattr_release_block()). The problem could also been triggered by |
| multiple threads loop untar/rm a kernel tree. |
| |
| The race is caused by missing a memory barrier at unlock_buffer() before the |
| lock bit being cleared, resulting in possible concurrent h_refcounter update. |
| That causes a reference counter leak, then later leads to the double free that |
| we have seen. |
| |
| Inside unlock_buffer(), there is a memory barrier is placed *after* the lock |
| bit is being cleared, however, there is no memory barrier *before* the bit is |
| cleared. On some arch the h_refcount update instruction and the clear bit |
| instruction could be reordered, thus leave the critical section re-entered. |
| |
| The race is like this: For example, if the h_refcount is initialized as 1, |
| |
| cpu 0: cpu1 |
| -------------------------------------- ----------------------------------- |
| lock_buffer() /* test_and_set_bit */ |
| clear_buffer_locked(bh); |
| lock_buffer() /* test_and_set_bit */ |
| h_refcount = h_refcount+1; /* = 2*/ h_refcount = h_refcount + 1; /*= 2 */ |
| clear_buffer_locked(bh); |
| .... ...... |
| |
| |
| We lost a h_refcount here. We need a memory barrier before the buffer head |
| lock bit being cleared to force the order of the two writes. Please apply. |
| |
| |
| Signed-off-by: Nick Piggin <npiggin@suse.de> |
| Cc: Mingming Cao <cmm@us.ibm.com> |
| Signed-off-by: Andrew Morton <akpm@linux-foundation.org> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> |
| |
| --- |
| fs/buffer.c | 1 + |
| 1 file changed, 1 insertion(+) |
| |
| --- linux-2.6.20.1.orig/fs/buffer.c |
| +++ linux-2.6.20.1/fs/buffer.c |
| @@ -78,6 +78,7 @@ EXPORT_SYMBOL(__lock_buffer); |
| |
| void fastcall unlock_buffer(struct buffer_head *bh) |
| { |
| + smp_mb__before_clear_bit(); |
| clear_buffer_locked(bh); |
| smp_mb__after_clear_bit(); |
| wake_up_bit(&bh->b_state, BH_Lock); |