[PATCH 6/7] tomoyo: Convert from sb_mount to granular mount hooks

Tetsuo Handa penguin-kernel at I-love.SAKURA.ne.jp
Tue Mar 24 06:12:08 UTC 2026


On 2026/03/24 4:31, Song Liu wrote:
>> Then, how can LSM modules know that how the requested filesystem resolves
>> the dev_name argument, without embedding filesystem specific resolution
>> logic into individual LSM module?
> 
> IIUC, if an LSM cares about the dev_name of a new mount, it will have to look
> into each individual filesystem. We can add a LSM hook for the filesystems to
> call. But this will require changes to individual filesystem code. OTOH,
> dev_name can probably bridge the gap as we change filesystems.
> 
> Would this work?

I guess something like untested diff shown below would work.

 block/bdev.c               |   26 ++++++++++++++------------
 fs/fs_context.c            |    4 ++++
 fs/namespace.c             |   10 ++++++----
 fs/super.c                 |    2 +-
 include/linux/blkdev.h     |   12 +++++++++++-
 include/linux/fs_context.h |    1 +
 security/tomoyo/mount.c    |   26 ++------------------------
 security/tomoyo/tomoyo.c   |    2 +-
 8 files changed, 40 insertions(+), 43 deletions(-)

diff --git a/block/bdev.c b/block/bdev.c
index ed022f8c48c7..35707a6144fa 100644
--- a/block/bdev.c
+++ b/block/bdev.c
@@ -1199,44 +1199,46 @@ void bdev_fput(struct file *bdev_file)
 EXPORT_SYMBOL(bdev_fput);
 
 /**
- * lookup_bdev() - Look up a struct block_device by name.
+ * lookup_bdev_path() - Look up a struct block_device by name.
  * @pathname: Name of the block device in the filesystem.
  * @dev: Pointer to the block device's dev_t, if found.
+ * @path: Pointer to the block device's path, if found.
  *
- * Lookup the block device's dev_t at @pathname in the current
- * namespace if possible and return it in @dev.
+ * Lookup the block device's dev_t and path at @pathname in the current
+ * namespace if possible and return these in @dev and @path
  *
  * Context: May sleep.
  * Return: 0 if succeeded, negative errno otherwise.
+ * Caller must call path_put(@path) if this function returned 0.
  */
-int lookup_bdev(const char *pathname, dev_t *dev)
+int lookup_bdev_path(const char *pathname, dev_t *dev, struct path *path)
 {
 	struct inode *inode;
-	struct path path;
 	int error;
 
 	if (!pathname || !*pathname)
 		return -EINVAL;
 
-	error = kern_path(pathname, LOOKUP_FOLLOW, &path);
+	error = kern_path(pathname, LOOKUP_FOLLOW, path);
 	if (error)
 		return error;
 
-	inode = d_backing_inode(path.dentry);
+	inode = d_backing_inode(path->dentry);
 	error = -ENOTBLK;
 	if (!S_ISBLK(inode->i_mode))
 		goto out_path_put;
 	error = -EACCES;
-	if (!may_open_dev(&path))
+	if (!may_open_dev(path))
 		goto out_path_put;
-
 	*dev = inode->i_rdev;
-	error = 0;
+	return 0;
 out_path_put:
-	path_put(&path);
+	path_put(path);
+	path->dentry = NULL;
+	path->mnt = NULL;
 	return error;
 }
-EXPORT_SYMBOL(lookup_bdev);
+EXPORT_SYMBOL(lookup_bdev_path);
 
 /**
  * bdev_mark_dead - mark a block device as dead
diff --git a/fs/fs_context.c b/fs/fs_context.c
index a37b0a093505..e5294f48eb32 100644
--- a/fs/fs_context.c
+++ b/fs/fs_context.c
@@ -377,6 +377,8 @@ struct fs_context *vfs_dup_fs_context(struct fs_context *src_fc)
 	fc->fs_private	= NULL;
 	fc->s_fs_info	= NULL;
 	fc->source	= NULL;
+	fc->source_path.dentry = NULL;
+	fc->source_path.mnt = NULL;
 	fc->security	= NULL;
 	get_filesystem(fc->fs_type);
 	get_net(fc->net_ns);
@@ -504,6 +506,8 @@ void put_fs_context(struct fs_context *fc)
 	put_cred(fc->cred);
 	put_fc_log(fc);
 	put_filesystem(fc->fs_type);
+	if (fc->source_path.dentry)
+		path_put(&fc->source_path);
 	kfree(fc->source);
 	kfree(fc);
 }
diff --git a/fs/namespace.c b/fs/namespace.c
index ba5baccdde67..621b8205a0af 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -3777,7 +3777,7 @@ static bool mount_too_revealing(const struct super_block *sb, int *new_mnt_flags
  * be added to the namespace tree.
  */
 static int do_new_mount_fc(struct fs_context *fc, const struct path *mountpoint,
-			   unsigned int mnt_flags)
+			   unsigned int mnt_flags, void *data, unsigned long flags)
 {
 	struct super_block *sb;
 	struct vfsmount *mnt __free(mntput) = fc_mount(fc);
@@ -3786,6 +3786,10 @@ static int do_new_mount_fc(struct fs_context *fc, const struct path *mountpoint,
 	if (IS_ERR(mnt))
 		return PTR_ERR(mnt);
 
+	error = security_mount_new(fc, mountpoint, mnt_flags, flags, data);
+	if (error)
+		return error;
+
 	sb = fc->root->d_sb;
 	error = security_sb_kern_mount(sb);
 	if (unlikely(error))
@@ -3857,9 +3861,7 @@ static int do_new_mount(const struct path *path, const char *fstype,
 		err = -EPERM;
 
 	if (!err)
-		err = security_mount_new(fc, path, mnt_flags, flags, data);
-	if (!err)
-		err = do_new_mount_fc(fc, path, mnt_flags);
+		err = do_new_mount_fc(fc, path, mnt_flags, data, flags);
 
 	put_fs_context(fc);
 	return err;
diff --git a/fs/super.c b/fs/super.c
index 378e81efe643..588f207f26ae 100644
--- a/fs/super.c
+++ b/fs/super.c
@@ -1670,7 +1670,7 @@ int get_tree_bdev_flags(struct fs_context *fc,
 	if (!fc->source)
 		return invalf(fc, "No source specified");
 
-	error = lookup_bdev(fc->source, &dev);
+	error = lookup_bdev_path(fc->source, &dev, &fc->source_path);
 	if (error) {
 		if (!(flags & GET_TREE_BDEV_QUIET_LOOKUP))
 			errorf(fc, "%s: Can't lookup blockdev", fc->source);
diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h
index d463b9b5a0a5..c38d538f2a07 100644
--- a/include/linux/blkdev.h
+++ b/include/linux/blkdev.h
@@ -1723,7 +1723,17 @@ static inline void bio_end_io_acct(struct bio *bio, unsigned long start_time)
 int bdev_validate_blocksize(struct block_device *bdev, int block_size);
 int set_blocksize(struct file *file, int size);
 
-int lookup_bdev(const char *pathname, dev_t *dev);
+int lookup_bdev_path(const char *pathname, dev_t *dev, struct path *path);
+static inline int lookup_bdev(const char *pathname, dev_t *dev)
+{
+	struct path path = {};
+	int ret = lookup_bdev_path(pathname, dev, &path);
+
+	if (!ret)
+		path_put(&path);
+	return ret;
+}
+
 
 void blkdev_show(struct seq_file *seqf, off_t offset);
 
diff --git a/include/linux/fs_context.h b/include/linux/fs_context.h
index 0d6c8a6d7be2..0dfa6b6fc256 100644
--- a/include/linux/fs_context.h
+++ b/include/linux/fs_context.h
@@ -99,6 +99,7 @@ struct fs_context {
 	const struct cred	*cred;		/* The mounter's credentials */
 	struct p_log		log;		/* Logging buffer */
 	const char		*source;	/* The source name (eg. dev path) */
+	struct path		source_path;    /* Fields are NULL unless resolved from the source name. */
 	void			*security;	/* LSM options */
 	void			*s_fs_info;	/* Proposed s_fs_info */
 	unsigned int		sb_flags;	/* Proposed superblock flags (SB_*) */
diff --git a/security/tomoyo/mount.c b/security/tomoyo/mount.c
index 82ffe7d02814..3a384b698557 100644
--- a/security/tomoyo/mount.c
+++ b/security/tomoyo/mount.c
@@ -84,7 +84,6 @@ static int tomoyo_mount_acl(struct tomoyo_request_info *r,
 	__must_hold_shared(&tomoyo_ss)
 {
 	struct tomoyo_obj_info obj = { };
-	struct file_system_type *fstype = NULL;
 	const char *requested_type = NULL;
 	const char *requested_dir_name = NULL;
 	const char *requested_dev_name = NULL;
@@ -124,32 +123,16 @@ static int tomoyo_mount_acl(struct tomoyo_request_info *r,
 	} else if (type == tomoyo_mounts[TOMOYO_MOUNT_BIND] ||
 		   type == tomoyo_mounts[TOMOYO_MOUNT_MOVE]) {
 		need_dev = -1; /* dev_name is a directory */
-	} else {
-		fstype = get_fs_type(type);
-		if (!fstype) {
-			error = -ENODEV;
-			goto out;
-		}
-		if (fstype->fs_flags & FS_REQUIRES_DEV)
-			/* dev_name is a block device file. */
-			need_dev = 1;
+	} else if (dev_path) {
+		need_dev = 1; /* dev_name is a block device file. */
 	}
 	if (need_dev) {
 		if (dev_path) {
 			/* Use pre-resolved path to avoid TOCTOU issues. */
 			obj.path1 = *dev_path;
-			path_get(&obj.path1);
 		} else if (!dev_name) {
 			error = -ENOENT;
 			goto out;
-		} else {
-			struct path path;
-
-			if (kern_path(dev_name, LOOKUP_FOLLOW, &path)) {
-				error = -ENOENT;
-				goto out;
-			}
-			obj.path1 = path;
 		}
 		requested_dev_name = tomoyo_realpath_from_path(&obj.path1);
 		if (!requested_dev_name) {
@@ -181,12 +164,7 @@ static int tomoyo_mount_acl(struct tomoyo_request_info *r,
  out:
 	kfree(requested_dev_name);
 	kfree(requested_dir_name);
-	if (fstype)
-		put_filesystem(fstype);
 	kfree(requested_type);
-	/* Drop refcount obtained by kern_path() or path_get(). */
-	if (obj.path1.dentry)
-		path_put(&obj.path1);
 	return error;
 }
 
diff --git a/security/tomoyo/tomoyo.c b/security/tomoyo/tomoyo.c
index ac84e1f03d5e..6235e527cc20 100644
--- a/security/tomoyo/tomoyo.c
+++ b/security/tomoyo/tomoyo.c
@@ -413,7 +413,7 @@ static int tomoyo_mount_new(struct fs_context *fc, const struct path *mp,
 {
 	/* Use original MS_* flags for policy matching */
 	return tomoyo_mount_permission(fc->source, mp, fc->fs_type->name,
-				       flags, NULL);
+				       flags, &fc->source_path);
 }
 
 static int tomoyo_mount_remount(struct fs_context *fc, const struct path *mp,



More information about the Linux-security-module-archive mailing list