Skip to content

Commit edfb2e6

Browse files
Tetsuo Handagregkh
authored andcommitted
hfsplus: Verify inode mode when loading from disk
[ Upstream commit 005d4b0d33f6b4a23d382b7930f7a96b95b01f39 ] syzbot is reporting that S_IFMT bits of inode->i_mode can become bogus when the S_IFMT bits of the 16bits "mode" field loaded from disk are corrupted. According to [1], the permissions field was treated as reserved in Mac OS 8 and 9. According to [2], the reserved field was explicitly initialized with 0, and that field must remain 0 as long as reserved. Therefore, when the "mode" field is not 0 (i.e. no longer reserved), the file must be S_IFDIR if dir == 1, and the file must be one of S_IFREG/S_IFLNK/S_IFCHR/ S_IFBLK/S_IFIFO/S_IFSOCK if dir == 0. Reported-by: syzbot <syzbot+895c23f6917da440ed0d@syzkaller.appspotmail.com> Closes: https://syzkaller.appspot.com/bug?extid=895c23f6917da440ed0d Link: https://developer.apple.com/library/archive/technotes/tn/tn1150.html#HFSPlusPermissions [1] Link: https://developer.apple.com/library/archive/technotes/tn/tn1150.html#ReservedAndPadFields [2] Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> Reviewed-by: Viacheslav Dubeyko <slava@dubeyko.com> Signed-off-by: Viacheslav Dubeyko <slava@dubeyko.com> Link: https://lore.kernel.org/r/04ded9f9-73fb-496c-bfa5-89c4f5d1d7bb@I-love.SAKURA.ne.jp Signed-off-by: Viacheslav Dubeyko <slava@dubeyko.com> Signed-off-by: Sasha Levin <sashal@kernel.org>
1 parent 457f795 commit edfb2e6

File tree

1 file changed

+28
-4
lines changed

1 file changed

+28
-4
lines changed

fs/hfsplus/inode.c

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -178,13 +178,29 @@ const struct dentry_operations hfsplus_dentry_operations = {
178178
.d_compare = hfsplus_compare_dentry,
179179
};
180180

181-
static void hfsplus_get_perms(struct inode *inode,
182-
struct hfsplus_perm *perms, int dir)
181+
static int hfsplus_get_perms(struct inode *inode,
182+
struct hfsplus_perm *perms, int dir)
183183
{
184184
struct hfsplus_sb_info *sbi = HFSPLUS_SB(inode->i_sb);
185185
u16 mode;
186186

187187
mode = be16_to_cpu(perms->mode);
188+
if (dir) {
189+
if (mode && !S_ISDIR(mode))
190+
goto bad_type;
191+
} else if (mode) {
192+
switch (mode & S_IFMT) {
193+
case S_IFREG:
194+
case S_IFLNK:
195+
case S_IFCHR:
196+
case S_IFBLK:
197+
case S_IFIFO:
198+
case S_IFSOCK:
199+
break;
200+
default:
201+
goto bad_type;
202+
}
203+
}
188204

189205
i_uid_write(inode, be32_to_cpu(perms->owner));
190206
if ((test_bit(HFSPLUS_SB_UID, &sbi->flags)) || (!i_uid_read(inode) && !mode))
@@ -210,6 +226,10 @@ static void hfsplus_get_perms(struct inode *inode,
210226
inode->i_flags |= S_APPEND;
211227
else
212228
inode->i_flags &= ~S_APPEND;
229+
return 0;
230+
bad_type:
231+
pr_err("invalid file type 0%04o for inode %lu\n", mode, inode->i_ino);
232+
return -EIO;
213233
}
214234

215235
static int hfsplus_file_open(struct inode *inode, struct file *file)
@@ -514,7 +534,9 @@ int hfsplus_cat_read_inode(struct inode *inode, struct hfs_find_data *fd)
514534
}
515535
hfs_bnode_read(fd->bnode, &entry, fd->entryoffset,
516536
sizeof(struct hfsplus_cat_folder));
517-
hfsplus_get_perms(inode, &folder->permissions, 1);
537+
res = hfsplus_get_perms(inode, &folder->permissions, 1);
538+
if (res)
539+
goto out;
518540
set_nlink(inode, 1);
519541
inode->i_size = 2 + be32_to_cpu(folder->valence);
520542
inode_set_atime_to_ts(inode, hfsp_mt2ut(folder->access_date));
@@ -543,7 +565,9 @@ int hfsplus_cat_read_inode(struct inode *inode, struct hfs_find_data *fd)
543565

544566
hfsplus_inode_read_fork(inode, HFSPLUS_IS_RSRC(inode) ?
545567
&file->rsrc_fork : &file->data_fork);
546-
hfsplus_get_perms(inode, &file->permissions, 0);
568+
res = hfsplus_get_perms(inode, &file->permissions, 0);
569+
if (res)
570+
goto out;
547571
set_nlink(inode, 1);
548572
if (S_ISREG(inode->i_mode)) {
549573
if (file->permissions.dev)

0 commit comments

Comments
 (0)