Date: Wed, Apr  5 2006 15:11:45 +1000
From: David Chinner <dgc@sgi.com>
References: SGI:PV946321, 142533
Subject: Fix an inode use-after-free during an unpin

When reclaiming inodes that have been unlinked, we may
need to execute transactions during reclaim. By the time
the transaction has hit the disk, the linux inode and xfs
vnode may already have been freed so we can't reference
them safely. Use the known xfs inode state to determine
if it is safe to reference the vnode and linux inode
during the unpin operation.

Acked-by: Andreas Gruenbacher <agruen@suse.de>

Index: linux/fs/xfs/xfs_inode.c
===================================================================
--- linux.orig/fs/xfs/xfs_inode.c	2006-04-05 15:18:14.271491196 +1000
+++ linux/fs/xfs/xfs_inode.c	2006-04-05 15:30:36.851657832 +1000
@@ -2860,16 +2860,29 @@ xfs_iunpin(
 	ASSERT(atomic_read(&ip->i_pincount) > 0);
 
 	if (atomic_dec_and_test(&ip->i_pincount)) {
-		vnode_t	*vp = XFS_ITOV_NULL(ip);
+		/*
+		 * If the inode is currently being reclaimed, the
+		 * linux inode _and_ the xfs vnode may have been
+		 * freed so we cannot reference either of them safely.
+		 * Hence we should not try to do anything to them
+		 * if the xfs inode is currently in the reclaim
+		 * path.
+		 *
+		 * However, we still need to issue the unpin wakeup
+		 * call as the inode reclaim may be blocked waiting for
+		 * the inode to become unpinned.
+		 */
+		if (!(ip->i_flags & (XFS_IRECLAIM|XFS_IRECLAIMABLE))) {
+			vnode_t	*vp = XFS_ITOV_NULL(ip);
 
-		/* make sync come back and flush this inode */
-		if (vp) {
-			struct inode	*inode = LINVFS_GET_IP(vp);
+			/* make sync come back and flush this inode */
+			if (vp) {
+				struct inode	*inode = LINVFS_GET_IP(vp);
 
-			if (!(inode->i_state & I_NEW))
-				mark_inode_dirty_sync(inode);
+				if (!(inode->i_state & I_NEW))
+					mark_inode_dirty_sync(inode);
+			}
 		}
-
 		wake_up(&ip->i_ipin_wait);
 	}
 }
