[PATCH v8 3/6] tracefs: Check file permission even if user has CAP_DAC_OVERRIDE

Masami Hiramatsu (Google) posted 6 patches 8 hours ago
[PATCH v8 3/6] tracefs: Check file permission even if user has CAP_DAC_OVERRIDE
Posted by Masami Hiramatsu (Google) 8 hours ago
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 */