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>
This commit is contained in:
parent
463aea1a1c
commit
a61d90d75d
1 changed files with 4 additions and 2 deletions
|
@ -241,7 +241,7 @@ static int journal_submit_data_buffers(journal_t *journal,
|
||||||
spin_lock(&journal->j_list_lock);
|
spin_lock(&journal->j_list_lock);
|
||||||
}
|
}
|
||||||
/* Someone already cleaned up the buffer? */
|
/* Someone already cleaned up the buffer? */
|
||||||
if (!buffer_jbd(bh)
|
if (!buffer_jbd(bh) || bh2jh(bh) != jh
|
||||||
|| jh->b_transaction != commit_transaction
|
|| jh->b_transaction != commit_transaction
|
||||||
|| jh->b_jlist != BJ_SyncData) {
|
|| jh->b_jlist != BJ_SyncData) {
|
||||||
jbd_unlock_bh_state(bh);
|
jbd_unlock_bh_state(bh);
|
||||||
|
@ -478,7 +478,9 @@ void journal_commit_transaction(journal_t *journal)
|
||||||
spin_lock(&journal->j_list_lock);
|
spin_lock(&journal->j_list_lock);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (buffer_jbd(bh) && jh->b_jlist == BJ_Locked) {
|
if (buffer_jbd(bh) && bh2jh(bh) == jh &&
|
||||||
|
jh->b_transaction == commit_transaction &&
|
||||||
|
jh->b_jlist == BJ_Locked) {
|
||||||
__journal_unfile_buffer(jh);
|
__journal_unfile_buffer(jh);
|
||||||
jbd_unlock_bh_state(bh);
|
jbd_unlock_bh_state(bh);
|
||||||
journal_remove_journal_head(bh);
|
journal_remove_journal_head(bh);
|
||||||
|
|
Loading…
Reference in a new issue