jbd: fix race in buffer processing in commit code [Linux 2.6.30]

This Linux kernel change "jbd: fix race in buffer processing in commit code" is included in the Linux 2.6.30 release. This change is authored by Jan Kara <jack [at] suse.cz> on Tue Jun 9 16:26:26 2009 -0700. The commit for this change in Linux stable tree is a61d90d (patch).

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 <[email protected]>
Cc: <[email protected]>
Cc: <[email protected]>
Signed-off-by: Andrew Morton <[email protected]>
Signed-off-by: Linus Torvalds <[email protected]>

There are 6 lines of Linux source code added/deleted in this change. Code changes to Linux kernel are as follows.

 fs/jbd/commit.c | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/fs/jbd/commit.c b/fs/jbd/commit.c
index 06560c5..618e21c 100644
--- a/fs/jbd/commit.c
+++ b/fs/jbd/commit.c
@@ -241,7 +241,7 @@ static int journal_submit_data_buffers(journal_t *journal,
            spin_lock(&journal->j_list_lock);
        }
        /* Someone already cleaned up the buffer? */
-       if (!buffer_jbd(bh)
+       if (!buffer_jbd(bh) || bh2jh(bh) != jh
            || jh->b_transaction != commit_transaction
            || jh->b_jlist != BJ_SyncData) {
            jbd_unlock_bh_state(bh);
@@ -478,7 +478,9 @@ void journal_commit_transaction(journal_t *journal)
            spin_lock(&journal->j_list_lock);
            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);
            jbd_unlock_bh_state(bh);
            journal_remove_journal_head(bh);

Leave a Reply

Your email address will not be published. Required fields are marked *