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>

authored by Jan Kara and committed by Linus Torvalds a61d90d7 463aea1a

Changed files
+4 -2
fs
jbd
+4 -2
fs/jbd/commit.c
··· 241 241 spin_lock(&journal->j_list_lock); 242 242 } 243 243 /* Someone already cleaned up the buffer? */ 244 - if (!buffer_jbd(bh) 244 + if (!buffer_jbd(bh) || bh2jh(bh) != jh 245 245 || jh->b_transaction != commit_transaction 246 246 || jh->b_jlist != BJ_SyncData) { 247 247 jbd_unlock_bh_state(bh); ··· 478 478 spin_lock(&journal->j_list_lock); 479 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 484 __journal_unfile_buffer(jh); 483 485 jbd_unlock_bh_state(bh); 484 486 journal_remove_journal_head(bh);