| From c9fbd7bbc23dbdd73364be4d045e5d3612cf6e82 Mon Sep 17 00:00:00 2001 |
| From: Dave Chinner <dchinner@redhat.com> |
| Date: Thu, 10 May 2018 21:50:23 -0700 |
| Subject: xfs: clear sb->s_fs_info on mount failure |
| |
| From: Dave Chinner <dchinner@redhat.com> |
| |
| commit c9fbd7bbc23dbdd73364be4d045e5d3612cf6e82 upstream. |
| |
| We recently had an oops reported on a 4.14 kernel in |
| xfs_reclaim_inodes_count() where sb->s_fs_info pointed to garbage |
| and so the m_perag_tree lookup walked into lala land. |
| |
| Essentially, the machine was under memory pressure when the mount |
| was being run, xfs_fs_fill_super() failed after allocating the |
| xfs_mount and attaching it to sb->s_fs_info. It then cleaned up and |
| freed the xfs_mount, but the sb->s_fs_info field still pointed to |
| the freed memory. Hence when the superblock shrinker then ran |
| it fell off the bad pointer. |
| |
| With the superblock shrinker problem fixed at teh VFS level, this |
| stale s_fs_info pointer is still a problem - we use it |
| unconditionally in ->put_super when the superblock is being torn |
| down, and hence we can still trip over it after a ->fill_super |
| call failure. Hence we need to clear s_fs_info if |
| xfs-fs_fill_super() fails, and we need to check if it's valid in |
| the places it can potentially be dereferenced after a ->fill_super |
| failure. |
| |
| Signed-Off-By: Dave Chinner <dchinner@redhat.com> |
| Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com> |
| Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com> |
| Signed-off-by: Ajay Kaher <akaher@vmware.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| fs/xfs/xfs_super.c | 10 ++++++++++ |
| 1 file changed, 10 insertions(+) |
| |
| --- a/fs/xfs/xfs_super.c |
| +++ b/fs/xfs/xfs_super.c |
| @@ -1674,6 +1674,7 @@ xfs_fs_fill_super( |
| out_close_devices: |
| xfs_close_devices(mp); |
| out_free_fsname: |
| + sb->s_fs_info = NULL; |
| xfs_free_fsname(mp); |
| kfree(mp); |
| out: |
| @@ -1691,6 +1692,10 @@ xfs_fs_put_super( |
| { |
| struct xfs_mount *mp = XFS_M(sb); |
| |
| + /* if ->fill_super failed, we have no mount to tear down */ |
| + if (!sb->s_fs_info) |
| + return; |
| + |
| xfs_notice(mp, "Unmounting Filesystem"); |
| xfs_filestream_unmount(mp); |
| xfs_unmountfs(mp); |
| @@ -1700,6 +1705,8 @@ xfs_fs_put_super( |
| xfs_destroy_percpu_counters(mp); |
| xfs_destroy_mount_workqueues(mp); |
| xfs_close_devices(mp); |
| + |
| + sb->s_fs_info = NULL; |
| xfs_free_fsname(mp); |
| kfree(mp); |
| } |
| @@ -1719,6 +1726,9 @@ xfs_fs_nr_cached_objects( |
| struct super_block *sb, |
| struct shrink_control *sc) |
| { |
| + /* Paranoia: catch incorrect calls during mount setup or teardown */ |
| + if (WARN_ON_ONCE(!sb->s_fs_info)) |
| + return 0; |
| return xfs_reclaim_inodes_count(XFS_M(sb)); |
| } |
| |