[XFS] Prevent use-after-free caused by synchronous inode reclaim
authorDavid Chinner <david@fromorbit.com>
Thu, 30 Oct 2008 06:36:40 +0000 (17:36 +1100)
committerLachlan McIlroy <lachlan@sgi.com>
Thu, 30 Oct 2008 06:36:40 +0000 (17:36 +1100)
With the combined linux and XFS inode, we need to ensure that the combined
structure is not freed before the generic code is finished with the inode.
As it turns out, there is a case where the XFS inode is freed before the
linux inode - when xfs_reclaim() is called from ->clear_inode() on a clean
inode, the xfs inode is freed during that call. The generic code
references the inode after the ->clear_inode() call, so this is a use
after free situation.

Fix the problem by moving the xfs_reclaim() call to ->destroy_inode()
instead of in ->clear_inode(). This ensures the combined inode structure
is not freed until after the generic code has finished with it.

SGI-PV: 988141

SGI-Modid: xfs-linux-melb:xfs-kern:32324a

Signed-off-by: David Chinner <david@fromorbit.com>
Signed-off-by: Lachlan McIlroy <lachlan@sgi.com>
Signed-off-by: Christoph Hellwig <hch@infradead.org>
fs/xfs/linux-2.6/xfs_super.c

index c6ef684bf2e327073027541a0587aeac5e2bc2b2..bb535a7737b5ebafe42721a4e2208138bcfd2599 100644 (file)
@@ -875,13 +875,18 @@ xfs_fs_alloc_inode(
 }
 
 /*
- * we need to provide an empty inode free function to prevent
- * the generic code from trying to free our combined inode.
+ * Now that the generic code is guaranteed not to be accessing
+ * the linux inode, we can reclaim the inode.
  */
 STATIC void
 xfs_fs_destroy_inode(
        struct inode    *inode)
 {
+       xfs_inode_t             *ip = XFS_I(inode);
+
+       XFS_STATS_INC(vn_reclaim);
+       if (xfs_reclaim(ip))
+               panic("%s: cannot reclaim 0x%p\n", __func__, inode);
 }
 
 /*
@@ -958,22 +963,13 @@ xfs_fs_clear_inode(
 {
        xfs_inode_t             *ip = XFS_I(inode);
 
-       /*
-        * ip can be null when xfs_iget_core calls xfs_idestroy if we
-        * find an inode with di_mode == 0 but without IGET_CREATE set.
-        */
-       if (ip) {
-               xfs_itrace_entry(ip);
-               XFS_STATS_INC(vn_rele);
-               XFS_STATS_INC(vn_remove);
-               XFS_STATS_INC(vn_reclaim);
-               XFS_STATS_DEC(vn_active);
-
-               xfs_inactive(ip);
-               xfs_iflags_clear(ip, XFS_IMODIFIED);
-               if (xfs_reclaim(ip))
-                       panic("%s: cannot reclaim 0x%p\n", __func__, inode);
-       }
+       xfs_itrace_entry(ip);
+       XFS_STATS_INC(vn_rele);
+       XFS_STATS_INC(vn_remove);
+       XFS_STATS_DEC(vn_active);
+
+       xfs_inactive(ip);
+       xfs_iflags_clear(ip, XFS_IMODIFIED);
 }
 
 STATIC void