summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Chinner <dgc@sgi.com>2006-04-11 15:11:20 +1000
committerNathan Scott <nathans@sgi.com>2006-04-11 15:11:20 +1000
commit58829e490ee805f1c8b3009abc90e2a1a7a0d278 (patch)
tree6eb08b56ec5cf164ba70113749cb800a7a0ed83d
parent1fc5d959d88a5f77aa7e4435f6c9d0e2d2236704 (diff)
downloadlinux-58829e490ee805f1c8b3009abc90e2a1a7a0d278.tar.gz
linux-58829e490ee805f1c8b3009abc90e2a1a7a0d278.tar.bz2
linux-58829e490ee805f1c8b3009abc90e2a1a7a0d278.zip
[XFS] Fix an inode use-after-free durin 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. SGI-PV: 946321 SGI-Modid: xfs-linux-melb:xfs-kern:25687a Signed-off-by: David Chinner <dgc@sgi.com> Signed-off-by: Nathan Scott <nathans@sgi.com>
-rw-r--r--fs/xfs/xfs_inode.c27
1 files changed, 20 insertions, 7 deletions
diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c
index 48146bdc6bdd..94b60dd03801 100644
--- a/fs/xfs/xfs_inode.c
+++ b/fs/xfs/xfs_inode.c
@@ -2732,16 +2732,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 = vn_to_inode(vp);
+ /* make sync come back and flush this inode */
+ if (vp) {
+ struct inode *inode = vn_to_inode(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);
}
}