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 | |
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>
-rw-r--r-- | fs/xfs/linux-2.6/xfs_super.c | 2 | ||||
-rw-r--r-- | fs/xfs/xfs_iget.c | 6 | ||||
-rw-r--r-- | fs/xfs/xfs_inode.c | 23 | ||||
-rw-r--r-- | fs/xfs/xfs_inode.h | 1 | ||||
-rw-r--r-- | fs/xfs/xfs_itable.c | 1 | ||||
-rw-r--r-- | fs/xfs/xfs_vnodeops.c | 5 |
6 files changed, 35 insertions, 3 deletions
diff --git a/fs/xfs/linux-2.6/xfs_super.c b/fs/xfs/linux-2.6/xfs_super.c index 9df9ed37d219..38c4d128a8c0 100644 --- a/fs/xfs/linux-2.6/xfs_super.c +++ b/fs/xfs/linux-2.6/xfs_super.c | |||
@@ -227,7 +227,9 @@ xfs_initialize_vnode( | |||
227 | xfs_revalidate_inode(XFS_BHVTOM(bdp), vp, ip); | 227 | xfs_revalidate_inode(XFS_BHVTOM(bdp), vp, ip); |
228 | xfs_set_inodeops(inode); | 228 | xfs_set_inodeops(inode); |
229 | 229 | ||
230 | spin_lock(&ip->i_flags_lock); | ||
230 | ip->i_flags &= ~XFS_INEW; | 231 | ip->i_flags &= ~XFS_INEW; |
232 | spin_unlock(&ip->i_flags_lock); | ||
231 | barrier(); | 233 | barrier(); |
232 | 234 | ||
233 | unlock_new_inode(inode); | 235 | unlock_new_inode(inode); |
diff --git a/fs/xfs/xfs_iget.c b/fs/xfs/xfs_iget.c index 9b9379792f37..b73d216ecaf9 100644 --- a/fs/xfs/xfs_iget.c +++ b/fs/xfs/xfs_iget.c | |||
@@ -243,7 +243,9 @@ again: | |||
243 | 243 | ||
244 | XFS_STATS_INC(xs_ig_found); | 244 | XFS_STATS_INC(xs_ig_found); |
245 | 245 | ||
246 | spin_lock(&ip->i_flags_lock); | ||
246 | ip->i_flags &= ~XFS_IRECLAIMABLE; | 247 | ip->i_flags &= ~XFS_IRECLAIMABLE; |
248 | spin_unlock(&ip->i_flags_lock); | ||
247 | version = ih->ih_version; | 249 | version = ih->ih_version; |
248 | read_unlock(&ih->ih_lock); | 250 | read_unlock(&ih->ih_lock); |
249 | xfs_ihash_promote(ih, ip, version); | 251 | xfs_ihash_promote(ih, ip, version); |
@@ -297,7 +299,9 @@ finish_inode: | |||
297 | if (lock_flags != 0) | 299 | if (lock_flags != 0) |
298 | xfs_ilock(ip, lock_flags); | 300 | xfs_ilock(ip, lock_flags); |
299 | 301 | ||
302 | spin_lock(&ip->i_flags_lock); | ||
300 | ip->i_flags &= ~XFS_ISTALE; | 303 | ip->i_flags &= ~XFS_ISTALE; |
304 | spin_unlock(&ip->i_flags_lock); | ||
301 | 305 | ||
302 | vn_trace_exit(vp, "xfs_iget.found", | 306 | vn_trace_exit(vp, "xfs_iget.found", |
303 | (inst_t *)__return_address); | 307 | (inst_t *)__return_address); |
@@ -367,7 +371,9 @@ finish_inode: | |||
367 | ih->ih_next = ip; | 371 | ih->ih_next = ip; |
368 | ip->i_udquot = ip->i_gdquot = NULL; | 372 | ip->i_udquot = ip->i_gdquot = NULL; |
369 | ih->ih_version++; | 373 | ih->ih_version++; |
374 | spin_lock(&ip->i_flags_lock); | ||
370 | ip->i_flags |= XFS_INEW; | 375 | ip->i_flags |= XFS_INEW; |
376 | spin_unlock(&ip->i_flags_lock); | ||
371 | 377 | ||
372 | write_unlock(&ih->ih_lock); | 378 | write_unlock(&ih->ih_lock); |
373 | 379 | ||
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 | ||
diff --git a/fs/xfs/xfs_inode.h b/fs/xfs/xfs_inode.h index fdb89f72db95..e96eb0835fe6 100644 --- a/fs/xfs/xfs_inode.h +++ b/fs/xfs/xfs_inode.h | |||
@@ -267,6 +267,7 @@ typedef struct xfs_inode { | |||
267 | sema_t i_flock; /* inode flush lock */ | 267 | sema_t i_flock; /* inode flush lock */ |
268 | atomic_t i_pincount; /* inode pin count */ | 268 | atomic_t i_pincount; /* inode pin count */ |
269 | wait_queue_head_t i_ipin_wait; /* inode pinning wait queue */ | 269 | wait_queue_head_t i_ipin_wait; /* inode pinning wait queue */ |
270 | spinlock_t i_flags_lock; /* inode i_flags lock */ | ||
270 | #ifdef HAVE_REFCACHE | 271 | #ifdef HAVE_REFCACHE |
271 | struct xfs_inode **i_refcache; /* ptr to entry in ref cache */ | 272 | struct xfs_inode **i_refcache; /* ptr to entry in ref cache */ |
272 | struct xfs_inode *i_release; /* inode to unref */ | 273 | struct xfs_inode *i_release; /* inode to unref */ |
diff --git a/fs/xfs/xfs_itable.c b/fs/xfs/xfs_itable.c index b608b8ab1dd6..136c6b06f1c9 100644 --- a/fs/xfs/xfs_itable.c +++ b/fs/xfs/xfs_itable.c | |||
@@ -577,6 +577,7 @@ xfs_bulkstat( | |||
577 | KM_SLEEP); | 577 | KM_SLEEP); |
578 | ip->i_ino = ino; | 578 | ip->i_ino = ino; |
579 | ip->i_mount = mp; | 579 | ip->i_mount = mp; |
580 | spin_lock_init(&ip->i_flags_lock); | ||
580 | if (bp) | 581 | if (bp) |
581 | xfs_buf_relse(bp); | 582 | xfs_buf_relse(bp); |
582 | error = xfs_itobp(mp, NULL, ip, | 583 | error = xfs_itobp(mp, NULL, ip, |
diff --git a/fs/xfs/xfs_vnodeops.c b/fs/xfs/xfs_vnodeops.c index 1a6782eaf5d4..061e2ffdd1de 100644 --- a/fs/xfs/xfs_vnodeops.c +++ b/fs/xfs/xfs_vnodeops.c | |||
@@ -3844,7 +3844,9 @@ xfs_reclaim( | |||
3844 | XFS_MOUNT_ILOCK(mp); | 3844 | XFS_MOUNT_ILOCK(mp); |
3845 | vn_bhv_remove(VN_BHV_HEAD(vp), XFS_ITOBHV(ip)); | 3845 | vn_bhv_remove(VN_BHV_HEAD(vp), XFS_ITOBHV(ip)); |
3846 | list_add_tail(&ip->i_reclaim, &mp->m_del_inodes); | 3846 | list_add_tail(&ip->i_reclaim, &mp->m_del_inodes); |
3847 | spin_lock(&ip->i_flags_lock); | ||
3847 | ip->i_flags |= XFS_IRECLAIMABLE; | 3848 | ip->i_flags |= XFS_IRECLAIMABLE; |
3849 | spin_unlock(&ip->i_flags_lock); | ||
3848 | XFS_MOUNT_IUNLOCK(mp); | 3850 | XFS_MOUNT_IUNLOCK(mp); |
3849 | } | 3851 | } |
3850 | return 0; | 3852 | return 0; |
@@ -3869,8 +3871,10 @@ xfs_finish_reclaim( | |||
3869 | * us. | 3871 | * us. |
3870 | */ | 3872 | */ |
3871 | write_lock(&ih->ih_lock); | 3873 | write_lock(&ih->ih_lock); |
3874 | spin_lock(&ip->i_flags_lock); | ||
3872 | if ((ip->i_flags & XFS_IRECLAIM) || | 3875 | if ((ip->i_flags & XFS_IRECLAIM) || |
3873 | (!(ip->i_flags & XFS_IRECLAIMABLE) && vp == NULL)) { | 3876 | (!(ip->i_flags & XFS_IRECLAIMABLE) && vp == NULL)) { |
3877 | spin_unlock(&ip->i_flags_lock); | ||
3874 | write_unlock(&ih->ih_lock); | 3878 | write_unlock(&ih->ih_lock); |
3875 | if (locked) { | 3879 | if (locked) { |
3876 | xfs_ifunlock(ip); | 3880 | xfs_ifunlock(ip); |
@@ -3879,6 +3883,7 @@ xfs_finish_reclaim( | |||
3879 | return 1; | 3883 | return 1; |
3880 | } | 3884 | } |
3881 | ip->i_flags |= XFS_IRECLAIM; | 3885 | ip->i_flags |= XFS_IRECLAIM; |
3886 | spin_unlock(&ip->i_flags_lock); | ||
3882 | write_unlock(&ih->ih_lock); | 3887 | write_unlock(&ih->ih_lock); |
3883 | 3888 | ||
3884 | /* | 3889 | /* |