* [PATCH v2 0/2] btrfs: fix an on-stack path leak and migrate to auto-release for on-stack paths
@ 2025-11-25 21:50 Qu Wenruo
2025-11-25 21:50 ` [PATCH v2 1/2] btrfs: fix a potential path leak in print_data_reloc_error() Qu Wenruo
` (2 more replies)
0 siblings, 3 replies; 6+ messages in thread
From: Qu Wenruo @ 2025-11-25 21:50 UTC (permalink / raw)
To: linux-btrfs
[CHANGELOG]
v2:
- Add back an early btrfs_release_path() call in
print_data_reloc_error()
This is to avoid holding the path (and its extent buffers locked)
during time consuming iterate_extent_inodes() calls.
And add a comment on that early release, with updated commit message.
I thought patch "btrfs: make sure extent and csum paths are always released in
scrub_raid56_parity_stripe()" has already taught us that tag based
manual cleanup is never reliable, now there is another similar bug in
print_data_reloc_error().
This time it is harder to expose, as we always imply if the function
returned an error, they should do the proper cleanup.
But extent_to_logical() does not follow that assumption.
The first patch is the minimal fix for backport, the second patch is
going to solve the problem by using auto-release for all on-stack btrfs
paths.
Qu Wenruo (2):
btrfs: fix a potential path leak in print_data_reloc_error()
btrfs: introduce BTRFS_PATH_AUTO_RELEASE() helper
fs/btrfs/ctree.h | 9 +++++++++
fs/btrfs/defrag.c | 5 +----
fs/btrfs/inode.c | 7 +++++--
fs/btrfs/scrub.c | 18 ++++++------------
4 files changed, 21 insertions(+), 18 deletions(-)
--
2.52.0
^ permalink raw reply [flat|nested] 6+ messages in thread* [PATCH v2 1/2] btrfs: fix a potential path leak in print_data_reloc_error() 2025-11-25 21:50 [PATCH v2 0/2] btrfs: fix an on-stack path leak and migrate to auto-release for on-stack paths Qu Wenruo @ 2025-11-25 21:50 ` Qu Wenruo 2025-11-25 21:50 ` [PATCH v2 2/2] btrfs: introduce BTRFS_PATH_AUTO_RELEASE() helper Qu Wenruo 2025-12-08 20:56 ` [PATCH v2 0/2] btrfs: fix an on-stack path leak and migrate to auto-release for on-stack paths David Sterba 2 siblings, 0 replies; 6+ messages in thread From: Qu Wenruo @ 2025-11-25 21:50 UTC (permalink / raw) To: linux-btrfs Inside print_data_reloc_error(), if extent_from_logical() failed we return immediately. However there are the following cases where extent_from_logical() can return error but still holds a path: - btrfs_search_slot() returned 0 - No backref item found in extent tree - No @flags_ret provided This is not possible in this call site though. So for the above two cases, we can return without releasing the path, causing extent buffer leaks. Fixes: b9a9a85059cd ("btrfs: output affected files when relocation fails") Signed-off-by: Qu Wenruo <wqu@suse.com> --- fs/btrfs/inode.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 3cf30abcdb08..46b5d29f7e29 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -255,6 +255,7 @@ static void print_data_reloc_error(const struct btrfs_inode *inode, u64 file_off if (ret < 0) { btrfs_err_rl(fs_info, "failed to lookup extent item for logical %llu: %d", logical, ret); + btrfs_release_path(&path); return; } eb = path.nodes[0]; -- 2.52.0 ^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH v2 2/2] btrfs: introduce BTRFS_PATH_AUTO_RELEASE() helper 2025-11-25 21:50 [PATCH v2 0/2] btrfs: fix an on-stack path leak and migrate to auto-release for on-stack paths Qu Wenruo 2025-11-25 21:50 ` [PATCH v2 1/2] btrfs: fix a potential path leak in print_data_reloc_error() Qu Wenruo @ 2025-11-25 21:50 ` Qu Wenruo 2025-11-26 14:27 ` David Sterba 2025-12-08 20:56 ` [PATCH v2 0/2] btrfs: fix an on-stack path leak and migrate to auto-release for on-stack paths David Sterba 2 siblings, 1 reply; 6+ messages in thread From: Qu Wenruo @ 2025-11-25 21:50 UTC (permalink / raw) To: linux-btrfs There are already several bugs with on-stack btrfs_path involved, even it is already a little safer than btrfs_path pointers (only leaks the extent buffers, not the btrfs_path structure itself) - Patch "btrfs: make sure extent and csum paths are always released in scrub_raid56_parity_stripe()" Which is going into v6.19 release cycle. - Patch "btrfs: fix a potential path leak in print_datA_reloc_error()" The previous patch in the series. Thus there is a real need to apply auto release for those on-stack paths. This patch introduces a new macro, BTRFS_PATH_AUTO_RELEASE(), which defines one on-stack btrfs_path structure, initialize it all to 0, then call btrfs_release_path() on it when exiting the scope. This will applies to all 3 on-stack path usages: - defrag_get_extent() in defrag.c - print_data_reloc_error() in inode.c There is a special case where we want to release the path early before the time consuming iterate_extent_inodes() call, thus that manual early release is kept as is, with an extra comment added. - scrub_radi56_parity_stripe() in scrub.c Signed-off-by: Qu Wenruo <wqu@suse.com> --- fs/btrfs/ctree.h | 9 +++++++++ fs/btrfs/defrag.c | 5 +---- fs/btrfs/inode.c | 8 +++++--- fs/btrfs/scrub.c | 18 ++++++------------ 4 files changed, 21 insertions(+), 19 deletions(-) diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index 692370fc07b2..d5bd01c4e414 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h @@ -85,6 +85,14 @@ struct btrfs_path { #define BTRFS_PATH_AUTO_FREE(path_name) \ struct btrfs_path *path_name __free(btrfs_free_path) = NULL +/* + * This defines an on-stack path that will be auto released exiting the scope. + * + * This one is compatible with any existing manual btrfs_release_path() calls. + */ +#define BTRFS_PATH_AUTO_RELEASE(path_name) \ + struct btrfs_path path_name __free(btrfs_release_path) = { 0 } + /* * The state of btrfs root */ @@ -601,6 +609,7 @@ void btrfs_release_path(struct btrfs_path *p); struct btrfs_path *btrfs_alloc_path(void); void btrfs_free_path(struct btrfs_path *p); DEFINE_FREE(btrfs_free_path, struct btrfs_path *, btrfs_free_path(_T)) +DEFINE_FREE(btrfs_release_path, struct btrfs_path, btrfs_release_path(&_T)) int btrfs_del_items(struct btrfs_trans_handle *trans, struct btrfs_root *root, struct btrfs_path *path, int slot, int nr); diff --git a/fs/btrfs/defrag.c b/fs/btrfs/defrag.c index 2e3c011d410a..02a2e7736da0 100644 --- a/fs/btrfs/defrag.c +++ b/fs/btrfs/defrag.c @@ -610,7 +610,7 @@ static struct extent_map *defrag_get_extent(struct btrfs_inode *inode, { struct btrfs_root *root = inode->root; struct btrfs_file_extent_item *fi; - struct btrfs_path path = { 0 }; + BTRFS_PATH_AUTO_RELEASE(path); struct extent_map *em; struct btrfs_key key; u64 ino = btrfs_ino(inode); @@ -721,16 +721,13 @@ static struct extent_map *defrag_get_extent(struct btrfs_inode *inode, if (ret > 0) goto not_found; } - btrfs_release_path(&path); return em; not_found: - btrfs_release_path(&path); btrfs_free_extent_map(em); return NULL; err: - btrfs_release_path(&path); btrfs_free_extent_map(em); return ERR_PTR(ret); } diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 46b5d29f7e29..6ae94b7777bb 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -217,7 +217,7 @@ static void print_data_reloc_error(const struct btrfs_inode *inode, u64 file_off int mirror_num) { struct btrfs_fs_info *fs_info = inode->root->fs_info; - struct btrfs_path path = { 0 }; + BTRFS_PATH_AUTO_RELEASE(path); struct btrfs_key found_key = { 0 }; struct extent_buffer *eb; struct btrfs_extent_item *ei; @@ -255,7 +255,6 @@ static void print_data_reloc_error(const struct btrfs_inode *inode, u64 file_off if (ret < 0) { btrfs_err_rl(fs_info, "failed to lookup extent item for logical %llu: %d", logical, ret); - btrfs_release_path(&path); return; } eb = path.nodes[0]; @@ -285,11 +284,14 @@ static void print_data_reloc_error(const struct btrfs_inode *inode, u64 file_off (ref_level ? "node" : "leaf"), ref_level, ref_root); } - btrfs_release_path(&path); } else { struct btrfs_backref_walk_ctx ctx = { 0 }; struct data_reloc_warn reloc_warn = { 0 }; + /* + * Do not hold the path as later iterate_extent_inodes() call + * can be time consuming. + */ btrfs_release_path(&path); ctx.bytenr = found_key.objectid; diff --git a/fs/btrfs/scrub.c b/fs/btrfs/scrub.c index 41ead4a929b0..de09cfeb3bf1 100644 --- a/fs/btrfs/scrub.c +++ b/fs/btrfs/scrub.c @@ -2173,8 +2173,8 @@ static int scrub_raid56_parity_stripe(struct scrub_ctx *sctx, u64 full_stripe_start) { struct btrfs_fs_info *fs_info = sctx->fs_info; - struct btrfs_path extent_path = { 0 }; - struct btrfs_path csum_path = { 0 }; + BTRFS_PATH_AUTO_RELEASE(extent_path); + BTRFS_PATH_AUTO_RELEASE(csum_path); struct scrub_stripe *stripe; bool all_empty = true; const int data_stripes = nr_data_stripes(map); @@ -2226,7 +2226,7 @@ static int scrub_raid56_parity_stripe(struct scrub_ctx *sctx, full_stripe_start + btrfs_stripe_nr_to_offset(i), BTRFS_STRIPE_LEN, stripe); if (ret < 0) - goto out; + return ret; /* * No extent in this data stripe, need to manually mark them * initialized to make later read submission happy. @@ -2248,10 +2248,8 @@ static int scrub_raid56_parity_stripe(struct scrub_ctx *sctx, break; } } - if (all_empty) { - ret = 0; - goto out; - } + if (all_empty) + return 0; for (int i = 0; i < data_stripes; i++) { stripe = &sctx->raid56_data_stripes[i]; @@ -2292,8 +2290,7 @@ static int scrub_raid56_parity_stripe(struct scrub_ctx *sctx, "scrub: unrepaired sectors detected, full stripe %llu data stripe %u errors %*pbl", full_stripe_start, i, stripe->nr_sectors, &error); - ret = -EIO; - goto out; + return ret; } bitmap_or(&extent_bitmap, &extent_bitmap, &has_extent, stripe->nr_sectors); @@ -2302,9 +2299,6 @@ static int scrub_raid56_parity_stripe(struct scrub_ctx *sctx, /* Now we can check and regenerate the P/Q stripe. */ ret = scrub_raid56_cached_parity(sctx, scrub_dev, map, full_stripe_start, &extent_bitmap); -out: - btrfs_release_path(&extent_path); - btrfs_release_path(&csum_path); return ret; } -- 2.52.0 ^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [PATCH v2 2/2] btrfs: introduce BTRFS_PATH_AUTO_RELEASE() helper 2025-11-25 21:50 ` [PATCH v2 2/2] btrfs: introduce BTRFS_PATH_AUTO_RELEASE() helper Qu Wenruo @ 2025-11-26 14:27 ` David Sterba 2025-11-26 20:40 ` Qu Wenruo 0 siblings, 1 reply; 6+ messages in thread From: David Sterba @ 2025-11-26 14:27 UTC (permalink / raw) To: Qu Wenruo; +Cc: linux-btrfs On Wed, Nov 26, 2025 at 08:20:21AM +1030, Qu Wenruo wrote: > There are already several bugs with on-stack btrfs_path involved, even > it is already a little safer than btrfs_path pointers (only leaks the > extent buffers, not the btrfs_path structure itself) > > - Patch "btrfs: make sure extent and csum paths are always released in > scrub_raid56_parity_stripe()" > Which is going into v6.19 release cycle. > > - Patch "btrfs: fix a potential path leak in print_datA_reloc_error()" > The previous patch in the series. > > Thus there is a real need to apply auto release for those on-stack paths. > > This patch introduces a new macro, BTRFS_PATH_AUTO_RELEASE(), which > defines one on-stack btrfs_path structure, initialize it all to 0, then > call btrfs_release_path() on it when exiting the scope. > > This will applies to all 3 on-stack path usages: > > - defrag_get_extent() in defrag.c > > - print_data_reloc_error() in inode.c > There is a special case where we want to release the path early before > the time consuming iterate_extent_inodes() call, thus that manual > early release is kept as is, with an extra comment added. > > - scrub_radi56_parity_stripe() in scrub.c > > Signed-off-by: Qu Wenruo <wqu@suse.com> > --- > fs/btrfs/ctree.h | 9 +++++++++ > fs/btrfs/defrag.c | 5 +---- > fs/btrfs/inode.c | 8 +++++--- > fs/btrfs/scrub.c | 18 ++++++------------ > 4 files changed, 21 insertions(+), 19 deletions(-) > > diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h > index 692370fc07b2..d5bd01c4e414 100644 > --- a/fs/btrfs/ctree.h > +++ b/fs/btrfs/ctree.h > @@ -85,6 +85,14 @@ struct btrfs_path { > #define BTRFS_PATH_AUTO_FREE(path_name) \ > struct btrfs_path *path_name __free(btrfs_free_path) = NULL > > +/* > + * This defines an on-stack path that will be auto released exiting the scope. > + * > + * This one is compatible with any existing manual btrfs_release_path() calls. > + */ > +#define BTRFS_PATH_AUTO_RELEASE(path_name) \ > + struct btrfs_path path_name __free(btrfs_release_path) = { 0 } For the on-stack path it makes sense though it's a bit confusing because BTRFS_PATH_AUTO_FREE defines a pointer. It's way more common to get a path from a parameter and then call btrfs_release_path() on the pointer, so it's not possible to use the AUTO_RELEASE pattern anyway. ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH v2 2/2] btrfs: introduce BTRFS_PATH_AUTO_RELEASE() helper 2025-11-26 14:27 ` David Sterba @ 2025-11-26 20:40 ` Qu Wenruo 0 siblings, 0 replies; 6+ messages in thread From: Qu Wenruo @ 2025-11-26 20:40 UTC (permalink / raw) To: dsterba, Qu Wenruo; +Cc: linux-btrfs 在 2025/11/27 00:57, David Sterba 写道: > On Wed, Nov 26, 2025 at 08:20:21AM +1030, Qu Wenruo wrote: >> There are already several bugs with on-stack btrfs_path involved, even >> it is already a little safer than btrfs_path pointers (only leaks the >> extent buffers, not the btrfs_path structure itself) >> >> - Patch "btrfs: make sure extent and csum paths are always released in >> scrub_raid56_parity_stripe()" >> Which is going into v6.19 release cycle. >> >> - Patch "btrfs: fix a potential path leak in print_datA_reloc_error()" >> The previous patch in the series. >> >> Thus there is a real need to apply auto release for those on-stack paths. >> >> This patch introduces a new macro, BTRFS_PATH_AUTO_RELEASE(), which >> defines one on-stack btrfs_path structure, initialize it all to 0, then >> call btrfs_release_path() on it when exiting the scope. >> >> This will applies to all 3 on-stack path usages: >> >> - defrag_get_extent() in defrag.c >> >> - print_data_reloc_error() in inode.c >> There is a special case where we want to release the path early before >> the time consuming iterate_extent_inodes() call, thus that manual >> early release is kept as is, with an extra comment added. >> >> - scrub_radi56_parity_stripe() in scrub.c >> >> Signed-off-by: Qu Wenruo <wqu@suse.com> >> --- >> fs/btrfs/ctree.h | 9 +++++++++ >> fs/btrfs/defrag.c | 5 +---- >> fs/btrfs/inode.c | 8 +++++--- >> fs/btrfs/scrub.c | 18 ++++++------------ >> 4 files changed, 21 insertions(+), 19 deletions(-) >> >> diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h >> index 692370fc07b2..d5bd01c4e414 100644 >> --- a/fs/btrfs/ctree.h >> +++ b/fs/btrfs/ctree.h >> @@ -85,6 +85,14 @@ struct btrfs_path { >> #define BTRFS_PATH_AUTO_FREE(path_name) \ >> struct btrfs_path *path_name __free(btrfs_free_path) = NULL >> >> +/* >> + * This defines an on-stack path that will be auto released exiting the scope. >> + * >> + * This one is compatible with any existing manual btrfs_release_path() calls. >> + */ >> +#define BTRFS_PATH_AUTO_RELEASE(path_name) \ >> + struct btrfs_path path_name __free(btrfs_release_path) = { 0 } > > For the on-stack path it makes sense though it's a bit confusing because > BTRFS_PATH_AUTO_FREE defines a pointer. It's way more common to get a > path from a parameter and then call btrfs_release_path() on the pointer, > so it's not possible to use the AUTO_RELEASE pattern anyway. I can remove the last sentence during commit. Thanks, Qu ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH v2 0/2] btrfs: fix an on-stack path leak and migrate to auto-release for on-stack paths 2025-11-25 21:50 [PATCH v2 0/2] btrfs: fix an on-stack path leak and migrate to auto-release for on-stack paths Qu Wenruo 2025-11-25 21:50 ` [PATCH v2 1/2] btrfs: fix a potential path leak in print_data_reloc_error() Qu Wenruo 2025-11-25 21:50 ` [PATCH v2 2/2] btrfs: introduce BTRFS_PATH_AUTO_RELEASE() helper Qu Wenruo @ 2025-12-08 20:56 ` David Sterba 2 siblings, 0 replies; 6+ messages in thread From: David Sterba @ 2025-12-08 20:56 UTC (permalink / raw) To: Qu Wenruo; +Cc: linux-btrfs On Wed, Nov 26, 2025 at 08:20:19AM +1030, Qu Wenruo wrote: > [CHANGELOG] > v2: > - Add back an early btrfs_release_path() call in > print_data_reloc_error() > This is to avoid holding the path (and its extent buffers locked) > during time consuming iterate_extent_inodes() calls. > > And add a comment on that early release, with updated commit message. > > I thought patch "btrfs: make sure extent and csum paths are always released in > scrub_raid56_parity_stripe()" has already taught us that tag based > manual cleanup is never reliable, now there is another similar bug in > print_data_reloc_error(). > > This time it is harder to expose, as we always imply if the function > returned an error, they should do the proper cleanup. > But extent_to_logical() does not follow that assumption. > > The first patch is the minimal fix for backport, the second patch is > going to solve the problem by using auto-release for all on-stack btrfs > paths. > > Qu Wenruo (2): > btrfs: fix a potential path leak in print_data_reloc_error() > btrfs: introduce BTRFS_PATH_AUTO_RELEASE() helper Reviewed-by: David Sterba <dsterba@suse.com> ^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2025-12-08 20:56 UTC | newest] Thread overview: 6+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2025-11-25 21:50 [PATCH v2 0/2] btrfs: fix an on-stack path leak and migrate to auto-release for on-stack paths Qu Wenruo 2025-11-25 21:50 ` [PATCH v2 1/2] btrfs: fix a potential path leak in print_data_reloc_error() Qu Wenruo 2025-11-25 21:50 ` [PATCH v2 2/2] btrfs: introduce BTRFS_PATH_AUTO_RELEASE() helper Qu Wenruo 2025-11-26 14:27 ` David Sterba 2025-11-26 20:40 ` Qu Wenruo 2025-12-08 20:56 ` [PATCH v2 0/2] btrfs: fix an on-stack path leak and migrate to auto-release for on-stack paths David Sterba
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox