Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

hfsplus: Verify inode mode when loading from disk

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>

authored by

Tetsuo Handa and committed by
Viacheslav Dubeyko
005d4b0d ed490f36

+28 -4
+28 -4
fs/hfsplus/inode.c
··· 180 180 .d_compare = hfsplus_compare_dentry, 181 181 }; 182 182 183 - static void hfsplus_get_perms(struct inode *inode, 184 - struct hfsplus_perm *perms, int dir) 183 + static int hfsplus_get_perms(struct inode *inode, 184 + struct hfsplus_perm *perms, int dir) 185 185 { 186 186 struct hfsplus_sb_info *sbi = HFSPLUS_SB(inode->i_sb); 187 187 u16 mode; 188 188 189 189 mode = be16_to_cpu(perms->mode); 190 + if (dir) { 191 + if (mode && !S_ISDIR(mode)) 192 + goto bad_type; 193 + } else if (mode) { 194 + switch (mode & S_IFMT) { 195 + case S_IFREG: 196 + case S_IFLNK: 197 + case S_IFCHR: 198 + case S_IFBLK: 199 + case S_IFIFO: 200 + case S_IFSOCK: 201 + break; 202 + default: 203 + goto bad_type; 204 + } 205 + } 190 206 191 207 i_uid_write(inode, be32_to_cpu(perms->owner)); 192 208 if ((test_bit(HFSPLUS_SB_UID, &sbi->flags)) || (!i_uid_read(inode) && !mode)) ··· 228 212 inode->i_flags |= S_APPEND; 229 213 else 230 214 inode->i_flags &= ~S_APPEND; 215 + return 0; 216 + bad_type: 217 + pr_err("invalid file type 0%04o for inode %lu\n", mode, inode->i_ino); 218 + return -EIO; 231 219 } 232 220 233 221 static int hfsplus_file_open(struct inode *inode, struct file *file) ··· 536 516 } 537 517 hfs_bnode_read(fd->bnode, &entry, fd->entryoffset, 538 518 sizeof(struct hfsplus_cat_folder)); 539 - hfsplus_get_perms(inode, &folder->permissions, 1); 519 + res = hfsplus_get_perms(inode, &folder->permissions, 1); 520 + if (res) 521 + goto out; 540 522 set_nlink(inode, 1); 541 523 inode->i_size = 2 + be32_to_cpu(folder->valence); 542 524 inode_set_atime_to_ts(inode, hfsp_mt2ut(folder->access_date)); ··· 567 545 568 546 hfsplus_inode_read_fork(inode, HFSPLUS_IS_RSRC(inode) ? 569 547 &file->rsrc_fork : &file->data_fork); 570 - hfsplus_get_perms(inode, &file->permissions, 0); 548 + res = hfsplus_get_perms(inode, &file->permissions, 0); 549 + if (res) 550 + goto out; 571 551 set_nlink(inode, 1); 572 552 if (S_ISREG(inode->i_mode)) { 573 553 if (file->permissions.dev)