linux/fs/xfs
Darrick J. Wong c78c2d0903 xfs: don't leak memory when attr fork loading fails
I observed the following evidence of a memory leak while running xfs/399
from the xfs fsck test suite (edited for brevity):

XFS (sde): Metadata corruption detected at xfs_attr_shortform_verify_struct.part.0+0x7b/0xb0 [xfs], inode 0x1172 attr fork
XFS: Assertion failed: ip->i_af.if_u1.if_data == NULL, file: fs/xfs/libxfs/xfs_inode_fork.c, line: 315
------------[ cut here ]------------
WARNING: CPU: 2 PID: 91635 at fs/xfs/xfs_message.c:104 assfail+0x46/0x4a [xfs]
CPU: 2 PID: 91635 Comm: xfs_scrub Tainted: G        W         5.19.0-rc7-xfsx #rc7 6e6475eb29fd9dda3181f81b7ca7ff961d277a40
Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.15.0-1 04/01/2014
RIP: 0010:assfail+0x46/0x4a [xfs]
Call Trace:
 <TASK>
 xfs_ifork_zap_attr+0x7c/0xb0
 xfs_iformat_attr_fork+0x86/0x110
 xfs_inode_from_disk+0x41d/0x480
 xfs_iget+0x389/0xd70
 xfs_bulkstat_one_int+0x5b/0x540
 xfs_bulkstat_iwalk+0x1e/0x30
 xfs_iwalk_ag_recs+0xd1/0x160
 xfs_iwalk_run_callbacks+0xb9/0x180
 xfs_iwalk_ag+0x1d8/0x2e0
 xfs_iwalk+0x141/0x220
 xfs_bulkstat+0x105/0x180
 xfs_ioc_bulkstat.constprop.0.isra.0+0xc5/0x130
 xfs_file_ioctl+0xa5f/0xef0
 __x64_sys_ioctl+0x82/0xa0
 do_syscall_64+0x2b/0x80
 entry_SYSCALL_64_after_hwframe+0x46/0xb0

This newly-added assertion checks that there aren't any incore data
structures hanging off the incore fork when we're trying to reset its
contents.  From the call trace, it is evident that iget was trying to
construct an incore inode from the ondisk inode, but the attr fork
verifier failed and we were trying to undo all the memory allocations
that we had done earlier.

The three assertions in xfs_ifork_zap_attr check that the caller has
already called xfs_idestroy_fork, which clearly has not been done here.
As the zap function then zeroes the pointers, we've effectively leaked
the memory.

The shortest change would have been to insert an extra call to
xfs_idestroy_fork, but it makes more sense to bundle the _idestroy_fork
call into _zap_attr, since all other callsites call _idestroy_fork
immediately prior to calling _zap_attr.  IOWs, it eliminates one way to
fail.

Note: This change only applies cleanly to 2ed5b09b3e, since we just
reworked the attr fork lifetime.  However, I think this memory leak has
existed since 0f45a1b20c, since the chain xfs_iformat_attr_fork ->
xfs_iformat_local -> xfs_init_local_fork will allocate
ifp->if_u1.if_data, but if xfs_ifork_verify_local_attr fails,
xfs_iformat_attr_fork will free i_afp without freeing any of the stuff
hanging off i_afp.  The solution for older kernels I think is to add the
missing call to xfs_idestroy_fork just prior to calling kmem_cache_free.

Found by fuzzing a.sfattr.hdr.totsize = lastbit in xfs/399.

Fixes: 2ed5b09b3e ("xfs: make inode attribute forks a permanent part of struct xfs_inode")
Probably-Fixes: 0f45a1b20c ("xfs: improve local fork verification")
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Dave Chinner <dchinner@redhat.com>
2022-07-20 16:40:39 -07:00
..
libxfs xfs: don't leak memory when attr fork loading fails 2022-07-20 16:40:39 -07:00
scrub xfs: fix for variable set but not used warning 2022-07-20 16:40:39 -07:00
Kconfig
kmem.c
kmem.h
Makefile xfs: add in-memory iunlink log item 2022-07-14 11:47:42 +10:00
mrlock.h
xfs_acl.c
xfs_acl.h
xfs_aops.c
xfs_aops.h
xfs_attr_inactive.c xfs: don't leak memory when attr fork loading fails 2022-07-20 16:40:39 -07:00
xfs_attr_item.c xfs: don't hold xattr leaf buffers across transaction rolls 2022-06-29 08:47:56 -07:00
xfs_attr_item.h
xfs_attr_list.c xfs: use XFS_IFORK_Q to determine the presence of an xattr fork 2022-07-09 15:17:21 -07:00
xfs_bio_io.c
xfs_bmap_item.c
xfs_bmap_item.h
xfs_bmap_util.c xfs: replace inode fork size macros with functions 2022-07-12 11:17:27 -07:00
xfs_bmap_util.h
xfs_buf_item_recover.c
xfs_buf_item.c
xfs_buf_item.h
xfs_buf.c xfs: xfs_buf cache destroy isn't RCU safe 2022-07-20 16:40:39 -07:00
xfs_buf.h xfs: xfs_buf cache destroy isn't RCU safe 2022-07-20 16:40:39 -07:00
xfs_dir2_readdir.c xfs: convert XFS_IFORK_PTR to a static inline helper 2022-07-09 15:17:21 -07:00
xfs_discard.c xfs: pass perag to xfs_alloc_read_agf() 2022-07-07 19:07:40 +10:00
xfs_discard.h
xfs_dquot_item_recover.c
xfs_dquot_item.c
xfs_dquot_item.h
xfs_dquot.c
xfs_dquot.h
xfs_error.c
xfs_error.h
xfs_export.c
xfs_export.h
xfs_extent_busy.c
xfs_extent_busy.h
xfs_extfree_item.c xfs: pass perag to xfs_alloc_read_agf() 2022-07-07 19:07:40 +10:00
xfs_extfree_item.h
xfs_file.c
xfs_filestream.c xfs: pass perag to xfs_alloc_read_agf() 2022-07-07 19:07:40 +10:00
xfs_filestream.h
xfs_fsmap.c xfs: pass perag to xfs_alloc_read_agf() 2022-07-07 19:07:40 +10:00
xfs_fsmap.h
xfs_fsops.c xfs: Pre-calculate per-AG agbno geometry 2022-07-07 19:13:02 +10:00
xfs_fsops.h
xfs_globals.c
xfs_health.c
xfs_icache.c xfs: don't leak memory when attr fork loading fails 2022-07-20 16:40:39 -07:00
xfs_icache.h xfs: introduce xfs_inodegc_push() 2022-06-23 13:34:38 -07:00
xfs_icreate_item.c
xfs_icreate_item.h
xfs_inode_item_recover.c
xfs_inode_item.c xfs: replace inode fork size macros with functions 2022-07-12 11:17:27 -07:00
xfs_inode_item.h
xfs_inode.c xfs: make attr forks permanent 2022-07-14 09:46:37 -07:00
xfs_inode.h xfs: make attr forks permanent 2022-07-14 09:46:37 -07:00
xfs_ioctl32.c
xfs_ioctl32.h
xfs_ioctl.c xfs: convert XFS_IFORK_PTR to a static inline helper 2022-07-09 15:17:21 -07:00
xfs_ioctl.h
xfs_iomap.c xfs: replace XFS_IFORK_Q with a proper predicate function 2022-07-12 11:17:27 -07:00
xfs_iomap.h
xfs_iops.c xfs: replace XFS_IFORK_Q with a proper predicate function 2022-07-12 11:17:27 -07:00
xfs_iops.h xfs: add selinux labels to whiteout inodes 2022-07-09 10:56:02 -07:00
xfs_itable.c xfs: replace inode fork size macros with functions 2022-07-12 11:17:27 -07:00
xfs_itable.h
xfs_iunlink_item.c xfs: add in-memory iunlink log item 2022-07-14 11:47:42 +10:00
xfs_iunlink_item.h xfs: add in-memory iunlink log item 2022-07-14 11:47:42 +10:00
xfs_iwalk.c
xfs_iwalk.h
xfs_linux.h
xfs_log_cil.c xfs: xlog_sync() manually adjusts grant head space 2022-07-07 18:56:09 +10:00
xfs_log_priv.h xfs: xlog_sync() manually adjusts grant head space 2022-07-07 18:56:09 +10:00
xfs_log_recover.c xfs: double link the unlinked inode list 2022-07-14 11:46:43 +10:00
xfs_log.c xfs: xlog_sync() manually adjusts grant head space 2022-07-07 18:56:09 +10:00
xfs_log.h xfs: move CIL ordering to the logvec chain 2022-07-07 18:56:08 +10:00
xfs_message.c
xfs_message.h
xfs_mount.c xfs: Pre-calculate per-AG agbno geometry 2022-07-07 19:13:02 +10:00
xfs_mount.h
xfs_mru_cache.c
xfs_mru_cache.h
xfs_ondisk.h
xfs_pnfs.c
xfs_pnfs.h
xfs_pwork.c
xfs_pwork.h
xfs_qm_bhv.c
xfs_qm_syscalls.c xfs: introduce xfs_inodegc_push() 2022-06-23 13:34:38 -07:00
xfs_qm.c xfs: make attr forks permanent 2022-07-14 09:46:37 -07:00
xfs_qm.h
xfs_quota.h
xfs_quotaops.c
xfs_refcount_item.c
xfs_refcount_item.h
xfs_reflink.c xfs: convert XFS_IFORK_PTR to a static inline helper 2022-07-09 15:17:21 -07:00
xfs_reflink.h xfs: pass perag to xfs_alloc_read_agf() 2022-07-07 19:07:40 +10:00
xfs_rmap_item.c
xfs_rmap_item.h
xfs_rtalloc.c
xfs_rtalloc.h
xfs_stats.c
xfs_stats.h
xfs_super.c xfs: xfs_buf cache destroy isn't RCU safe 2022-07-20 16:40:39 -07:00
xfs_super.h
xfs_symlink.c xfs: replace inode fork size macros with functions 2022-07-12 11:17:27 -07:00
xfs_symlink.h
xfs_sysctl.c
xfs_sysctl.h
xfs_sysfs.c
xfs_sysfs.h
xfs_trace.c
xfs_trace.h xfs: make attr forks permanent 2022-07-14 09:46:37 -07:00
xfs_trans_ail.c
xfs_trans_buf.c
xfs_trans_dquot.c
xfs_trans_priv.h xfs: convert log vector chain to use list heads 2022-07-07 18:55:59 +10:00
xfs_trans.c xfs: introduce in-memory inode unlink log items 2022-07-14 09:21:42 -07:00
xfs_trans.h xfs: introduce in-memory inode unlink log items 2022-07-14 09:21:42 -07:00
xfs_xattr.c
xfs_xattr.h
xfs.h