diff options
author | David Chinner <dgc@sgi.com> | 2006-09-27 21:06:03 -0400 |
---|---|---|
committer | Tim Shimmin <tes@sgi.com> | 2006-09-27 21:06:03 -0400 |
commit | f273ab848b7cbc0088b0ac7457b3769e6566074e (patch) | |
tree | 27f1b0ce7b056f77e7105284524cbdb658943ae5 /fs/xfs/xfs_inode.c | |
parent | 01106eae97b70399ce5a273a3cceb5246e8d9cc8 (diff) |
[XFS] Really fix use after free in xfs_iunpin.
The previous attempts to fix the linux inode use-after-free in xfs_iunpin
simply made the problem harder to hit. We actually need complete exclusion
between xfs_reclaim and xfs_iunpin, as well as ensuring that the i_flags
are consistent during both of these functions. Introduce a new spinlock
for exclusion and the i_flags, and fix up xfs_iunpin to use igrab before
marking the inode dirty.
SGI-PV: 952967
SGI-Modid: xfs-linux-melb:xfs-kern:26964a
Signed-off-by: David Chinner <dgc@sgi.com>
Signed-off-by: Tim Shimmin <tes@sgi.com>
Diffstat (limited to 'fs/xfs/xfs_inode.c')
-rw-r--r-- | fs/xfs/xfs_inode.c | 23 |
1 files changed, 20 insertions, 3 deletions
diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c index 66a78287a957..c27d7d495aa0 100644 --- a/fs/xfs/xfs_inode.c +++ b/fs/xfs/xfs_inode.c | |||
@@ -867,6 +867,7 @@ xfs_iread( | |||
867 | ip = kmem_zone_zalloc(xfs_inode_zone, KM_SLEEP); | 867 | ip = kmem_zone_zalloc(xfs_inode_zone, KM_SLEEP); |
868 | ip->i_ino = ino; | 868 | ip->i_ino = ino; |
869 | ip->i_mount = mp; | 869 | ip->i_mount = mp; |
870 | spin_lock_init(&ip->i_flags_lock); | ||
870 | 871 | ||
871 | /* | 872 | /* |
872 | * Get pointer's to the on-disk inode and the buffer containing it. | 873 | * Get pointer's to the on-disk inode and the buffer containing it. |
@@ -2214,7 +2215,9 @@ xfs_ifree_cluster( | |||
2214 | 2215 | ||
2215 | if (ip == free_ip) { | 2216 | if (ip == free_ip) { |
2216 | if (xfs_iflock_nowait(ip)) { | 2217 | if (xfs_iflock_nowait(ip)) { |
2218 | spin_lock(&ip->i_flags_lock); | ||
2217 | ip->i_flags |= XFS_ISTALE; | 2219 | ip->i_flags |= XFS_ISTALE; |
2220 | spin_unlock(&ip->i_flags_lock); | ||
2218 | 2221 | ||
2219 | if (xfs_inode_clean(ip)) { | 2222 | if (xfs_inode_clean(ip)) { |
2220 | xfs_ifunlock(ip); | 2223 | xfs_ifunlock(ip); |
@@ -2228,7 +2231,9 @@ xfs_ifree_cluster( | |||
2228 | 2231 | ||
2229 | if (xfs_ilock_nowait(ip, XFS_ILOCK_EXCL)) { | 2232 | if (xfs_ilock_nowait(ip, XFS_ILOCK_EXCL)) { |
2230 | if (xfs_iflock_nowait(ip)) { | 2233 | if (xfs_iflock_nowait(ip)) { |
2234 | spin_lock(&ip->i_flags_lock); | ||
2231 | ip->i_flags |= XFS_ISTALE; | 2235 | ip->i_flags |= XFS_ISTALE; |
2236 | spin_unlock(&ip->i_flags_lock); | ||
2232 | 2237 | ||
2233 | if (xfs_inode_clean(ip)) { | 2238 | if (xfs_inode_clean(ip)) { |
2234 | xfs_ifunlock(ip); | 2239 | xfs_ifunlock(ip); |
@@ -2258,7 +2263,9 @@ xfs_ifree_cluster( | |||
2258 | AIL_LOCK(mp,s); | 2263 | AIL_LOCK(mp,s); |
2259 | iip->ili_flush_lsn = iip->ili_item.li_lsn; | 2264 | iip->ili_flush_lsn = iip->ili_item.li_lsn; |
2260 | AIL_UNLOCK(mp, s); | 2265 | AIL_UNLOCK(mp, s); |
2266 | spin_lock(&iip->ili_inode->i_flags_lock); | ||
2261 | iip->ili_inode->i_flags |= XFS_ISTALE; | 2267 | iip->ili_inode->i_flags |= XFS_ISTALE; |
2268 | spin_unlock(&iip->ili_inode->i_flags_lock); | ||
2262 | pre_flushed++; | 2269 | pre_flushed++; |
2263 | } | 2270 | } |
2264 | lip = lip->li_bio_list; | 2271 | lip = lip->li_bio_list; |
@@ -2754,19 +2761,29 @@ xfs_iunpin( | |||
2754 | * call as the inode reclaim may be blocked waiting for | 2761 | * call as the inode reclaim may be blocked waiting for |
2755 | * the inode to become unpinned. | 2762 | * the inode to become unpinned. |
2756 | */ | 2763 | */ |
2764 | struct inode *inode = NULL; | ||
2765 | |||
2766 | spin_lock(&ip->i_flags_lock); | ||
2757 | if (!(ip->i_flags & (XFS_IRECLAIM|XFS_IRECLAIMABLE))) { | 2767 | if (!(ip->i_flags & (XFS_IRECLAIM|XFS_IRECLAIMABLE))) { |
2758 | bhv_vnode_t *vp = XFS_ITOV_NULL(ip); | 2768 | bhv_vnode_t *vp = XFS_ITOV_NULL(ip); |
2759 | 2769 | ||
2760 | /* make sync come back and flush this inode */ | 2770 | /* make sync come back and flush this inode */ |
2761 | if (vp) { | 2771 | if (vp) { |
2762 | struct inode *inode = vn_to_inode(vp); | 2772 | inode = vn_to_inode(vp); |
2763 | 2773 | ||
2764 | if (!(inode->i_state & | 2774 | if (!(inode->i_state & |
2765 | (I_NEW|I_FREEING|I_CLEAR))) | 2775 | (I_NEW|I_FREEING|I_CLEAR))) { |
2766 | mark_inode_dirty_sync(inode); | 2776 | inode = igrab(inode); |
2777 | if (inode) | ||
2778 | mark_inode_dirty_sync(inode); | ||
2779 | } else | ||
2780 | inode = NULL; | ||
2767 | } | 2781 | } |
2768 | } | 2782 | } |
2783 | spin_unlock(&ip->i_flags_lock); | ||
2769 | wake_up(&ip->i_ipin_wait); | 2784 | wake_up(&ip->i_ipin_wait); |
2785 | if (inode) | ||
2786 | iput(inode); | ||
2770 | } | 2787 | } |
2771 | } | 2788 | } |
2772 | 2789 | ||