Commit ad56edad authored by Jan Kara's avatar Jan Kara Committed by Theodore Ts'o

jbd2: fix use after free in jbd2_journal_dirty_metadata()

jbd2_journal_dirty_metadata() didn't get a reference to journal_head it
was working with. This is OK in most of the cases since the journal head
should be attached to a transaction but in rare occasions when we are
journalling data, __ext4_journalled_writepage() can race with
jbd2_journal_invalidatepage() stripping buffers from a page and thus
journal head can be freed under hands of jbd2_journal_dirty_metadata().

Fix the problem by getting own journal head reference in
jbd2_journal_dirty_metadata() (and also in jbd2_journal_set_triggers()
which can possibly have the same issue).
Reported-by: default avatarZheng Liu <gnehzuil.liu@gmail.com>
Signed-off-by: default avatarJan Kara <jack@suse.cz>
Signed-off-by: default avatar"Theodore Ts'o" <tytso@mit.edu>
Cc: stable@vger.kernel.org
parent 386ad67c
...@@ -1065,9 +1065,12 @@ int jbd2_journal_get_undo_access(handle_t *handle, struct buffer_head *bh) ...@@ -1065,9 +1065,12 @@ int jbd2_journal_get_undo_access(handle_t *handle, struct buffer_head *bh)
void jbd2_journal_set_triggers(struct buffer_head *bh, void jbd2_journal_set_triggers(struct buffer_head *bh,
struct jbd2_buffer_trigger_type *type) struct jbd2_buffer_trigger_type *type)
{ {
struct journal_head *jh = bh2jh(bh); struct journal_head *jh = jbd2_journal_grab_journal_head(bh);
if (WARN_ON(!jh))
return;
jh->b_triggers = type; jh->b_triggers = type;
jbd2_journal_put_journal_head(jh);
} }
void jbd2_buffer_frozen_trigger(struct journal_head *jh, void *mapped_data, void jbd2_buffer_frozen_trigger(struct journal_head *jh, void *mapped_data,
...@@ -1119,17 +1122,18 @@ int jbd2_journal_dirty_metadata(handle_t *handle, struct buffer_head *bh) ...@@ -1119,17 +1122,18 @@ int jbd2_journal_dirty_metadata(handle_t *handle, struct buffer_head *bh)
{ {
transaction_t *transaction = handle->h_transaction; transaction_t *transaction = handle->h_transaction;
journal_t *journal = transaction->t_journal; journal_t *journal = transaction->t_journal;
struct journal_head *jh = bh2jh(bh); struct journal_head *jh;
int ret = 0; int ret = 0;
jbd_debug(5, "journal_head %p\n", jh);
JBUFFER_TRACE(jh, "entry");
if (is_handle_aborted(handle)) if (is_handle_aborted(handle))
goto out; goto out;
if (!buffer_jbd(bh)) { jh = jbd2_journal_grab_journal_head(bh);
if (!jh) {
ret = -EUCLEAN; ret = -EUCLEAN;
goto out; goto out;
} }
jbd_debug(5, "journal_head %p\n", jh);
JBUFFER_TRACE(jh, "entry");
jbd_lock_bh_state(bh); jbd_lock_bh_state(bh);
...@@ -1220,6 +1224,7 @@ int jbd2_journal_dirty_metadata(handle_t *handle, struct buffer_head *bh) ...@@ -1220,6 +1224,7 @@ int jbd2_journal_dirty_metadata(handle_t *handle, struct buffer_head *bh)
spin_unlock(&journal->j_list_lock); spin_unlock(&journal->j_list_lock);
out_unlock_bh: out_unlock_bh:
jbd_unlock_bh_state(bh); jbd_unlock_bh_state(bh);
jbd2_journal_put_journal_head(jh);
out: out:
JBUFFER_TRACE(jh, "exit"); JBUFFER_TRACE(jh, "exit");
WARN_ON(ret); /* All errors are bugs, so dump the stack */ WARN_ON(ret); /* All errors are bugs, so dump the stack */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment