aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDave Chinner <dchinner@redhat.com>2018-04-02 23:08:27 -0400
committerDarrick J. Wong <darrick.wong@oracle.com>2018-04-02 23:08:27 -0400
commit0612d1166330697d91b8d2d1e71e41485bb0b18e (patch)
tree3f7110808ec01042bf6ad6756ccb1e5752f7b7b6
parentc959025edad924c8f4a8a3140221f3cde22243db (diff)
xfs: fix intent use-after-free on abort
When an intent is aborted during it's initial commit through xfs_defer_trans_abort(), there is a use after free. The current report is for a RUI through this path in generic/388: Freed by task 6274: __kasan_slab_free+0x136/0x180 kmem_cache_free+0xe7/0x4b0 xfs_trans_free_items+0x198/0x2e0 __xfs_trans_commit+0x27f/0xcc0 xfs_trans_roll+0x17b/0x2a0 xfs_defer_trans_roll+0x6ad/0xe60 xfs_defer_finish+0x2a6/0x2140 xfs_alloc_file_space+0x53a/0xf90 xfs_file_fallocate+0x5c6/0xac0 vfs_fallocate+0x2f5/0x930 ioctl_preallocate+0x1dc/0x320 do_vfs_ioctl+0xfe4/0x1690 The problem is that the RUI has two active references - one in the current transaction, and another held by the defer_ops structure that is passed to the RUD (intent done) so that both the intent and the intent done structures are freed on commit of the intent done. Hence during abort, we need to release the intent item, because the defer_ops reference is released separately via ->abort_intent callback. Fix all the intent code to do this correctly. Signed-Off-By: Dave Chinner <dchinner@redhat.com> Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com> Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
-rw-r--r--fs/xfs/xfs_bmap_item.c39
-rw-r--r--fs/xfs/xfs_extfree_item.c38
-rw-r--r--fs/xfs/xfs_refcount_item.c39
-rw-r--r--fs/xfs/xfs_rmap_item.c38
4 files changed, 78 insertions, 76 deletions
diff --git a/fs/xfs/xfs_bmap_item.c b/fs/xfs/xfs_bmap_item.c
index e5fb008d75e8..2203465e63ea 100644
--- a/fs/xfs/xfs_bmap_item.c
+++ b/fs/xfs/xfs_bmap_item.c
@@ -53,6 +53,25 @@ xfs_bui_item_free(
53 kmem_zone_free(xfs_bui_zone, buip); 53 kmem_zone_free(xfs_bui_zone, buip);
54} 54}
55 55
56/*
57 * Freeing the BUI requires that we remove it from the AIL if it has already
58 * been placed there. However, the BUI may not yet have been placed in the AIL
59 * when called by xfs_bui_release() from BUD processing due to the ordering of
60 * committed vs unpin operations in bulk insert operations. Hence the reference
61 * count to ensure only the last caller frees the BUI.
62 */
63void
64xfs_bui_release(
65 struct xfs_bui_log_item *buip)
66{
67 ASSERT(atomic_read(&buip->bui_refcount) > 0);
68 if (atomic_dec_and_test(&buip->bui_refcount)) {
69 xfs_trans_ail_remove(&buip->bui_item, SHUTDOWN_LOG_IO_ERROR);
70 xfs_bui_item_free(buip);
71 }
72}
73
74
56STATIC void 75STATIC void
57xfs_bui_item_size( 76xfs_bui_item_size(
58 struct xfs_log_item *lip, 77 struct xfs_log_item *lip,
@@ -142,7 +161,7 @@ xfs_bui_item_unlock(
142 struct xfs_log_item *lip) 161 struct xfs_log_item *lip)
143{ 162{
144 if (lip->li_flags & XFS_LI_ABORTED) 163 if (lip->li_flags & XFS_LI_ABORTED)
145 xfs_bui_item_free(BUI_ITEM(lip)); 164 xfs_bui_release(BUI_ITEM(lip));
146} 165}
147 166
148/* 167/*
@@ -206,24 +225,6 @@ xfs_bui_init(
206 return buip; 225 return buip;
207} 226}
208 227
209/*
210 * Freeing the BUI requires that we remove it from the AIL if it has already
211 * been placed there. However, the BUI may not yet have been placed in the AIL
212 * when called by xfs_bui_release() from BUD processing due to the ordering of
213 * committed vs unpin operations in bulk insert operations. Hence the reference
214 * count to ensure only the last caller frees the BUI.
215 */
216void
217xfs_bui_release(
218 struct xfs_bui_log_item *buip)
219{
220 ASSERT(atomic_read(&buip->bui_refcount) > 0);
221 if (atomic_dec_and_test(&buip->bui_refcount)) {
222 xfs_trans_ail_remove(&buip->bui_item, SHUTDOWN_LOG_IO_ERROR);
223 xfs_bui_item_free(buip);
224 }
225}
226
227static inline struct xfs_bud_log_item *BUD_ITEM(struct xfs_log_item *lip) 228static inline struct xfs_bud_log_item *BUD_ITEM(struct xfs_log_item *lip)
228{ 229{
229 return container_of(lip, struct xfs_bud_log_item, bud_item); 230 return container_of(lip, struct xfs_bud_log_item, bud_item);
diff --git a/fs/xfs/xfs_extfree_item.c b/fs/xfs/xfs_extfree_item.c
index 64da90655e95..b5b1e567b9f4 100644
--- a/fs/xfs/xfs_extfree_item.c
+++ b/fs/xfs/xfs_extfree_item.c
@@ -51,6 +51,24 @@ xfs_efi_item_free(
51} 51}
52 52
53/* 53/*
54 * Freeing the efi requires that we remove it from the AIL if it has already
55 * been placed there. However, the EFI may not yet have been placed in the AIL
56 * when called by xfs_efi_release() from EFD processing due to the ordering of
57 * committed vs unpin operations in bulk insert operations. Hence the reference
58 * count to ensure only the last caller frees the EFI.
59 */
60void
61xfs_efi_release(
62 struct xfs_efi_log_item *efip)
63{
64 ASSERT(atomic_read(&efip->efi_refcount) > 0);
65 if (atomic_dec_and_test(&efip->efi_refcount)) {
66 xfs_trans_ail_remove(&efip->efi_item, SHUTDOWN_LOG_IO_ERROR);
67 xfs_efi_item_free(efip);
68 }
69}
70
71/*
54 * This returns the number of iovecs needed to log the given efi item. 72 * This returns the number of iovecs needed to log the given efi item.
55 * We only need 1 iovec for an efi item. It just logs the efi_log_format 73 * We only need 1 iovec for an efi item. It just logs the efi_log_format
56 * structure. 74 * structure.
@@ -151,7 +169,7 @@ xfs_efi_item_unlock(
151 struct xfs_log_item *lip) 169 struct xfs_log_item *lip)
152{ 170{
153 if (lip->li_flags & XFS_LI_ABORTED) 171 if (lip->li_flags & XFS_LI_ABORTED)
154 xfs_efi_item_free(EFI_ITEM(lip)); 172 xfs_efi_release(EFI_ITEM(lip));
155} 173}
156 174
157/* 175/*
@@ -279,24 +297,6 @@ xfs_efi_copy_format(xfs_log_iovec_t *buf, xfs_efi_log_format_t *dst_efi_fmt)
279 return -EFSCORRUPTED; 297 return -EFSCORRUPTED;
280} 298}
281 299
282/*
283 * Freeing the efi requires that we remove it from the AIL if it has already
284 * been placed there. However, the EFI may not yet have been placed in the AIL
285 * when called by xfs_efi_release() from EFD processing due to the ordering of
286 * committed vs unpin operations in bulk insert operations. Hence the reference
287 * count to ensure only the last caller frees the EFI.
288 */
289void
290xfs_efi_release(
291 struct xfs_efi_log_item *efip)
292{
293 ASSERT(atomic_read(&efip->efi_refcount) > 0);
294 if (atomic_dec_and_test(&efip->efi_refcount)) {
295 xfs_trans_ail_remove(&efip->efi_item, SHUTDOWN_LOG_IO_ERROR);
296 xfs_efi_item_free(efip);
297 }
298}
299
300static inline struct xfs_efd_log_item *EFD_ITEM(struct xfs_log_item *lip) 300static inline struct xfs_efd_log_item *EFD_ITEM(struct xfs_log_item *lip)
301{ 301{
302 return container_of(lip, struct xfs_efd_log_item, efd_item); 302 return container_of(lip, struct xfs_efd_log_item, efd_item);
diff --git a/fs/xfs/xfs_refcount_item.c b/fs/xfs/xfs_refcount_item.c
index 7a39f40645f7..15c9393dd7a7 100644
--- a/fs/xfs/xfs_refcount_item.c
+++ b/fs/xfs/xfs_refcount_item.c
@@ -52,6 +52,25 @@ xfs_cui_item_free(
52 kmem_zone_free(xfs_cui_zone, cuip); 52 kmem_zone_free(xfs_cui_zone, cuip);
53} 53}
54 54
55/*
56 * Freeing the CUI requires that we remove it from the AIL if it has already
57 * been placed there. However, the CUI may not yet have been placed in the AIL
58 * when called by xfs_cui_release() from CUD processing due to the ordering of
59 * committed vs unpin operations in bulk insert operations. Hence the reference
60 * count to ensure only the last caller frees the CUI.
61 */
62void
63xfs_cui_release(
64 struct xfs_cui_log_item *cuip)
65{
66 ASSERT(atomic_read(&cuip->cui_refcount) > 0);
67 if (atomic_dec_and_test(&cuip->cui_refcount)) {
68 xfs_trans_ail_remove(&cuip->cui_item, SHUTDOWN_LOG_IO_ERROR);
69 xfs_cui_item_free(cuip);
70 }
71}
72
73
55STATIC void 74STATIC void
56xfs_cui_item_size( 75xfs_cui_item_size(
57 struct xfs_log_item *lip, 76 struct xfs_log_item *lip,
@@ -141,7 +160,7 @@ xfs_cui_item_unlock(
141 struct xfs_log_item *lip) 160 struct xfs_log_item *lip)
142{ 161{
143 if (lip->li_flags & XFS_LI_ABORTED) 162 if (lip->li_flags & XFS_LI_ABORTED)
144 xfs_cui_item_free(CUI_ITEM(lip)); 163 xfs_cui_release(CUI_ITEM(lip));
145} 164}
146 165
147/* 166/*
@@ -211,24 +230,6 @@ xfs_cui_init(
211 return cuip; 230 return cuip;
212} 231}
213 232
214/*
215 * Freeing the CUI requires that we remove it from the AIL if it has already
216 * been placed there. However, the CUI may not yet have been placed in the AIL
217 * when called by xfs_cui_release() from CUD processing due to the ordering of
218 * committed vs unpin operations in bulk insert operations. Hence the reference
219 * count to ensure only the last caller frees the CUI.
220 */
221void
222xfs_cui_release(
223 struct xfs_cui_log_item *cuip)
224{
225 ASSERT(atomic_read(&cuip->cui_refcount) > 0);
226 if (atomic_dec_and_test(&cuip->cui_refcount)) {
227 xfs_trans_ail_remove(&cuip->cui_item, SHUTDOWN_LOG_IO_ERROR);
228 xfs_cui_item_free(cuip);
229 }
230}
231
232static inline struct xfs_cud_log_item *CUD_ITEM(struct xfs_log_item *lip) 233static inline struct xfs_cud_log_item *CUD_ITEM(struct xfs_log_item *lip)
233{ 234{
234 return container_of(lip, struct xfs_cud_log_item, cud_item); 235 return container_of(lip, struct xfs_cud_log_item, cud_item);
diff --git a/fs/xfs/xfs_rmap_item.c b/fs/xfs/xfs_rmap_item.c
index 49d3124863a8..06a07846c9b3 100644
--- a/fs/xfs/xfs_rmap_item.c
+++ b/fs/xfs/xfs_rmap_item.c
@@ -52,6 +52,24 @@ xfs_rui_item_free(
52 kmem_zone_free(xfs_rui_zone, ruip); 52 kmem_zone_free(xfs_rui_zone, ruip);
53} 53}
54 54
55/*
56 * Freeing the RUI requires that we remove it from the AIL if it has already
57 * been placed there. However, the RUI may not yet have been placed in the AIL
58 * when called by xfs_rui_release() from RUD processing due to the ordering of
59 * committed vs unpin operations in bulk insert operations. Hence the reference
60 * count to ensure only the last caller frees the RUI.
61 */
62void
63xfs_rui_release(
64 struct xfs_rui_log_item *ruip)
65{
66 ASSERT(atomic_read(&ruip->rui_refcount) > 0);
67 if (atomic_dec_and_test(&ruip->rui_refcount)) {
68 xfs_trans_ail_remove(&ruip->rui_item, SHUTDOWN_LOG_IO_ERROR);
69 xfs_rui_item_free(ruip);
70 }
71}
72
55STATIC void 73STATIC void
56xfs_rui_item_size( 74xfs_rui_item_size(
57 struct xfs_log_item *lip, 75 struct xfs_log_item *lip,
@@ -141,7 +159,7 @@ xfs_rui_item_unlock(
141 struct xfs_log_item *lip) 159 struct xfs_log_item *lip)
142{ 160{
143 if (lip->li_flags & XFS_LI_ABORTED) 161 if (lip->li_flags & XFS_LI_ABORTED)
144 xfs_rui_item_free(RUI_ITEM(lip)); 162 xfs_rui_release(RUI_ITEM(lip));
145} 163}
146 164
147/* 165/*
@@ -233,24 +251,6 @@ xfs_rui_copy_format(
233 return 0; 251 return 0;
234} 252}
235 253
236/*
237 * Freeing the RUI requires that we remove it from the AIL if it has already
238 * been placed there. However, the RUI may not yet have been placed in the AIL
239 * when called by xfs_rui_release() from RUD processing due to the ordering of
240 * committed vs unpin operations in bulk insert operations. Hence the reference
241 * count to ensure only the last caller frees the RUI.
242 */
243void
244xfs_rui_release(
245 struct xfs_rui_log_item *ruip)
246{
247 ASSERT(atomic_read(&ruip->rui_refcount) > 0);
248 if (atomic_dec_and_test(&ruip->rui_refcount)) {
249 xfs_trans_ail_remove(&ruip->rui_item, SHUTDOWN_LOG_IO_ERROR);
250 xfs_rui_item_free(ruip);
251 }
252}
253
254static inline struct xfs_rud_log_item *RUD_ITEM(struct xfs_log_item *lip) 254static inline struct xfs_rud_log_item *RUD_ITEM(struct xfs_log_item *lip)
255{ 255{
256 return container_of(lip, struct xfs_rud_log_item, rud_item); 256 return container_of(lip, struct xfs_rud_log_item, rud_item);