Skip to content
This repository was archived by the owner on Jun 4, 2020. It is now read-only.

Commit 20022ad

Browse files
sandeengregkh
authored andcommitted
xfs: fix up xfs_swap_extent_forks inline extent handling
commit 4dfce57db6354603641132fac3c887614e3ebe81 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] OnePlusOSS#9 [ffff8800a57bbbe0] xfs_bunmapi at ffffffffa037398e [xfs] OnePlusOSS#10 [ffff8800a57bbce8] xfs_itruncate_extents at ffffffffa0391b29 [xfs] OnePlusOSS#11 [ffff8800a57bbd88] xfs_inactive_truncate at ffffffffa0391d0c [xfs] OnePlusOSS#12 [ffff8800a57bbdb8] xfs_inactive at ffffffffa0392508 [xfs] OnePlusOSS#13 [ffff8800a57bbdd8] xfs_fs_evict_inode at ffffffffa035907e [xfs] OnePlusOSS#14 [ffff8800a57bbe00] evict at ffffffff811e1b67 OnePlusOSS#15 [ffff8800a57bbe28] iput at ffffffff811e23a5 OnePlusOSS#16 [ffff8800a57bbe58] dentry_kill at ffffffff811dcfc8 OnePlusOSS#17 [ffff8800a57bbe88] dput at ffffffff811dd06c OnePlusOSS#18 [ffff8800a57bbea8] __fput at ffffffff811c823b OnePlusOSS#19 [ffff8800a57bbef0] ____fput at ffffffff811c846e OnePlusOSS#20 [ffff8800a57bbf00] task_work_run at ffffffff81093b27 OnePlusOSS#21 [ffff8800a57bbf30] do_notify_resume at ffffffff81013b0c OnePlusOSS#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: Eric Sandeen <sandeen@redhat.com> Reviewed-by: Brian Foster <bfoster@redhat.com> Signed-off-by: Dave Chinner <david@fromorbit.com> Signed-off-by: Nikolay Borisov <nborisov@suse.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1 parent 0f27d9d commit 20022ad

1 file changed

Lines changed: 5 additions & 2 deletions

File tree

fs/xfs/xfs_bmap_util.c

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1622,6 +1622,7 @@ xfs_swap_extents(
16221622
xfs_trans_t *tp;
16231623
xfs_bstat_t *sbp = &sxp->sx_stat;
16241624
xfs_ifork_t *tempifp, *ifp, *tifp;
1625+
xfs_extnum_t nextents;
16251626
int src_log_flags, target_log_flags;
16261627
int error = 0;
16271628
int aforkblks = 0;
@@ -1802,7 +1803,8 @@ xfs_swap_extents(
18021803
* pointer. Otherwise it's already NULL or
18031804
* pointing to the extent.
18041805
*/
1805-
if (ip->i_d.di_nextents <= XFS_INLINE_EXTS) {
1806+
nextents = ip->i_df.if_bytes / (uint)sizeof(xfs_bmbt_rec_t);
1807+
if (nextents <= XFS_INLINE_EXTS) {
18061808
ifp->if_u1.if_extents =
18071809
ifp->if_u2.if_inline_ext;
18081810
}
@@ -1821,7 +1823,8 @@ xfs_swap_extents(
18211823
* pointer. Otherwise it's already NULL or
18221824
* pointing to the extent.
18231825
*/
1824-
if (tip->i_d.di_nextents <= XFS_INLINE_EXTS) {
1826+
nextents = tip->i_df.if_bytes / (uint)sizeof(xfs_bmbt_rec_t);
1827+
if (nextents <= XFS_INLINE_EXTS) {
18251828
tifp->if_u1.if_extents =
18261829
tifp->if_u2.if_inline_ext;
18271830
}

0 commit comments

Comments
 (0)