Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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: Zheng Liu <gnehzuil.liu@gmail.com>
Signed-off-by: Jan Kara <jack@suse.cz>
Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
Cc: stable@vger.kernel.org

authored by

Jan Kara and committed by
Theodore Ts'o
ad56edad 386ad67c

+10 -5
+10 -5
fs/jbd2/transaction.c
··· 1065 1065 void jbd2_journal_set_triggers(struct buffer_head *bh, 1066 1066 struct jbd2_buffer_trigger_type *type) 1067 1067 { 1068 - struct journal_head *jh = bh2jh(bh); 1068 + struct journal_head *jh = jbd2_journal_grab_journal_head(bh); 1069 1069 1070 + if (WARN_ON(!jh)) 1071 + return; 1070 1072 jh->b_triggers = type; 1073 + jbd2_journal_put_journal_head(jh); 1071 1074 } 1072 1075 1073 1076 void jbd2_buffer_frozen_trigger(struct journal_head *jh, void *mapped_data, ··· 1122 1119 { 1123 1120 transaction_t *transaction = handle->h_transaction; 1124 1121 journal_t *journal = transaction->t_journal; 1125 - struct journal_head *jh = bh2jh(bh); 1122 + struct journal_head *jh; 1126 1123 int ret = 0; 1127 1124 1128 - jbd_debug(5, "journal_head %p\n", jh); 1129 - JBUFFER_TRACE(jh, "entry"); 1130 1125 if (is_handle_aborted(handle)) 1131 1126 goto out; 1132 - if (!buffer_jbd(bh)) { 1127 + jh = jbd2_journal_grab_journal_head(bh); 1128 + if (!jh) { 1133 1129 ret = -EUCLEAN; 1134 1130 goto out; 1135 1131 } 1132 + jbd_debug(5, "journal_head %p\n", jh); 1133 + JBUFFER_TRACE(jh, "entry"); 1136 1134 1137 1135 jbd_lock_bh_state(bh); 1138 1136 ··· 1224 1220 spin_unlock(&journal->j_list_lock); 1225 1221 out_unlock_bh: 1226 1222 jbd_unlock_bh_state(bh); 1223 + jbd2_journal_put_journal_head(jh); 1227 1224 out: 1228 1225 JBUFFER_TRACE(jh, "exit"); 1229 1226 WARN_ON(ret); /* All errors are bugs, so dump the stack */