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

jbd2: fix use after free in jbd2_log_do_checkpoint()

The code cleaning transaction's lists of checkpoint buffers has a bug
where it increases bh refcount only after releasing
journal->j_list_lock. Thus the following race is possible:

CPU0					CPU1
jbd2_log_do_checkpoint()
					jbd2_journal_try_to_free_buffers()
					  __journal_try_to_free_buffer(bh)
  ...
  while (transaction->t_checkpoint_io_list)
  ...
    if (buffer_locked(bh)) {

<-- IO completes now, buffer gets unlocked -->

      spin_unlock(&journal->j_list_lock);
					    spin_lock(&journal->j_list_lock);
					    __jbd2_journal_remove_checkpoint(jh);
					    spin_unlock(&journal->j_list_lock);
					  try_to_free_buffers(page);
      get_bh(bh) <-- accesses freed bh

Fix the problem by grabbing bh reference before unlocking
journal->j_list_lock.

Fixes: dc6e8d66 ("jbd2: don't call get_bh() before calling __jbd2_journal_remove_checkpoint()")
Fixes: be1158cc ("jbd2: fold __process_buffer() into jbd2_log_do_checkpoint()")
Reported-by: syzbot+7f4a27091759e2fe7453@syzkaller.appspotmail.com
CC: stable@vger.kernel.org
Reviewed-by: default avatarLukas Czerner <lczerner@redhat.com>
Signed-off-by: default avatarJan Kara <jack@suse.cz>
Signed-off-by: default avatarTheodore Ts'o <tytso@mit.edu>
parent 182a79e0
...@@ -251,8 +251,8 @@ int jbd2_log_do_checkpoint(journal_t *journal) ...@@ -251,8 +251,8 @@ int jbd2_log_do_checkpoint(journal_t *journal)
bh = jh2bh(jh); bh = jh2bh(jh);
if (buffer_locked(bh)) { if (buffer_locked(bh)) {
spin_unlock(&journal->j_list_lock);
get_bh(bh); get_bh(bh);
spin_unlock(&journal->j_list_lock);
wait_on_buffer(bh); wait_on_buffer(bh);
/* the journal_head may have gone by now */ /* the journal_head may have gone by now */
BUFFER_TRACE(bh, "brelse"); BUFFER_TRACE(bh, "brelse");
...@@ -333,8 +333,8 @@ int jbd2_log_do_checkpoint(journal_t *journal) ...@@ -333,8 +333,8 @@ int jbd2_log_do_checkpoint(journal_t *journal)
jh = transaction->t_checkpoint_io_list; jh = transaction->t_checkpoint_io_list;
bh = jh2bh(jh); bh = jh2bh(jh);
if (buffer_locked(bh)) { if (buffer_locked(bh)) {
spin_unlock(&journal->j_list_lock);
get_bh(bh); get_bh(bh);
spin_unlock(&journal->j_list_lock);
wait_on_buffer(bh); wait_on_buffer(bh);
/* the journal_head may have gone by now */ /* the journal_head may have gone by now */
BUFFER_TRACE(bh, "brelse"); BUFFER_TRACE(bh, "brelse");
......
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