aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJan Kara <jack@suse.cz>2009-06-09 19:26:26 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2009-06-09 19:59:03 -0400
commita61d90d75d0f9e86432c45b496b4b0fbf0fd03dc (patch)
tree85d557e9d67cbad0347c6f12f7a60c474e485f7c
parent463aea1a1c49f1a7d4b50656dfd6c8bb33358b1b (diff)
jbd: fix race in buffer processing in commit code
In commit code, we scan buffers attached to a transaction. During this scan, we sometimes have to drop j_list_lock and then we recheck whether the journal buffer head didn't get freed by journal_try_to_free_buffers(). But checking for buffer_jbd(bh) isn't enough because a new journal head could get attached to our buffer head. So add a check whether the journal head remained the same and whether it's still at the same transaction and list. This is a nasty bug and can cause problems like memory corruption (use after free) or trigger various assertions in JBD code (observed). Signed-off-by: Jan Kara <jack@suse.cz> Cc: <stable@kernel.org> Cc: <linux-ext4@vger.kernel.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r--fs/jbd/commit.c6
1 files changed, 4 insertions, 2 deletions
diff --git a/fs/jbd/commit.c b/fs/jbd/commit.c
index 06560c520f49..618e21c0b7a3 100644
--- a/fs/jbd/commit.c
+++ b/fs/jbd/commit.c
@@ -241,7 +241,7 @@ write_out_data:
241 spin_lock(&journal->j_list_lock); 241 spin_lock(&journal->j_list_lock);
242 } 242 }
243 /* Someone already cleaned up the buffer? */ 243 /* Someone already cleaned up the buffer? */
244 if (!buffer_jbd(bh) 244 if (!buffer_jbd(bh) || bh2jh(bh) != jh
245 || jh->b_transaction != commit_transaction 245 || jh->b_transaction != commit_transaction
246 || jh->b_jlist != BJ_SyncData) { 246 || jh->b_jlist != BJ_SyncData) {
247 jbd_unlock_bh_state(bh); 247 jbd_unlock_bh_state(bh);
@@ -478,7 +478,9 @@ void journal_commit_transaction(journal_t *journal)
478 spin_lock(&journal->j_list_lock); 478 spin_lock(&journal->j_list_lock);
479 continue; 479 continue;
480 } 480 }
481 if (buffer_jbd(bh) && jh->b_jlist == BJ_Locked) { 481 if (buffer_jbd(bh) && bh2jh(bh) == jh &&
482 jh->b_transaction == commit_transaction &&
483 jh->b_jlist == BJ_Locked) {
482 __journal_unfile_buffer(jh); 484 __journal_unfile_buffer(jh);
483 jbd_unlock_bh_state(bh); 485 jbd_unlock_bh_state(bh);
484 journal_remove_journal_head(bh); 486 journal_remove_journal_head(bh);