lkml.org 
[lkml]   [2018]   [Nov]   [12]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH 4.18 218/350] jbd2: fix use after free in jbd2_log_do_checkpoint()
    Date
    4.18-stable review patch.  If anyone has any objections, please let me know.

    ------------------

    From: Jan Kara <jack@suse.cz>

    commit ccd3c4373eacb044eb3832966299d13d2631f66f upstream.

    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: dc6e8d669cf5 ("jbd2: don't call get_bh() before calling __jbd2_journal_remove_checkpoint()")
    Fixes: be1158cc615f ("jbd2: fold __process_buffer() into jbd2_log_do_checkpoint()")
    Reported-by: syzbot+7f4a27091759e2fe7453@syzkaller.appspotmail.com
    CC: stable@vger.kernel.org
    Reviewed-by: Lukas Czerner <lczerner@redhat.com>
    Signed-off-by: Jan Kara <jack@suse.cz>
    Signed-off-by: Theodore Ts'o <tytso@mit.edu>
    Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

    ---
    fs/jbd2/checkpoint.c | 4 ++--
    1 file changed, 2 insertions(+), 2 deletions(-)

    --- a/fs/jbd2/checkpoint.c
    +++ b/fs/jbd2/checkpoint.c
    @@ -251,8 +251,8 @@ restart:
    bh = jh2bh(jh);

    if (buffer_locked(bh)) {
    - spin_unlock(&journal->j_list_lock);
    get_bh(bh);
    + spin_unlock(&journal->j_list_lock);
    wait_on_buffer(bh);
    /* the journal_head may have gone by now */
    BUFFER_TRACE(bh, "brelse");
    @@ -333,8 +333,8 @@ restart2:
    jh = transaction->t_checkpoint_io_list;
    bh = jh2bh(jh);
    if (buffer_locked(bh)) {
    - spin_unlock(&journal->j_list_lock);
    get_bh(bh);
    + spin_unlock(&journal->j_list_lock);
    wait_on_buffer(bh);
    /* the journal_head may have gone by now */
    BUFFER_TRACE(bh, "brelse");

    \
     
     \ /
      Last update: 2018-11-11 23:32    [W:4.362 / U:0.164 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site