mirror of
https://github.com/torvalds/linux.git
synced 2025-11-30 23:16:01 +07:00
bfs: Reconstruct file type 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 32bits "mode" field loaded from disk are corrupted or when the 32bits "attributes" field loaded from disk are corrupted. A documentation says that BFS uses only lower 9 bits of the "mode" field. But I can't find an explicit explanation that the unused upper 23 bits (especially, the S_IFMT bits) are initialized with 0. Therefore, ignore the S_IFMT bits of the "mode" field loaded from disk. Also, verify that the value of the "attributes" field loaded from disk is either BFS_VREG or BFS_VDIR (because BFS supports only regular files and the root directory). Reported-by: syzbot+895c23f6917da440ed0d@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=895c23f6917da440ed0d Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> Link: https://patch.msgid.link/fabce673-d5b9-4038-8287-0fd65d80203b@I-love.SAKURA.ne.jp Reviewed-by: Tigran Aivazian <aivazian.tigran@gmail.com> Signed-off-by: Christian Brauner <brauner@kernel.org>
This commit is contained in:
committed by
Christian Brauner
parent
330e2c5148
commit
34ab4c7558
@@ -61,7 +61,19 @@ struct inode *bfs_iget(struct super_block *sb, unsigned long ino)
|
||||
off = (ino - BFS_ROOT_INO) % BFS_INODES_PER_BLOCK;
|
||||
di = (struct bfs_inode *)bh->b_data + off;
|
||||
|
||||
inode->i_mode = 0x0000FFFF & le32_to_cpu(di->i_mode);
|
||||
/*
|
||||
* https://martin.hinner.info/fs/bfs/bfs-structure.html explains that
|
||||
* BFS in SCO UnixWare environment used only lower 9 bits of di->i_mode
|
||||
* value. This means that, although bfs_write_inode() saves whole
|
||||
* inode->i_mode bits (which include S_IFMT bits and S_IS{UID,GID,VTX}
|
||||
* bits), middle 7 bits of di->i_mode value can be garbage when these
|
||||
* bits were not saved by bfs_write_inode().
|
||||
* Since we can't tell whether middle 7 bits are garbage, use only
|
||||
* lower 12 bits (i.e. tolerate S_IS{UID,GID,VTX} bits possibly being
|
||||
* garbage) and reconstruct S_IFMT bits for Linux environment from
|
||||
* di->i_vtype value.
|
||||
*/
|
||||
inode->i_mode = 0x00000FFF & le32_to_cpu(di->i_mode);
|
||||
if (le32_to_cpu(di->i_vtype) == BFS_VDIR) {
|
||||
inode->i_mode |= S_IFDIR;
|
||||
inode->i_op = &bfs_dir_inops;
|
||||
@@ -71,6 +83,11 @@ struct inode *bfs_iget(struct super_block *sb, unsigned long ino)
|
||||
inode->i_op = &bfs_file_inops;
|
||||
inode->i_fop = &bfs_file_operations;
|
||||
inode->i_mapping->a_ops = &bfs_aops;
|
||||
} else {
|
||||
brelse(bh);
|
||||
printf("Unknown vtype=%u %s:%08lx\n",
|
||||
le32_to_cpu(di->i_vtype), inode->i_sb->s_id, ino);
|
||||
goto error;
|
||||
}
|
||||
|
||||
BFS_I(inode)->i_sblock = le32_to_cpu(di->i_sblock);
|
||||
|
||||
Reference in New Issue
Block a user