diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2026-04-13 16:50:38 -0700 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2026-04-13 16:50:38 -0700 |
| commit | 4d9981429aa61c31e67371ac09e7dbba6b59de14 (patch) | |
| tree | d2ef472a3272c96c2a35c5dfb02925f26280b935 /fs/hfsplus/super.c | |
| parent | f3756afb6f6cdcbbc246f1edd0580b8c7485124a (diff) | |
| parent | c1307d18caa819ddc28459d858eb38fdd6c3f8a0 (diff) | |
| download | linux-next-4d9981429aa61c31e67371ac09e7dbba6b59de14.tar.gz linux-next-4d9981429aa61c31e67371ac09e7dbba6b59de14.zip | |
Merge tag 'hfs-v7.1-tag1' of git://git.kernel.org/pub/scm/linux/kernel/git/vdubeyko/hfs
Pull hfsplus updates from Viacheslav Dubeyko:
"This contains several fixes of syzbot reported issues and HFS+ fixes
of xfstests failures.
- Fix a syzbot reported issue of a KMSAN uninit-value in
hfsplus_strcasecmp().
The root cause was that hfs_brec_read() doesn't validate that the
on-disk record size matches the expected size for the record type
being read. The fix introduced hfsplus_brec_read_cat() wrapper that
validates the record size based on the type field and returns -EIO
if size doesn't match (Deepanshu Kartikey)
- Fix a syzbot reported issue of processing corrupted HFS+ images
where the b-tree allocation bitmap indicates that the header node
(Node 0) is free. Node 0 must always be allocated. Violating this
invariant leads to allocator corruption, which cascades into kernel
panics or undefined behavior.
Prevent trusting a corrupted allocator state by adding a validation
check during hfs_btree_open(). If corruption is detected, print a
warning identifying the specific corrupted tree and force the
filesystem to mount read-only (SB_RDONLY).
This prevents kernel panics from corrupted images while enabling
data recovery (Shardul Bankar)
- Fix a potential deadlock in hfsplus_fill_super().
hfsplus_fill_super() calls hfs_find_init() to initialize a search
structure, which acquires tree->tree_lock. If the subsequent call
to hfsplus_cat_build_key() fails, the function jumps to the
out_put_root error label without releasing the lock.
Fix this by adding the missing hfs_find_exit(&fd) call before
jumping to the out_put_root error label. This ensures that
tree->tree_lock is properly released on the error path (Zilin Guan)
- Update a files ctime after rename in hfsplus_rename() (Yangtao Li)
The rest of the patches introduce the HFS+ fixes for the case of
generic/348, generic/728, generic/533, generic/523, and generic/642
test-cases of xfstests suite"
* tag 'hfs-v7.1-tag1' of git://git.kernel.org/pub/scm/linux/kernel/git/vdubeyko/hfs:
hfsplus: fix generic/642 failure
hfsplus: rework logic of map nodes creation in xattr b-tree
hfsplus: fix logic of alloc/free b-tree node
hfsplus: fix error processing issue in hfs_bmap_free()
hfsplus: fix potential race conditions in b-tree functionality
hfsplus: extract hidden directory search into a helper function
hfsplus: fix held lock freed on hfsplus_fill_super()
hfsplus: fix generic/523 test-case failure
hfsplus: validate b-tree node 0 bitmap at mount time
hfsplus: refactor b-tree map page access and add node-type validation
hfsplus: fix to update ctime after rename
hfsplus: fix generic/533 test-case failure
hfsplus: set ctime after setxattr and removexattr
hfsplus: fix uninit-value by validating catalog record size
hfsplus: fix potential Allocation File corruption after fsync
Diffstat (limited to 'fs/hfsplus/super.c')
| -rw-r--r-- | fs/hfsplus/super.c | 47 |
1 files changed, 37 insertions, 10 deletions
diff --git a/fs/hfsplus/super.c b/fs/hfsplus/super.c index b3917249c206..40a0feda716b 100644 --- a/fs/hfsplus/super.c +++ b/fs/hfsplus/super.c @@ -153,7 +153,10 @@ static int hfsplus_system_write_inode(struct inode *inode) } hfsplus_inode_write_fork(inode, fork); if (tree) { + mutex_lock_nested(&tree->tree_lock, + hfsplus_btree_lock_class(tree)); int err = hfs_btree_write(tree); + mutex_unlock(&tree->tree_lock); if (err) { pr_err("b-tree write err: %d, ino %llu\n", @@ -424,12 +427,35 @@ void hfsplus_prepare_volume_header_for_commit(struct hfsplus_vh *vhdr) vhdr->attributes |= cpu_to_be32(HFSPLUS_VOL_INCNSTNT); } +static inline int hfsplus_get_hidden_dir_entry(struct super_block *sb, + const struct qstr *str, + hfsplus_cat_entry *entry) +{ + struct hfs_find_data fd; + int err; + + err = hfs_find_init(HFSPLUS_SB(sb)->cat_tree, &fd); + if (unlikely(err)) + return err; + + err = hfsplus_cat_build_key(sb, fd.search_key, HFSPLUS_ROOT_CNID, str); + if (unlikely(err)) + goto free_fd; + + err = hfsplus_brec_read_cat(&fd, entry); + if (err) + err = -ENOENT; + +free_fd: + hfs_find_exit(&fd); + return err; +} + static int hfsplus_fill_super(struct super_block *sb, struct fs_context *fc) { struct hfsplus_vh *vhdr; struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb); hfsplus_cat_entry entry; - struct hfs_find_data fd; struct inode *root, *inode; struct qstr str; struct nls_table *nls; @@ -565,14 +591,14 @@ static int hfsplus_fill_super(struct super_block *sb, struct fs_context *fc) 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; - err = hfsplus_cat_build_key(sb, fd.search_key, HFSPLUS_ROOT_CNID, &str); - if (unlikely(err < 0)) + err = hfsplus_get_hidden_dir_entry(sb, &str, &entry); + if (err == -ENOENT) { + /* + * Hidden directory is absent or it cannot be read. + */ + } else if (unlikely(err)) { goto out_put_root; - if (!hfs_brec_read(&fd, &entry, sizeof(entry))) { - hfs_find_exit(&fd); + } else { if (entry.type != cpu_to_be16(HFSPLUS_FOLDER)) { err = -EIO; goto out_put_root; @@ -583,8 +609,7 @@ static int hfsplus_fill_super(struct super_block *sb, struct fs_context *fc) goto out_put_root; } sbi->hidden_dir = inode; - } else - hfs_find_exit(&fd); + } if (!sb_rdonly(sb)) { /* @@ -625,6 +650,8 @@ static int hfsplus_fill_super(struct super_block *sb, struct fs_context *fc) } mutex_unlock(&sbi->vh_mutex); + hfsplus_mark_inode_dirty(HFSPLUS_CAT_TREE_I(sb), + HFSPLUS_I_CAT_DIRTY); hfsplus_mark_inode_dirty(sbi->hidden_dir, HFSPLUS_I_CAT_DIRTY); } |
