hfsplus: fix s_fs_info leak on mount setup failure
Subsystems (2)
PATCH v2 1/1 hfsplus: fix s_fs_info leak on mount setup failure
Syzkaller reported a memory leak in hfsplus where s_fs_info (sbi) is allocated in hfsplus_init_fs_context() but never freed if the mount setup fails during setup_bdev_super().
In get_tree_bdev_flags(), if setup_bdev_super() fails, the superblock is torn down via deactivate_locked_super(). Since this failure occurs before fill_super() is called, the superblock's operations (sb->s_op) are not yet set. Consequently, the standard ->put_super() callback cannot be invoked, and the allocated s_fs_info remains leaked.
Fix this by implementing a custom ->kill_sb() handler, hfsplus_kill_sb(), which explicitly frees s_fs_info using RCU synchronization. This ensures cleanup happens regardless of whether fill_super() succeeded or ->put_super() was called.
To support this new lifecycle:
1. In hfsplus_put_super(), remove the call_rcu() call. The actual freeing of s_fs_info is deferred to hfsplus_kill_sb().
2. In hfsplus_fill_super(), remove the explicit cleanup of sbi (both kfree and unload_nls) in the error path. The VFS will call ->kill_sb() on failure, so retaining these would result in double-frees or refcount underflows.
3. Implement hfsplus_kill_sb() to invoke kill_block_super() and then free s_fs_info via RCU.
---
v1:
- tried to fix the leak in fs/super.c (VFS layer).
- Link: https://lore.kernel.org/all/20260201082724.GC3183987@ZenIV/
v2:
- abandons the VFS changes in favor of a driver-specific fix in hfsplus, implementing a custom ->kill_sb() to handle the cleanup lifecycle, as suggested by Al Viro.
fs/hfsplus/super.c | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-)
fs/hfsplus/super.c +18 -12
@@ -311,14 +311,6 @@ void hfsplus_mark_mdb_dirty(struct super_block *sb)spin_unlock (&sbi->work_lock); } -static void delayed_free (struct rcu_head *p) -{ - struct hfsplus_sb_info *sbi = container_of(p, struct hfsplus_sb_info, rcu); - -unload_nls (sbi->nls); -kfree (sbi); -} -static void hfsplus_put_super (struct super_block *sb) {struct hfsplus_sb_info *sbi =HFSPLUS_SB (sb);
@@ -344,7 +336,6 @@ static void hfsplus_put_super(struct super_block *sb)hfs_btree_close (sbi->ext_tree);kfree (sbi->s_vhdr_buf);kfree (sbi->s_backup_vhdr_buf); -call_rcu (&sbi->rcu, delayed_free);hfs_dbg ("finished\n" ); }
@@ -648,9 +639,7 @@ static int hfsplus_fill_super(struct super_block *sb, struct fs_context *fc)kfree (sbi->s_vhdr_buf);kfree (sbi->s_backup_vhdr_buf); out_unload_nls: -unload_nls (sbi->nls);unload_nls (nls); -kfree (sbi);return err; }
@@ -709,10 +698,27 @@ static int hfsplus_init_fs_context(struct fs_context *fc)return 0 ; } +static void delayed_free (struct rcu_head *p) +{ + struct hfsplus_sb_info *sbi = container_of(p, struct hfsplus_sb_info, rcu); + +unload_nls (sbi->nls); +kfree (sbi); +} + +static void hfsplus_kill_sb (struct super_block *sb) +{ +struct hfsplus_sb_info *sbi = sb->s_fs_info; + +kill_block_super (sb); +if (sbi) +call_rcu (&sbi->rcu, delayed_free); +} +static struct file_system_type hfsplus_fs_type = { .owner =THIS_MODULE , .name ="hfsplus" , - .kill_sb = kill_block_super, + .kill_sb = hfsplus_kill_sb, .fs_flags =FS_REQUIRES_DEV , .init_fs_context = hfsplus_init_fs_context, };
Signature · 1 line
2.34.1
Quoted 96 lines · Shardul Bankar
Syzkaller reported a memory leak in hfsplus where
s_fs_info(sbi) is allocated inhfsplus_init_fs_context()but never freed if the mount setup fails duringsetup_bdev_super().In
get_tree_bdev_flags(), ifsetup_bdev_super()fails, the superblock is torn down viadeactivate_locked_super(). Since this failure occurs beforefill_super()is called, the superblock's operations (sb->s_op) are not yet set. Consequently, the standard ->put_super() callback cannot be invoked, and the allocateds_fs_inforemains leaked.Fix this by implementing a custom ->kill_sb() handler,
hfsplus_kill_sb(), which explicitly freess_fs_infousing RCU synchronization. This ensures cleanup happens regardless of whetherfill_super()succeeded or ->put_super() was called.To support this new lifecycle:
1. Inhfsplus_put_super(), remove thecall_rcu()call. The actual freeing ofs_fs_infois deferred tohfsplus_kill_sb().
2. Inhfsplus_fill_super(), remove the explicit cleanup of sbi (both kfree andunload_nls) in the error path. The VFS will call ->kill_sb() on failure, so retaining these would result in double-frees or refcount underflows.
3. Implementhfsplus_kill_sb()to invokekill_block_super()and then frees_fs_infovia RCU.Reported-by [email protected]Signed-off-by Shardul Bankar <[email protected]>---
v1:
- tried to fix the leak infs/super.c(VFS layer).
- Link: https://urldefense.proofpoint.com/v2/url?u=https-3A__lore.kernel.org_all_20260201082724.GC3183987-40ZenIV_&d=DwIDAg&c=BSDicqBQBDjDI9RkVyTcHQ&r=q5bIm4AXMzc8NJu1_RGmnQ2fMWKq4Y4RAkElvUgSs00&m=cdfmHveCNB06e-RCTCO9KaiHPPrhWiFs1cPzJzLjy18vWg3XFh8UctRvQeTTr3CH&s=q6W22LIrAIHCp7LSxAZI00f4z8FBpgFEYVnbQklH9KU&e=
v2:
- abandons the VFS changes in favor of a driver-specific fix in
hfsplus, implementing a custom ->kill_sb() to handle the cleanup
lifecycle, as suggested by Al Viro.fs/hfsplus/super.c | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-)fs/hfsplus/super.c +15
-11@@ -311,14 +311,6 @@ void hfsplus_mark_mdb_dirty(struct super_block *sb)spin_unlock (&sbi->work_lock); } -static void delayed_free (struct rcu_head *p) -{ - struct hfsplus_sb_info *sbi = container_of(p, struct hfsplus_sb_info, rcu); - -unload_nls (sbi->nls); -kfree (sbi); -} -static void hfsplus_put_super (struct super_block *sb) {struct hfsplus_sb_info *sbi =HFSPLUS_SB (sb);@@ -344,7 +336,6 @@ static void hfsplus_put_super(struct super_block *sb)hfs_btree_close (sbi->ext_tree);kfree (sbi->s_vhdr_buf);kfree (sbi->s_backup_vhdr_buf); -call_rcu (&sbi->rcu, delayed_free);hfs_dbg ("finished\n" ); }@@ -648,9 +639,7 @@ static int hfsplus_fill_super(struct super_block *sb, struct fs_context *fc)kfree (sbi->s_vhdr_buf);kfree (sbi->s_backup_vhdr_buf); out_unload_nls: -unload_nls (sbi->nls);unload_nls (nls); -kfree (sbi);return err; }@@ -709,10 +698,27 @@ static int hfsplus_init_fs_context(struct fs_context *fc)return 0 ; } +static void delayed_free (struct rcu_head *p) +{ + struct hfsplus_sb_info *sbi = container_of(p, struct hfsplus_sb_info, rcu); + +unload_nls (sbi->nls); +kfree (sbi); +} + +static void hfsplus_kill_sb (struct super_block *sb) +{ +struct hfsplus_sb_info *sbi = sb->s_fs_info; + +kill_block_super (sb); +if (sbi) +call_rcu (&sbi->rcu, delayed_free);
fs/hfsplus/super.c static int hfsplus_init_fs_context(struct fs_context *fc) · line 724
Quoted 10 lines
fs/hfsplus/super.c +3
-1+} +static struct file_system_type hfsplus_fs_type = { .owner =THIS_MODULE , .name ="hfsplus" , - .kill_sb = kill_block_super, + .kill_sb = hfsplus_kill_sb, .fs_flags =FS_REQUIRES_DEV , .init_fs_context = hfsplus_init_fs_context, };
The patch [1] fixes the issue and it in HFS/HFS+ tree already.
Thanks,
Slava.
Al Viro
maintainerQuoted 2 lines · Viacheslav Dubeyko
Review
Quoted 2 lines
out_unload_nls:(quoted diff) +0
-1- unload_nls(sbi->nls);
^^^^^^^^^^^^^^^^^^^^
Quoted 4 lines
fs/hfsplus/super.c static int hfsplus_fill_super(struct super_block *sb, struct fs_context *fc) · line 653
Quoted 2 lines
fs/hfsplus/super.c +0
-1unload_nls (nls); -kfree (sbi);The patch [1] fixes the issue and it in HFS/HFS+ tree already.
AFAICS, [1] lacks this removal of unload_nls() on failure exit. IOW, the variant in your tree does unload_nls(sbi->nls) twice...
Quoted 11 lines · Al Viro
Quoted 2 lines · Viacheslav Dubeyko
Review
Quoted 2 lines
out_unload_nls:(quoted diff) +0
-1- unload_nls(sbi->nls);^^^^^^^^^^^^^^^^^^^^Quoted 4 lines
fs/hfsplus/super.c static int hfsplus_fill_super(struct super_block *sb, struct fs_context *fc) · line 653
Quoted 2 lines
fs/hfsplus/super.c +0
-1unload_nls (nls); -kfree (sbi);The patch [1] fixes the issue and it in HFS/HFS+ tree already.
AFAICS, [1] lacks this removal of
unload_nls()on failure exit. IOW, the variant in your tree doesunload_nls(sbi->nls)twice...
Yeah, I think you are right here.
Shardul, you already spend the time on this solution. Could you please modify your patch to fix the issue finally by correcting the patch that already in HFS/HFS+ tree?
Thanks a lot,
Slava.
Quoted 15 lines · Viacheslav Dubeyko
Quoted 11 lines · Al Viro
Quoted 2 lines · Viacheslav Dubeyko
Quoted 2 lines
out_unload_nls:
- unload_nls(sbi->nls);^^^^^^^^^^^^^^^^^^^^
Quoted 4 lines
Quoted 2 lines
unload_nls(nls);
- kfree(sbi);The patch [1] fixes the issue and it in HFS/HFS+ tree already.
AFAICS, [1] lacks this removal of
unload_nls()on failure exit. IOW, the variant in your tree doesunload_nls(sbi->nls)twice...Yeah, I think you are right here.
Quoted 8 lines
Shardul, you already spend the time on this solution. Could you please modify
your patch to fix the issue finally by correcting the patch that already in
HFS/HFS+ tree?Thanks a lot,
Slava.
Sure, will send a v2 with just the unload_nls removed.
Thanks,
Shardul
PATCH 1/1 hfsplus: avoid double unload_nls() on mount failure
The recent commit "hfsplus: ensure sb->s_fs_info is always cleaned up"
[1] introduced a custom ->kill_sb() handler (hfsplus_kill_super) that cleans up the s_fs_info structure (including the NLS table) on superblock destruction.
However, the error handling path in hfsplus_fill_super() still calls unload_nls() before returning an error. Since the VFS layer calls ->kill_sb() when fill_super fails, this results in unload_nls() being called twice for the same sbi->nls pointer: once in hfsplus_fill_super() and again in hfsplus_kill_super() (via delayed_free).
Remove the explicit unload_nls() call from the error path in hfsplus_fill_super() to rely solely on the cleanup in ->kill_sb().
[1] https://lore.kernel.org/r/[email protected]/
--- fs/hfsplus/super.c | 1 - 1 file changed, 1 deletion(-)
fs/hfsplus/super.c +0 -1
@@ -646,7 +646,6 @@ static int hfsplus_fill_super(struct super_block *sb, struct fs_context *fc)kfree (sbi->s_vhdr_buf);kfree (sbi->s_backup_vhdr_buf); out_unload_nls: -unload_nls (sbi->nls);unload_nls (nls);return err; }
Signature · 1 line
2.34.1
Re: [PATCH] hfsplus: avoid double unload_nls() on mount failure
Quoted 28 lines · Shardul Bankar
The recent commit "hfsplus: ensure
sb->s_fs_infois always cleaned up"
[1] introduced a custom ->kill_sb() handler (hfsplus_kill_super) that cleans up thes_fs_infostructure (including the NLS table) on superblock destruction.However, the error handling path in
hfsplus_fill_super()still callsunload_nls()before returning an error. Since the VFS layer calls ->kill_sb() whenfill_superfails, this results inunload_nls()being called twice for the samesbi->nlspointer: once inhfsplus_fill_super()and again inhfsplus_kill_super()(viadelayed_free).Remove the explicit
unload_nls()call from the error path inhfsplus_fill_super()to rely solely on the cleanup in ->kill_sb().Reported-by Al Viro <[email protected]>Signed-off-by Shardul Bankar <[email protected]>--- fs/hfsplus/super.c | 1 - 1 file changed, 1 deletion(-)fs/hfsplus/super.c +0
-0
fs/hfsplus/super.c static int hfsplus_fill_super(struct super_block *sb, struct fs_context *fc) · line 651
Quoted 7 lines
fs/hfsplus/super.c +0
-1@@ -646,7 +646,6 @@ static int hfsplus_fill_super(struct super_block *sb, struct fs_context *fc)kfree (sbi->s_vhdr_buf);kfree (sbi->s_backup_vhdr_buf); out_unload_nls: -unload_nls (sbi->nls);unload_nls (nls);return err; }
Makes sense and looks good.
Thanks,
Slava.
Al Viro
maintainerQuoted 8 lines · Viacheslav Dubeyko
Quoted 7 lines · Al Viro
Quoted 2 lines · Viacheslav Dubeyko
Review
Quoted 2 lines
out_unload_nls:(quoted diff) +0
-1- unload_nls(sbi->nls);^^^^^^^^^^^^^^^^^^^^Quoted 2 lines
Quoted 2 lines
(quoted diff) +0
-1unload_nls(nls); - kfree(sbi);
Quoted 6 lines
Quoted 4 lines
Quoted 1 line
The patch [1] fixes the issue and it in HFS/HFS+ tree already.
AFAICS, [1] lacks this removal of
unload_nls()on failure exit. IOW, the variant in your tree doesunload_nls(sbi->nls)twice...Yeah, I think you are right here.
While we are at it, this
kfree(sbi->s_vhdr_buf); kfree(sbi->s_backup_vhdr_buf);
might as well go into ->kill_sb(). That would result in the (untested) delta below and IMO it's easier to follow that way...
fs/hfsplus/super.c +5 -8
@@ -348,8 +348,6 @@ static void hfsplus_put_super(struct super_block *sb)hfs_btree_close (sbi->attr_tree);hfs_btree_close (sbi->cat_tree);hfs_btree_close (sbi->ext_tree); -kfree (sbi->s_vhdr_buf); -kfree (sbi->s_backup_vhdr_buf);hfs_dbg ("finished\n" ); }
@@ -471,7 +469,7 @@ static int hfsplus_fill_super(struct super_block *sb, struct fs_context *fc)if (be16_to_cpu (vhdr->version) <HFSPLUS_MIN_VERSION ||be16_to_cpu (vhdr->version) >HFSPLUS_CURRENT_VERSION ) {pr_err ("wrong filesystem version\n" ); - goto out_free_vhdr; + goto out_unload_nls; } sbi->total_blocks =be32_to_cpu (vhdr->total_blocks); sbi->free_blocks =be32_to_cpu (vhdr->free_blocks);
@@ -495,7 +493,7 @@ static int hfsplus_fill_super(struct super_block *sb, struct fs_context *fc)if ((last_fs_block > (sector_t)(~0ULL ) >> (sbi->alloc_blksz_shift -9 )) || (last_fs_page > (pgoff_t)(~0ULL ))) {pr_err ("filesystem size too large\n" ); - goto out_free_vhdr; + goto out_unload_nls; }/* Set up operations so we can load metadata */
@@ -522,7 +520,7 @@ static int hfsplus_fill_super(struct super_block *sb, struct fs_context *fc) sbi->ext_tree =hfs_btree_open (sb,HFSPLUS_EXT_CNID );if (!sbi->ext_tree) {pr_err ("failed to load extents file\n" ); - goto out_free_vhdr; + goto out_unload_nls; } sbi->cat_tree =hfs_btree_open (sb,HFSPLUS_CAT_CNID );if (!sbi->cat_tree) {
@@ -648,9 +646,6 @@ static int hfsplus_fill_super(struct super_block *sb, struct fs_context *fc)hfs_btree_close (sbi->cat_tree); out_close_ext_tree:hfs_btree_close (sbi->ext_tree); -out_free_vhdr: -kfree (sbi->s_vhdr_buf); -kfree (sbi->s_backup_vhdr_buf); out_unload_nls:unload_nls (nls);return err;
@@ -716,6 +711,8 @@ static void hfsplus_kill_super(struct super_block *sb)struct hfsplus_sb_info *sbi =HFSPLUS_SB (sb);kill_block_super (sb); +kfree (sbi->s_vhdr_buf); +kfree (sbi->s_backup_vhdr_buf);call_rcu (&sbi->rcu, delayed_free); }
fs/hfsplus/wrapper.c +8 -19
@@ -139,32 +139,29 @@ int hfsplus_read_wrapper(struct super_block *sb)u32 blocksize;int error =0 ; - error = -EINVAL ; blocksize =sb_min_blocksize (sb,HFSPLUS_SECTOR_SIZE );if (!blocksize) - goto out; +return -EINVAL ; sbi->min_io_size = blocksize;if (hfsplus_get_last_session (sb, &part_start, &part_size)) - goto out; +return -EINVAL ; - error = -ENOMEM ; sbi->s_vhdr_buf =kmalloc (hfsplus_min_io_size (sb),GFP_KERNEL );if (!sbi->s_vhdr_buf) - goto out; +return -ENOMEM ; sbi->s_backup_vhdr_buf =kmalloc (hfsplus_min_io_size (sb),GFP_KERNEL );if (!sbi->s_backup_vhdr_buf) - goto out_free_vhdr; +return -ENOMEM ; reread: error =hfsplus_submit_bio (sb, part_start +HFSPLUS_VOLHEAD_SECTOR , sbi->s_vhdr_buf, (void **)&sbi->s_vhdr,REQ_OP_READ );if (error) - goto out_free_backup_vhdr; +return error; - error = -EINVAL ;switch (sbi->s_vhdr->signature) {case cpu_to_be16 (HFSPLUS_VOLHEAD_SIGX ):set_bit (HFSPLUS_SB_HFSX , &sbi->flags);
@@ -194,12 +191,11 @@ int hfsplus_read_wrapper(struct super_block *sb) sbi->s_backup_vhdr_buf, (void **)&sbi->s_backup_vhdr, REQ_OP_READ);if (error) - goto out_free_backup_vhdr; +return error; - error = -EINVAL ;if (sbi->s_backup_vhdr->signature != sbi->s_vhdr->signature) {pr_warn ("invalid secondary volume header\n" ); - goto out_free_backup_vhdr; +return -EINVAL ; } blocksize =be32_to_cpu (sbi->s_vhdr->blocksize);
@@ -221,7 +217,7 @@ int hfsplus_read_wrapper(struct super_block *sb)if (sb_set_blocksize (sb, blocksize) != blocksize) {pr_err ("unable to set blocksize to %u!\n" , blocksize); - goto out_free_backup_vhdr; +return -EINVAL ; } sbi->blockoffset =
@@ -230,11 +226,4 @@ int hfsplus_read_wrapper(struct super_block *sb) sbi->sect_count = part_size; sbi->fs_shift = sbi->alloc_blksz_shift - sb->s_blocksize_bits;return 0 ; - -out_free_backup_vhdr: -kfree (sbi->s_backup_vhdr_buf); -out_free_vhdr: -kfree (sbi->s_vhdr_buf); -out: -return error; }
Al Viro
maintainerQuoted 5 lines · Al Viro
While we are at it, this
kfree(sbi->s_vhdr_buf); kfree(sbi->s_backup_vhdr_buf);might as well go into ->kill_sb(). That would result in the (untested) delta below and IMO it's easier to follow that way...
AFAICS once you've got ->s_root set, you can just return an error and be done with that - regular cleanup should take care of those parts (note that iput(NULL) is explicitly a no-op and the same goes for cancel_delayed_work_sync() on something that has never been through queue_delayed_work()).
Incremental to the previous would be
fs/hfsplus/super.c +10 -20
@@ -560,26 +560,23 @@ static int hfsplus_fill_super(struct super_block *sb, struct fs_context *fc) err = -ENOMEM ; goto out_put_alloc_file; } +/* from that point on we just return an error on failure */ str.len =sizeof (HFSP_HIDDENDIR_NAME ) -1 ; str.name =HFSP_HIDDENDIR_NAME ; err =hfs_find_init (sbi->cat_tree, &fd);if (err) - goto out_put_root; +return err; err =hfsplus_cat_build_key (sb, fd.search_key,HFSPLUS_ROOT_CNID , &str);if (unlikely (err <0 )) - goto out_put_root; +return err;if (!hfs_brec_read (&fd, &entry,sizeof (entry))) {hfs_find_exit (&fd); -if (entry.type !=cpu_to_be16 (HFSPLUS_FOLDER )) { - err = -EIO ; - goto out_put_root; - } +if (entry.type !=cpu_to_be16 (HFSPLUS_FOLDER )) +return -EIO ; inode =hfsplus_iget (sb,be32_to_cpu (entry.folder.id)); -if (IS_ERR (inode)) { - err =PTR_ERR (inode); - goto out_put_root; - } +if (IS_ERR (inode)) +return PTR_ERR (inode); sbi->hidden_dir = inode; }else hfs_find_exit (&fd);
@@ -597,14 +594,13 @@ static int hfsplus_fill_super(struct super_block *sb, struct fs_context *fc) sbi->hidden_dir =hfsplus_new_inode (sb, root,S_IFDIR );if (!sbi->hidden_dir) {mutex_unlock (&sbi->vh_mutex); - err = -ENOMEM ; - goto out_put_root; +return -ENOMEM ; } err =hfsplus_create_cat (sbi->hidden_dir->i_ino, root, &str, sbi->hidden_dir);if (err) {mutex_unlock (&sbi->vh_mutex); - goto out_put_hidden_dir; +return err; } err = hfsplus_init_security(sbi->hidden_dir,
@@ -619,7 +615,7 @@ static int hfsplus_fill_super(struct super_block *sb, struct fs_context *fc)hfsplus_delete_cat (sbi->hidden_dir->i_ino, root, &str);mutex_unlock (&sbi->vh_mutex); - goto out_put_hidden_dir; +return err; }mutex_unlock (&sbi->vh_mutex);
@@ -632,12 +628,6 @@ static int hfsplus_fill_super(struct super_block *sb, struct fs_context *fc) sbi->nls = nls;return 0 ; -out_put_hidden_dir: -cancel_delayed_work_sync (&sbi->sync_work); -iput (sbi->hidden_dir); -out_put_root: -dput (sb->s_root); - sb->s_root =NULL ; out_put_alloc_file:iput (sbi->alloc_file); out_close_attr_tree:
Al Viro
maintainerQuoted 8 lines · Al Viro
Quoted 5 lines · Al Viro
While we are at it, this
kfree(sbi->s_vhdr_buf); kfree(sbi->s_backup_vhdr_buf);might as well go into ->kill_sb(). That would result in the (untested) delta below and IMO it's easier to follow that way...
Quoted 5 lines
AFAICS once you've got ->s_root set, you can just return an error and be done with that - regular cleanup should take care of those parts (note that iput(NULL) is explicitly a no-op and the same goes for
cancel_delayed_work_sync()on something that has never been throughqueue_delayed_work()).
Scratch the last one - you'd get nls leak that way, thanks to the trickery in there... Depending on how much do you dislike cleanup.h stuff, there might be a way to deal with that, though...
Al Viro
maintainerQuoted 15 lines · Al Viro
Quoted 8 lines · Al Viro
Quoted 5 lines · Al Viro
While we are at it, this
kfree(sbi->s_vhdr_buf); kfree(sbi->s_backup_vhdr_buf);might as well go into ->kill_sb(). That would result in the (untested) delta below and IMO it's easier to follow that way...
Quoted 5 lines
AFAICS once you've got ->s_root set, you can just return an error and be done with that - regular cleanup should take care of those parts (note that iput(NULL) is explicitly a no-op and the same goes for
cancel_delayed_work_sync()on something that has never been throughqueue_delayed_work()).
Quoted 3 lines
Scratch the last one - you'd get nls leak that way, thanks to the trickery in there... Depending on how much do you dislike
cleanup.hstuff, there might be a way to deal with that, though...
See viro/vfs.git #untested.hfsplus (I've applied leak fix to your #for-next, commits in question are done on top of that).
It builds, but I've done no other testing on it. And nls.h bit needs to be discussed on fsdevel, obviously.
Quoted 20 lines · Al Viro
Quoted 15 lines · Al Viro
Quoted 8 lines · Al Viro
Quoted 5 lines · Al Viro
While we are at it, this
kfree(sbi->s_vhdr_buf); kfree(sbi->s_backup_vhdr_buf);might as well go into ->kill_sb(). That would result in the (untested) delta below and IMO it's easier to follow that way...
Quoted 5 lines
AFAICS once you've got ->s_root set, you can just return an error and be done with that - regular cleanup should take care of those parts (note that iput(NULL) is explicitly a no-op and the same goes for
cancel_delayed_work_sync()on something that has never been throughqueue_delayed_work()).Quoted 3 lines
Scratch the last one - you'd get nls leak that way, thanks to the trickery in there... Depending on how much do you dislike
cleanup.hstuff, there might be a way to deal with that, though...
Quoted 5 lines
See viro/vfs.git #untested.hfsplus (I've applied leak fix to your #for-next, commits in question are done on top of that).
It builds, but I've done no other testing on it. And
nls.hbit needs to be discussed on fsdevel, obviously.
OK. Let me spend some time on testing and reviewing the fix.
Thanks,
Slava.
Quoted 20 lines · Al Viro
Quoted 15 lines · Al Viro
Quoted 8 lines · Al Viro
Quoted 5 lines · Al Viro
While we are at it, this
kfree(sbi->s_vhdr_buf); kfree(sbi->s_backup_vhdr_buf);might as well go into ->kill_sb(). That would result in the (untested) delta below and IMO it's easier to follow that way...
Quoted 5 lines
AFAICS once you've got ->s_root set, you can just return an error and be done with that - regular cleanup should take care of those parts (note that iput(NULL) is explicitly a no-op and the same goes for
cancel_delayed_work_sync()on something that has never been throughqueue_delayed_work()).Quoted 3 lines
Scratch the last one - you'd get nls leak that way, thanks to the trickery in there... Depending on how much do you dislike
cleanup.hstuff, there might be a way to deal with that, though...
Quoted 5 lines
See viro/vfs.git #untested.hfsplus (I've applied leak fix to your #for-next, commits in question are done on top of that).
It builds, but I've done no other testing on it. And
nls.hbit needs to be discussed on fsdevel, obviously.
I did run the xfstests for HFS+ with viro/vfs.git #untested.hfsplus. Everything looks good, I don't see any new issues. Currently, around 29 test-cases fail for HFS+. I see the same number of failures with applied patchset.
The code looks good. And I am ready to take the patchset into HFS/HFS+ tree. Would you like to send the pathset for nls.h modification discussion?
Thanks,
Slava.
Al Viro
maintainerQuoted 6 lines · Viacheslav Dubeyko
I did run the xfstests for HFS+ with viro/vfs.git #untested.hfsplus. Everything looks good, I don't see any new issues. Currently, around 29 test-cases fail for HFS+. I see the same number of failures with applied patchset.
The code looks good. And I am ready to take the patchset into HFS/HFS+ tree. Would you like to send the pathset for
nls.hmodification discussion?
FWIW, I wonder what the hell is that code doing:
err = -EINVAL;
if (!sbi->nls) {
/* try utf8 first, as this is the old default behaviour */
sbi->nls = load_nls("utf8");
if (!sbi->nls)
sbi->nls = load_nls_default();
}
/* temporarily use utf8 to correctly find the hidden dir below */
nls = sbi->nls;
sbi->nls = load_nls("utf8");
if (!sbi->nls) {
pr_err("unable to load nls for utf8\n");
goto out_unload_nls;
}If load_nls("utf8") fails on the first call, I don't see how the second one might succeed. What's the intended behaviour here?
What that code actually does is
* if UTF8 isn't loadable, fail hard, no matter what
* if it is loadable, use it for the duration of fill_super,
then if we had something configured, switch back to that, otherwise stay with UTF8.
Quoted 9 lines · Al Viro
Quoted 6 lines · Viacheslav Dubeyko
I did run the xfstests for HFS+ with viro/vfs.git #untested.hfsplus. Everything looks good, I don't see any new issues. Currently, around 29 test-cases fail for HFS+. I see the same number of failures with applied patchset.
The code looks good. And I am ready to take the patchset into HFS/HFS+ tree. Would you like to send the pathset for
nls.hmodification discussion?
Quoted 8 lines
FWIW, I wonder what the hell is that code doing:
err = -EINVAL; if (!sbi->nls) { /* try utf8 first, as this is the old default behaviour */ sbi->nls = load_nls("utf8"); if (!sbi->nls) sbi->nls = load_nls_default(); }
As far as I can see, this code has been carefully moved from hfsplus_parse_options() by this commit [1]. Also, we have logic of loading nls in hfsplus_parse_param() [2].
It looks like redundant code to me. The whole logic looks like:
(1) try to load nls during parsing mount options;
(2) temporary load of "utf8" for hidden dir search;
(3) return previously loaded (or NULL) nls back.
Thanks,
Slava.
Quoted 9 lines
/* temporarily use utf8 to correctly find the hidden dir below */
nls = sbi->nls;
sbi->nls = load_nls("utf8");
if (!sbi->nls) {
pr_err("unable to load nls for utf8\n");
goto out_unload_nls;
}Quoted 8 lines
If
load_nls("utf8")fails on the first call, I don't see how the second one might succeed. What's the intended behaviour here?What that code actually does is
* if UTF8 isn't loadable, fail hard, no matter what
* if it is loadable, use it for the duration offill_super,
then if we had something configured, switch back to that, otherwise stay with UTF8.
[1] https://lore.kernel.org/all/[email protected]/
[2] https://elixir.bootlin.com/linux/v6.19-rc5/source/fs/hfsplus/options.c#L118