Skip to content

Commit d29fd59

Browse files
committed
Merge tag 'hfs-v7.2-tag1' of git://git.kernel.org/pub/scm/linux/kernel/git/vdubeyko/hfs
Pull hfs/hfsplus updates from Viacheslav Dubeyko: "Several fixes in HFS/HFS+ of syzbot reported issues and HFS//HFS+ fixes of xfstests failures. - fix a null-ptr-deref issue reported by syzbot (Edward Adam Davis) If the attributes file is not loaded during system mount hfsplus_create_attributes_file can dereference a NULL pointer. Also, add a b-tree node size check in hfs_btree_open() with the goal to prevent an uninit-value bug reported by syzbot for the case of corrupted HFS+ image. - fix __hfs_bnode_create() by using kzalloc_flex() instead of kzalloc() (Rosen Penev) - fix early return in hfs_bnode_read() (Tristan Madani) hfs_bnode_read() can return early without writing to the output buffer when is_bnode_offset_valid() fails or when check_and_correct_requested_ length() corrects the length to zero. Callers such as hfs_bnode_read_ u16() and hfs_bnode_read_u8() pass stack-allocated buffers and use the result unconditionally, leading to KMSAN uninit-value reports. The rest fix (1) generic/637, generic/729 issue for the case of HFS+ file system, (2) generic/003, generic/637 for the case of HFS file system" * tag 'hfs-v7.2-tag1' of git://git.kernel.org/pub/scm/linux/kernel/git/vdubeyko/hfs: hfs: rework hfsplus_readdir() logic hfs: disable the updating of file access times (atime) hfs: fix incorrect inode ID assignment in hfs_new_inode() hfsplus: rework hfsplus_readdir() logic hfs/hfsplus: zero-initialize buffer in hfs_bnode_read hfs/hfsplus: fix u32 overflow in check_and_correct_requested_length hfsplus: Add a sanity check for btree node size hfsplus: fix issue of direct writes beyond end-of-file hfs/hfxplus: use kzalloc_flex() hfsplus: Remove the duplicate attr inode dirty marking action
2 parents 6f60a60 + 7fde7e8 commit d29fd59

16 files changed

Lines changed: 92 additions & 92 deletions

File tree

fs/hfs/bnode.c

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ u32 check_and_correct_requested_length(struct hfs_bnode *node, u32 off, u32 len)
4141

4242
node_size = node->tree->node_size;
4343

44-
if ((off + len) > node_size) {
44+
if ((u64)off + len > node_size) {
4545
u32 new_len = node_size - off;
4646

4747
pr_err("requested length has been corrected: "
@@ -64,6 +64,8 @@ void hfs_bnode_read(struct hfs_bnode *node, void *buf, u32 off, u32 len)
6464
u32 bytes_read;
6565
u32 bytes_to_read;
6666

67+
memset(buf, 0, len);
68+
6769
if (!is_bnode_offset_valid(node, off))
6870
return;
6971

@@ -344,17 +346,15 @@ static struct hfs_bnode *__hfs_bnode_create(struct hfs_btree *tree, u32 cnid)
344346
struct hfs_bnode *node, *node2;
345347
struct address_space *mapping;
346348
struct page *page;
347-
int size, block, i, hash;
349+
int block, i, hash;
348350
loff_t off;
349351

350352
if (cnid >= tree->node_count) {
351353
pr_err("request for non-existent node %d in B*Tree\n", cnid);
352354
return NULL;
353355
}
354356

355-
size = sizeof(struct hfs_bnode) + tree->pages_per_bnode *
356-
sizeof(struct page *);
357-
node = kzalloc(size, GFP_KERNEL);
357+
node = kzalloc_flex(*node, page, tree->pages_per_bnode, GFP_KERNEL);
358358
if (!node)
359359
return NULL;
360360
node->tree = tree;

fs/hfs/catalog.c

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -340,7 +340,6 @@ int hfs_cat_delete(u32 cnid, struct inode *dir, const struct qstr *str)
340340
{
341341
struct super_block *sb;
342342
struct hfs_find_data fd;
343-
struct hfs_readdir_data *rd;
344343
int res, type;
345344

346345
hfs_dbg("name %s, cnid %u\n", str ? str->name : NULL, cnid);
@@ -366,14 +365,6 @@ int hfs_cat_delete(u32 cnid, struct inode *dir, const struct qstr *str)
366365
}
367366
}
368367

369-
/* we only need to take spinlock for exclusion with ->release() */
370-
spin_lock(&HFS_I(dir)->open_dir_lock);
371-
list_for_each_entry(rd, &HFS_I(dir)->open_dir_list, list) {
372-
if (fd.tree->keycmp(fd.search_key, (void *)&rd->key) < 0)
373-
rd->file->f_pos--;
374-
}
375-
spin_unlock(&HFS_I(dir)->open_dir_lock);
376-
377368
res = hfs_brec_remove(&fd);
378369
if (res)
379370
goto out;

fs/hfs/dir.c

Lines changed: 18 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,15 @@ static int hfs_readdir(struct file *file, struct dir_context *ctx)
9797
}
9898
if (ctx->pos >= inode->i_size)
9999
goto out;
100-
err = hfs_brec_goto(&fd, ctx->pos - 1);
100+
rd = file->private_data;
101+
if (rd && rd->pos == ctx->pos) {
102+
memcpy(fd.search_key, &rd->key, sizeof(struct hfs_cat_key));
103+
err = hfs_brec_find(&fd);
104+
if (err == -ENOENT)
105+
err = hfs_brec_goto(&fd, 1);
106+
} else {
107+
err = hfs_brec_goto(&fd, ctx->pos - 1);
108+
}
101109
if (err)
102110
goto out;
103111

@@ -146,23 +154,15 @@ static int hfs_readdir(struct file *file, struct dir_context *ctx)
146154
if (err)
147155
goto out;
148156
}
149-
rd = file->private_data;
150157
if (!rd) {
151158
rd = kmalloc_obj(struct hfs_readdir_data);
152159
if (!rd) {
153160
err = -ENOMEM;
154161
goto out;
155162
}
156163
file->private_data = rd;
157-
rd->file = file;
158-
spin_lock(&HFS_I(inode)->open_dir_lock);
159-
list_add(&rd->list, &HFS_I(inode)->open_dir_list);
160-
spin_unlock(&HFS_I(inode)->open_dir_lock);
161164
}
162-
/*
163-
* Can be done after the list insertion; exclusion with
164-
* hfs_delete_cat() is provided by directory lock.
165-
*/
165+
rd->pos = ctx->pos;
166166
memcpy(&rd->key, &fd.key->cat, sizeof(struct hfs_cat_key));
167167
out:
168168
hfs_find_exit(&fd);
@@ -171,13 +171,7 @@ static int hfs_readdir(struct file *file, struct dir_context *ctx)
171171

172172
static int hfs_dir_release(struct inode *inode, struct file *file)
173173
{
174-
struct hfs_readdir_data *rd = file->private_data;
175-
if (rd) {
176-
spin_lock(&HFS_I(inode)->open_dir_lock);
177-
list_del(&rd->list);
178-
spin_unlock(&HFS_I(inode)->open_dir_lock);
179-
kfree(rd);
180-
}
174+
kfree(file->private_data);
181175
return 0;
182176
}
183177

@@ -306,10 +300,15 @@ static int hfs_rename(struct mnt_idmap *idmap, struct inode *old_dir,
306300
res = hfs_cat_move(d_inode(old_dentry)->i_ino,
307301
old_dir, &old_dentry->d_name,
308302
new_dir, &new_dentry->d_name);
309-
if (!res)
303+
if (!res) {
304+
struct inode *inode = d_inode(old_dentry);
305+
310306
hfs_cat_build_key(old_dir->i_sb,
311-
(btree_key *)&HFS_I(d_inode(old_dentry))->cat_key,
307+
(btree_key *)&HFS_I(inode)->cat_key,
312308
new_dir->i_ino, &new_dentry->d_name);
309+
inode_set_ctime_current(inode);
310+
mark_inode_dirty(inode);
311+
}
313312
return res;
314313
}
315314

fs/hfs/hfs.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,7 @@
1414
/*======== Data structures kept in memory ========*/
1515

1616
struct hfs_readdir_data {
17-
struct list_head list;
18-
struct file *file;
17+
loff_t pos;
1918
struct hfs_cat_key key;
2019
};
2120

fs/hfs/hfs_fs.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,6 @@ struct hfs_inode_info {
3636

3737
struct hfs_cat_key cat_key;
3838

39-
struct list_head open_dir_list;
40-
spinlock_t open_dir_lock;
4139
struct inode *rsrc_inode;
4240

4341
struct mutex extents_lock;

fs/hfs/inode.c

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -196,16 +196,14 @@ struct inode *hfs_new_inode(struct inode *dir, const struct qstr *name, umode_t
196196
err = -ERANGE;
197197

198198
mutex_init(&HFS_I(inode)->extents_lock);
199-
INIT_LIST_HEAD(&HFS_I(inode)->open_dir_list);
200-
spin_lock_init(&HFS_I(inode)->open_dir_lock);
201199
hfs_cat_build_key(sb, (btree_key *)&HFS_I(inode)->cat_key, dir->i_ino, name);
202200
next_id = atomic64_inc_return(&HFS_SB(sb)->next_id);
203201
if (next_id > U32_MAX) {
204202
atomic64_dec(&HFS_SB(sb)->next_id);
205203
pr_err("cannot create new inode: next CNID exceeds limit\n");
206204
goto out_discard;
207205
}
208-
inode->i_ino = (u32)next_id;
206+
inode->i_ino = (u32)next_id - 1;
209207
inode->i_mode = mode;
210208
inode->i_uid = current_fsuid();
211209
inode->i_gid = current_fsgid();
@@ -349,12 +347,11 @@ static int hfs_read_inode(struct inode *inode, void *data)
349347
struct hfs_iget_data *idata = data;
350348
struct hfs_sb_info *hsb = HFS_SB(inode->i_sb);
351349
hfs_cat_rec *rec;
350+
struct timespec64 mtime;
352351

353352
HFS_I(inode)->flags = 0;
354353
HFS_I(inode)->rsrc_inode = NULL;
355354
mutex_init(&HFS_I(inode)->extents_lock);
356-
INIT_LIST_HEAD(&HFS_I(inode)->open_dir_list);
357-
spin_lock_init(&HFS_I(inode)->open_dir_lock);
358355

359356
/* Initialize the inode */
360357
inode->i_uid = hsb->s_uid;
@@ -384,8 +381,10 @@ static int hfs_read_inode(struct inode *inode, void *data)
384381
inode->i_mode |= S_IWUGO;
385382
inode->i_mode &= ~hsb->s_file_umask;
386383
inode->i_mode |= S_IFREG;
387-
inode_set_mtime_to_ts(inode,
388-
inode_set_atime_to_ts(inode, inode_set_ctime_to_ts(inode, hfs_m_to_utime(rec->file.MdDat))));
384+
mtime = hfs_m_to_utime(rec->file.MdDat);
385+
inode_set_ctime_to_ts(inode, mtime);
386+
inode_set_atime_to_ts(inode, mtime);
387+
inode_set_mtime_to_ts(inode, mtime);
389388
inode->i_op = &hfs_file_inode_operations;
390389
inode->i_fop = &hfs_file_operations;
391390
inode->i_mapping->a_ops = &hfs_aops;
@@ -395,8 +394,10 @@ static int hfs_read_inode(struct inode *inode, void *data)
395394
inode->i_size = be16_to_cpu(rec->dir.Val) + 2;
396395
HFS_I(inode)->fs_blocks = 0;
397396
inode->i_mode = S_IFDIR | (S_IRWXUGO & ~hsb->s_dir_umask);
398-
inode_set_mtime_to_ts(inode,
399-
inode_set_atime_to_ts(inode, inode_set_ctime_to_ts(inode, hfs_m_to_utime(rec->dir.MdDat))));
397+
mtime = hfs_m_to_utime(rec->dir.MdDat);
398+
inode_set_ctime_to_ts(inode, mtime);
399+
inode_set_atime_to_ts(inode, mtime);
400+
inode_set_mtime_to_ts(inode, mtime);
400401
inode->i_op = &hfs_dir_inode_operations;
401402
inode->i_fop = &hfs_dir_operations;
402403
break;
@@ -666,7 +667,7 @@ int hfs_inode_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
666667

667668
truncate_setsize(inode, attr->ia_size);
668669
hfs_file_truncate(inode);
669-
simple_inode_init_ts(inode);
670+
inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode));
670671
}
671672

672673
setattr_copy(&nop_mnt_idmap, inode, attr);

fs/hfs/super.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -338,7 +338,7 @@ static int hfs_fill_super(struct super_block *sb, struct fs_context *fc)
338338
sbi->sb = sb;
339339
sb->s_op = &hfs_super_operations;
340340
sb->s_xattr = hfs_xattr_handlers;
341-
sb->s_flags |= SB_NODIRATIME;
341+
sb->s_flags |= SB_NOATIME | SB_NODIRATIME;
342342
mutex_init(&sbi->bitmap_lock);
343343

344344
res = hfs_mdb_get(sb);

fs/hfsplus/bnode.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ void hfs_bnode_read(struct hfs_bnode *node, void *buf, u32 off, u32 len)
2525
struct page **pagep;
2626
u32 l;
2727

28+
memset(buf, 0, len);
29+
2830
if (!is_bnode_offset_valid(node, off))
2931
return;
3032

@@ -455,7 +457,7 @@ static struct hfs_bnode *__hfs_bnode_create(struct hfs_btree *tree, u32 cnid)
455457
struct hfs_bnode *node, *node2;
456458
struct address_space *mapping;
457459
struct page *page;
458-
int size, block, i, hash;
460+
int block, i, hash;
459461
loff_t off;
460462

461463
if (cnid >= tree->node_count) {
@@ -464,9 +466,7 @@ static struct hfs_bnode *__hfs_bnode_create(struct hfs_btree *tree, u32 cnid)
464466
return NULL;
465467
}
466468

467-
size = sizeof(struct hfs_bnode) + tree->pages_per_bnode *
468-
sizeof(struct page *);
469-
node = kzalloc(size, GFP_KERNEL);
469+
node = kzalloc_flex(*node, page, tree->pages_per_bnode);
470470
if (!node)
471471
return NULL;
472472
node->tree = tree;

fs/hfsplus/btree.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,8 @@ struct hfs_btree *hfs_btree_open(struct super_block *sb, u32 id)
365365
}
366366

367367
size = tree->node_size;
368+
if (size < HFSPLUS_NODE_MINSZ || size > HFSPLUS_NODE_MXSZ)
369+
goto fail_page;
368370
if (!is_power_of_2(size))
369371
goto fail_page;
370372
if (!tree->node_count)

fs/hfsplus/catalog.c

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -333,7 +333,6 @@ int hfsplus_delete_cat(u32 cnid, struct inode *dir, const struct qstr *str)
333333
struct super_block *sb = dir->i_sb;
334334
struct hfs_find_data fd;
335335
struct hfsplus_fork_raw fork;
336-
struct list_head *pos;
337336
int err, off;
338337
u16 type;
339338

@@ -392,16 +391,6 @@ int hfsplus_delete_cat(u32 cnid, struct inode *dir, const struct qstr *str)
392391
hfsplus_free_fork(sb, cnid, &fork, HFSPLUS_TYPE_RSRC);
393392
}
394393

395-
/* we only need to take spinlock for exclusion with ->release() */
396-
spin_lock(&HFSPLUS_I(dir)->open_dir_lock);
397-
list_for_each(pos, &HFSPLUS_I(dir)->open_dir_list) {
398-
struct hfsplus_readdir_data *rd =
399-
list_entry(pos, struct hfsplus_readdir_data, list);
400-
if (fd.tree->keycmp(fd.search_key, (void *)&rd->key) < 0)
401-
rd->file->f_pos--;
402-
}
403-
spin_unlock(&HFSPLUS_I(dir)->open_dir_lock);
404-
405394
err = hfs_brec_remove(&fd);
406395
if (err)
407396
goto out;

0 commit comments

Comments
 (0)