diff options
author | Dave Chinner <david@fromorbit.com> | 2009-04-06 12:40:17 -0400 |
---|---|---|
committer | Christoph Hellwig <hch@brick.lst.de> | 2009-04-06 12:40:17 -0400 |
commit | 705db3fd4660174a27418bbcb874d209a76044eb (patch) | |
tree | 84ec380710246cb3b97cd46ee57b3555d3afd07b | |
parent | a6cb767e24b1dbedfcfa8077eab0aa2eab224038 (diff) |
xfs: fix double free of inode
If we fail to initialise the VFS inode in inode_init_always(),
it will call ->delete_inode internally resulting in the inode being
freed. Hence we need to delay the call to inode_init_always()
until after the XFS inode is sufficient set up to handle a
call to ->delete_inode, and then if that fails do not touch
the inode again at all as it has been freed.
Signed-off-by: Dave Chinner <david@fromorbit.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
-rw-r--r-- | fs/xfs/xfs_iget.c | 23 |
1 files changed, 14 insertions, 9 deletions
diff --git a/fs/xfs/xfs_iget.c b/fs/xfs/xfs_iget.c index 478e587087fe..89b81eedce6a 100644 --- a/fs/xfs/xfs_iget.c +++ b/fs/xfs/xfs_iget.c | |||
@@ -69,15 +69,6 @@ xfs_inode_alloc( | |||
69 | ASSERT(!spin_is_locked(&ip->i_flags_lock)); | 69 | ASSERT(!spin_is_locked(&ip->i_flags_lock)); |
70 | ASSERT(completion_done(&ip->i_flush)); | 70 | ASSERT(completion_done(&ip->i_flush)); |
71 | 71 | ||
72 | /* | ||
73 | * initialise the VFS inode here to get failures | ||
74 | * out of the way early. | ||
75 | */ | ||
76 | if (!inode_init_always(mp->m_super, VFS_I(ip))) { | ||
77 | kmem_zone_free(xfs_inode_zone, ip); | ||
78 | return NULL; | ||
79 | } | ||
80 | |||
81 | /* initialise the xfs inode */ | 72 | /* initialise the xfs inode */ |
82 | ip->i_ino = ino; | 73 | ip->i_ino = ino; |
83 | ip->i_mount = mp; | 74 | ip->i_mount = mp; |
@@ -113,6 +104,20 @@ xfs_inode_alloc( | |||
113 | #ifdef XFS_DIR2_TRACE | 104 | #ifdef XFS_DIR2_TRACE |
114 | ip->i_dir_trace = ktrace_alloc(XFS_DIR2_KTRACE_SIZE, KM_NOFS); | 105 | ip->i_dir_trace = ktrace_alloc(XFS_DIR2_KTRACE_SIZE, KM_NOFS); |
115 | #endif | 106 | #endif |
107 | /* | ||
108 | * Now initialise the VFS inode. We do this after the xfs_inode | ||
109 | * initialisation as internal failures will result in ->destroy_inode | ||
110 | * being called and that will pass down through the reclaim path and | ||
111 | * free the XFS inode. This path requires the XFS inode to already be | ||
112 | * initialised. Hence if this call fails, the xfs_inode has already | ||
113 | * been freed and we should not reference it at all in the error | ||
114 | * handling. | ||
115 | */ | ||
116 | if (!inode_init_always(mp->m_super, VFS_I(ip))) | ||
117 | return NULL; | ||
118 | |||
119 | /* prevent anyone from using this yet */ | ||
120 | VFS_I(ip)->i_state = I_NEW|I_LOCK; | ||
116 | 121 | ||
117 | return ip; | 122 | return ip; |
118 | } | 123 | } |