aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorDavid Chinner <dgc@sgi.com>2006-09-27 21:06:03 -0400
committerTim Shimmin <tes@sgi.com>2006-09-27 21:06:03 -0400
commitf273ab848b7cbc0088b0ac7457b3769e6566074e (patch)
tree27f1b0ce7b056f77e7105284524cbdb658943ae5 /fs
parent01106eae97b70399ce5a273a3cceb5246e8d9cc8 (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')
-rw-r--r--fs/xfs/linux-2.6/xfs_super.c2
-rw-r--r--fs/xfs/xfs_iget.c6
-rw-r--r--fs/xfs/xfs_inode.c23
-rw-r--r--fs/xfs/xfs_inode.h1
-rw-r--r--fs/xfs/xfs_itable.c1
-rw-r--r--fs/xfs/xfs_vnodeops.c5
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 /*