• Eric Sandeen's avatar
    xfs: fix up xfs_swap_extent_forks inline extent handling · 20022ad6
    Eric Sandeen authored
    commit 4dfce57d upstream.
    
    There have been several reports over the years of NULL pointer
    dereferences in xfs_trans_log_inode during xfs_fsr processes,
    when the process is doing an fput and tearing down extents
    on the temporary inode, something like:
    
    BUG: unable to handle kernel NULL pointer dereference at 0000000000000018
    PID: 29439  TASK: ffff880550584fa0  CPU: 6   COMMAND: "xfs_fsr"
        [exception RIP: xfs_trans_log_inode+0x10]
     #9 [ffff8800a57bbbe0] xfs_bunmapi at ffffffffa037398e [xfs]
    #10 [ffff8800a57bbce8] xfs_itruncate_extents at ffffffffa0391b29 [xfs]
    #11 [ffff8800a57bbd88] xfs_inactive_truncate at ffffffffa0391d0c [xfs]
    #12 [ffff8800a57bbdb8] xfs_inactive at ffffffffa0392508 [xfs]
    #13 [ffff8800a57bbdd8] xfs_fs_evict_inode at ffffffffa035907e [xfs]
    #14 [ffff8800a57bbe00] evict at ffffffff811e1b67
    #15 [ffff8800a57bbe28] iput at ffffffff811e23a5
    #16 [ffff8800a57bbe58] dentry_kill at ffffffff811dcfc8
    #17 [ffff8800a57bbe88] dput at ffffffff811dd06c
    #18 [ffff8800a57bbea8] __fput at ffffffff811c823b
    #19 [ffff8800a57bbef0] ____fput at ffffffff811c846e
    #20 [ffff8800a57bbf00] task_work_run at ffffffff81093b27
    #21 [ffff8800a57bbf30] do_notify_resume at ffffffff81013b0c
    #22 [ffff8800a57bbf50] int_signal at ffffffff8161405d
    
    As it turns out, this is because the i_itemp pointer, along
    with the d_ops pointer, has been overwritten with zeros
    when we tear down the extents during truncate.  When the in-core
    inode fork on the temporary inode used by xfs_fsr was originally
    set up during the extent swap, we mistakenly looked at di_nextents
    to determine whether all extents fit inline, but this misses extents
    generated by speculative preallocation; we should be using if_bytes
    instead.
    
    This mistake corrupts the in-memory inode, and code in
    xfs_iext_remove_inline eventually gets bad inputs, causing
    it to memmove and memset incorrect ranges; this became apparent
    because the two values in ifp->if_u2.if_inline_ext[1] contained
    what should have been in d_ops and i_itemp; they were memmoved due
    to incorrect array indexing and then the original locations
    were zeroed with memset, again due to an array overrun.
    
    Fix this by properly using i_df.if_bytes to determine the number
    of extents, not di_nextents.
    
    Thanks to dchinner for looking at this with me and spotting the
    root cause.
    
    [nborisov: backported to 4.4]
    Signed-off-by: default avatarEric Sandeen <sandeen@redhat.com>
    Reviewed-by: default avatarBrian Foster <bfoster@redhat.com>
    Signed-off-by: default avatarDave Chinner <david@fromorbit.com>
    Signed-off-by: default avatarNikolay Borisov <nborisov@suse.com>
    Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
    20022ad6
xfs_bmap_util.c 50.2 KB