From: Masami Hiramatsu (Google) <[email protected]> Strictly checking the file read/write permission even if the owner has CAP_DAC_OVERRIDE on tracefs as same as sysfs. Tracefs is a pseudo filesystem, just like sysfs, so any file that the system defines as unwritable should actually be unwritable by anyone.
Signed-off-by: Masami Hiramatsu (Google) <[email protected]> --- fs/tracefs/event_inode.c | 2 ++ fs/tracefs/inode.c | 36 +++++++++++++++++++++++++++++++++--- fs/tracefs/internal.h | 3 +++ 3 files changed, 38 insertions(+), 3 deletions(-) diff --git a/fs/tracefs/event_inode.c b/fs/tracefs/event_inode.c index 61cbdafa2411..65e8be761e79 100644 --- a/fs/tracefs/event_inode.c +++ b/fs/tracefs/event_inode.c @@ -233,10 +233,12 @@ static int eventfs_set_attr(struct mnt_idmap *idmap, struct dentry *dentry, static const struct inode_operations eventfs_dir_inode_operations = { .lookup = eventfs_root_lookup, .setattr = eventfs_set_attr, + .permission = tracefs_permission, }; static const struct inode_operations eventfs_file_inode_operations = { .setattr = eventfs_set_attr, + .permission = tracefs_permission, }; static const struct file_operations eventfs_file_operations = { diff --git a/fs/tracefs/inode.c b/fs/tracefs/inode.c index d9d8932a7b9c..eb1ddc0cc13a 100644 --- a/fs/tracefs/inode.c +++ b/fs/tracefs/inode.c @@ -212,10 +212,40 @@ static void set_tracefs_inode_owner(struct inode *inode) inode->i_gid = gid; } -static int tracefs_permission(struct mnt_idmap *idmap, - struct inode *inode, int mask) +int tracefs_permission(struct mnt_idmap *idmap, + struct inode *inode, int mask) { - set_tracefs_inode_owner(inode); + struct tracefs_inode *ti = get_tracefs(inode); + const struct file_operations *fops; + + if (!(ti->flags & TRACEFS_EVENT_INODE)) + set_tracefs_inode_owner(inode); + + /* + * Like sysfs, file permission checks are performed even for superuser + * with CAP_DAC_OVERRIDE. See the KERNFS_ROOT_EXTRA_OPEN_PERM_CHECK + * definition in linux/kernfs.h. + */ + if (mask & MAY_OPEN) { + fops = inode->i_fop; + + if (mask & MAY_WRITE) { + if (!(inode->i_mode & 0222)) + return -EACCES; + if (!fops || (!fops->write && !fops->write_iter && + !fops->mmap)) + return -EACCES; + } + + if (mask & MAY_READ) { + if (!(inode->i_mode & 0444)) + return -EACCES; + if (!fops || (!fops->read && !fops->read_iter && + !fops->mmap && !fops->splice_read)) + return -EACCES; + } + } + return generic_permission(idmap, inode, mask); } diff --git a/fs/tracefs/internal.h b/fs/tracefs/internal.h index d83c2a25f288..1e49ba445ba3 100644 --- a/fs/tracefs/internal.h +++ b/fs/tracefs/internal.h @@ -76,4 +76,7 @@ struct inode *tracefs_get_inode(struct super_block *sb); void eventfs_remount(struct tracefs_inode *ti, bool update_uid, bool update_gid); void eventfs_d_release(struct dentry *dentry); +int tracefs_permission(struct mnt_idmap *idmap, + struct inode *inode, int mask); + #endif /* _TRACEFS_INTERNAL_H */
