diff options
author | Nick Piggin <npiggin@suse.de> | 2007-02-10 01:46:22 -0800 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2007-03-09 10:50:30 -0800 |
commit | 54710f60049c613cd0ae4bb0d3180f8bfb8c323c (patch) | |
tree | a5b0ca8f6ed51afb56568f4fb2152346928e4f56 /fs | |
parent | 5556eaffcc2704d9dca81c80cf3e7cbcc869f680 (diff) |
buffer: memorder fix
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
Diffstat (limited to 'fs')
-rw-r--r-- | fs/buffer.c | 1 |
1 files changed, 1 insertions, 0 deletions
diff --git a/fs/buffer.c b/fs/buffer.c index 1ad674fd348..05c5d02e000 100644 --- a/fs/buffer.c +++ b/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); |