aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorAndrew Morton <akpm@osdl.org>2004-08-07 00:52:47 -0700
committerLinus Torvalds <torvalds@ppc970.osdl.org>2004-08-07 00:52:47 -0700
commit9d294bd89e1e8491e5521baa812329de1bb9cbf6 (patch)
treef7ae1e63f6cf3293a300bb0986355a639891893d /fs
parentaa524be4777d03386a4c5a35573dc464a1371948 (diff)
downloadhistory-9d294bd89e1e8491e5521baa812329de1bb9cbf6.tar.gz
[PATCH] jbd: journal_head unmapping race fix
Fix a race identified by Chris Mason <mason@suse.com> journal_unmap_buffer -> __dispose_buffers has the j_list_lock and the jbd_lock_bh_state held. journal_get_write_access calls journal_put_journal_head, which takes jbd_lock_bh_journal_head(bh) and doesn't seem to have any other locks held. Since journal_unmap_buffers trusts the buffer_jbd bit to see if we need to call __dispose_buffer, and nobody seems to test buffer_jbd after taking jbd_lock_bh_journal_head. The kernel dereferences a null jh pointer in __journal_remove_journal_head. The patch fixes this by using journal_grab_journal_head() in journal_unmap_buffer(). It ensures that we either grab and pin the journal_head if the bh has one, or we bale out if the bh doesn't have a journal_head. Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'fs')
-rw-r--r--fs/jbd/transaction.c15
1 files changed, 8 insertions, 7 deletions
diff --git a/fs/jbd/transaction.c b/fs/jbd/transaction.c
index 8f69595f9f0a24..18a678ce2591a9 100644
--- a/fs/jbd/transaction.c
+++ b/fs/jbd/transaction.c
@@ -1773,14 +1773,10 @@ static int journal_unmap_buffer(journal_t *journal, struct buffer_head *bh)
jbd_lock_bh_state(bh);
spin_lock(&journal->j_list_lock);
- /*
- * Now we have the locks, check again to see whether kjournald has
- * taken the buffer off the transaction.
- */
- if (!buffer_jbd(bh))
- goto zap_buffer;
+ jh = journal_grab_journal_head(bh);
+ if (!jh)
+ goto zap_buffer_no_jh;
- jh = bh2jh(bh);
transaction = jh->b_transaction;
if (transaction == NULL) {
/* First case: not on any transaction. If it
@@ -1811,6 +1807,7 @@ static int journal_unmap_buffer(journal_t *journal, struct buffer_head *bh)
spin_unlock(&journal->j_list_lock);
jbd_unlock_bh_state(bh);
spin_unlock(&journal->j_state_lock);
+ journal_put_journal_head(jh);
return ret;
} else {
/* There is no currently-running transaction. So the
@@ -1824,6 +1821,7 @@ static int journal_unmap_buffer(journal_t *journal, struct buffer_head *bh)
spin_unlock(&journal->j_list_lock);
jbd_unlock_bh_state(bh);
spin_unlock(&journal->j_state_lock);
+ journal_put_journal_head(jh);
return ret;
} else {
/* The orphan record's transaction has
@@ -1847,6 +1845,7 @@ static int journal_unmap_buffer(journal_t *journal, struct buffer_head *bh)
spin_unlock(&journal->j_list_lock);
jbd_unlock_bh_state(bh);
spin_unlock(&journal->j_state_lock);
+ journal_put_journal_head(jh);
return 0;
} else {
/* Good, the buffer belongs to the running transaction.
@@ -1860,6 +1859,8 @@ static int journal_unmap_buffer(journal_t *journal, struct buffer_head *bh)
}
zap_buffer:
+ journal_put_journal_head(jh);
+zap_buffer_no_jh:
spin_unlock(&journal->j_list_lock);
jbd_unlock_bh_state(bh);
spin_unlock(&journal->j_state_lock);