Commit 35c9f7f2 authored by Eric Sandeen's avatar Eric Sandeen Committed by Jiri Slaby

xfs: fix up xfs_swap_extent_forks inline extent handling

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]

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 3.12]
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 avatarJiri Slaby <jslaby@suse.cz>
parent 510c971a
...@@ -1776,6 +1776,7 @@ xfs_swap_extents( ...@@ -1776,6 +1776,7 @@ xfs_swap_extents(
xfs_trans_t *tp; xfs_trans_t *tp;
xfs_bstat_t *sbp = &sxp->sx_stat; xfs_bstat_t *sbp = &sxp->sx_stat;
xfs_ifork_t *tempifp, *ifp, *tifp; xfs_ifork_t *tempifp, *ifp, *tifp;
xfs_extnum_t nextents;
int src_log_flags, target_log_flags; int src_log_flags, target_log_flags;
int error = 0; int error = 0;
int aforkblks = 0; int aforkblks = 0;
...@@ -1984,7 +1985,8 @@ xfs_swap_extents( ...@@ -1984,7 +1985,8 @@ xfs_swap_extents(
* pointer. Otherwise it's already NULL or * pointer. Otherwise it's already NULL or
* pointing to the extent. * pointing to the extent.
*/ */
if (ip->i_d.di_nextents <= XFS_INLINE_EXTS) { nextents = ip->i_df.if_bytes / (uint)sizeof(xfs_bmbt_rec_t);
if (nextents <= XFS_INLINE_EXTS) {
ifp->if_u1.if_extents = ifp->if_u1.if_extents =
ifp->if_u2.if_inline_ext; ifp->if_u2.if_inline_ext;
} }
...@@ -2003,7 +2005,8 @@ xfs_swap_extents( ...@@ -2003,7 +2005,8 @@ xfs_swap_extents(
* pointer. Otherwise it's already NULL or * pointer. Otherwise it's already NULL or
* pointing to the extent. * pointing to the extent.
*/ */
if (tip->i_d.di_nextents <= XFS_INLINE_EXTS) { nextents = tip->i_df.if_bytes / (uint)sizeof(xfs_bmbt_rec_t);
if (nextents <= XFS_INLINE_EXTS) {
tifp->if_u1.if_extents = tifp->if_u1.if_extents =
tifp->if_u2.if_inline_ext; tifp->if_u2.if_inline_ext;
} }
......
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