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 */


Reply via email to