xfs: fix potential log item leak
authorDave Chinner <dchinner@redhat.com>
Tue, 14 Feb 2023 21:25:26 +0000 (13:25 -0800)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 22 Feb 2023 11:57:03 +0000 (12:57 +0100)
[ Upstream commit c230a4a85bcdbfc1a7415deec6caf04e8fca1301 ]

Ever since we added shadown format buffers to the log items, log
items need to handle the item being released with shadow buffers
attached. Due to the fact this requirement was added at the same
time we added new rmap/reflink intents, we missed the cleanup of
those items.

In theory, this means shadow buffers can be leaked in a very small
window when a shutdown is initiated. Testing with KASAN shows this
leak does not happen in practice - we haven't identified a single
leak in several years of shutdown testing since ~v4.8 kernels.

However, the intent whiteout cleanup mechanism results in every
cancelled intent in exactly the same state as this tiny race window
creates and so if intents down clean up shadow buffers on final
release we will leak the shadow buffer for just about every intent
we create.

Hence we start with this patch to close this condition off and
ensure that when whiteouts start to be used we don't leak lots of
memory.

Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Allison Henderson <allison.henderson@oracle.com>
Signed-off-by: Dave Chinner <david@fromorbit.com>
Signed-off-by: Leah Rumancik <leah.rumancik@gmail.com>
Acked-by: Darrick J. Wong <djwong@kernel.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
fs/xfs/xfs_bmap_item.c
fs/xfs/xfs_icreate_item.c
fs/xfs/xfs_refcount_item.c
fs/xfs/xfs_rmap_item.c

index 03159970133ffed2821f7ebf916d9d41520a01ed..51ffdec5e4faa8a75feb64086d4988f94b82d5d0 100644 (file)
@@ -39,6 +39,7 @@ STATIC void
 xfs_bui_item_free(
        struct xfs_bui_log_item *buip)
 {
+       kmem_free(buip->bui_item.li_lv_shadow);
        kmem_cache_free(xfs_bui_zone, buip);
 }
 
@@ -198,6 +199,7 @@ xfs_bud_item_release(
        struct xfs_bud_log_item *budp = BUD_ITEM(lip);
 
        xfs_bui_release(budp->bud_buip);
+       kmem_free(budp->bud_item.li_lv_shadow);
        kmem_cache_free(xfs_bud_zone, budp);
 }
 
index 017904a34c023ce27f30a24e26a545d2a8310be6..c265ae20946d597df7bf6d55ab3b5677b78ca18c 100644 (file)
@@ -63,6 +63,7 @@ STATIC void
 xfs_icreate_item_release(
        struct xfs_log_item     *lip)
 {
+       kmem_free(ICR_ITEM(lip)->ic_item.li_lv_shadow);
        kmem_cache_free(xfs_icreate_zone, ICR_ITEM(lip));
 }
 
index 46904b793bd48317cd0ebf76d73c6da538451422..8ef842d17916ae27444835e3fb14abab38d6eadc 100644 (file)
@@ -35,6 +35,7 @@ STATIC void
 xfs_cui_item_free(
        struct xfs_cui_log_item *cuip)
 {
+       kmem_free(cuip->cui_item.li_lv_shadow);
        if (cuip->cui_format.cui_nextents > XFS_CUI_MAX_FAST_EXTENTS)
                kmem_free(cuip);
        else
@@ -204,6 +205,7 @@ xfs_cud_item_release(
        struct xfs_cud_log_item *cudp = CUD_ITEM(lip);
 
        xfs_cui_release(cudp->cud_cuip);
+       kmem_free(cudp->cud_item.li_lv_shadow);
        kmem_cache_free(xfs_cud_zone, cudp);
 }
 
index 5f06959804678dbb5b226244355a0947132056c9..15e7b01740a77429de12354b795e8cfd93ee9396 100644 (file)
@@ -35,6 +35,7 @@ STATIC void
 xfs_rui_item_free(
        struct xfs_rui_log_item *ruip)
 {
+       kmem_free(ruip->rui_item.li_lv_shadow);
        if (ruip->rui_format.rui_nextents > XFS_RUI_MAX_FAST_EXTENTS)
                kmem_free(ruip);
        else
@@ -227,6 +228,7 @@ xfs_rud_item_release(
        struct xfs_rud_log_item *rudp = RUD_ITEM(lip);
 
        xfs_rui_release(rudp->rud_ruip);
+       kmem_free(rudp->rud_item.li_lv_shadow);
        kmem_cache_free(xfs_rud_zone, rudp);
 }