From: Masami Hiramatsu (Google) <mhiramat@kernel.org>
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) <mhiramat@kernel.org>
---
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 */